diff --git a/.gitignore b/.gitignore
index 2dec6f8c..61230e2b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,4 +11,7 @@ Gemfile.lock
.vscode
.DS_Store
tmp/
+.ruby-version
+.ruby-gemset
+*.gem
.tool-versions
diff --git a/.rubocop.yml b/.rubocop.yml
new file mode 100644
index 00000000..ecc2c16d
--- /dev/null
+++ b/.rubocop.yml
@@ -0,0 +1,26 @@
+AllCops:
+ TargetRubyVersion: 2.7
+ NewCops: enable
+ Exclude:
+ - 'lib/generators/**/*'
+ - 'tmp/**/*'
+ - 'spec/**/*'
+ - 'vendor/**/*' # added by github actions
+
+Style/Documentation:
+ Enabled: false
+
+Style/SafeNavigation:
+ Enabled: false
+
+Layout/LineLength:
+ Max: 125
+
+Lint/MissingSuper:
+ Enabled: false
+
+Metrics/MethodLength:
+ Max: 15
+
+Metrics/AbcSize:
+ Enabled: false
diff --git a/CHANGELOG.md b/CHANGELOG.md
index bca4849b..14ff8a5e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,35 +1,40 @@
+## 0.30.0 - 2023/09/16
+* 🚀 [FEATURE] Allow configuring custom array-like classes to be treated as collections when serializing. More details can be found [here](https://github.com/procore-oss/blueprinter/pull/327). Thanks to [@toddnestor](https://github.com/toddnestor).
+* 💅 [ENHANCEMENT] Reduce object allocations in fields calculations to save some memory. More details can be found [here](https://github.com/procore-oss/blueprinter/pull/327). Thanks to [@nametoolong](https://github.com/nametoolong).
+* 💅 [ENHANCEMENT] Introduce rubocop
+* 💅 [ENHANCEMENT] if/:unless procs with two arguments and invalid empty type deprecations are now removed
## 0.26.0 - 2023/08/17
* 🐛 [BREAKING] Transition to GitHub Actions from CircleCI and update to handle Ruby versions 2.7, 3.0, 3.1, 3.2. Drop support for any ruby version less than 2.7. See [#307](https://github.com/procore-oss/blueprinter/pull/307)
## 0.25.3 - 2021/03/03
-* 🐛 [BUGFIX] Fixes issue where fields and associations that are redefined by later views were not properly overwritten. See [#201](https://github.com/procore/blueprinter/pull/201) thanks to [@Berardpi](https://github.com/Berardpi).
+* 🐛 [BUGFIX] Fixes issue where fields and associations that are redefined by later views were not properly overwritten. See [#201](https://github.com/procore-oss/blueprinter/pull/201) thanks to [@Berardpi](https://github.com/Berardpi).
## 0.25.2 - 2020/11/19
-* 🚀 [FEATURE] Make deprecation behavior configurable (`:silence`, `:stderror`, `:raise`). See [#248](https://github.com/procore/blueprinter/pull/248) thanks to [@mcclayton](https://github.com/mcclayton).
+* 🚀 [FEATURE] Make deprecation behavior configurable (`:silence`, `:stderror`, `:raise`). See [#248](https://github.com/procore-oss/blueprinter/pull/248) thanks to [@mcclayton](https://github.com/mcclayton).
## 0.25.1 - 2020/08/18
-* 🐛 [BUGFIX] Raise Blueprinter::BlueprinterError if Blueprint given is not of class Blueprinter::Base. Before it just raised a generic `undefined method 'prepare'`. See [#233](https://github.com/procore/blueprinter/pull/233) thanks to [@caws](https://github.com/caws).
+* 🐛 [BUGFIX] Raise Blueprinter::BlueprinterError if Blueprint given is not of class Blueprinter::Base. Before it just raised a generic `undefined method 'prepare'`. See [#233](https://github.com/procore-oss/blueprinter/pull/233) thanks to [@caws](https://github.com/caws).
## 0.25.0 - 2020/07/06
-* 🚀 [FEATURE] Enable default `Blueprinter::Transformer`s to be set in the global configuration. [#222](https://github.com/procore/blueprinter/pull/222). Thanks to [@supremebeing7](https://github.com/supremebeing7).
+* 🚀 [FEATURE] Enable default `Blueprinter::Transformer`s to be set in the global configuration. [#222](https://github.com/procore-oss/blueprinter/pull/222). Thanks to [@supremebeing7](https://github.com/supremebeing7).
## 0.24.0 - 2020/06/22
-* 🚀 [FEATURE] Add an `options` option to associations to facilitate passing options from one blueprint to another. [#220](https://github.com/procore/blueprinter/pull/220). Thanks to [@mcclayton](https://github.com/mcclayton).
+* 🚀 [FEATURE] Add an `options` option to associations to facilitate passing options from one blueprint to another. [#220](https://github.com/procore-oss/blueprinter/pull/220). Thanks to [@mcclayton](https://github.com/mcclayton).
## 0.23.4 - 2020/04/28
-* 🚀 [FEATURE] Public class method `has_view?` on Blueprinter::Base subclasses introduced in [#213](https://github.com/procore/blueprinter/pull/213). Thanks to [@spencerneste](https://github.com/spencerneste).
+* 🚀 [FEATURE] Public class method `has_view?` on Blueprinter::Base subclasses introduced in [#213](https://github.com/procore-oss/blueprinter/pull/213). Thanks to [@spencerneste](https://github.com/spencerneste).
## 0.23.3 - 2020/04/07
-* 🐛 [BUGFIX] Fixes issue where `exclude` fields in deeply nested views were not respected. Resolved issue [207](https://github.com/procore/blueprinter/issues/207) in [#208](https://github.com/procore/blueprinter/pull/208) by [@tpltn](https://github.com/tpltn).
+* 🐛 [BUGFIX] Fixes issue where `exclude` fields in deeply nested views were not respected. Resolved issue [207](https://github.com/procore-oss/blueprinter/issues/207) in [#208](https://github.com/procore-oss/blueprinter/pull/208) by [@tpltn](https://github.com/tpltn).
## 0.23.2 - 2020/03/16
-* 🐛 [BUGFIX] Fixes issue where fields "bled" into other views due to merge side-effects. Resolved issue [205](https://github.com/procore/blueprinter/issues/205) in [#204](https://github.com/procore/blueprinter/pull/204) by [@trevorrjohn](https://github.com/trevorrjohn).
+* 🐛 [BUGFIX] Fixes issue where fields "bled" into other views due to merge side-effects. Resolved issue [205](https://github.com/procore-oss/blueprinter/issues/205) in [#204](https://github.com/procore-oss/blueprinter/pull/204) by [@trevorrjohn](https://github.com/trevorrjohn).
## 0.23.1 - 2020/03/13
-* 🐛 [BUGFIX] Fixes #172 where views would unintentionally ignore `sort_fields_by: :definition` configuration. Resolved in [#197](https://github.com/procore/blueprinter/pull/197) by [@wlkrw](https://github.com/wlkrw).
+* 🐛 [BUGFIX] Fixes #172 where views would unintentionally ignore `sort_fields_by: :definition` configuration. Resolved in [#197](https://github.com/procore-oss/blueprinter/pull/197) by [@wlkrw](https://github.com/wlkrw).
## 0.23.0 - 2020/01/31
-* 🚀 [FEATURE] Configurable default extractor introduced in [#198](https://github.com/procore/blueprinter/pull/198) by [@wlkrw](https://github.com/wlkrw). You can now set a default extractor like so:
+* 🚀 [FEATURE] Configurable default extractor introduced in [#198](https://github.com/procore-oss/blueprinter/pull/198) by [@wlkrw](https://github.com/wlkrw). You can now set a default extractor like so:
```
Blueprinter.configure do |config|
config.extractor_default = MyAutoExtractor
@@ -37,22 +42,22 @@ end
```
## 0.22.0 - 2019/12/26
-* 🚀 [FEATURE] Add rails generators. See `rails g blueprinter:blueprint --help` for usage. Introduced in [#176](https://github.com/procore/blueprinter/pull/176) by [@wlkrw](https://github.com/wlkrw).
+* 🚀 [FEATURE] Add rails generators. See `rails g blueprinter:blueprint --help` for usage. Introduced in [#176](https://github.com/procore-oss/blueprinter/pull/176) by [@wlkrw](https://github.com/wlkrw).
## 0.21.0 - 2019/12/19
-* 🚀 [FEATURE] Ability to specify `default_if` field/association option for more control on when the default value is applied. [191](https://github.com/procore/blueprinter/pull/191). Thanks to [@mcclayton](https://github.com/mcclayton).
+* 🚀 [FEATURE] Ability to specify `default_if` field/association option for more control on when the default value is applied. [191](https://github.com/procore-oss/blueprinter/pull/191). Thanks to [@mcclayton](https://github.com/mcclayton).
## 0.20.0 - 2019/10/15
-* 🚀 [FEATURE] Ability to include multiple views in a single method call with `include_views`. [184](https://github.com/procore/blueprinter/pull/184). Thanks to [@narendranvelmurugan](https://github.com/narendranvelmurugan).
+* 🚀 [FEATURE] Ability to include multiple views in a single method call with `include_views`. [184](https://github.com/procore-oss/blueprinter/pull/184). Thanks to [@narendranvelmurugan](https://github.com/narendranvelmurugan).
-* 💅 [ENHANCEMENT] Update field-level conditional settings to reflect new three-argument syntax. [183](https://github.com/procore/blueprinter/pull/183). Thanks to [@danirod](https://github.com/danirod).
+* 💅 [ENHANCEMENT] Update field-level conditional settings to reflect new three-argument syntax. [183](https://github.com/procore-oss/blueprinter/pull/183). Thanks to [@danirod](https://github.com/danirod).
-* 💅 [ENHANCEMENT] Modify Extractor access control in documentation. [182](https://github.com/procore/blueprinter/pull/182). Thanks to [@cagmz](https://github.com/cagmz).
+* 💅 [ENHANCEMENT] Modify Extractor access control in documentation. [182](https://github.com/procore-oss/blueprinter/pull/182). Thanks to [@cagmz](https://github.com/cagmz).
-* 💅 [ENHANCEMENT] Fix the Transformer example documentation. [174](https://github.com/procore/blueprinter/pull/174). Thanks to [@tjwallace](https://github.com/tjwallace).
+* 💅 [ENHANCEMENT] Fix the Transformer example documentation. [174](https://github.com/procore-oss/blueprinter/pull/174). Thanks to [@tjwallace](https://github.com/tjwallace).
## 0.19.0 - 2019/07/24
-* 🚀 [FEATURE] Added ability to specify transformers for Blueprinter views to further process the resulting hash before serialization. [#164](https://github.com/procore/blueprinter/pull/164). Thanks to [@amalarayfreshworks](https://github.com/amalarayfreshworks).
+* 🚀 [FEATURE] Added ability to specify transformers for Blueprinter views to further process the resulting hash before serialization. [#164](https://github.com/procore-oss/blueprinter/pull/164). Thanks to [@amalarayfreshworks](https://github.com/amalarayfreshworks).
## 0.18.0 - 2019/05/29
@@ -60,83 +65,83 @@ end
In order to be compliant with the the next major release, all conditional :if/:unless procs must be augmented to take in three arguments instead of two. i.e. `(obj, options)` to `(field_name, obj, options)`.
## 0.17.0 - 2019/05/23
-* 🐛 [BUGFIX] Fixing view: :identifier including non-identifier fields. [#154](https://github.com/procore/blueprinter/pull/154). Thanks to [@AllPurposeName](https://github.com/AllPurposeName).
+* 🐛 [BUGFIX] Fixing view: :identifier including non-identifier fields. [#154](https://github.com/procore-oss/blueprinter/pull/154). Thanks to [@AllPurposeName](https://github.com/AllPurposeName).
-* 💅 [ENHANCEMENT] Add ability to override :extractor option for an ::association. [#152](https://github.com/procore/blueprinter/pull/152). Thanks to [@hugopeixoto](https://github.com/hugopeixoto).
+* 💅 [ENHANCEMENT] Add ability to override :extractor option for an ::association. [#152](https://github.com/procore-oss/blueprinter/pull/152). Thanks to [@hugopeixoto](https://github.com/hugopeixoto).
## 0.16.0 - 2019/04/03
-* 🚀 [FEATURE] Add ability to exclude multiple fields inline using `excludes`. [#141](https://github.com/procore/blueprinter/pull/141). Thanks to [@pabhinaya](https://github.com/pabhinaya).
+* 🚀 [FEATURE] Add ability to exclude multiple fields inline using `excludes`. [#141](https://github.com/procore-oss/blueprinter/pull/141). Thanks to [@pabhinaya](https://github.com/pabhinaya).
## 0.15.0 - 2019/04/01
-* 🚀 [FEATURE] Add ability to pass in `datetime_format` field option as either a string representing the strftime format, or a Proc which takes in the Date or DateTime object and returns the formatted date. [#145](https://github.com/procore/blueprinter/pull/145). Thanks to [@mcclayton](https://github.com/mcclayton).
+* 🚀 [FEATURE] Add ability to pass in `datetime_format` field option as either a string representing the strftime format, or a Proc which takes in the Date or DateTime object and returns the formatted date. [#145](https://github.com/procore-oss/blueprinter/pull/145). Thanks to [@mcclayton](https://github.com/mcclayton).
## 0.14.0 - 2019/04/01
-* 🚀 [FEATURE] Added a global `datetime_format` option. [#135](https://github.com/procore/blueprinter/pull/143). Thanks to [@ritikesh](https://github.com/ritikesh).
+* 🚀 [FEATURE] Added a global `datetime_format` option. [#135](https://github.com/procore-oss/blueprinter/pull/143). Thanks to [@ritikesh](https://github.com/ritikesh).
## 0.13.2 - 2019/03/14
-* 🐛 [BUGFIX] Replacing use of rails-specific method `Hash::except` so that Blueprinter continues to work in non-Rails environments. [#140](https://github.com/procore/blueprinter/pull/140). Thanks to [@checkbutton](https://github.com/checkbutton).
+* 🐛 [BUGFIX] Replacing use of rails-specific method `Hash::except` so that Blueprinter continues to work in non-Rails environments. [#140](https://github.com/procore-oss/blueprinter/pull/140). Thanks to [@checkbutton](https://github.com/checkbutton).
## 0.13.1 - 2019/03/02
-* 💅 [MAINTENANCE | ENHANCEMENT] Cleaning up the `include_associations` section. This is not a documented/supported feature and is calling `respond_to?(:klass)` on every object passed to blueprinter. [#139](https://github.com/procore/blueprinter/pull/139). Thanks to [@ritikesh](https://github.com/ritikesh).
+* 💅 [MAINTENANCE | ENHANCEMENT] Cleaning up the `include_associations` section. This is not a documented/supported feature and is calling `respond_to?(:klass)` on every object passed to blueprinter. [#139](https://github.com/procore-oss/blueprinter/pull/139). Thanks to [@ritikesh](https://github.com/ritikesh).
## 0.13.0 - 2019/02/07
-* 🚀 [FEATURE] Added an option to render with a root key. [#135](https://github.com/procore/blueprinter/pull/135). Thanks to [@ritikesh](https://github.com/ritikesh).
-* 🚀 [FEATURE] Added an option to render with a top-level meta attribute. [#135](https://github.com/procore/blueprinter/pull/135). Thanks to [@ritikesh](https://github.com/ritikesh).
+* 🚀 [FEATURE] Added an option to render with a root key. [#135](https://github.com/procore-oss/blueprinter/pull/135). Thanks to [@ritikesh](https://github.com/ritikesh).
+* 🚀 [FEATURE] Added an option to render with a top-level meta attribute. [#135](https://github.com/procore-oss/blueprinter/pull/135). Thanks to [@ritikesh](https://github.com/ritikesh).
## 0.12.1 - 2019/01/24
-* 🐛 [BUGFIX] Fix boolean `false` values getting serialized as `null`. Please see PR [#132](https://github.com/procore/blueprinter/pull/132). Thanks to [@samsongz](https://github.com/samsongz).
+* 🐛 [BUGFIX] Fix boolean `false` values getting serialized as `null`. Please see PR [#132](https://github.com/procore-oss/blueprinter/pull/132). Thanks to [@samsongz](https://github.com/samsongz).
## 0.12.0 - 2019/01/16
-* 🚀 [FEATURE] Enables the setting of global `:field_default` and `:association_default` option value in the Blueprinter Configuration that will be used as default values for fields and associations that evaluate to nil. [#128](https://github.com/procore/blueprinter/pull/128). Thanks to [@mcclayton](https://github.com/mcclayton).
+* 🚀 [FEATURE] Enables the setting of global `:field_default` and `:association_default` option value in the Blueprinter Configuration that will be used as default values for fields and associations that evaluate to nil. [#128](https://github.com/procore-oss/blueprinter/pull/128). Thanks to [@mcclayton](https://github.com/mcclayton).
## 0.11.0 - 2019/01/15
-* 🚀 [FEATURE] Enables the setting of a global `:if`/`:unless` proc in the Blueprinter Configuration that will be used to evaluate the conditional render of all fields. [#127](https://github.com/procore/blueprinter/pull/127). Thanks to [@mcclayton](https://github.com/mcclayton).
+* 🚀 [FEATURE] Enables the setting of a global `:if`/`:unless` proc in the Blueprinter Configuration that will be used to evaluate the conditional render of all fields. [#127](https://github.com/procore-oss/blueprinter/pull/127). Thanks to [@mcclayton](https://github.com/mcclayton).
## 0.10.0 - 2018/12/20
-* 🚀 [FEATURE] Association Blueprints can be dynamically evaluated using a proc. [#122](https://github.com/procore/blueprinter/pull/122). Thanks to [@ritikesh](https://github.com/ritikesh).
+* 🚀 [FEATURE] Association Blueprints can be dynamically evaluated using a proc. [#122](https://github.com/procore-oss/blueprinter/pull/122). Thanks to [@ritikesh](https://github.com/ritikesh).
## 0.9.0 - 2018/11/29
-* 🚀 [FEATURE] Added a `render_as_json` API. Similar to `render_as_hash` but returns a JSONified hash. Please see pr [#119](https://github.com/procore/blueprinter/pull/119). Thanks to [@ritikesh](https://github.com/ritikesh).
-* 🚀 [FEATURE] Sorting of fields in the response is now configurable to sort by definition or by name(asc only). Please see pr [#119](https://github.com/procore/blueprinter/pull/119). Thanks to [@ritikesh](https://github.com/ritikesh).
-* 💅 [ENHANCEMENT] Updated readme for above features and some existing undocumented features like `exclude fields`, `render_as_hash`. Please see pr [#119](https://github.com/procore/blueprinter/pull/119). Thanks to [@ritikesh](https://github.com/ritikesh).
+* 🚀 [FEATURE] Added a `render_as_json` API. Similar to `render_as_hash` but returns a JSONified hash. Please see pr [#119](https://github.com/procore-oss/blueprinter/pull/119). Thanks to [@ritikesh](https://github.com/ritikesh).
+* 🚀 [FEATURE] Sorting of fields in the response is now configurable to sort by definition or by name(asc only). Please see pr [#119](https://github.com/procore-oss/blueprinter/pull/119). Thanks to [@ritikesh](https://github.com/ritikesh).
+* 💅 [ENHANCEMENT] Updated readme for above features and some existing undocumented features like `exclude fields`, `render_as_hash`. Please see pr [#119](https://github.com/procore-oss/blueprinter/pull/119). Thanks to [@ritikesh](https://github.com/ritikesh).
## 0.8.0 - 2018/11/19
-* 🚀 [FEATURE] Extend Support for other JSON encoders like yajl-ruby. Please see pr [#118](https://github.com/procore/blueprinter/pull/118). Thanks to [@ritikesh](https://github.com/ritikesh).
-* 🐛 [BUGFIX] Do not raise error on null date with `date_format` option. Please see pr [#117](https://github.com/procore/blueprinter/pull/117). Thanks to [@tpltn](https://github.com/tpltn).
-* 🚀 [FEATURE] Add `default` option to `field`s which will be used as the serialized value instead of `null` when the field evaluates to null. Please see pr [#115](https://github.com/procore/blueprinter/pull/115). Thanks to [@mcclayton](https://github.com/mcclayton).
-* 🐛 [BUGFIX] Made Base.associations completely private since they are not used outside of the Blueprinter base. Please see pr [#112](https://github.com/procore/blueprinter/pull/112). Thanks to [@philipqnguyen](https://github.com/philipqnguyen).
-* 🐛 [BUGFIX] Fix issue where entire Blueprinter module was marked api private. Please see pr [#111](https://github.com/procore/blueprinter/pull/111). Thanks to [@philipqnguyen](https://github.com/philipqnguyen).
-* 🚀 [FEATURE] Allow identifiers to be defined with a block. Please see pr [#110](https://github.com/procore/blueprinter/pull/110). Thanks to [@hugopeixoto](https://github.com/hugopeixoto).
-* 💅 [ENHANCEMENT] Update docs regarding the args yielded to blocks. Please see pr [#108](https://github.com/procore/blueprinter/pull/108). Thanks to [@philipqnguyen](https://github.com/philipqnguyen).
-* 💅 [ENHANCEMENT] Use `field` method in fields. Please see pr [#107](https://github.com/procore/blueprinter/pull/107). Thanks to [@hugopeixoto](https://github.com/hugopeixoto).
+* 🚀 [FEATURE] Extend Support for other JSON encoders like yajl-ruby. Please see pr [#118](https://github.com/procore-oss/blueprinter/pull/118). Thanks to [@ritikesh](https://github.com/ritikesh).
+* 🐛 [BUGFIX] Do not raise error on null date with `date_format` option. Please see pr [#117](https://github.com/procore-oss/blueprinter/pull/117). Thanks to [@tpltn](https://github.com/tpltn).
+* 🚀 [FEATURE] Add `default` option to `field`s which will be used as the serialized value instead of `null` when the field evaluates to null. Please see pr [#115](https://github.com/procore-oss/blueprinter/pull/115). Thanks to [@mcclayton](https://github.com/mcclayton).
+* 🐛 [BUGFIX] Made Base.associations completely private since they are not used outside of the Blueprinter base. Please see pr [#112](https://github.com/procore-oss/blueprinter/pull/112). Thanks to [@philipqnguyen](https://github.com/philipqnguyen).
+* 🐛 [BUGFIX] Fix issue where entire Blueprinter module was marked api private. Please see pr [#111](https://github.com/procore-oss/blueprinter/pull/111). Thanks to [@philipqnguyen](https://github.com/philipqnguyen).
+* 🚀 [FEATURE] Allow identifiers to be defined with a block. Please see pr [#110](https://github.com/procore-oss/blueprinter/pull/110). Thanks to [@hugopeixoto](https://github.com/hugopeixoto).
+* 💅 [ENHANCEMENT] Update docs regarding the args yielded to blocks. Please see pr [#108](https://github.com/procore-oss/blueprinter/pull/108). Thanks to [@philipqnguyen](https://github.com/philipqnguyen).
+* 💅 [ENHANCEMENT] Use `field` method in fields. Please see pr [#107](https://github.com/procore-oss/blueprinter/pull/107). Thanks to [@hugopeixoto](https://github.com/hugopeixoto).
## 0.7.0 - 2018/10/17
-* [FEATURE] Allow associations to be defined with a block. Please see pr [#106](https://github.com/procore/blueprinter/pull/106). Thanks to [@hugopeixoto](https://github.com/hugopeixoto).
-* [FEATURE] Inherit view definition when using inheritance. Please see pr [#105](https://github.com/procore/blueprinter/pull/105). Thanks to [@hugopeixoto](https://github.com/hugopeixoto).
+* [FEATURE] Allow associations to be defined with a block. Please see pr [#106](https://github.com/procore-oss/blueprinter/pull/106). Thanks to [@hugopeixoto](https://github.com/hugopeixoto).
+* [FEATURE] Inherit view definition when using inheritance. Please see pr [#105](https://github.com/procore-oss/blueprinter/pull/105). Thanks to [@hugopeixoto](https://github.com/hugopeixoto).
## 0.6.0 - 2018/06/05
* 🚀 [FEATURE] Add `date_time` format as an option to `field`. Please see pr #68. Thanks to [@njbbaer](https://github.com/njbbaer).
-* 🚀 [FEATURE] Add conditional field support `:unless` and `:if` as an option to `field`. Please see pr [#86](https://github.com/procore/blueprinter/pull/86). Thanks to [@ojab](https://github.com/ojab).
-* 🐛 [BUGFIX] Fix case where miscellaneous options were not being passed through the `AutoExtractor`. See pr [#83](https://github.com/procore/blueprinter/pull/83).
+* 🚀 [FEATURE] Add conditional field support `:unless` and `:if` as an option to `field`. Please see pr [#86](https://github.com/procore-oss/blueprinter/pull/86). Thanks to [@ojab](https://github.com/ojab).
+* 🐛 [BUGFIX] Fix case where miscellaneous options were not being passed through the `AutoExtractor`. See pr [#83](https://github.com/procore-oss/blueprinter/pull/83).
## 0.5.0 - 2018/05/15
* 🚀 [FEATURE] Add `default` option to `association` which will be used as the serialized value instead of `null` when the association evaluates to null.
-See PR [#78](https://github.com/procore/blueprinter/pull/78) by [@vinaya-procore](https://github.com/vinaya-procore).
+See PR [#78](https://github.com/procore-oss/blueprinter/pull/78) by [@vinaya-procore](https://github.com/vinaya-procore).
## 0.4.0 - 2018/05/02
* 🚀 [FEATURE] Add `render_as_hash` which will output a hash instead of
-a JSON String. See PR [#76](https://github.com/procore/blueprinter/pull/76) by [@amayer171](https://github.com/amayer171) and Issue [#73](https://github.com/procore/blueprinter/issues/73).
+a JSON String. See PR [#76](https://github.com/procore-oss/blueprinter/pull/76) by [@amayer171](https://github.com/amayer171) and Issue [#73](https://github.com/procore-oss/blueprinter/issues/73).
## 0.3.0 - 2018/04/05
@@ -153,7 +158,7 @@ field(:first_name, extractor: CustomExtractor)
```
* 💅 [ENHANCEMENT] Renamed Serializer classes to Extractor. See #72.
-* 💅 [ENHANCEMENT] Updated README. See pr [#66](https://github.com/procore/blueprinter/pull/66), [#65](https://github.com/procore/blueprinter/pull/65)
+* 💅 [ENHANCEMENT] Updated README. See pr [#66](https://github.com/procore-oss/blueprinter/pull/66), [#65](https://github.com/procore-oss/blueprinter/pull/65)
## 0.2.0 - 2018/01/22
@@ -162,8 +167,8 @@ field(:first_name, extractor: CustomExtractor)
association :comments, blueprint: CommentsBlueprint
```
-* 🐛 [BUGFIX] Remove Optimizer class. See [#61](https://github.com/procore/blueprinter/pull/61).
-* 🐛 [BUGFIX] Require associated objects to have a Blueprint, so that objects will always serialize properly. See [#60](https://github.com/procore/blueprinter/pull/60).
+* 🐛 [BUGFIX] Remove Optimizer class. See [#61](https://github.com/procore-oss/blueprinter/pull/61).
+* 🐛 [BUGFIX] Require associated objects to have a Blueprint, so that objects will always serialize properly. See [#60](https://github.com/procore-oss/blueprinter/pull/60).
## 0.1.0 - 2018/01/17
diff --git a/Gemfile b/Gemfile
index e94d938f..3e6dd5dc 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
source 'https://rubygems.org'
# Declare your gem's dependencies in blueprinter.gemspec.
@@ -5,10 +7,22 @@ source 'https://rubygems.org'
# development dependencies will be added by default to the :development group.
gemspec
+# officially supported gems for serialisation
+gem 'oj', '~> 3.13'
+gem 'yajl-ruby', '~> 1.4'
+
# Declare any dependencies that are still in development here instead of in
# your gemspec. These might include edge Rails or gems from your path or
# Git. Remember to move these dependencies to your gemspec before releasing
# your gem to rubygems.org.
-# To use a debugger
-# gem 'byebug', group: [:development, :test]
+gem 'activerecord', '>= 5.2'
+gem 'ammeter', '~> 1.1'
+gem 'factory_bot', '~> 6.2'
+gem 'pry', '~> 0.14'
+gem 'rspec', '~> 3.12'
+gem 'rspec-rails', '~> 6.0'
+gem 'rubocop', '~> 1.44'
+gem 'rubocop-rake'
+gem 'sqlite3', '~> 1.5'
+gem 'yard', '>= 0.9.34'
diff --git a/README.md b/README.md
index 7976a6a5..e208abde 100644
--- a/README.md
+++ b/README.md
@@ -33,8 +33,6 @@ Docs can be found [here](http://www.rubydoc.info/gems/blueprinter).
Basic
----
-
If you have an object you would like serialized, simply create a blueprint. Say, for example, you have a User record with the following attributes `[:uuid, :email, :first_name, :last_name, :password, :address]`.
You may define a simple blueprint like so:
@@ -63,14 +61,11 @@ And the output would look like:
"last_name": "Doe"
}
```
-
----
Collections
----
You can also pass a collection object or an array to the render method.
@@ -97,13 +92,28 @@ This will result in JSON that looks something like this:
]
```
----
+
+You can also configure other classes to be treated like collections. For example, if you are using Mongoid, you can configure it to treat `Mongoid::Criteria` objects as collections:
+
+```ruby
+Blueprinter.configure do |config|
+ config.custom_array_like_classes = [Mongoid::Criteria]
+end
+```
+
+Or if you wanted it to treat the `Set` class as a collection:
+
+```ruby
+Blueprinter.configure do |config|
+ config.custom_array_like_classes = [Set]
+end
+```
+
Renaming
----
You can rename the resulting JSON keys in both fields and associations by using the `name` option.
@@ -127,13 +137,11 @@ This will result in JSON that looks something like this:
}
```
----
Views
----
You may define different outputs by utilizing views:
@@ -174,13 +182,11 @@ Output:
}
```
----
Identifiers
----
`identifier`s are used to specify a field or method name used as an identifier. Usually, this is something like `:id`.
@@ -199,14 +205,12 @@ Blueprinter `identifier`s have a few properties that set them apart from `field`
If either of the above two developer conveniences are not desired, you can simply create your identifier fields as regular `field`s.
----
Root
----
You can also optionally pass in a root key to wrap your resulting json in:
@@ -240,13 +244,11 @@ Output:
}
```
----
Meta Attributes
----
You can additionally add meta-data to the json as well:
@@ -292,13 +294,11 @@ Output:
_NOTE:_ For meta attributes, a [root](#root) is mandatory.
----
Exclude Fields
----
You can specifically choose to exclude certain fields for specific views
@@ -355,13 +355,11 @@ class UserBlueprint < Blueprinter::Base
end
```
----
Associations
----
You may include associated objects. Say for example, a user has projects:
@@ -430,13 +428,11 @@ class DriverBlueprint < Blueprinter::Base
end
```
----
Default Association/Field Option
----
By default, an association or field that evaluates to `nil` is serialized as `nil`. A default serialized value can be specified as an option on the association or field for cases when the association/field could potentially evaluate to `nil`. You can also specify a global `field_default` or `association_default` in the Blueprinter config which will be used for all fields/associations that evaluate to nil.
@@ -462,13 +458,11 @@ class UserBlueprint < Blueprinter::Base
end
```
----
default_if
----
Sometimes, you may want certain "empty" values to pass through to the default value.
Blueprinter provides the ability to treat the following empty types as the default value (or `nil` if no default provided).
@@ -500,13 +494,11 @@ class UserBlueprint < Blueprinter::Base
end
```
----
Supporting Dynamic Blueprints For Associations
----
When defining an association, we can dynamically evaluate the blueprint. This comes in handy when adding polymorphic associations, by allowing reuse of existing blueprints.
@@ -535,13 +527,11 @@ end
_NOTE:_ `taskable.blueprint` should return a valid Blueprint class. Currently, `has_many` is not supported because of the very nature of polymorphic associations.
----
Defining A Field Directly In The Blueprint
----
You can define a field directly in the Blueprint by passing it a block. This is especially useful if the object does not already have such an attribute or method defined, and you want to define it specifically for use with the Blueprint. This is done by passing `field` a block. The block also yields the object and any options that were passed from `render`. For example:
@@ -569,13 +559,11 @@ Output:
}
```
----
Defining An Identifier Directly In The Blueprint
----
You can also pass a block to an identifier:
@@ -601,13 +589,11 @@ Output:
}
```
----
Defining An Association Directly In The Blueprint
----
You can also pass a block to an association:
@@ -645,13 +631,11 @@ Output:
}
```
----
Passing Additional Properties To #render
----
`render` takes an options hash which you can pass additional properties, allowing you to utilize those additional properties in the `field` block. For example:
@@ -679,13 +663,11 @@ Output:
}
```
----
Conditional Fields
----
Both the `field` and the global Blueprinter Configuration supports `:if` and `:unless` options that can be used to serialize fields conditionally.
@@ -710,13 +692,11 @@ end
_NOTE:_ The field-level setting overrides the global config setting (for the field) if both are set.
----
Custom Formatting for Dates and Times
----
To define a custom format for a Date or DateTime field, include the option `datetime_format`.
This global or field-level option can be either a string representing the associated `strftime` format,
@@ -774,13 +754,11 @@ Output:
_NOTE:_ The field-level setting overrides the global config setting (for the field) if both are set.
----
Transform Classes
----
Blueprinter provides the ability to specify `transform`s on views, which enable further
processing and transforming of resulting view field hashes prior to serialization.
@@ -842,13 +820,11 @@ end
**Note: Any transforms specified on a per-blueprint or per-view level will override the `default_transformers` in the configuration.**
----
Configurable Extractors
----
Blueprinter gets a given objects' values from the fields definitions using extractor classes. You can substitute your own extractor class globally or per-field.
@@ -896,13 +872,11 @@ Blueprinter.configure do |config|
end
```
----
Sorting Fields
----
By default the response sorts the keys by name. If you want the fields to be sorted in the order of definition, use the below configuration option.
@@ -932,13 +906,11 @@ Output:
}
```
----
Deprecations
----
When functionality in Blueprinter is invoked, that has been deprecated, the default behavior is to
write a deprecation notice to stderror.
@@ -959,13 +931,11 @@ Blueprinter.configure do |config|
end
```
----
render_as_hash
----
Same as `render`, returns a Ruby Hash.
@@ -984,13 +954,11 @@ Output:
}
```
----
render_as_json
----
Same as `render`, returns a Ruby Hash JSONified. This will call JSONify all keys and values.
@@ -1009,7 +977,6 @@ Output:
}
```
----
## Installation
diff --git a/Rakefile b/Rakefile
index 1ba485b5..363b14fe 100644
--- a/Rakefile
+++ b/Rakefile
@@ -1,7 +1,11 @@
+# frozen_string_literal: true
+
require 'rdoc/task'
require 'bundler/gem_tasks'
require 'rake/testtask'
require 'rspec/core/rake_task'
+require 'yard'
+require 'rubocop/rake_task'
begin
require 'bundler/setup'
@@ -21,10 +25,18 @@ RSpec::Core::RakeTask.new(:spec) do |t|
t.rspec_opts = '--pattern spec/**/*_spec.rb --warnings'
end
+RuboCop::RakeTask.new
+
+YARD::Rake::YardocTask.new do |t|
+ t.files = Dir['lib/**/*'].reject do |file|
+ file.include?('lib/generators')
+ end
+end
+
Rake::TestTask.new(:benchmarks) do |t|
t.libs << 'spec'
t.pattern = 'spec/benchmarks/**/*_test.rb'
t.verbose = false
end
-task default: :spec
+task default: %i[spec rubocop]
diff --git a/bin/test b/bin/test
deleted file mode 100755
index 2f98ca32..00000000
--- a/bin/test
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/usr/bin/env ruby
-$: << File.expand_path(File.expand_path("../../test", __FILE__))
-
-require "bundler/setup"
-require "rails/plugin/test"
diff --git a/blueprinter.gemspec b/blueprinter.gemspec
index 23a78636..01cc3f26 100644
--- a/blueprinter.gemspec
+++ b/blueprinter.gemspec
@@ -1,4 +1,6 @@
-$:.push File.expand_path('lib', __dir__)
+# frozen_string_literal: true
+
+$LOAD_PATH.push File.expand_path('lib', __dir__)
# Maintain your gem's version:
require 'blueprinter/version'
@@ -11,23 +13,14 @@ Gem::Specification.new do |s|
s.email = ['blueprinter@googlegroups.com']
s.homepage = 'https://github.com/procore-oss/blueprinter'
s.summary = 'Simple Fast Declarative Serialization Library'
- s.description = 'Blueprinter is a JSON Object Presenter for Ruby that takes business objects and breaks them down into simple hashes and serializes them to JSON. It can be used in Rails in place of other serializers (like JBuilder or ActiveModelSerializers). It is designed to be simple, direct, and performant.'
+ s.description = 'Blueprinter is a JSON Object Presenter for Ruby that takes business objects ' \
+ 'and breaks them down into simple hashes and serializes them to JSON. ' \
+ 'It can be used in Rails in place of other serializers (like JBuilder or ActiveModelSerializers). ' \
+ 'It is designed to be simple, direct, and performant.'
s.license = 'MIT'
s.files = Dir['{app,config,db,lib}/**/*', 'CHANGELOG.md', 'MIT-LICENSE', 'Rakefile', 'README.md']
s.required_ruby_version = '>= 2.7'
-
- s.add_development_dependency 'activerecord', '~> 6.1.7'
- s.add_development_dependency 'ammeter', '~> 1.1.4'
- s.add_development_dependency 'factory_bot'
- s.add_development_dependency 'nokogiri', '>= 1.8.2'
- s.add_development_dependency 'oj', '~> 3.0'
- s.add_development_dependency 'pry'
- s.add_development_dependency 'rake'
- s.add_development_dependency 'rspec', '~> 3.7'
- s.add_development_dependency 'rspec-rails', '~> 6.0'
- s.add_development_dependency 'sqlite3', '~> 1.4.2'
- s.add_development_dependency 'yajl-ruby', '~> 1.4.1'
- s.add_development_dependency 'yard', '~> 0.9.11'
+ s.metadata['rubygems_mfa_required'] = 'true'
end
diff --git a/lib/blueprinter.rb b/lib/blueprinter.rb
index e5ba580a..7c1f1e74 100644
--- a/lib/blueprinter.rb
+++ b/lib/blueprinter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative 'blueprinter/base'
module Blueprinter
diff --git a/lib/blueprinter/base.rb b/lib/blueprinter/base.rb
index 84644e96..acb4182e 100644
--- a/lib/blueprinter/base.rb
+++ b/lib/blueprinter/base.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative 'blueprinter_error'
require_relative 'configuration'
require_relative 'deprecation'
@@ -58,7 +60,7 @@ def self.identifier(method, name: method, extractor: Blueprinter.configuration.e
name,
extractor,
self,
- block: block,
+ block: block
)
end
@@ -123,7 +125,7 @@ def self.field(method, options = {}, &block)
options.fetch(:name) { method },
options.fetch(:extractor) { Blueprinter.configuration.extractor_default.new },
self,
- options.merge(block: block),
+ options.merge(block: block)
)
end
@@ -163,7 +165,7 @@ def self.association(method, options = {}, &block)
method,
options.merge(
association: true,
- extractor: options.fetch(:extractor) { AssociationExtractor.new },
+ extractor: options.fetch(:extractor) { AssociationExtractor.new }
),
&block
)
@@ -252,9 +254,7 @@ def self.render_as_json(object, options = {})
#
# @api private
def self.prepare(object, view_name:, local_options:, root: nil, meta: nil)
- unless view_collection.has_view? view_name
- raise BlueprinterError, "View '#{view_name}' is not defined"
- end
+ raise BlueprinterError, "View '#{view_name}' is not defined" unless view_collection.view? view_name
data = prepare_data(object, view_name, local_options)
prepend_root_and_meta(data, root, meta)
@@ -279,7 +279,6 @@ def self.fields(*field_names)
end
end
-
# Specify one transformer to be included for serialization.
# Takes a class which extends Blueprinter::Transformer
#
@@ -315,7 +314,6 @@ def self.transform(transformer)
current_view.add_transformer(transformer)
end
-
# Specify another view that should be mixed into the current view.
#
# @param view_name [Symbol] the view to mix into the current view.
@@ -338,7 +336,6 @@ def self.include_view(view_name)
current_view.include_view(view_name)
end
-
# Specify additional views that should be mixed into the current view.
#
# @param view_name [Array] the views to mix into the current view.
@@ -361,12 +358,10 @@ def self.include_view(view_name)
#
# @return [Array] an array of view names.
-
def self.include_views(*view_names)
current_view.include_views(view_names)
end
-
# Exclude a field that was mixed into the current view.
#
# @param field_name [Symbol] the field to exclude from the current view.
@@ -444,13 +439,13 @@ def self.view(view_name)
# end
# end
#
- # ExampleBlueprint.has_view?(:custom) => true
- # ExampleBlueprint.has_view?(:doesnt_exist) => false
+ # ExampleBlueprint.view?(:custom) => true
+ # ExampleBlueprint.view?(:doesnt_exist) => false
#
# @return [Boolean] a boolean value indicating if the view is
# supported by this Blueprint.
- def self.has_view?(view_name)
- view_collection.has_view? view_name
+ def self.view?(view_name)
+ view_collection.view? view_name
end
end
end
diff --git a/lib/blueprinter/blueprinter_error.rb b/lib/blueprinter/blueprinter_error.rb
index 7c4d281e..2428fea2 100644
--- a/lib/blueprinter/blueprinter_error.rb
+++ b/lib/blueprinter/blueprinter_error.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Blueprinter
class BlueprinterError < StandardError; end
end
diff --git a/lib/blueprinter/configuration.rb b/lib/blueprinter/configuration.rb
index abab8aff..050d07a9 100644
--- a/lib/blueprinter/configuration.rb
+++ b/lib/blueprinter/configuration.rb
@@ -1,8 +1,11 @@
+# frozen_string_literal: true
+
module Blueprinter
class Configuration
- attr_accessor :association_default, :datetime_format, :deprecations, :field_default, :generator, :if, :method, :sort_fields_by, :unless, :extractor_default, :default_transformers
+ attr_accessor :association_default, :datetime_format, :deprecations, :field_default, :generator, :if, :method,
+ :sort_fields_by, :unless, :extractor_default, :default_transformers, :custom_array_like_classes
- VALID_CALLABLES = %i(if unless).freeze
+ VALID_CALLABLES = %i[if unless].freeze
def initialize
@deprecations = :stderror
@@ -16,6 +19,15 @@ def initialize
@unless = nil
@extractor_default = AutoExtractor
@default_transformers = []
+ @custom_array_like_classes = []
+ end
+
+ def array_like_classes
+ @array_like_classes ||= [
+ Array,
+ defined?(ActiveRecord::Relation) && ActiveRecord::Relation,
+ *custom_array_like_classes
+ ].compact
end
def jsonify(blob)
diff --git a/lib/blueprinter/deprecation.rb b/lib/blueprinter/deprecation.rb
index 0c6cbd2c..ae6cdd64 100644
--- a/lib/blueprinter/deprecation.rb
+++ b/lib/blueprinter/deprecation.rb
@@ -1,9 +1,11 @@
+# frozen_string_literal: true
+
# @api private
module Blueprinter
class Deprecation
class << self
- VALID_BEHAVIORS = %i(silence stderror raise).freeze
- MESSAGE_PREFIX = "[DEPRECATION::WARNING] Blueprinter:".freeze
+ VALID_BEHAVIORS = %i[silence stderror raise].freeze
+ MESSAGE_PREFIX = '[DEPRECATION::WARNING] Blueprinter:'
def report(message)
full_msg = qualified_message(message)
@@ -26,7 +28,7 @@ def qualified_message(message)
def behavior
configured = Blueprinter.configuration.deprecations
- return configured unless !VALID_BEHAVIORS.include?(configured)
+ return configured if VALID_BEHAVIORS.include?(configured)
:stderror
end
diff --git a/lib/blueprinter/empty_types.rb b/lib/blueprinter/empty_types.rb
index 611e3ab7..03debe98 100644
--- a/lib/blueprinter/empty_types.rb
+++ b/lib/blueprinter/empty_types.rb
@@ -1,12 +1,15 @@
+# frozen_string_literal: true
+
require_relative 'helpers/type_helpers'
module Blueprinter
- EMPTY_COLLECTION = "empty_collection".freeze
- EMPTY_HASH = "empty_hash".freeze
- EMPTY_STRING = "empty_string".freeze
+ EMPTY_COLLECTION = 'empty_collection'
+ EMPTY_HASH = 'empty_hash'
+ EMPTY_STRING = 'empty_string'
module EmptyTypes
include TypeHelpers
+
private
def use_default_value?(value, empty_type)
@@ -18,12 +21,7 @@ def use_default_value?(value, empty_type)
when Blueprinter::EMPTY_HASH
value.is_a?(Hash) && value.empty?
when Blueprinter::EMPTY_STRING
- value.to_s == ""
- else
- Blueprinter::Deprecation.report(
- "Invalid empty type '#{empty_type}' received. Blueprinter will raise an error in the next major version."\
- "Must be one of [nil, Blueprinter::EMPTY_COLLECTION, Blueprinter::EMPTY_HASH, Blueprinter::EMPTY_STRING]"
- )
+ value.to_s == ''
end
end
end
diff --git a/lib/blueprinter/extractor.rb b/lib/blueprinter/extractor.rb
index c73cbfa5..373c56eb 100644
--- a/lib/blueprinter/extractor.rb
+++ b/lib/blueprinter/extractor.rb
@@ -1,11 +1,13 @@
+# frozen_string_literal: true
+
module Blueprinter
class Extractor
- def extract(_field_name, _object, _local_options, _options={})
- fail NotImplementedError, "An Extractor must implement #extract"
+ def extract(_field_name, _object, _local_options, _options = {})
+ raise NotImplementedError, 'An Extractor must implement #extract'
end
- def self.extract(field_name, object, local_options, options={})
- self.new.extract(field_name, object, local_options, options)
+ def self.extract(field_name, object, local_options, options = {})
+ new.extract(field_name, object, local_options, options)
end
end
end
diff --git a/lib/blueprinter/extractors/association_extractor.rb b/lib/blueprinter/extractors/association_extractor.rb
index d6cd7f6a..a87ba0eb 100644
--- a/lib/blueprinter/extractors/association_extractor.rb
+++ b/lib/blueprinter/extractors/association_extractor.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Blueprinter
# @api private
class AssociationExtractor < Extractor
@@ -7,12 +9,13 @@ def initialize
@extractor = Blueprinter.configuration.extractor_default.new
end
- def extract(association_name, object, local_options, options={})
- options_without_default = options.reject { |k,_| k == :default || k == :default_if }
+ def extract(association_name, object, local_options, options = {})
+ options_without_default = options.reject { |k, _| %i[default default_if].include?(k) }
# Merge in assocation options hash
local_options = local_options.merge(options[:options]) if options[:options].is_a?(Hash)
value = @extractor.extract(association_name, object, local_options, options_without_default)
return default_value(options) if use_default_value?(value, options[:default_if])
+
view = options[:view] || :default
blueprint = association_blueprint(options[:blueprint], value)
blueprint.prepare(value, view_name: view, local_options: local_options)
@@ -21,7 +24,9 @@ def extract(association_name, object, local_options, options={})
private
def default_value(association_options)
- association_options.key?(:default) ? association_options.fetch(:default) : Blueprinter.configuration.association_default
+ return association_options.fetch(:default) if association_options.key?(:default)
+
+ Blueprinter.configuration.association_default
end
def association_blueprint(blueprint, value)
diff --git a/lib/blueprinter/extractors/auto_extractor.rb b/lib/blueprinter/extractors/auto_extractor.rb
index d256e44b..226ba214 100644
--- a/lib/blueprinter/extractors/auto_extractor.rb
+++ b/lib/blueprinter/extractors/auto_extractor.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Blueprinter
# @api private
class AutoExtractor < Extractor
diff --git a/lib/blueprinter/extractors/block_extractor.rb b/lib/blueprinter/extractors/block_extractor.rb
index 727dc711..925d8e8e 100644
--- a/lib/blueprinter/extractors/block_extractor.rb
+++ b/lib/blueprinter/extractors/block_extractor.rb
@@ -1,7 +1,9 @@
+# frozen_string_literal: true
+
module Blueprinter
# @api private
class BlockExtractor < Extractor
- def extract(field_name, object, local_options, options = {})
+ def extract(_field_name, object, local_options, options = {})
options[:block].call(object, local_options)
end
end
diff --git a/lib/blueprinter/extractors/hash_extractor.rb b/lib/blueprinter/extractors/hash_extractor.rb
index f2b28e8b..714739df 100644
--- a/lib/blueprinter/extractors/hash_extractor.rb
+++ b/lib/blueprinter/extractors/hash_extractor.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Blueprinter
# @api private
class HashExtractor < Extractor
diff --git a/lib/blueprinter/extractors/public_send_extractor.rb b/lib/blueprinter/extractors/public_send_extractor.rb
index 99c205ad..d3a0c320 100644
--- a/lib/blueprinter/extractors/public_send_extractor.rb
+++ b/lib/blueprinter/extractors/public_send_extractor.rb
@@ -1,7 +1,9 @@
+# frozen_string_literal: true
+
module Blueprinter
# @api private
class PublicSendExtractor < Extractor
- def extract(field_name, object, local_options, options = {})
+ def extract(field_name, object, _local_options, _options = {})
object.public_send(field_name)
end
end
diff --git a/lib/blueprinter/field.rb b/lib/blueprinter/field.rb
index 91e0d913..8783aab5 100644
--- a/lib/blueprinter/field.rb
+++ b/lib/blueprinter/field.rb
@@ -1,63 +1,60 @@
+# frozen_string_literal: true
+
# @api private
-class Blueprinter::Field
- attr_reader :method, :name, :extractor, :options, :blueprint
- def initialize(method, name, extractor, blueprint, options = {})
- @method = method
- @name = name
- @extractor = extractor
- @blueprint = blueprint
- @options = options
- end
+module Blueprinter
+ class Field
+ attr_reader :method, :name, :extractor, :options, :blueprint
+
+ def initialize(method, name, extractor, blueprint, options = {})
+ @method = method
+ @name = name
+ @extractor = extractor
+ @blueprint = blueprint
+ @options = options
+ end
- def extract(object, local_options)
- extractor.extract(method, object, local_options, options)
- end
+ def extract(object, local_options)
+ extractor.extract(method, object, local_options, options)
+ end
- def skip?(field_name, object, local_options)
- return true if if_callable && !if_callable.call(field_name, object, local_options)
- unless_callable && unless_callable.call(field_name, object, local_options)
- end
+ def skip?(field_name, object, local_options)
+ return true if if_callable && !if_callable.call(field_name, object, local_options)
- private
+ unless_callable && unless_callable.call(field_name, object, local_options)
+ end
- def if_callable
- return @if_callable if defined?(@if_callable)
- @if_callable = callable_from(:if)
- end
+ private
- def unless_callable
- return @unless_callable if defined?(@unless_callable)
- @unless_callable = callable_from(:unless)
- end
+ def if_callable
+ return @if_callable if defined?(@if_callable)
- def callable_from(condition)
- callable = old_callable_from(condition)
+ @if_callable = callable_from(:if)
+ end
+
+ def unless_callable
+ return @unless_callable if defined?(@unless_callable)
- if callable && callable.arity == 2
- Blueprinter::Deprecation.report("`:#{condition}` conditions now expects 3 arguments instead of 2.")
- ->(_field_name, obj, options) { callable.call(obj, options) }
- else
- callable
+ @unless_callable = callable_from(:unless)
end
- end
- def old_callable_from(condition)
- config = Blueprinter.configuration
+ def callable_from(condition)
+ config = Blueprinter.configuration
- # Use field-level callable, or when not defined, try global callable
- tmp = if options.key?(condition)
- options.fetch(condition)
- elsif config.valid_callable?(condition)
- config.public_send(condition)
- end
+ # Use field-level callable, or when not defined, try global callable
+ tmp = if options.key?(condition)
+ options.fetch(condition)
+ elsif config.valid_callable?(condition)
+ config.public_send(condition)
+ end
- return false unless tmp
+ return false unless tmp
- case tmp
- when Proc then tmp
- when Symbol then blueprint.method(tmp)
- else
- raise ArgumentError, "#{tmp.class} is passed to :#{condition}"
+ case tmp
+ when Proc then tmp
+ when Symbol then blueprint.method(tmp)
+ else
+ raise ArgumentError, "#{tmp.class} is passed to :#{condition}"
+ end
end
end
end
diff --git a/lib/blueprinter/formatters/date_time_formatter.rb b/lib/blueprinter/formatters/date_time_formatter.rb
index db358a13..e63cdaa0 100644
--- a/lib/blueprinter/formatters/date_time_formatter.rb
+++ b/lib/blueprinter/formatters/date_time_formatter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Blueprinter
class DateTimeFormatter
InvalidDateTimeFormatterError = Class.new(BlueprinterError)
@@ -24,7 +26,7 @@ def format_datetime(value, field_format)
when Proc then format.call(value)
when String then value.strftime(format)
else
- raise InvalidDateTimeFormatterError, 'Cannot format DateTime object with invalid formatter: #{format.class}'
+ raise InvalidDateTimeFormatterError, "Cannot format DateTime object with invalid formatter: #{format.class}"
end
end
end
diff --git a/lib/blueprinter/helpers/base_helpers.rb b/lib/blueprinter/helpers/base_helpers.rb
index 99b40845..f87960cc 100644
--- a/lib/blueprinter/helpers/base_helpers.rb
+++ b/lib/blueprinter/helpers/base_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Blueprinter
module BaseHelpers
def self.included(base)
@@ -33,7 +35,8 @@ def prepare_data(object, view_name, local_options)
def prepend_root_and_meta(data, root, meta)
return data unless root
- ret = {root => data}
+
+ ret = { root => data }
meta ? ret.merge!(meta: meta) : ret
end
@@ -44,6 +47,7 @@ def inherited(subclass)
def object_to_hash(object, view_name:, local_options:)
result_hash = view_collection.fields_for(view_name).each_with_object({}) do |field, hash|
next if field.skip?(field.name, object, local_options)
+
hash[field.name] = field.extract(object, local_options)
end
view_collection.transformers(view_name).each do |transformer|
@@ -57,9 +61,9 @@ def validate_root_and_meta!(root, meta)
when String, Symbol
# no-op
when NilClass
- raise BlueprinterError, "meta requires a root to be passed" if meta
+ raise BlueprinterError, 'meta requires a root to be passed' if meta
else
- raise BlueprinterError, "root should be one of String, Symbol, NilClass"
+ raise BlueprinterError, 'root should be one of String, Symbol, NilClass'
end
end
@@ -69,10 +73,10 @@ def dynamic_blueprint?(blueprint)
def validate_blueprint!(blueprint, method)
validate_presence_of_blueprint!(blueprint)
- unless dynamic_blueprint?(blueprint)
- validate_blueprint_has_ancestors!(blueprint, method)
- validate_blueprint_has_blueprinter_base_ancestor!(blueprint, method)
- end
+ return if dynamic_blueprint?(blueprint)
+
+ validate_blueprint_has_ancestors!(blueprint, method)
+ validate_blueprint_has_blueprinter_base_ancestor!(blueprint, method)
end
def validate_presence_of_blueprint!(blueprint)
@@ -84,10 +88,10 @@ def validate_blueprint_has_ancestors!(blueprint, association_name)
# it means it, at the very least, does not have Blueprinter::Base as
# one of its ancestor classes (e.g: Hash) and thus an error should
# be raised.
- unless blueprint.respond_to?(:ancestors)
- raise BlueprinterError, "Blueprint provided for #{association_name} "\
+ return if blueprint.respond_to?(:ancestors)
+
+ raise BlueprinterError, "Blueprint provided for #{association_name} " \
'association is not valid.'
- end
end
def validate_blueprint_has_blueprinter_base_ancestor!(blueprint, association_name)
@@ -96,9 +100,9 @@ def validate_blueprint_has_blueprinter_base_ancestor!(blueprint, association_nam
return if blueprint.ancestors.include? Blueprinter::Base
# Raise error describing what's wrong.
- raise BlueprinterError, "Class #{blueprint.name} does not inherit from "\
- 'Blueprinter::Base and is not a valid Blueprinter '\
- "for #{association_name} association."
+ raise BlueprinterError, "Class #{blueprint.name} does not inherit from " \
+ 'Blueprinter::Base and is not a valid Blueprinter ' \
+ "for #{association_name} association."
end
def jsonify(blob)
diff --git a/lib/blueprinter/helpers/type_helpers.rb b/lib/blueprinter/helpers/type_helpers.rb
index 3d878d70..632463ac 100644
--- a/lib/blueprinter/helpers/type_helpers.rb
+++ b/lib/blueprinter/helpers/type_helpers.rb
@@ -1,13 +1,13 @@
+# frozen_string_literal: true
+
module Blueprinter
module TypeHelpers
private
- def active_record_relation?(object)
- !!(defined?(ActiveRecord::Relation) &&
- object.is_a?(ActiveRecord::Relation))
- end
def array_like?(object)
- object.is_a?(Array) || active_record_relation?(object)
+ Blueprinter.configuration.array_like_classes.any? do |klass|
+ object.is_a?(klass)
+ end
end
end
end
diff --git a/lib/blueprinter/transformer.rb b/lib/blueprinter/transformer.rb
index 0b5c06a5..73d8d928 100644
--- a/lib/blueprinter/transformer.rb
+++ b/lib/blueprinter/transformer.rb
@@ -1,12 +1,14 @@
+# frozen_string_literal: true
+
module Blueprinter
# @api private
class Transformer
def transform(_result_hash, _primary_obj, _options = {})
- fail NotImplementedError, "A Transformer must implement #transform"
+ raise NotImplementedError, 'A Transformer must implement #transform'
end
def self.transform(result_hash, primary_obj, options = {})
- self.new.transform(result_hash, primary_obj, options)
+ new.transform(result_hash, primary_obj, options)
end
end
end
diff --git a/lib/blueprinter/version.rb b/lib/blueprinter/version.rb
index 2c1f6105..2e640cbc 100644
--- a/lib/blueprinter/version.rb
+++ b/lib/blueprinter/version.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Blueprinter
- VERSION = '0.26.0'.freeze
+ VERSION = '0.30.0'
end
diff --git a/lib/blueprinter/view.rb b/lib/blueprinter/view.rb
index 56df9481..4db27600 100644
--- a/lib/blueprinter/view.rb
+++ b/lib/blueprinter/view.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Blueprinter
# @api private
DefinitionPlaceholder = Struct.new :name, :view?
@@ -18,14 +20,14 @@ def transformers
view_transformers.empty? ? Blueprinter.configuration.default_transformers : view_transformers
end
- def track_definition_order(method, is_view = true)
- if @sort_by_definition
- @definition_order << DefinitionPlaceholder.new(method, is_view)
- end
+ def track_definition_order(method, viewable: true)
+ return unless @sort_by_definition
+
+ @definition_order << DefinitionPlaceholder.new(method, viewable)
end
def inherit(view)
- view.fields.values.each do |field|
+ view.fields.each_value do |field|
self << field
end
@@ -69,7 +71,7 @@ def add_transformer(custom_transformer)
end
def <<(field)
- track_definition_order(field.name,false)
+ track_definition_order(field.name, viewable: false)
fields[field.name] = field
end
end
diff --git a/lib/blueprinter/view_collection.rb b/lib/blueprinter/view_collection.rb
index 56be69ac..0f4f9103 100644
--- a/lib/blueprinter/view_collection.rb
+++ b/lib/blueprinter/view_collection.rb
@@ -1,7 +1,10 @@
+# frozen_string_literal: true
+
module Blueprinter
# @api private
class ViewCollection
attr_reader :views, :sort_by_definition
+
def initialize
@views = {
identifier: View.new(:identifier),
@@ -16,8 +19,8 @@ def inherit(view_collection)
end
end
- def has_view?(view_name)
- views.has_key? view_name
+ def view?(view_name)
+ views.key? view_name
end
def fields_for(view_name)
@@ -26,7 +29,9 @@ def fields_for(view_name)
fields, excluded_fields = sortable_fields(view_name)
sorted_fields = sort_by_definition ? sort_by_def(view_name, fields) : fields.values.sort_by(&:name)
- (identifier_fields + sorted_fields).reject { |field| excluded_fields.include?(field.name) }
+ (identifier_fields + sorted_fields).tap do |fields_array|
+ fields_array.reject! { |field| excluded_fields.include?(field.name) }
+ end
end
def transformers(view_name)
@@ -47,15 +52,15 @@ def identifier_fields
# @return [Array<(Hash, Hash)>] fields, excluded_fields
def sortable_fields(view_name)
excluded_fields = {}
- fields = views[:default].fields
+ fields = views[:default].fields.clone
views[view_name].included_view_names.each do |included_view_name|
next if view_name == included_view_name
view_fields, view_excluded_fields = sortable_fields(included_view_name)
- fields = merge_fields(fields, view_fields)
+ fields.merge!(view_fields)
excluded_fields.merge!(view_excluded_fields)
end
- fields = merge_fields(fields, views[view_name].fields)
+ fields.merge!(views[view_name].fields) unless view_name == :default
views[view_name].excluded_field_names.each { |name| excluded_fields[name] = nil }
@@ -65,7 +70,9 @@ def sortable_fields(view_name)
# select and order members of fields according to traversal of the definition_orders
def sort_by_def(view_name, fields)
ordered_fields = {}
- views[:default].definition_order.each { |definition| add_to_ordered_fields(ordered_fields, definition, fields, view_name) }
+ views[:default].definition_order.each do |definition|
+ add_to_ordered_fields(ordered_fields, definition, fields, view_name)
+ end
ordered_fields.values
end
@@ -74,15 +81,13 @@ def sort_by_def(view_name, fields)
def add_to_ordered_fields(ordered_fields, definition, fields, view_name_filter = nil)
if definition.view?
if view_name_filter.nil? || view_name_filter == definition.name
- views[definition.name].definition_order.each { |_definition| add_to_ordered_fields(ordered_fields, _definition, fields) }
+ views[definition.name].definition_order.each do |defined|
+ add_to_ordered_fields(ordered_fields, defined, fields)
+ end
end
else
ordered_fields[definition.name] = fields[definition.name]
end
end
-
- def merge_fields(source_fields, included_fields)
- source_fields.merge included_fields
- end
end
end
diff --git a/lib/generators/blueprinter/blueprint_generator.rb b/lib/generators/blueprinter/blueprint_generator.rb
index 5edfdc91..8ffc5bb6 100644
--- a/lib/generators/blueprinter/blueprint_generator.rb
+++ b/lib/generators/blueprinter/blueprint_generator.rb
@@ -1,39 +1,33 @@
+# frozen_string_literal: true
+
module Blueprinter
module Generators
class BlueprintGenerator < ::Rails::Generators::NamedBase
- desc "Generates blueprint for ActiveRecord model with the given NAME."
+ desc 'Generates blueprint for ActiveRecord model with the given NAME.'
attr_accessor :options
- source_root File.expand_path("../templates", __FILE__)
-
-
-
- class_option :blueprints_dir, default: "app/blueprints", desc: "path to new blueprint", aliases: "-d"
-
-
-
- class_option :identifier, default: nil, desc: "Add an identifer to the generated blueprint, either uses :id or your specified value", aliases: "-i", banner: "id"
-
-
-
- class_option :fields, type: :array, default: [], desc: "Manually add specified fields"
+ source_root File.expand_path('templates', __dir__)
- class_option :detect_fields, type: :boolean, default: false, desc: "Introspect on the model to set fields in the generated blueprint. Will be merged with any manually specified"
+ class_option :blueprints_dir, default: 'app/blueprints', desc: 'path to new blueprint', aliases: '-d'
+ class_option :identifier, default: nil,
+ desc: 'Add an identifer to the generated blueprint, either uses :id or your specified value', aliases: '-i', banner: 'id'
+ class_option :fields, type: :array, default: [], desc: 'Manually add specified fields'
- class_option :associations, type: :array, default: [], desc: "Manually add specified associations", aliases: "-a"
+ class_option :detect_fields, type: :boolean, default: false,
+ desc: 'Introspect on the model to set fields in the generated blueprint. Will be merged with any manually specified'
- class_option :detect_associations, type: :boolean, default: false, desc: "Introspect on the model to set associations in the generated blueprint. Will be merged with any manually specified"
+ class_option :associations, type: :array, default: [], desc: 'Manually add specified associations', aliases: '-a'
+ class_option :detect_associations, type: :boolean, default: false,
+ desc: 'Introspect on the model to set associations in the generated blueprint. Will be merged with any manually specified'
+ class_option :wrap_at, type: :numeric, default: 80, desc: 'Maximum length of generated fields line', aliases: '-w'
- class_option :wrap_at, type: :numeric, default: 80, desc: "Maximum length of generated fields line", aliases: "-w"
-
- class_option :indentation, type: :string, default: "two", desc: "Indentation of generated file", banner: "two|four|tab"
-
-
+ class_option :indentation, type: :string, default: 'two', desc: 'Indentation of generated file',
+ banner: 'two|four|tab'
remove_class_option :skip_namespace
@@ -42,30 +36,28 @@ def ensure_blueprint_dir
end
def create_blueprint
- template "blueprint.rb", File.join(path, "#{file_path}_blueprint.rb")
+ template 'blueprint.rb', File.join(path, "#{file_path}_blueprint.rb")
end
-
-
private
def path
- options["blueprints_dir"]
+ options['blueprints_dir']
end
def identifier_symbol
- if options['identifier']
- options['identifier'] == "identifier" ? :id : options['identifier']
- end
+ return unless options['identifier']
+
+ options['identifier'] == 'identifier' ? :id : options['identifier']
end
def fields
- fs = if options["detect_fields"]
- Array.new(options["fields"]).concat(introspected_fields)
+ fs = if options['detect_fields']
+ Array.new(options['fields']).concat(introspected_fields)
else
- options["fields"]
+ options['fields']
end
- fs.reject {|f| f.blank? }.uniq
+ fs.reject(&:blank?).uniq
end
def introspected_fields
@@ -75,30 +67,29 @@ def introspected_fields
# split at wrap_at chars, two indentations
def formatted_fields
two_indents = indent * 2
- fields_string = fields.reduce([]) do |memo, f|
- if !memo.last.nil?
+ fields_string = fields.each_with_object([]) do |f, memo|
+ if memo.last.nil?
+ memo << " :#{f},"
+ else
now = "#{memo.last} :#{f},"
- if now.length > options["wrap_at"].to_i
+ if now.length > options['wrap_at'].to_i
memo << ":#{f},"
else
memo[memo.length - 1] = now
end
- else
- memo << " :#{f},"
end
- memo
end.join("\n#{two_indents}")
- fields_string[0,fields_string.length - 1]
+ fields_string[0, fields_string.length - 1]
end
def associations
- as = if options["detect_associations"]
- Array.new(options["associations"]).concat(introspected_associations.keys)
+ as = if options['detect_associations']
+ Array.new(options['associations']).concat(introspected_associations.keys)
else
- options["associations"]
+ options['associations']
end
- as.reject {|f| f.blank? }.uniq
+ as.reject(&:blank?).uniq
end
def introspected_associations
@@ -112,15 +103,13 @@ def association_blueprint(association_name)
def association_class(association_name)
introspected_name = if introspected_associations[association_name].respond_to?(:klass)
introspected_associations[association_name].klass.to_s
- else
- nil
end
"#{introspected_name || association_name.camelcase}Blueprint"
end
def indent
- user_intended = {two: " ", four: " ", tab:"\t"}[options["indentation"].intern]
- user_intended.nil? ? " " : user_intended
+ user_intended = { two: ' ', four: ' ', tab: "\t" }[options['indentation'].intern]
+ user_intended.nil? ? ' ' : user_intended
end
end
end
diff --git a/lib/generators/blueprinter/templates/blueprint.rb b/lib/generators/blueprinter/templates/blueprint.rb
index c07d6d5c..0635f424 100644
--- a/lib/generators/blueprinter/templates/blueprint.rb
+++ b/lib/generators/blueprinter/templates/blueprint.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class <%= class_name %>Blueprint < Blueprinter::Base
<% if identifier_symbol -%>
<%= indent -%>identifier :<%= identifier_symbol %>
diff --git a/lib/tasks/blueprinter_tasks.rake b/lib/tasks/blueprinter_tasks.rake
deleted file mode 100644
index 3f06cd9b..00000000
--- a/lib/tasks/blueprinter_tasks.rake
+++ /dev/null
@@ -1,4 +0,0 @@
-# desc "Explaining what the task does"
-# task :blueprinter do
-# # Task goes here
-# end
diff --git a/spec/activerecord_helper.rb b/spec/activerecord_helper.rb
index 37f82e6c..c975d0c5 100644
--- a/spec/activerecord_helper.rb
+++ b/spec/activerecord_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'active_record'
require 'factories/model_factories'
diff --git a/spec/benchmark_helper.rb b/spec/benchmark_helper.rb
index ee97180e..ede9daa5 100644
--- a/spec/benchmark_helper.rb
+++ b/spec/benchmark_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'minitest/autorun'
require 'minitest/benchmark'
diff --git a/spec/benchmarks/active_record_big_o_test.rb b/spec/benchmarks/active_record_big_o_test.rb
index 509c7bf3..ac2d163e 100644
--- a/spec/benchmarks/active_record_big_o_test.rb
+++ b/spec/benchmarks/active_record_big_o_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'activerecord_helper'
require 'benchmark_helper'
require 'blueprinter/base'
diff --git a/spec/benchmarks/active_record_ips_test.rb b/spec/benchmarks/active_record_ips_test.rb
index fea1162f..57b27db7 100644
--- a/spec/benchmarks/active_record_ips_test.rb
+++ b/spec/benchmarks/active_record_ips_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'activerecord_helper'
require 'benchmark_helper'
require 'blueprinter/base'
diff --git a/spec/benchmarks/big_o_test.rb b/spec/benchmarks/big_o_test.rb
index fb4611de..19e89420 100644
--- a/spec/benchmarks/big_o_test.rb
+++ b/spec/benchmarks/big_o_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'benchmark_helper'
require 'blueprinter/base'
require 'ostruct'
diff --git a/spec/benchmarks/ips_test.rb b/spec/benchmarks/ips_test.rb
index d9224f40..00b00605 100644
--- a/spec/benchmarks/ips_test.rb
+++ b/spec/benchmarks/ips_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'benchmark_helper'
require 'blueprinter/base'
require 'ostruct'
diff --git a/spec/factories/model_factories.rb b/spec/factories/model_factories.rb
index caddaaa4..f15cce34 100644
--- a/spec/factories/model_factories.rb
+++ b/spec/factories/model_factories.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'factory_bot'
FactoryBot.define do
diff --git a/spec/generator_helper.rb b/spec/generator_helper.rb
index 1eb31c5e..20ad247b 100644
--- a/spec/generator_helper.rb
+++ b/spec/generator_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'active_record/railtie' # see https://github.com/rspec/rspec-rails/issues/1690 for vague hints
require 'ammeter/init'
require 'generators/shared'
diff --git a/spec/generators/blueprint_generator_spec.rb b/spec/generators/blueprint_generator_spec.rb
index 811fa7a8..1f085856 100644
--- a/spec/generators/blueprint_generator_spec.rb
+++ b/spec/generators/blueprint_generator_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'generator_helper'
require 'generators/blueprinter/blueprint_generator'
diff --git a/spec/generators/shared.rb b/spec/generators/shared.rb
index ff778ff4..7c41550a 100644
--- a/spec/generators/shared.rb
+++ b/spec/generators/shared.rb
@@ -1,4 +1,5 @@
+# frozen_string_literal: true
+
RSpec.shared_examples "generated_file" do
it { is_expected.to have_correct_syntax }
end
-
diff --git a/spec/integrations/base_spec.rb b/spec/integrations/base_spec.rb
index 50afbd29..c6db132a 100644
--- a/spec/integrations/base_spec.rb
+++ b/spec/integrations/base_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'activerecord_helper'
require 'ostruct'
require_relative 'shared/base_render_examples'
@@ -56,6 +58,30 @@
end
end
+ context 'Given passed object is array-like' do
+ let(:blueprint) { blueprint_with_block }
+ let(:additional_object) { OpenStruct.new(obj_hash.merge(id: 2)) }
+ let(:obj) { Set.new([object_with_attributes, additional_object]) }
+
+ context 'and is an instance of a configured array-like class' do
+ before do
+ reset_blueprinter_config!
+ Blueprinter.configure { |config| config.custom_array_like_classes = [Set] }
+ end
+ after { reset_blueprinter_config! }
+
+ it 'should return the expected array of hashes' do
+ should eq('[{"id":1,"position_and_company":"Manager at Procore"},{"id":2,"position_and_company":"Manager at Procore"}]')
+ end
+ end
+
+ context 'and is not an instance of a configured array-like class' do
+ it 'should raise an error' do
+ expect { blueprint.render(obj) }.to raise_error(NoMethodError)
+ end
+ end
+ end
+
context 'Inside Rails project' do
include FactoryBot::Syntax::Methods
let(:obj) { create(:user) }
@@ -381,6 +407,36 @@ def extract(association_name, object, _local_options, _options={})
end
end
end
+
+ context 'Given passed object is an instance of a configured array-like class' do
+ let(:blueprint) do
+ Class.new(Blueprinter::Base) do
+ identifier :id
+ fields :make
+ end
+ end
+ let(:vehicle1) { build(:vehicle, id: 1) }
+ let(:vehicle2) { build(:vehicle, id: 2, make: 'Mediocre Car') }
+ let(:vehicle3) { build(:vehicle, id: 3, make: 'Terrible Car') }
+ let(:vehicles) { [vehicle1, vehicle2, vehicle3] }
+ let(:obj) { Set.new(vehicles) }
+ let(:result) do
+ vehicles_json = vehicles.map do |vehicle|
+ "{\"id\":#{vehicle.id},\"make\":\"#{vehicle.make}\"}"
+ end.join(',')
+ "[#{vehicles_json}]"
+ end
+
+ before do
+ reset_blueprinter_config!
+ Blueprinter.configure do |config|
+ config.custom_array_like_classes = [Set]
+ end
+ end
+ after { reset_blueprinter_config! }
+
+ it('returns the expected result') { should eq(result) }
+ end
end
end
describe '::render_as_hash' do
@@ -436,7 +492,7 @@ def extract(association_name, object, _local_options, _options={})
end
describe 'has_view?' do
- subject { blueprint.has_view?(view) }
+ subject { blueprint.view?(view) }
let(:blueprint) do
Class.new(Blueprinter::Base) do
diff --git a/spec/integrations/shared/base_render_examples.rb b/spec/integrations/shared/base_render_examples.rb
index 71f996fd..6113ffcc 100644
--- a/spec/integrations/shared/base_render_examples.rb
+++ b/spec/integrations/shared/base_render_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'Base::render' do
context 'Given blueprint has ::field' do
let(:result) { '{"first_name":"Meg","id":' + obj_id + '}' }
@@ -172,17 +174,18 @@ def extract(field_name, object, _local_options, _options={})
end
context 'Given default_if option is invalid' do
+ before do
+ obj[:first_name] = ""
+ end
+
+ let(:result) { %({"first_name":"","id":#{obj_id}}) }
let(:blueprint) do
Class.new(Blueprinter::Base) do
field :id
field :first_name, default_if: "INVALID_EMPTY_TYPE", default: "Unknown"
end
end
- it('reports a deprecation message') do
- allow(Blueprinter::Deprecation).to receive(:report)
- blueprint.render(obj)
- expect(Blueprinter::Deprecation).to have_received(:report).with(match(/Invalid empty type '.*' received. Blueprinter will raise an error in the next major version./))
- end
+ it('does not use the default value') { should eq(result) }
end
context "Given blueprint has ::field with nil value" do
@@ -254,26 +257,6 @@ def extract(field_name, object, _local_options, _options={})
end
context 'Given blueprint has ::field with a conditional argument' do
- context 'Given conditional proc has deprecated two argument signature' do
- let(:if_proc) { ->(_obj, _local_opts) { true } }
- let(:unless_proc) { ->(_obj, _local_opts) { true } }
-
- let(:blueprint) do
- Class.new(Blueprinter::Base) do
- field :id
- field :first_name, if: ->(_obj, _local_opts) { true }
- field :last_name, unless: ->(_obj, _local_opts) { true }
- end
- end
-
- it('reports a deprecation warning') do
- allow(Blueprinter::Deprecation).to receive(:report)
- blueprint.render(obj)
- expect(Blueprinter::Deprecation).to have_received(:report).with("`:if` conditions now expects 3 arguments instead of 2.")
- expect(Blueprinter::Deprecation).to have_received(:report).with("`:unless` conditions now expects 3 arguments instead of 2.")
- end
- end
-
context 'Given conditional proc has three argument signature' do
variants = %i[proc method].product([true, false])
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index c615aef8..8a0215cb 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
require 'blueprinter'
diff --git a/spec/units/configuration_spec.rb b/spec/units/configuration_spec.rb
index 20ecb2de..c162dc5d 100644
--- a/spec/units/configuration_spec.rb
+++ b/spec/units/configuration_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'oj'
require 'yajl'
diff --git a/spec/units/date_time_formatter_spec.rb b/spec/units/date_time_formatter_spec.rb
index e78ae577..8b49107d 100644
--- a/spec/units/date_time_formatter_spec.rb
+++ b/spec/units/date_time_formatter_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
describe '::DateTimeFormatter' do
let(:formatter) { Blueprinter::DateTimeFormatter.new }
let(:valid_date) { Date.new(1994, 3, 4) }
diff --git a/spec/units/deprecation_spec.rb b/spec/units/deprecation_spec.rb
index cbaaf06a..a1e6e3b5 100644
--- a/spec/units/deprecation_spec.rb
+++ b/spec/units/deprecation_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
describe 'Blueprinter::Deprecation' do
describe '#report' do
TEST_MESSAGE = "Test Message"
diff --git a/spec/units/view_spec.rb b/spec/units/view_spec.rb
index cc16cf67..22466aec 100644
--- a/spec/units/view_spec.rb
+++ b/spec/units/view_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
describe '::View' do
let(:view) { Blueprinter::View.new('Basic View') }
let(:field) { MockField.new(:first_name) }