Allow block for id customization

Allow an ID of object to be customized directly on the serializer by
passing a block to `set_id` as opposed to only through a model property.

We already allow for attributes that do not have a model property of the
same name to be customized directly on the serializer using a block.

This customization can be useful in situation in which you have
different classes being serialized using the same serializer. For
example, if we have `HorrorMovie`, `ComedyMovie` and `DramaMovie` using
the same `MovieSerializer`, we can unify their IDs using

```
class MovieSerializer
  include FastJsonapi::ObjectSerializer

  attributes :name, :year
  set_id do |record|
    "#{record.name.downcase}-#{record.id}"
  end
```

which is preferable to creating a `#serialized_id` method in every model
that will use `MovieSerializer` to encapsulate the customization.

Closes #315
This commit is contained in:
Larissa Reis 2018-10-04 22:14:02 -06:00
parent 11b5255010
commit 9fa26fa588
3 changed files with 50 additions and 20 deletions

View File

@ -167,8 +167,8 @@ module FastJsonapi
self.record_type = run_key_transform(type_name)
end
def set_id(id_name)
self.record_id = id_name
def set_id(id_name = nil, &block)
self.record_id = block || id_name
end
def cache_options(cache_options)

View File

@ -86,9 +86,10 @@ module FastJsonapi
end
def id_from_record(record)
return record.send(record_id) if record_id
raise MandatoryField, 'id is a mandatory field in the jsonapi spec' unless record.respond_to?(:id)
record.id
return record_id.call(record) if record_id.is_a?(Proc)
return record.send(record_id) if record_id
raise MandatoryField, 'id is a mandatory field in the jsonapi spec' unless record.respond_to?(:id)
record.id
end
# Override #to_json for alternative implementation

View File

@ -195,28 +195,57 @@ describe FastJsonapi::ObjectSerializer do
describe '#set_id' do
subject(:serializable_hash) { MovieSerializer.new(resource).serializable_hash }
before do
MovieSerializer.set_id :owner_id
end
context 'method name' do
before do
MovieSerializer.set_id :owner_id
end
after do
MovieSerializer.set_id nil
end
after do
MovieSerializer.set_id nil
end
context 'when one record is given' do
let(:resource) { movie }
context 'when one record is given' do
let(:resource) { movie }
it 'returns correct hash which id equals owner_id' do
expect(serializable_hash[:data][:id].to_i).to eq movie.owner_id
it 'returns correct hash which id equals owner_id' do
expect(serializable_hash[:data][:id].to_i).to eq movie.owner_id
end
end
context 'when an array of records is given' do
let(:resource) { [movie, movie] }
it 'returns correct hash which id equals owner_id' do
expect(serializable_hash[:data][0][:id].to_i).to eq movie.owner_id
expect(serializable_hash[:data][1][:id].to_i).to eq movie.owner_id
end
end
end
context 'when an array of records is given' do
let(:resource) { [movie, movie] }
context 'with block' do
before do
MovieSerializer.set_id { |record| "movie-#{record.owner_id}" }
end
it 'returns correct hash which id equals owner_id' do
expect(serializable_hash[:data][0][:id].to_i).to eq movie.owner_id
expect(serializable_hash[:data][1][:id].to_i).to eq movie.owner_id
after do
MovieSerializer.set_id nil
end
context 'when one record is given' do
let(:resource) { movie }
it 'returns correct hash which id equals movie-id' do
expect(serializable_hash[:data][:id]).to eq "movie-#{movie.owner_id}"
end
end
context 'when an array of records is given' do
let(:resource) { [movie, movie] }
it 'returns correct hash which id equals movie-id' do
expect(serializable_hash[:data][0][:id]).to eq "movie-#{movie.owner_id}"
expect(serializable_hash[:data][1][:id]).to eq "movie-#{movie.owner_id}"
end
end
end
end