Merge branch 'master' into params-in-nested-includes
This commit is contained in:
commit
dfcbe263fb
28
README.md
28
README.md
@ -32,6 +32,7 @@ Fast JSON API serialized 250 records in 3.01 ms
|
||||
* [Params](#params)
|
||||
* [Conditional Attributes](#conditional-attributes)
|
||||
* [Conditional Relationships](#conditional-relationships)
|
||||
* [Sparse Fieldsets](#sparse-fieldsets)
|
||||
* [Contributing](#contributing)
|
||||
|
||||
|
||||
@ -207,6 +208,18 @@ class MovieSerializer
|
||||
end
|
||||
```
|
||||
|
||||
Attributes can also use a different name by passing the original method or accessor with a proc shortcut:
|
||||
|
||||
```ruby
|
||||
class MovieSerializer
|
||||
include FastJsonapi::ObjectSerializer
|
||||
|
||||
attributes :name
|
||||
|
||||
attribute :released_in_year, &:year
|
||||
end
|
||||
```
|
||||
|
||||
### Links Per Object
|
||||
Links are defined in FastJsonapi using the `link` method. By default, link are read directly from the model property of the same name.In this example, `public_url` is expected to be a property of the object being serialized.
|
||||
|
||||
@ -376,6 +389,21 @@ serializer = MovieSerializer.new(movie, { params: { admin: current_user.admin? }
|
||||
serializer.serializable_hash
|
||||
```
|
||||
|
||||
### Sparse Fieldsets
|
||||
|
||||
Attributes and relationships can be selectively returned per record type by using the `fields` option.
|
||||
|
||||
```ruby
|
||||
class MovieSerializer
|
||||
include FastJsonapi::ObjectSerializer
|
||||
|
||||
attributes :name, :year
|
||||
end
|
||||
|
||||
serializer = MovieSerializer.new(movie, { fields: { movie: [:name] } })
|
||||
serializer.serializable_hash
|
||||
```
|
||||
|
||||
### Customizable Options
|
||||
|
||||
Option | Purpose | Example
|
||||
|
@ -11,7 +11,7 @@ module FastJsonapi
|
||||
def serialize(record, serialization_params, output_hash)
|
||||
if include_attribute?(record, serialization_params)
|
||||
output_hash[key] = if method.is_a?(Proc)
|
||||
method.arity == 1 ? method.call(record) : method.call(record, serialization_params)
|
||||
method.arity.abs == 1 ? method.call(record) : method.call(record, serialization_params)
|
||||
else
|
||||
record.public_send(method)
|
||||
end
|
||||
@ -26,4 +26,4 @@ module FastJsonapi
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -41,8 +41,8 @@ module FastJsonapi
|
||||
|
||||
return serializable_hash unless @resource
|
||||
|
||||
serializable_hash[:data] = self.class.record_hash(@resource, @params)
|
||||
serializable_hash[:included] = self.class.get_included_records(@resource, @includes, @known_included_objects, @params) if @includes.present?
|
||||
serializable_hash[:data] = self.class.record_hash(@resource, @fieldsets[self.class.record_type.to_sym], @params)
|
||||
serializable_hash[:included] = self.class.get_included_records(@resource, @includes, @known_included_objects, @fieldsets, @params) if @includes.present?
|
||||
serializable_hash
|
||||
end
|
||||
|
||||
@ -51,9 +51,10 @@ module FastJsonapi
|
||||
|
||||
data = []
|
||||
included = []
|
||||
fieldset = @fieldsets[self.class.record_type.to_sym]
|
||||
@resource.each do |record|
|
||||
data << self.class.record_hash(record, @params)
|
||||
included.concat self.class.get_included_records(record, @includes, @known_included_objects, @params) if @includes.present?
|
||||
data << self.class.record_hash(record, fieldset, @params)
|
||||
included.concat self.class.get_included_records(record, @includes, @known_included_objects, @fieldsets, @params) if @includes.present?
|
||||
end
|
||||
|
||||
serializable_hash[:data] = data
|
||||
@ -70,6 +71,8 @@ module FastJsonapi
|
||||
private
|
||||
|
||||
def process_options(options)
|
||||
@fieldsets = deep_symbolize(options[:fields].presence || {})
|
||||
|
||||
return if options.blank?
|
||||
|
||||
@known_included_objects = {}
|
||||
@ -85,6 +88,18 @@ module FastJsonapi
|
||||
end
|
||||
end
|
||||
|
||||
def deep_symbolize(collection)
|
||||
if collection.is_a? Hash
|
||||
Hash[collection.map do |k, v|
|
||||
[k.to_sym, deep_symbolize(v)]
|
||||
end]
|
||||
elsif collection.is_a? Array
|
||||
collection.map { |i| deep_symbolize(i) }
|
||||
else
|
||||
collection.to_sym
|
||||
end
|
||||
end
|
||||
|
||||
def is_collection?(resource, force_is_collection = nil)
|
||||
return force_is_collection unless force_is_collection.nil?
|
||||
|
||||
@ -104,6 +119,7 @@ module FastJsonapi
|
||||
subclass.race_condition_ttl = race_condition_ttl
|
||||
subclass.data_links = data_links
|
||||
subclass.cached = cached
|
||||
subclass.set_type(subclass.reflected_record_type) if subclass.reflected_record_type
|
||||
end
|
||||
|
||||
def reflected_record_type
|
||||
|
@ -40,27 +40,30 @@ module FastJsonapi
|
||||
end
|
||||
end
|
||||
|
||||
def attributes_hash(record, params = {})
|
||||
attributes_to_serialize.each_with_object({}) do |(_k, attribute), hash|
|
||||
def attributes_hash(record, fieldset = nil, params = {})
|
||||
attributes = attributes_to_serialize
|
||||
attributes = attributes.slice(*fieldset) if fieldset.present?
|
||||
attributes.each_with_object({}) do |(_k, attribute), hash|
|
||||
attribute.serialize(record, params, hash)
|
||||
end
|
||||
end
|
||||
|
||||
def relationships_hash(record, relationships = nil, params = {})
|
||||
def relationships_hash(record, relationships = nil, fieldset = nil, params = {})
|
||||
relationships = relationships_to_serialize if relationships.nil?
|
||||
relationships = relationships.slice(*fieldset) if fieldset.present?
|
||||
|
||||
relationships.each_with_object({}) do |(_k, relationship), hash|
|
||||
relationship.serialize(record, params, hash)
|
||||
end
|
||||
end
|
||||
|
||||
def record_hash(record, params = {})
|
||||
def record_hash(record, fieldset, params = {})
|
||||
if cached
|
||||
record_hash = Rails.cache.fetch(record.cache_key, expires_in: cache_length, race_condition_ttl: race_condition_ttl) do
|
||||
temp_hash = id_hash(id_from_record(record), record_type, true)
|
||||
temp_hash[:attributes] = attributes_hash(record, params) if attributes_to_serialize.present?
|
||||
temp_hash[:attributes] = attributes_hash(record, fieldset, params) if attributes_to_serialize.present?
|
||||
temp_hash[:relationships] = {}
|
||||
temp_hash[:relationships] = relationships_hash(record, cachable_relationships_to_serialize, params) if cachable_relationships_to_serialize.present?
|
||||
temp_hash[:relationships] = relationships_hash(record, cachable_relationships_to_serialize, fieldset, params) if cachable_relationships_to_serialize.present?
|
||||
temp_hash[:links] = links_hash(record, params) if data_links.present?
|
||||
temp_hash
|
||||
end
|
||||
@ -68,8 +71,8 @@ module FastJsonapi
|
||||
record_hash
|
||||
else
|
||||
record_hash = id_hash(id_from_record(record), record_type, true)
|
||||
record_hash[:attributes] = attributes_hash(record, params) if attributes_to_serialize.present?
|
||||
record_hash[:relationships] = relationships_hash(record, nil, params) if relationships_to_serialize.present?
|
||||
record_hash[:attributes] = attributes_hash(record, fieldset, params) if attributes_to_serialize.present?
|
||||
record_hash[:relationships] = relationships_hash(record, nil, fieldset, params) if relationships_to_serialize.present?
|
||||
record_hash[:links] = links_hash(record, params) if data_links.present?
|
||||
record_hash
|
||||
end
|
||||
@ -100,7 +103,7 @@ module FastJsonapi
|
||||
end
|
||||
|
||||
# includes handler
|
||||
def get_included_records(record, includes_list, known_included_objects, params = {})
|
||||
def get_included_records(record, includes_list, known_included_objects, fieldsets, params = {})
|
||||
return unless includes_list.present?
|
||||
|
||||
includes_list.sort.each_with_object([]) do |include_item, included_records|
|
||||
@ -120,7 +123,7 @@ module FastJsonapi
|
||||
|
||||
included_objects.each do |inc_obj|
|
||||
if remaining_items(items)
|
||||
serializer_records = serializer.get_included_records(inc_obj, remaining_items(items), known_included_objects, params)
|
||||
serializer_records = serializer.get_included_records(inc_obj, remaining_items(items), known_included_objects, fieldsets, params)
|
||||
included_records.concat(serializer_records) unless serializer_records.empty?
|
||||
end
|
||||
|
||||
@ -128,7 +131,8 @@ module FastJsonapi
|
||||
next if known_included_objects.key?(code)
|
||||
|
||||
known_included_objects[code] = inc_obj
|
||||
included_records << serializer.record_hash(inc_obj, params)
|
||||
|
||||
included_records << serializer.record_hash(inc_obj, fieldsets[serializer.record_type], params)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1,3 +1,3 @@
|
||||
module FastJsonapi
|
||||
VERSION = "1.2"
|
||||
VERSION = "1.3"
|
||||
end
|
||||
|
@ -230,6 +230,23 @@ describe FastJsonapi::ObjectSerializer do
|
||||
expect(serializable_hash[:data][:attributes][:title_with_year]).to eq "#{movie.name} (#{movie.release_year})"
|
||||
end
|
||||
end
|
||||
|
||||
context 'with &:proc' do
|
||||
before do
|
||||
movie.release_year = 2008
|
||||
MovieSerializer.attribute :released_in_year, &:release_year
|
||||
MovieSerializer.attribute :name, &:local_name
|
||||
end
|
||||
|
||||
after do
|
||||
MovieSerializer.attributes_to_serialize.delete(:released_in_year)
|
||||
end
|
||||
|
||||
it 'returns correct hash when serializable_hash is called' do
|
||||
expect(serializable_hash[:data][:attributes][:name]).to eq "english #{movie.name}"
|
||||
expect(serializable_hash[:data][:attributes][:released_in_year]).to eq movie.release_year
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#link' do
|
||||
|
48
spec/lib/object_serializer_fields_spec.rb
Normal file
48
spec/lib/object_serializer_fields_spec.rb
Normal file
@ -0,0 +1,48 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe FastJsonapi::ObjectSerializer do
|
||||
include_context 'movie class'
|
||||
|
||||
let(:fields) do
|
||||
{
|
||||
movie: %i[name actors advertising_campaign],
|
||||
actor: %i[name agency]
|
||||
}
|
||||
end
|
||||
|
||||
it 'only returns specified fields' do
|
||||
hash = MovieSerializer.new(movie, fields: fields).serializable_hash
|
||||
|
||||
expect(hash[:data][:attributes].keys.sort).to eq %i[name]
|
||||
end
|
||||
|
||||
it 'only returns specified relationships' do
|
||||
hash = MovieSerializer.new(movie, fields: fields).serializable_hash
|
||||
|
||||
expect(hash[:data][:relationships].keys.sort).to eq %i[actors advertising_campaign]
|
||||
end
|
||||
|
||||
it 'only returns specified fields for included relationships' do
|
||||
hash = MovieSerializer.new(movie, fields: fields, include: %i[actors]).serializable_hash
|
||||
|
||||
expect(hash[:included].first[:attributes].keys.sort).to eq %i[name]
|
||||
end
|
||||
|
||||
it 'only returns specified relationships for included relationships' do
|
||||
hash = MovieSerializer.new(movie, fields: fields, include: %i[actors advertising_campaign]).serializable_hash
|
||||
|
||||
expect(hash[:included].first[:relationships].keys.sort).to eq %i[agency]
|
||||
end
|
||||
|
||||
it 'returns all fields for included relationships when no explicit fields have been specified' do
|
||||
hash = MovieSerializer.new(movie, fields: fields, include: %i[actors advertising_campaign]).serializable_hash
|
||||
|
||||
expect(hash[:included][3][:attributes].keys.sort).to eq %i[id name]
|
||||
end
|
||||
|
||||
it 'returns all fields for included relationships when no explicit fields have been specified' do
|
||||
hash = MovieSerializer.new(movie, fields: fields, include: %i[actors advertising_campaign]).serializable_hash
|
||||
|
||||
expect(hash[:included][3][:relationships].keys.sort).to eq %i[movie]
|
||||
end
|
||||
end
|
@ -95,6 +95,11 @@ describe FastJsonapi::ObjectSerializer do
|
||||
has_one :account
|
||||
end
|
||||
|
||||
it 'sets the correct record type' do
|
||||
expect(EmployeeSerializer.reflected_record_type).to eq :employee
|
||||
expect(EmployeeSerializer.record_type).to eq :employee
|
||||
end
|
||||
|
||||
context 'when testing inheritance of attributes' do
|
||||
|
||||
it 'includes parent attributes' do
|
||||
|
@ -64,7 +64,7 @@ describe FastJsonapi::ObjectSerializer do
|
||||
known_included_objects = {}
|
||||
included_records = []
|
||||
[movie, movie].each do |record|
|
||||
included_records.concat MovieSerializer.send(:get_included_records, record, includes_list, known_included_objects, nil)
|
||||
included_records.concat MovieSerializer.send(:get_included_records, record, includes_list, known_included_objects, {}, nil)
|
||||
end
|
||||
expect(included_records.size).to eq 3
|
||||
end
|
||||
|
@ -54,6 +54,10 @@ RSpec.shared_context 'movie class' do
|
||||
"#{id}"
|
||||
end
|
||||
|
||||
def local_name(locale = :english)
|
||||
"#{locale} #{name}"
|
||||
end
|
||||
|
||||
def url
|
||||
"http://movies.com/#{id}"
|
||||
end
|
||||
|
Loading…
x
Reference in New Issue
Block a user