Skip to content

Commit ecb92f0

Browse files
ababichshishirmk
authored andcommitted
add is_collection parameter to force corresponding serialization (#239)
* add is_collection parameter to force corresponding serialization * add documentation for is_collection purpose, behavior and notes re. default autodetect logic
1 parent af38b30 commit ecb92f0

3 files changed

Lines changed: 72 additions & 3 deletions

File tree

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,26 @@ hash = MovieSerializer.new([movie, movie], options).serializable_hash
261261
json_string = MovieSerializer.new([movie, movie], options).serialized_json
262262
```
263263

264+
#### Control Over Collection Serialization
265+
266+
You can use `is_collection` option to have better control over collection serialization.
267+
268+
If this option is not provided or `nil` autedetect logic is used to try understand
269+
if provided resource is a single object or collection.
270+
271+
Autodetect logic is compatible with most DB toolkits (ActiveRecord, Sequel, etc.) but
272+
**cannot** guarantee that single vs collection will be always detected properly.
273+
274+
```ruby
275+
options[:is_collection]
276+
```
277+
278+
was introduced to be able to have precise control this behavior
279+
280+
- `nil` or not provided: will try to autodetect single vs collection (please, see notes above)
281+
- `true` will always treat input resource as *collection*
282+
- `false` will always treat input resource as *single object*
283+
264284
### Caching
265285
Requires a `cache_key` method be defined on model:
266286

lib/fast_jsonapi/object_serializer.rb

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def initialize(resource, options = {})
2828
end
2929

3030
def serializable_hash
31-
return hash_for_collection if is_collection?(@resource)
31+
return hash_for_collection if is_collection?(@resource, @is_collection)
3232

3333
hash_for_one_record
3434
end
@@ -75,6 +75,7 @@ def process_options(options)
7575
@known_included_objects = {}
7676
@meta = options[:meta]
7777
@links = options[:links]
78+
@is_collection = options[:is_collection]
7879
@params = options[:params] || {}
7980
raise ArgumentError.new("`params` option passed to serializer must be a hash") unless @params.is_a?(Hash)
8081

@@ -84,8 +85,10 @@ def process_options(options)
8485
end
8586
end
8687

87-
def is_collection?(resource)
88-
resource.respond_to?(:each) && !resource.respond_to?(:each_pair)
88+
def is_collection?(resource, force_is_collection = nil)
89+
return force_is_collection unless force_is_collection.nil?
90+
91+
resource.respond_to?(:size) && !resource.respond_to?(:each_pair)
8992
end
9093

9194
class_methods do

spec/lib/object_serializer_spec.rb

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,52 @@ class BlahSerializer
310310
end
311311
end
312312

313+
context 'when is_collection option present' do
314+
subject { MovieSerializer.new(resource, is_collection_options).serializable_hash }
315+
316+
context 'autodetect' do
317+
let(:is_collection_options) { {} }
318+
319+
context 'collection if no option present' do
320+
let(:resource) { [movie] }
321+
it { expect(subject[:data]).to be_a(Array) }
322+
end
323+
324+
context 'single if no option present' do
325+
let(:resource) { movie }
326+
it { expect(subject[:data]).to be_a(Hash) }
327+
end
328+
end
329+
330+
context 'force is_collection to true' do
331+
let(:is_collection_options) { { is_collection: true } }
332+
333+
context 'collection will pass' do
334+
let(:resource) { [movie] }
335+
it { expect(subject[:data]).to be_a(Array) }
336+
end
337+
338+
context 'single will raise error' do
339+
let(:resource) { movie }
340+
it { expect { subject }.to raise_error(NoMethodError, /method(.*)each/) }
341+
end
342+
end
343+
344+
context 'force is_collection to false' do
345+
let(:is_collection_options) { { is_collection: false } }
346+
347+
context 'collection will fail without id' do
348+
let(:resource) { [movie] }
349+
it { expect { subject }.to raise_error(FastJsonapi::MandatoryField, /id is a mandatory field/) }
350+
end
351+
352+
context 'single will pass' do
353+
let(:resource) { movie }
354+
it { expect(subject[:data]).to be_a(Hash) }
355+
end
356+
end
357+
end
358+
313359
context 'when optional attributes are determined by record data' do
314360
it 'returns optional attribute when attribute is included' do
315361
movie.release_year = 2001

0 commit comments

Comments
 (0)