Skip to content
This repository has been archived by the owner on Sep 20, 2023. It is now read-only.

Make array like classes configurable #6

Merged
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
## 1.0.0 - 2023/05/01
## 1.1.0 - 2023/02/02
* 🚀 [FEATURE] Allow configuring custom array-like classes to be treated as collections when serializing. More details can be found [here](https://github.com/blueprinter-ruby/blueprinter/pull/6). Thanks to [@toddnestor](https://github.com/toddnestor).
ritikesh marked this conversation as resolved.
Show resolved Hide resolved
* 💅 [ENHANCEMENT] Reduce object allocations in fields calculations to save some memory. More details can be found [here](https://github.com/blueprinter-ruby/blueprinter/pull/5). Thanks to [@nametoolong](https://github.com/nametoolong).

## 1.0.0 - 2023/01/05
ritikesh marked this conversation as resolved.
Show resolved Hide resolved
* 🚀 [Official Release] Released [`blueprinter-rb`](https://rubygems.org/gems/blueprinter-rb) under a new organisation. More details can be found [here](https://github.com/procore/blueprinter/issues/288).

## 0.25.3 - 2021/03/03
Expand Down
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,24 @@ 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
```

---
</details>


Expand Down
11 changes: 10 additions & 1 deletion lib/blueprinter/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

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
ritikesh marked this conversation as resolved.
Show resolved Hide resolved

VALID_CALLABLES = %i(if unless).freeze

Expand All @@ -18,6 +18,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)
Expand Down
8 changes: 3 additions & 5 deletions lib/blueprinter/helpers/type_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@
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
2 changes: 1 addition & 1 deletion lib/blueprinter/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module Blueprinter
VERSION = '1.0.0'
VERSION = '1.1.0'
end
54 changes: 54 additions & 0 deletions spec/integrations/base_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,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)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there's not really anything else to assert here. We already have tests for that cover a hash being handled properly, and hashes are "array-like" in that they are Enumerable, so here I just mimicked what would happen if you pass it a collection for which you didn't pass the class of to array_like_classes.

end
end
end

context 'Inside Rails project' do
include FactoryBot::Syntax::Methods
let(:obj) { create(:user) }
Expand Down Expand Up @@ -383,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
Expand Down