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
This commit is contained in:
Oleksiy Babich 2018-07-04 05:35:06 +03:00 committed by Shishir Kakaraddi
parent af38b30179
commit ecb92f07f5
3 changed files with 72 additions and 3 deletions

View File

@ -261,6 +261,26 @@ hash = MovieSerializer.new([movie, movie], options).serializable_hash
json_string = MovieSerializer.new([movie, movie], options).serialized_json
```
#### Control Over Collection Serialization
You can use `is_collection` option to have better control over collection serialization.
If this option is not provided or `nil` autedetect logic is used to try understand
if provided resource is a single object or collection.
Autodetect logic is compatible with most DB toolkits (ActiveRecord, Sequel, etc.) but
**cannot** guarantee that single vs collection will be always detected properly.
```ruby
options[:is_collection]
```
was introduced to be able to have precise control this behavior
- `nil` or not provided: will try to autodetect single vs collection (please, see notes above)
- `true` will always treat input resource as *collection*
- `false` will always treat input resource as *single object*
### Caching
Requires a `cache_key` method be defined on model:

View File

@ -28,7 +28,7 @@ module FastJsonapi
end
def serializable_hash
return hash_for_collection if is_collection?(@resource)
return hash_for_collection if is_collection?(@resource, @is_collection)
hash_for_one_record
end
@ -75,6 +75,7 @@ module FastJsonapi
@known_included_objects = {}
@meta = options[:meta]
@links = options[:links]
@is_collection = options[:is_collection]
@params = options[:params] || {}
raise ArgumentError.new("`params` option passed to serializer must be a hash") unless @params.is_a?(Hash)
@ -84,8 +85,10 @@ module FastJsonapi
end
end
def is_collection?(resource)
resource.respond_to?(:each) && !resource.respond_to?(:each_pair)
def is_collection?(resource, force_is_collection = nil)
return force_is_collection unless force_is_collection.nil?
resource.respond_to?(:size) && !resource.respond_to?(:each_pair)
end
class_methods do

View File

@ -310,6 +310,52 @@ describe FastJsonapi::ObjectSerializer do
end
end
context 'when is_collection option present' do
subject { MovieSerializer.new(resource, is_collection_options).serializable_hash }
context 'autodetect' do
let(:is_collection_options) { {} }
context 'collection if no option present' do
let(:resource) { [movie] }
it { expect(subject[:data]).to be_a(Array) }
end
context 'single if no option present' do
let(:resource) { movie }
it { expect(subject[:data]).to be_a(Hash) }
end
end
context 'force is_collection to true' do
let(:is_collection_options) { { is_collection: true } }
context 'collection will pass' do
let(:resource) { [movie] }
it { expect(subject[:data]).to be_a(Array) }
end
context 'single will raise error' do
let(:resource) { movie }
it { expect { subject }.to raise_error(NoMethodError, /method(.*)each/) }
end
end
context 'force is_collection to false' do
let(:is_collection_options) { { is_collection: false } }
context 'collection will fail without id' do
let(:resource) { [movie] }
it { expect { subject }.to raise_error(FastJsonapi::MandatoryField, /id is a mandatory field/) }
end
context 'single will pass' do
let(:resource) { movie }
it { expect(subject[:data]).to be_a(Hash) }
end
end
end
context 'when optional attributes are determined by record data' do
it 'returns optional attribute when attribute is included' do
movie.release_year = 2001