commit
5ff3fa97da
98
README.md
98
README.md
@ -30,6 +30,9 @@ Fast JSON API serialized 250 records in 3.01 ms
|
||||
* [Collection Serialization](#collection-serialization)
|
||||
* [Caching](#caching)
|
||||
* [Params](#params)
|
||||
* [Conditional Attributes](#conditional-attributes)
|
||||
* [Conditional Relationships](#conditional-relationships)
|
||||
* [Sparse Fieldsets](#sparse-fieldsets)
|
||||
* [Contributing](#contributing)
|
||||
|
||||
|
||||
@ -205,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.
|
||||
|
||||
@ -259,6 +274,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:
|
||||
|
||||
@ -284,7 +319,6 @@ block you opt-in to using params by adding it as a block parameter.
|
||||
|
||||
```ruby
|
||||
class MovieSerializer
|
||||
class MovieSerializer
|
||||
include FastJsonapi::ObjectSerializer
|
||||
|
||||
attributes :name, :year
|
||||
@ -308,6 +342,68 @@ serializer.serializable_hash
|
||||
Custom attributes and relationships that only receive the resource are still possible by defining
|
||||
the block to only receive one argument.
|
||||
|
||||
### Conditional Attributes
|
||||
|
||||
Conditional attributes can be defined by passing a Proc to the `if` key on the `attribute` method. Return `true` if the attribute should be serialized, and `false` if not. The record and any params passed to the serializer are available inside the Proc as the first and second parameters, respectively.
|
||||
|
||||
```ruby
|
||||
class MovieSerializer
|
||||
include FastJsonapi::ObjectSerializer
|
||||
|
||||
attributes :name, :year
|
||||
attribute :release_year, if: Proc.new do |record|
|
||||
# Release year will only be serialized if it's greater than 1990
|
||||
record.release_year > 1990
|
||||
end
|
||||
|
||||
attribute :director, if: Proc.new do |record, params|
|
||||
# The director will be serialized only if the :admin key of params is true
|
||||
params && params[:admin] == true
|
||||
end
|
||||
end
|
||||
|
||||
# ...
|
||||
current_user = User.find(cookies[:current_user_id])
|
||||
serializer = MovieSerializer.new(movie, { params: { admin: current_user.admin? }})
|
||||
serializer.serializable_hash
|
||||
```
|
||||
|
||||
### Conditional Relationships
|
||||
|
||||
Conditional relationships can be defined by passing a Proc to the `if` key. Return `true` if the relationship should be serialized, and `false` if not. The record and any params passed to the serializer are available inside the Proc as the first and second parameters, respectively.
|
||||
|
||||
```ruby
|
||||
class MovieSerializer
|
||||
include FastJsonapi::ObjectSerializer
|
||||
|
||||
# Actors will only be serialized if the record has any associated actors
|
||||
has_many :actors, if: Proc.new { |record| record.actors.any? }
|
||||
|
||||
# Owner will only be serialized if the :admin key of params is true
|
||||
belongs_to :owner, if: Proc.new { |record, params| params && params[:admin] == true }
|
||||
end
|
||||
|
||||
# ...
|
||||
current_user = User.find(cookies[:current_user_id])
|
||||
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
|
||||
|
@ -1,20 +1,18 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
if defined?(::ActiveRecord)
|
||||
::ActiveRecord::Associations::Builder::HasOne.class_eval do
|
||||
# Based on
|
||||
# https://github.com/rails/rails/blob/master/activerecord/lib/active_record/associations/builder/collection_association.rb#L50
|
||||
# https://github.com/rails/rails/blob/master/activerecord/lib/active_record/associations/builder/singular_association.rb#L11
|
||||
def self.define_accessors(mixin, reflection)
|
||||
super
|
||||
name = reflection.name
|
||||
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
||||
def #{name}_id
|
||||
# if an attribute is already defined with this methods name we should just use it
|
||||
return read_attribute(__method__) if has_attribute?(__method__)
|
||||
association(:#{name}).reader.try(:id)
|
||||
end
|
||||
CODE
|
||||
end
|
||||
::ActiveRecord::Associations::Builder::HasOne.class_eval do
|
||||
# Based on
|
||||
# https://github.com/rails/rails/blob/master/activerecord/lib/active_record/associations/builder/collection_association.rb#L50
|
||||
# https://github.com/rails/rails/blob/master/activerecord/lib/active_record/associations/builder/singular_association.rb#L11
|
||||
def self.define_accessors(mixin, reflection)
|
||||
super
|
||||
name = reflection.name
|
||||
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
||||
def #{name}_id
|
||||
# if an attribute is already defined with this methods name we should just use it
|
||||
return read_attribute(__method__) if has_attribute?(__method__)
|
||||
association(:#{name}).reader.try(:id)
|
||||
end
|
||||
CODE
|
||||
end
|
||||
end
|
||||
|
@ -2,5 +2,9 @@
|
||||
|
||||
module FastJsonapi
|
||||
require 'fast_jsonapi/object_serializer'
|
||||
require 'extensions/has_one'
|
||||
if defined?(::Rails)
|
||||
require 'fast_jsonapi/railtie'
|
||||
elsif defined?(::ActiveRecord)
|
||||
require 'extensions/has_one'
|
||||
end
|
||||
end
|
||||
|
29
lib/fast_jsonapi/attribute.rb
Normal file
29
lib/fast_jsonapi/attribute.rb
Normal file
@ -0,0 +1,29 @@
|
||||
module FastJsonapi
|
||||
class Attribute
|
||||
attr_reader :key, :method, :conditional_proc
|
||||
|
||||
def initialize(key:, method:, options: {})
|
||||
@key = key
|
||||
@method = method
|
||||
@conditional_proc = options[:if]
|
||||
end
|
||||
|
||||
def serialize(record, serialization_params, output_hash)
|
||||
if include_attribute?(record, serialization_params)
|
||||
output_hash[key] = if method.is_a?(Proc)
|
||||
method.arity.abs == 1 ? method.call(record) : method.call(record, serialization_params)
|
||||
else
|
||||
record.public_send(method)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def include_attribute?(record, serialization_params)
|
||||
if conditional_proc.present?
|
||||
conditional_proc.call(record, serialization_params)
|
||||
else
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
18
lib/fast_jsonapi/link.rb
Normal file
18
lib/fast_jsonapi/link.rb
Normal file
@ -0,0 +1,18 @@
|
||||
module FastJsonapi
|
||||
class Link
|
||||
attr_reader :key, :method
|
||||
|
||||
def initialize(key:, method:)
|
||||
@key = key
|
||||
@method = method
|
||||
end
|
||||
|
||||
def serialize(record, serialization_params, output_hash)
|
||||
output_hash[key] = if method.is_a?(Proc)
|
||||
method.arity == 1 ? method.call(record) : method.call(record, serialization_params)
|
||||
else
|
||||
record.public_send(method)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -3,6 +3,9 @@
|
||||
require 'active_support/core_ext/object'
|
||||
require 'active_support/concern'
|
||||
require 'active_support/inflector'
|
||||
require 'fast_jsonapi/attribute'
|
||||
require 'fast_jsonapi/relationship'
|
||||
require 'fast_jsonapi/link'
|
||||
require 'fast_jsonapi/serialization_core'
|
||||
|
||||
module FastJsonapi
|
||||
@ -25,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
|
||||
@ -38,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
|
||||
|
||||
@ -48,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
|
||||
@ -67,11 +71,14 @@ module FastJsonapi
|
||||
private
|
||||
|
||||
def process_options(options)
|
||||
@fieldsets = deep_symbolize(options[:fields].presence || {})
|
||||
|
||||
return if options.blank?
|
||||
|
||||
@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)
|
||||
|
||||
@ -81,8 +88,22 @@ module FastJsonapi
|
||||
end
|
||||
end
|
||||
|
||||
def is_collection?(resource)
|
||||
resource.respond_to?(:each) && !resource.respond_to?(:each_pair)
|
||||
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?
|
||||
|
||||
resource.respond_to?(:size) && !resource.respond_to?(:each_pair)
|
||||
end
|
||||
|
||||
class_methods do
|
||||
@ -98,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
|
||||
@ -118,6 +140,9 @@ module FastJsonapi
|
||||
underscore: :underscore
|
||||
}
|
||||
self.transform_method = mapping[transform_name.to_sym]
|
||||
|
||||
# ensure that the record type is correctly transformed
|
||||
set_type(reflected_record_type) if reflected_record_type
|
||||
end
|
||||
|
||||
def run_key_transform(input)
|
||||
@ -149,48 +174,51 @@ module FastJsonapi
|
||||
|
||||
def attributes(*attributes_list, &block)
|
||||
attributes_list = attributes_list.first if attributes_list.first.class.is_a?(Array)
|
||||
options = attributes_list.last.is_a?(Hash) ? attributes_list.pop : {}
|
||||
self.attributes_to_serialize = {} if self.attributes_to_serialize.nil?
|
||||
|
||||
attributes_list.each do |attr_name|
|
||||
method_name = attr_name
|
||||
key = run_key_transform(method_name)
|
||||
attributes_to_serialize[key] = block || method_name
|
||||
attributes_to_serialize[key] = Attribute.new(
|
||||
key: key,
|
||||
method: block || method_name,
|
||||
options: options
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
alias_method :attribute, :attributes
|
||||
|
||||
def add_relationship(name, relationship)
|
||||
def add_relationship(relationship)
|
||||
self.relationships_to_serialize = {} if relationships_to_serialize.nil?
|
||||
self.cachable_relationships_to_serialize = {} if cachable_relationships_to_serialize.nil?
|
||||
self.uncachable_relationships_to_serialize = {} if uncachable_relationships_to_serialize.nil?
|
||||
|
||||
if !relationship[:cached]
|
||||
self.uncachable_relationships_to_serialize[name] = relationship
|
||||
|
||||
if !relationship.cached
|
||||
self.uncachable_relationships_to_serialize[relationship.name] = relationship
|
||||
else
|
||||
self.cachable_relationships_to_serialize[name] = relationship
|
||||
self.cachable_relationships_to_serialize[relationship.name] = relationship
|
||||
end
|
||||
self.relationships_to_serialize[name] = relationship
|
||||
end
|
||||
self.relationships_to_serialize[relationship.name] = relationship
|
||||
end
|
||||
|
||||
def has_many(relationship_name, options = {}, &block)
|
||||
name = relationship_name.to_sym
|
||||
hash = create_relationship_hash(relationship_name, :has_many, options, block)
|
||||
add_relationship(name, hash)
|
||||
relationship = create_relationship(relationship_name, :has_many, options, block)
|
||||
add_relationship(relationship)
|
||||
end
|
||||
|
||||
def has_one(relationship_name, options = {}, &block)
|
||||
name = relationship_name.to_sym
|
||||
hash = create_relationship_hash(relationship_name, :has_one, options, block)
|
||||
add_relationship(name, hash)
|
||||
relationship = create_relationship(relationship_name, :has_one, options, block)
|
||||
add_relationship(relationship)
|
||||
end
|
||||
|
||||
def belongs_to(relationship_name, options = {}, &block)
|
||||
name = relationship_name.to_sym
|
||||
hash = create_relationship_hash(relationship_name, :belongs_to, options, block)
|
||||
add_relationship(name, hash)
|
||||
relationship = create_relationship(relationship_name, :belongs_to, options, block)
|
||||
add_relationship(relationship)
|
||||
end
|
||||
|
||||
def create_relationship_hash(base_key, relationship_type, options, block)
|
||||
def create_relationship(base_key, relationship_type, options, block)
|
||||
name = base_key.to_sym
|
||||
if relationship_type == :has_many
|
||||
base_serialization_key = base_key.to_s.singularize
|
||||
@ -201,7 +229,7 @@ module FastJsonapi
|
||||
base_key_sym = name
|
||||
id_postfix = '_id'
|
||||
end
|
||||
{
|
||||
Relationship.new(
|
||||
key: options[:key] || run_key_transform(base_key),
|
||||
name: name,
|
||||
id_method_name: options[:id_method_name] || "#{base_serialization_key}#{id_postfix}".to_sym,
|
||||
@ -210,9 +238,10 @@ module FastJsonapi
|
||||
object_block: block,
|
||||
serializer: compute_serializer_name(options[:serializer] || base_key_sym),
|
||||
relationship_type: relationship_type,
|
||||
cached: options[:cached] || false,
|
||||
polymorphic: fetch_polymorphic_option(options)
|
||||
}
|
||||
cached: options[:cached],
|
||||
polymorphic: fetch_polymorphic_option(options),
|
||||
conditional_proc: options[:if]
|
||||
)
|
||||
end
|
||||
|
||||
def compute_serializer_name(serializer_key)
|
||||
@ -233,7 +262,11 @@ module FastJsonapi
|
||||
self.data_links = {} if self.data_links.nil?
|
||||
link_method_name = link_name if link_method_name.nil?
|
||||
key = run_key_transform(link_name)
|
||||
self.data_links[key] = block || link_method_name
|
||||
|
||||
self.data_links[key] = Link.new(
|
||||
key: key,
|
||||
method: block || link_method_name
|
||||
)
|
||||
end
|
||||
|
||||
def validate_includes!(includes)
|
||||
@ -244,8 +277,8 @@ module FastJsonapi
|
||||
parse_include_item(include_item).each do |parsed_include|
|
||||
relationship_to_include = klass.relationships_to_serialize[parsed_include]
|
||||
raise ArgumentError, "#{parsed_include} is not specified as a relationship on #{klass.name}" unless relationship_to_include
|
||||
raise NotImplementedError if relationship_to_include[:polymorphic].is_a?(Hash)
|
||||
klass = relationship_to_include[:serializer].to_s.constantize
|
||||
raise NotImplementedError if relationship_to_include.polymorphic.is_a?(Hash)
|
||||
klass = relationship_to_include.serializer.to_s.constantize
|
||||
end
|
||||
end
|
||||
end
|
||||
|
11
lib/fast_jsonapi/railtie.rb
Normal file
11
lib/fast_jsonapi/railtie.rb
Normal file
@ -0,0 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails/railtie'
|
||||
|
||||
class Railtie < Rails::Railtie
|
||||
initializer 'fast_jsonapi.active_record' do
|
||||
ActiveSupport.on_load :active_record do
|
||||
require 'extensions/has_one'
|
||||
end
|
||||
end
|
||||
end
|
99
lib/fast_jsonapi/relationship.rb
Normal file
99
lib/fast_jsonapi/relationship.rb
Normal file
@ -0,0 +1,99 @@
|
||||
module FastJsonapi
|
||||
class Relationship
|
||||
attr_reader :key, :name, :id_method_name, :record_type, :object_method_name, :object_block, :serializer, :relationship_type, :cached, :polymorphic, :conditional_proc
|
||||
|
||||
def initialize(
|
||||
key:,
|
||||
name:,
|
||||
id_method_name:,
|
||||
record_type:,
|
||||
object_method_name:,
|
||||
object_block:,
|
||||
serializer:,
|
||||
relationship_type:,
|
||||
cached: false,
|
||||
polymorphic:,
|
||||
conditional_proc:
|
||||
)
|
||||
@key = key
|
||||
@name = name
|
||||
@id_method_name = id_method_name
|
||||
@record_type = record_type
|
||||
@object_method_name = object_method_name
|
||||
@object_block = object_block
|
||||
@serializer = serializer
|
||||
@relationship_type = relationship_type
|
||||
@cached = cached
|
||||
@polymorphic = polymorphic
|
||||
@conditional_proc = conditional_proc
|
||||
end
|
||||
|
||||
def serialize(record, serialization_params, output_hash)
|
||||
if include_relationship?(record, serialization_params)
|
||||
empty_case = relationship_type == :has_many ? [] : nil
|
||||
output_hash[key] = {
|
||||
data: ids_hash_from_record_and_relationship(record, serialization_params) || empty_case
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def fetch_associated_object(record, params)
|
||||
return object_block.call(record, params) unless object_block.nil?
|
||||
record.send(object_method_name)
|
||||
end
|
||||
|
||||
def include_relationship?(record, serialization_params)
|
||||
if conditional_proc.present?
|
||||
conditional_proc.call(record, serialization_params)
|
||||
else
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def ids_hash_from_record_and_relationship(record, params = {})
|
||||
return ids_hash(
|
||||
fetch_id(record, params)
|
||||
) unless polymorphic
|
||||
|
||||
return unless associated_object = fetch_associated_object(record, params)
|
||||
|
||||
return associated_object.map do |object|
|
||||
id_hash_from_record object, polymorphic
|
||||
end if associated_object.respond_to? :map
|
||||
|
||||
id_hash_from_record associated_object, polymorphic
|
||||
end
|
||||
|
||||
def id_hash_from_record(record, record_types)
|
||||
# memoize the record type within the record_types dictionary, then assigning to record_type:
|
||||
associated_record_type = record_types[record.class] ||= record.class.name.underscore.to_sym
|
||||
id_hash(record.id, associated_record_type)
|
||||
end
|
||||
|
||||
def ids_hash(ids)
|
||||
return ids.map { |id| id_hash(id, record_type) } if ids.respond_to? :map
|
||||
id_hash(ids, record_type) # ids variable is just a single id here
|
||||
end
|
||||
|
||||
def id_hash(id, record_type, default_return=false)
|
||||
if id.present?
|
||||
{ id: id.to_s, type: record_type }
|
||||
else
|
||||
default_return ? { id: nil, type: record_type } : nil
|
||||
end
|
||||
end
|
||||
|
||||
def fetch_id(record, params)
|
||||
unless object_block.nil?
|
||||
object = object_block.call(record, params)
|
||||
|
||||
return object.map(&:id) if object.respond_to? :map
|
||||
return object.try(:id)
|
||||
end
|
||||
|
||||
record.public_send(id_method_name)
|
||||
end
|
||||
end
|
||||
end
|
@ -34,73 +34,36 @@ module FastJsonapi
|
||||
end
|
||||
end
|
||||
|
||||
def ids_hash(ids, record_type)
|
||||
return ids.map { |id| id_hash(id, record_type) } if ids.respond_to? :map
|
||||
id_hash(ids, record_type) # ids variable is just a single id here
|
||||
end
|
||||
|
||||
def id_hash_from_record(record, record_types)
|
||||
# memoize the record type within the record_types dictionary, then assigning to record_type:
|
||||
record_type = record_types[record.class] ||= record.class.name.underscore.to_sym
|
||||
id_hash(record.id, record_type)
|
||||
end
|
||||
|
||||
def ids_hash_from_record_and_relationship(record, relationship, params = {})
|
||||
polymorphic = relationship[:polymorphic]
|
||||
|
||||
return ids_hash(
|
||||
fetch_id(record, relationship, params),
|
||||
relationship[:record_type]
|
||||
) unless polymorphic
|
||||
|
||||
return unless associated_object = fetch_associated_object(record, relationship, params)
|
||||
|
||||
return associated_object.map do |object|
|
||||
id_hash_from_record object, polymorphic
|
||||
end if associated_object.respond_to? :map
|
||||
|
||||
id_hash_from_record associated_object, polymorphic
|
||||
end
|
||||
|
||||
def links_hash(record, params = {})
|
||||
data_links.each_with_object({}) do |(key, method), link_hash|
|
||||
link_hash[key] = if method.is_a?(Proc)
|
||||
method.arity == 1 ? method.call(record) : method.call(record, params)
|
||||
else
|
||||
record.public_send(method)
|
||||
end
|
||||
data_links.each_with_object({}) do |(_k, link), hash|
|
||||
link.serialize(record, params, hash)
|
||||
end
|
||||
end
|
||||
|
||||
def attributes_hash(record, params = {})
|
||||
attributes_to_serialize.each_with_object({}) do |(key, method), attr_hash|
|
||||
attr_hash[key] = if method.is_a?(Proc)
|
||||
method.arity == 1 ? method.call(record) : method.call(record, params)
|
||||
else
|
||||
record.public_send(method)
|
||||
end
|
||||
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|
|
||||
name = relationship[:key]
|
||||
empty_case = relationship[:relationship_type] == :has_many ? [] : nil
|
||||
hash[name] = {
|
||||
data: ids_hash_from_record_and_relationship(record, relationship, params) || empty_case
|
||||
}
|
||||
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
|
||||
@ -108,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
|
||||
@ -140,25 +103,27 @@ 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|
|
||||
items = parse_include_item(include_item)
|
||||
items.each do |item|
|
||||
next unless relationships_to_serialize && relationships_to_serialize[item]
|
||||
raise NotImplementedError if @relationships_to_serialize[item][:polymorphic].is_a?(Hash)
|
||||
record_type = @relationships_to_serialize[item][:record_type]
|
||||
serializer = @relationships_to_serialize[item][:serializer].to_s.constantize
|
||||
relationship_type = @relationships_to_serialize[item][:relationship_type]
|
||||
relationship_item = relationships_to_serialize[item]
|
||||
next unless relationship_item.include_relationship?(record, params)
|
||||
raise NotImplementedError if relationship_item.polymorphic.is_a?(Hash)
|
||||
record_type = relationship_item.record_type
|
||||
serializer = relationship_item.serializer.to_s.constantize
|
||||
relationship_type = relationship_item.relationship_type
|
||||
|
||||
included_objects = fetch_associated_object(record, @relationships_to_serialize[item], params)
|
||||
included_objects = relationship_item.fetch_associated_object(record, params)
|
||||
next if included_objects.blank?
|
||||
included_objects = [included_objects] unless relationship_type == :has_many
|
||||
|
||||
included_objects.each do |inc_obj|
|
||||
if remaining_items(items)
|
||||
serializer_records = serializer.get_included_records(inc_obj, remaining_items(items), known_included_objects)
|
||||
serializer_records = serializer.get_included_records(inc_obj, remaining_items(items), known_included_objects, fieldsets)
|
||||
included_records.concat(serializer_records) unless serializer_records.empty?
|
||||
end
|
||||
|
||||
@ -166,27 +131,12 @@ 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
|
||||
end
|
||||
|
||||
def fetch_associated_object(record, relationship, params)
|
||||
return relationship[:object_block].call(record, params) unless relationship[:object_block].nil?
|
||||
record.send(relationship[:object_method_name])
|
||||
end
|
||||
|
||||
def fetch_id(record, relationship, params)
|
||||
unless relationship[:object_block].nil?
|
||||
object = relationship[:object_block].call(record, params)
|
||||
|
||||
return object.map(&:id) if object.respond_to? :map
|
||||
return object.id
|
||||
end
|
||||
|
||||
record.public_send(relationship[:id_method_name])
|
||||
end
|
||||
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
|
||||
@ -312,7 +329,6 @@ describe FastJsonapi::ObjectSerializer do
|
||||
movie_type_serializer_class.instance_eval do
|
||||
include FastJsonapi::ObjectSerializer
|
||||
set_key_transform key_transform
|
||||
set_type :movie_type
|
||||
attributes :name
|
||||
end
|
||||
end
|
||||
@ -321,25 +337,25 @@ describe FastJsonapi::ObjectSerializer do
|
||||
context 'when key_transform is dash' do
|
||||
let(:key_transform) { :dash }
|
||||
|
||||
it_behaves_like 'returning key transformed hash', :'movie-type', :'release-year'
|
||||
it_behaves_like 'returning key transformed hash', :'movie-type', :'dash-movie-type', :'release-year'
|
||||
end
|
||||
|
||||
context 'when key_transform is camel' do
|
||||
let(:key_transform) { :camel }
|
||||
|
||||
it_behaves_like 'returning key transformed hash', :MovieType, :ReleaseYear
|
||||
it_behaves_like 'returning key transformed hash', :MovieType, :CamelMovieType, :ReleaseYear
|
||||
end
|
||||
|
||||
context 'when key_transform is camel_lower' do
|
||||
let(:key_transform) { :camel_lower }
|
||||
|
||||
it_behaves_like 'returning key transformed hash', :movieType, :releaseYear
|
||||
it_behaves_like 'returning key transformed hash', :movieType, :camelLowerMovieType, :releaseYear
|
||||
end
|
||||
|
||||
context 'when key_transform is underscore' do
|
||||
let(:key_transform) { :underscore }
|
||||
|
||||
it_behaves_like 'returning key transformed hash', :movie_type, :release_year
|
||||
it_behaves_like 'returning key transformed hash', :movie_type, :underscore_movie_type, :release_year
|
||||
end
|
||||
end
|
||||
end
|
||||
|
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
|
||||
@ -113,7 +118,7 @@ describe FastJsonapi::ObjectSerializer do
|
||||
end
|
||||
|
||||
it 'includes child attributes' do
|
||||
expect(EmployeeSerializer.attributes_to_serialize[:location]).to eq(:location)
|
||||
expect(EmployeeSerializer.attributes_to_serialize[:location].method).to eq(:location)
|
||||
end
|
||||
|
||||
it 'doesnt change parent class attributes' do
|
||||
|
@ -97,6 +97,13 @@ describe FastJsonapi::ObjectSerializer do
|
||||
expect(serializable_hash['data']['relationships']['owner']['data']).to be nil
|
||||
end
|
||||
|
||||
it 'returns correct json when belongs_to returns nil and there is a block for the relationship' do
|
||||
movie.owner_id = nil
|
||||
json = MovieSerializer.new(movie, {include: [:owner]}).serialized_json
|
||||
serializable_hash = JSON.parse(json)
|
||||
expect(serializable_hash['data']['relationships']['owner']['data']).to be nil
|
||||
end
|
||||
|
||||
it 'returns correct json when has_one returns nil' do
|
||||
supplier.account_id = nil
|
||||
json = SupplierSerializer.new(supplier).serialized_json
|
||||
@ -302,4 +309,142 @@ describe FastJsonapi::ObjectSerializer do
|
||||
expect(serializable_hash[:included][0][:links][:self]).to eq url
|
||||
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
|
||||
json = MovieOptionalRecordDataSerializer.new(movie).serialized_json
|
||||
serializable_hash = JSON.parse(json)
|
||||
expect(serializable_hash['data']['attributes']['release_year']).to eq movie.release_year
|
||||
end
|
||||
|
||||
it "doesn't return optional attribute when attribute is not included" do
|
||||
movie.release_year = 1970
|
||||
json = MovieOptionalRecordDataSerializer.new(movie).serialized_json
|
||||
serializable_hash = JSON.parse(json)
|
||||
expect(serializable_hash['data']['attributes'].has_key?('release_year')).to be_falsey
|
||||
end
|
||||
end
|
||||
|
||||
context 'when optional attributes are determined by params data' do
|
||||
it 'returns optional attribute when attribute is included' do
|
||||
movie.director = 'steven spielberg'
|
||||
json = MovieOptionalParamsDataSerializer.new(movie, { params: { admin: true }}).serialized_json
|
||||
serializable_hash = JSON.parse(json)
|
||||
expect(serializable_hash['data']['attributes']['director']).to eq 'steven spielberg'
|
||||
end
|
||||
|
||||
it "doesn't return optional attribute when attribute is not included" do
|
||||
movie.director = 'steven spielberg'
|
||||
json = MovieOptionalParamsDataSerializer.new(movie, { params: { admin: false }}).serialized_json
|
||||
serializable_hash = JSON.parse(json)
|
||||
expect(serializable_hash['data']['attributes'].has_key?('director')).to be_falsey
|
||||
end
|
||||
end
|
||||
|
||||
context 'when optional relationships are determined by record data' do
|
||||
it 'returns optional relationship when relationship is included' do
|
||||
json = MovieOptionalRelationshipSerializer.new(movie).serialized_json
|
||||
serializable_hash = JSON.parse(json)
|
||||
expect(serializable_hash['data']['relationships'].has_key?('actors')).to be_truthy
|
||||
end
|
||||
|
||||
context "when relationship is not included" do
|
||||
let(:json) {
|
||||
MovieOptionalRelationshipSerializer.new(movie, options).serialized_json
|
||||
}
|
||||
let(:options) {
|
||||
{}
|
||||
}
|
||||
let(:serializable_hash) {
|
||||
JSON.parse(json)
|
||||
}
|
||||
|
||||
it "doesn't return optional relationship" do
|
||||
movie.actor_ids = []
|
||||
expect(serializable_hash['data']['relationships'].has_key?('actors')).to be_falsey
|
||||
end
|
||||
|
||||
it "doesn't include optional relationship" do
|
||||
movie.actor_ids = []
|
||||
options[:include] = [:actors]
|
||||
expect(serializable_hash['included']).to be_blank
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when optional relationships are determined by params data' do
|
||||
it 'returns optional relationship when relationship is included' do
|
||||
json = MovieOptionalRelationshipWithParamsSerializer.new(movie, { params: { admin: true }}).serialized_json
|
||||
serializable_hash = JSON.parse(json)
|
||||
expect(serializable_hash['data']['relationships'].has_key?('owner')).to be_truthy
|
||||
end
|
||||
|
||||
context "when relationship is not included" do
|
||||
let(:json) {
|
||||
MovieOptionalRelationshipWithParamsSerializer.new(movie, options).serialized_json
|
||||
}
|
||||
let(:options) {
|
||||
{ params: { admin: false }}
|
||||
}
|
||||
let(:serializable_hash) {
|
||||
JSON.parse(json)
|
||||
}
|
||||
|
||||
it "doesn't return optional relationship" do
|
||||
expect(serializable_hash['data']['relationships'].has_key?('owner')).to be_falsey
|
||||
end
|
||||
|
||||
it "doesn't include optional relationship" do
|
||||
options[:include] = [:owner]
|
||||
expect(serializable_hash['included']).to be_blank
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -17,31 +17,13 @@ describe FastJsonapi::ObjectSerializer do
|
||||
expect(result_hash).to be nil
|
||||
end
|
||||
|
||||
it 'returns the correct hash when ids_hash_from_record_and_relationship is called for a polymorphic association' do
|
||||
relationship = { name: :groupees, relationship_type: :has_many, object_method_name: :groupees, polymorphic: {} }
|
||||
results = GroupSerializer.send :ids_hash_from_record_and_relationship, group, relationship
|
||||
expect(results).to include({ id: "1", type: :person }, { id: "2", type: :group })
|
||||
end
|
||||
|
||||
it 'returns correct hash when ids_hash is called' do
|
||||
inputs = [{ids: %w(1 2 3), record_type: :movie}, {ids: %w(x y z), record_type: 'person'}]
|
||||
inputs.each do |hash|
|
||||
results = MovieSerializer.send(:ids_hash, hash[:ids], hash[:record_type])
|
||||
expect(results.map{|h| h[:id]}).to eq hash[:ids]
|
||||
expect(results[0][:type]).to eq hash[:record_type]
|
||||
end
|
||||
|
||||
result = MovieSerializer.send(:ids_hash, [], 'movie')
|
||||
expect(result).to be_empty
|
||||
end
|
||||
|
||||
it 'returns correct hash when attributes_hash is called' do
|
||||
attributes_hash = MovieSerializer.send(:attributes_hash, movie)
|
||||
attribute_names = attributes_hash.keys.sort
|
||||
expect(attribute_names).to eq MovieSerializer.attributes_to_serialize.keys.sort
|
||||
MovieSerializer.attributes_to_serialize.each do |key, method_name|
|
||||
MovieSerializer.attributes_to_serialize.each do |key, attribute|
|
||||
value = attributes_hash[key]
|
||||
expect(value).to eq movie.send(method_name)
|
||||
expect(value).to eq movie.send(attribute.method)
|
||||
end
|
||||
end
|
||||
|
||||
@ -57,7 +39,7 @@ describe FastJsonapi::ObjectSerializer do
|
||||
relationships_hash = MovieSerializer.send(:relationships_hash, movie)
|
||||
relationship_names = relationships_hash.keys.sort
|
||||
relationships_hashes = MovieSerializer.relationships_to_serialize.values
|
||||
expected_names = relationships_hashes.map{|relationship| relationship[:key]}.sort
|
||||
expected_names = relationships_hashes.map{|relationship| relationship.key}.sort
|
||||
expect(relationship_names).to eq expected_names
|
||||
end
|
||||
|
||||
@ -82,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
|
||||
|
@ -43,10 +43,21 @@ RSpec.shared_context 'movie class' do
|
||||
ac
|
||||
end
|
||||
|
||||
def owner
|
||||
return unless owner_id
|
||||
ow = Owner.new
|
||||
ow.id = owner_id
|
||||
ow
|
||||
end
|
||||
|
||||
def cache_key
|
||||
"#{id}"
|
||||
end
|
||||
|
||||
def local_name(locale = :english)
|
||||
"#{locale} #{name}"
|
||||
end
|
||||
|
||||
def url
|
||||
"http://movies.com/#{id}"
|
||||
end
|
||||
@ -146,6 +157,14 @@ RSpec.shared_context 'movie class' do
|
||||
attr_accessor :id
|
||||
end
|
||||
|
||||
class Owner
|
||||
attr_accessor :id
|
||||
end
|
||||
|
||||
class OwnerSerializer
|
||||
include FastJsonapi::ObjectSerializer
|
||||
end
|
||||
|
||||
# serializers
|
||||
class MovieSerializer
|
||||
include FastJsonapi::ObjectSerializer
|
||||
@ -153,7 +172,9 @@ RSpec.shared_context 'movie class' do
|
||||
# director attr is not mentioned intentionally
|
||||
attributes :name, :release_year
|
||||
has_many :actors
|
||||
belongs_to :owner, record_type: :user
|
||||
belongs_to :owner, record_type: :user do |object, params|
|
||||
object.owner
|
||||
end
|
||||
belongs_to :movie_type
|
||||
has_one :advertising_campaign
|
||||
end
|
||||
@ -261,6 +282,34 @@ RSpec.shared_context 'movie class' do
|
||||
set_type :account
|
||||
belongs_to :supplier
|
||||
end
|
||||
|
||||
class MovieOptionalRecordDataSerializer
|
||||
include FastJsonapi::ObjectSerializer
|
||||
set_type :movie
|
||||
attributes :name
|
||||
attribute :release_year, if: Proc.new { |record| record.release_year >= 2000 }
|
||||
end
|
||||
|
||||
class MovieOptionalParamsDataSerializer
|
||||
include FastJsonapi::ObjectSerializer
|
||||
set_type :movie
|
||||
attributes :name
|
||||
attribute :director, if: Proc.new { |record, params| params && params[:admin] == true }
|
||||
end
|
||||
|
||||
class MovieOptionalRelationshipSerializer
|
||||
include FastJsonapi::ObjectSerializer
|
||||
set_type :movie
|
||||
attributes :name
|
||||
has_many :actors, if: Proc.new { |record| record.actors.any? }
|
||||
end
|
||||
|
||||
class MovieOptionalRelationshipWithParamsSerializer
|
||||
include FastJsonapi::ObjectSerializer
|
||||
set_type :movie
|
||||
attributes :name
|
||||
belongs_to :owner, record_type: :user, if: Proc.new { |record, params| params && params[:admin] == true }
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
@ -1,18 +1,18 @@
|
||||
RSpec.shared_examples 'returning correct relationship hash' do |serializer, id_method_name, record_type|
|
||||
it 'returns correct relationship hash' do
|
||||
expect(relationship).to be_instance_of(Hash)
|
||||
expect(relationship.keys).to all(be_instance_of(Symbol))
|
||||
expect(relationship[:serializer]).to be serializer
|
||||
expect(relationship[:id_method_name]).to be id_method_name
|
||||
expect(relationship[:record_type]).to be record_type
|
||||
expect(relationship).to be_instance_of(FastJsonapi::Relationship)
|
||||
# expect(relationship.keys).to all(be_instance_of(Symbol))
|
||||
expect(relationship.serializer).to be serializer
|
||||
expect(relationship.id_method_name).to be id_method_name
|
||||
expect(relationship.record_type).to be record_type
|
||||
end
|
||||
end
|
||||
|
||||
RSpec.shared_examples 'returning key transformed hash' do |movie_type, release_year|
|
||||
RSpec.shared_examples 'returning key transformed hash' do |movie_type, serializer_type, release_year|
|
||||
it 'returns correctly transformed hash' do
|
||||
expect(hash[:data][0][:attributes]).to have_key(release_year)
|
||||
expect(hash[:data][0][:relationships]).to have_key(movie_type)
|
||||
expect(hash[:data][0][:relationships][movie_type][:data][:type]).to eq(movie_type)
|
||||
expect(hash[:included][0][:type]).to eq(movie_type)
|
||||
expect(hash[:included][0][:type]).to eq(serializer_type)
|
||||
end
|
||||
end
|
||||
|
Loading…
x
Reference in New Issue
Block a user