Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
ffa25cda0c | ||
|
f4ad6bc2ac |
85
README.md
85
README.md
@ -29,6 +29,7 @@ Fast JSON API serialized 250 records in 3.01 ms
|
|||||||
* [Key Transforms](#key-transforms)
|
* [Key Transforms](#key-transforms)
|
||||||
* [Collection Serialization](#collection-serialization)
|
* [Collection Serialization](#collection-serialization)
|
||||||
* [Caching](#caching)
|
* [Caching](#caching)
|
||||||
|
* [Params](#params)
|
||||||
* [Contributing](#contributing)
|
* [Contributing](#contributing)
|
||||||
|
|
||||||
|
|
||||||
@ -204,14 +205,44 @@ class MovieSerializer
|
|||||||
end
|
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.
|
||||||
|
|
||||||
|
You can configure the method to use on the object for example a link with key `self` will get set to the value returned by a method called `url` on the movie object.
|
||||||
|
|
||||||
|
You can also use a block to define a url as shown in `custom_url`. You can access params in these blocks as well as shown in `personalized_url`
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
class MovieSerializer
|
||||||
|
include FastJsonapi::ObjectSerializer
|
||||||
|
|
||||||
|
link :public_url
|
||||||
|
|
||||||
|
link :self, :url
|
||||||
|
|
||||||
|
link :custom_url do |object|
|
||||||
|
"http://movies.com/#{object.name}-(#{object.year})"
|
||||||
|
end
|
||||||
|
|
||||||
|
link :personalized_url do |object, params|
|
||||||
|
"http://movies.com/#{object.name}-#{params[:user].reference_code}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
### Compound Document
|
### Compound Document
|
||||||
|
|
||||||
Support for top-level included member through ` options[:include] `.
|
Support for top-level and nested included associations through ` options[:include] `.
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
options = {}
|
options = {}
|
||||||
options[:meta] = { total: 2 }
|
options[:meta] = { total: 2 }
|
||||||
options[:include] = [:actors]
|
options[:links] = {
|
||||||
|
self: '...',
|
||||||
|
next: '...',
|
||||||
|
prev: '...'
|
||||||
|
}
|
||||||
|
options[:include] = [:actors, :'actors.agency', :'actors.agency.state']
|
||||||
MovieSerializer.new([movie, movie], options).serialized_json
|
MovieSerializer.new([movie, movie], options).serialized_json
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -219,11 +250,17 @@ MovieSerializer.new([movie, movie], options).serialized_json
|
|||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
options[:meta] = { total: 2 }
|
options[:meta] = { total: 2 }
|
||||||
|
options[:links] = {
|
||||||
|
self: '...',
|
||||||
|
next: '...',
|
||||||
|
prev: '...'
|
||||||
|
}
|
||||||
hash = MovieSerializer.new([movie, movie], options).serializable_hash
|
hash = MovieSerializer.new([movie, movie], options).serializable_hash
|
||||||
json_string = MovieSerializer.new([movie, movie], options).serialized_json
|
json_string = MovieSerializer.new([movie, movie], options).serialized_json
|
||||||
```
|
```
|
||||||
|
|
||||||
### Caching
|
### Caching
|
||||||
|
Requires a `cache_key` method be defined on model:
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
class MovieSerializer
|
class MovieSerializer
|
||||||
@ -234,17 +271,56 @@ class MovieSerializer
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Params
|
||||||
|
|
||||||
|
In some cases, attribute values might require more information than what is
|
||||||
|
available on the record, for example, access privileges or other information
|
||||||
|
related to a current authenticated user. The `options[:params]` value covers these
|
||||||
|
cases by allowing you to pass in a hash of additional parameters necessary for
|
||||||
|
your use case.
|
||||||
|
|
||||||
|
Leveraging the new params is easy, when you define a custom attribute or relationship with a
|
||||||
|
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
|
||||||
|
attribute :can_view_early do |movie, params|
|
||||||
|
# in here, params is a hash containing the `:current_user` key
|
||||||
|
params[:current_user].is_employee? ? true : false
|
||||||
|
end
|
||||||
|
|
||||||
|
belongs_to :primary_agent do |movie, params|
|
||||||
|
# in here, params is a hash containing the `:current_user` key
|
||||||
|
params[:current_user].is_employee? ? true : false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# ...
|
||||||
|
current_user = User.find(cookies[:current_user_id])
|
||||||
|
serializer = MovieSerializer.new(movie, {params: {current_user: current_user}})
|
||||||
|
serializer.serializable_hash
|
||||||
|
```
|
||||||
|
|
||||||
|
Custom attributes and relationships that only receive the resource are still possible by defining
|
||||||
|
the block to only receive one argument.
|
||||||
|
|
||||||
### Customizable Options
|
### Customizable Options
|
||||||
|
|
||||||
Option | Purpose | Example
|
Option | Purpose | Example
|
||||||
------------ | ------------- | -------------
|
------------ | ------------- | -------------
|
||||||
set_type | Type name of Object | ```set_type :movie ```
|
set_type | Type name of Object | ```set_type :movie ```
|
||||||
set_id | ID of Object | ```set_id :owner_id ```
|
set_id | ID of Object | ```set_id :owner_id ```
|
||||||
cache_options | Hash to enable caching and set cache length | ```cache_options enabled: true, cache_length: 12.hours```
|
cache_options | Hash to enable caching and set cache length | ```cache_options enabled: true, cache_length: 12.hours, race_condition_ttl: 10.seconds```
|
||||||
id_method_name | Set custom method name to get ID of an object | ```has_many :locations, id_method_name: :place_ids ```
|
id_method_name | Set custom method name to get ID of an object | ```has_many :locations, id_method_name: :place_ids ```
|
||||||
object_method_name | Set custom method name to get related objects | ```has_many :locations, object_method_name: :places ```
|
object_method_name | Set custom method name to get related objects | ```has_many :locations, object_method_name: :places ```
|
||||||
record_type | Set custom Object Type for a relationship | ```belongs_to :owner, record_type: :user```
|
record_type | Set custom Object Type for a relationship | ```belongs_to :owner, record_type: :user```
|
||||||
serializer | Set custom Serializer for a relationship | ```has_many :actors, serializer: :custom_actor```
|
serializer | Set custom Serializer for a relationship | ```has_many :actors, serializer: :custom_actor``` or ```has_many :actors, serializer: MyApp::Api::V1::ActorSerializer```
|
||||||
|
polymorphic | Allows different record types for a polymorphic association | ```has_many :targets, polymorphic: true```
|
||||||
|
polymorphic | Sets custom record types for each object class in a polymorphic association | ```has_many :targets, polymorphic: { Person => :person, Group => :group }```
|
||||||
|
|
||||||
### Instrumentation
|
### Instrumentation
|
||||||
|
|
||||||
@ -304,4 +380,3 @@ rspec spec --tag performance:true
|
|||||||
Join the Netflix Studio Engineering team and help us build gems like this!
|
Join the Netflix Studio Engineering team and help us build gems like this!
|
||||||
|
|
||||||
* [Senior Ruby Engineer](https://jobs.netflix.com/jobs/864893)
|
* [Senior Ruby Engineer](https://jobs.netflix.com/jobs/864893)
|
||||||
* [Senior Platform Engineer](https://jobs.netflix.com/jobs/865783)
|
|
||||||
|
@ -1,12 +1,17 @@
|
|||||||
|
lib = File.expand_path('lib', __dir__)
|
||||||
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
||||||
|
|
||||||
|
require "fast_jsonapi/version"
|
||||||
|
|
||||||
Gem::Specification.new do |gem|
|
Gem::Specification.new do |gem|
|
||||||
gem.name = "fast_jsonapi"
|
gem.name = "fast_jsonapi"
|
||||||
gem.version = "1.1.1"
|
gem.version = FastJsonapi::VERSION
|
||||||
|
|
||||||
|
gem.required_ruby_version = '>= 2.0.0' if gem.respond_to? :required_ruby_version=
|
||||||
gem.required_rubygems_version = Gem::Requirement.new(">= 0") if gem.respond_to? :required_rubygems_version=
|
gem.required_rubygems_version = Gem::Requirement.new(">= 0") if gem.respond_to? :required_rubygems_version=
|
||||||
gem.metadata = { "allowed_push_host" => "https://rubygems.org" } if gem.respond_to? :metadata=
|
gem.metadata = { "allowed_push_host" => "https://rubygems.org" } if gem.respond_to? :metadata=
|
||||||
gem.require_paths = ["lib"]
|
gem.require_paths = ["lib"]
|
||||||
gem.authors = ["Shishir Kakaraddi", "Srinivas Raghunathan", "Adam Gross"]
|
gem.authors = ["Shishir Kakaraddi", "Srinivas Raghunathan", "Adam Gross"]
|
||||||
gem.date = "2018-02-01"
|
|
||||||
gem.description = "JSON API(jsonapi.org) serializer that works with rails and can be used to serialize any kind of ruby objects"
|
gem.description = "JSON API(jsonapi.org) serializer that works with rails and can be used to serialize any kind of ruby objects"
|
||||||
gem.email = ""
|
gem.email = ""
|
||||||
gem.extra_rdoc_files = [
|
gem.extra_rdoc_files = [
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
begin
|
if defined?(::ActiveRecord)
|
||||||
require 'active_record'
|
|
||||||
|
|
||||||
::ActiveRecord::Associations::Builder::HasOne.class_eval do
|
::ActiveRecord::Associations::Builder::HasOne.class_eval do
|
||||||
# Based on
|
# 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/collection_association.rb#L50
|
||||||
@ -12,11 +10,11 @@ begin
|
|||||||
name = reflection.name
|
name = reflection.name
|
||||||
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
||||||
def #{name}_id
|
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)
|
association(:#{name}).reader.try(:id)
|
||||||
end
|
end
|
||||||
CODE
|
CODE
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
rescue LoadError
|
|
||||||
# active_record can't be loaded so we shouldn't try to monkey-patch it.
|
|
||||||
end
|
end
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
require 'skylight'
|
||||||
|
|
||||||
|
SKYLIGHT_NORMALIZER_BASE_CLASS = begin
|
||||||
|
::Skylight::Core::Normalizers::Normalizer
|
||||||
|
rescue NameError
|
||||||
|
::Skylight::Normalizers::Normalizer
|
||||||
|
end
|
@ -1,11 +1,11 @@
|
|||||||
require 'skylight'
|
require 'fast_jsonapi/instrumentation/skylight/normalizers/base'
|
||||||
require 'fast_jsonapi/instrumentation/serializable_hash'
|
require 'fast_jsonapi/instrumentation/serializable_hash'
|
||||||
|
|
||||||
module FastJsonapi
|
module FastJsonapi
|
||||||
module Instrumentation
|
module Instrumentation
|
||||||
module Skylight
|
module Skylight
|
||||||
module Normalizers
|
module Normalizers
|
||||||
class SerializableHash < Skylight::Normalizers::Normalizer
|
class SerializableHash < SKYLIGHT_NORMALIZER_BASE_CLASS
|
||||||
|
|
||||||
register FastJsonapi::ObjectSerializer::SERIALIZABLE_HASH_NOTIFICATION
|
register FastJsonapi::ObjectSerializer::SERIALIZABLE_HASH_NOTIFICATION
|
||||||
|
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
require 'skylight'
|
require 'fast_jsonapi/instrumentation/skylight/normalizers/base'
|
||||||
require 'fast_jsonapi/instrumentation/serializable_hash'
|
require 'fast_jsonapi/instrumentation/serializable_hash'
|
||||||
|
|
||||||
module FastJsonapi
|
module FastJsonapi
|
||||||
module Instrumentation
|
module Instrumentation
|
||||||
module Skylight
|
module Skylight
|
||||||
module Normalizers
|
module Normalizers
|
||||||
class SerializedJson < Skylight::Normalizers::Normalizer
|
class SerializedJson < SKYLIGHT_NORMALIZER_BASE_CLASS
|
||||||
|
|
||||||
register FastJsonapi::ObjectSerializer::SERIALIZED_JSON_NOTIFICATION
|
register FastJsonapi::ObjectSerializer::SERIALIZED_JSON_NOTIFICATION
|
||||||
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'logger'
|
||||||
|
|
||||||
# Usage:
|
# Usage:
|
||||||
# class Movie
|
# class Movie
|
||||||
# def to_json(payload)
|
# def to_json(payload)
|
||||||
|
@ -10,8 +10,8 @@ module FastJsonapi
|
|||||||
extend ActiveSupport::Concern
|
extend ActiveSupport::Concern
|
||||||
include SerializationCore
|
include SerializationCore
|
||||||
|
|
||||||
SERIALIZABLE_HASH_NOTIFICATION = 'render.fast_jsonapi.serializable_hash'.freeze
|
SERIALIZABLE_HASH_NOTIFICATION = 'render.fast_jsonapi.serializable_hash'
|
||||||
SERIALIZED_JSON_NOTIFICATION = 'render.fast_jsonapi.serialized_json'.freeze
|
SERIALIZED_JSON_NOTIFICATION = 'render.fast_jsonapi.serialized_json'
|
||||||
|
|
||||||
included do
|
included do
|
||||||
# Set record_type based on the name of the serializer class
|
# Set record_type based on the name of the serializer class
|
||||||
@ -34,11 +34,12 @@ module FastJsonapi
|
|||||||
def hash_for_one_record
|
def hash_for_one_record
|
||||||
serializable_hash = { data: nil }
|
serializable_hash = { data: nil }
|
||||||
serializable_hash[:meta] = @meta if @meta.present?
|
serializable_hash[:meta] = @meta if @meta.present?
|
||||||
|
serializable_hash[:links] = @links if @links.present?
|
||||||
|
|
||||||
return serializable_hash unless @resource
|
return serializable_hash unless @resource
|
||||||
|
|
||||||
serializable_hash[:data] = self.class.record_hash(@resource)
|
serializable_hash[:data] = self.class.record_hash(@resource, @params)
|
||||||
serializable_hash[:included] = self.class.get_included_records(@resource, @includes, @known_included_objects) if @includes.present?
|
serializable_hash[:included] = self.class.get_included_records(@resource, @includes, @known_included_objects, @params) if @includes.present?
|
||||||
serializable_hash
|
serializable_hash
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -48,13 +49,14 @@ module FastJsonapi
|
|||||||
data = []
|
data = []
|
||||||
included = []
|
included = []
|
||||||
@resource.each do |record|
|
@resource.each do |record|
|
||||||
data << self.class.record_hash(record)
|
data << self.class.record_hash(record, @params)
|
||||||
included.concat self.class.get_included_records(record, @includes, @known_included_objects) if @includes.present?
|
included.concat self.class.get_included_records(record, @includes, @known_included_objects, @params) if @includes.present?
|
||||||
end
|
end
|
||||||
|
|
||||||
serializable_hash[:data] = data
|
serializable_hash[:data] = data
|
||||||
serializable_hash[:included] = included if @includes.present?
|
serializable_hash[:included] = included if @includes.present?
|
||||||
serializable_hash[:meta] = @meta if @meta.present?
|
serializable_hash[:meta] = @meta if @meta.present?
|
||||||
|
serializable_hash[:links] = @links if @links.present?
|
||||||
serializable_hash
|
serializable_hash
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -69,20 +71,13 @@ module FastJsonapi
|
|||||||
|
|
||||||
@known_included_objects = {}
|
@known_included_objects = {}
|
||||||
@meta = options[:meta]
|
@meta = options[:meta]
|
||||||
|
@links = options[:links]
|
||||||
|
@params = options[:params] || {}
|
||||||
|
raise ArgumentError.new("`params` option passed to serializer must be a hash") unless @params.is_a?(Hash)
|
||||||
|
|
||||||
if options[:include].present?
|
if options[:include].present?
|
||||||
@includes = options[:include].delete_if(&:blank?).map(&:to_sym)
|
@includes = options[:include].delete_if(&:blank?).map(&:to_sym)
|
||||||
validate_includes!(@includes)
|
self.class.validate_includes!(@includes)
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def validate_includes!(includes)
|
|
||||||
return if includes.blank?
|
|
||||||
|
|
||||||
existing_relationships = self.class.relationships_to_serialize.keys.to_set
|
|
||||||
|
|
||||||
unless existing_relationships.superset?(includes.to_set)
|
|
||||||
raise ArgumentError, "One of keys from #{includes} is not specified as a relationship on the serializer"
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -91,6 +86,20 @@ module FastJsonapi
|
|||||||
end
|
end
|
||||||
|
|
||||||
class_methods do
|
class_methods do
|
||||||
|
|
||||||
|
def inherited(subclass)
|
||||||
|
super(subclass)
|
||||||
|
subclass.attributes_to_serialize = attributes_to_serialize.dup if attributes_to_serialize.present?
|
||||||
|
subclass.relationships_to_serialize = relationships_to_serialize.dup if relationships_to_serialize.present?
|
||||||
|
subclass.cachable_relationships_to_serialize = cachable_relationships_to_serialize.dup if cachable_relationships_to_serialize.present?
|
||||||
|
subclass.uncachable_relationships_to_serialize = uncachable_relationships_to_serialize.dup if uncachable_relationships_to_serialize.present?
|
||||||
|
subclass.transform_method = transform_method
|
||||||
|
subclass.cache_length = cache_length
|
||||||
|
subclass.race_condition_ttl = race_condition_ttl
|
||||||
|
subclass.data_links = data_links
|
||||||
|
subclass.cached = cached
|
||||||
|
end
|
||||||
|
|
||||||
def reflected_record_type
|
def reflected_record_type
|
||||||
return @reflected_record_type if defined?(@reflected_record_type)
|
return @reflected_record_type if defined?(@reflected_record_type)
|
||||||
|
|
||||||
@ -108,11 +117,11 @@ module FastJsonapi
|
|||||||
dash: :dasherize,
|
dash: :dasherize,
|
||||||
underscore: :underscore
|
underscore: :underscore
|
||||||
}
|
}
|
||||||
@transform_method = mapping[transform_name.to_sym]
|
self.transform_method = mapping[transform_name.to_sym]
|
||||||
end
|
end
|
||||||
|
|
||||||
def run_key_transform(input)
|
def run_key_transform(input)
|
||||||
if @transform_method.present?
|
if self.transform_method.present?
|
||||||
input.to_s.send(*@transform_method).to_sym
|
input.to_s.send(*@transform_method).to_sym
|
||||||
else
|
else
|
||||||
input.to_sym
|
input.to_sym
|
||||||
@ -135,6 +144,7 @@ module FastJsonapi
|
|||||||
def cache_options(cache_options)
|
def cache_options(cache_options)
|
||||||
self.cached = cache_options[:enabled] || false
|
self.cached = cache_options[:enabled] || false
|
||||||
self.cache_length = cache_options[:cache_length] || 5.minutes
|
self.cache_length = cache_options[:cache_length] || 5.minutes
|
||||||
|
self.race_condition_ttl = cache_options[:race_condition_ttl] || 5.seconds
|
||||||
end
|
end
|
||||||
|
|
||||||
def attributes(*attributes_list, &block)
|
def attributes(*attributes_list, &block)
|
||||||
@ -162,67 +172,54 @@ module FastJsonapi
|
|||||||
self.relationships_to_serialize[name] = relationship
|
self.relationships_to_serialize[name] = relationship
|
||||||
end
|
end
|
||||||
|
|
||||||
def has_many(relationship_name, options = {})
|
def has_many(relationship_name, options = {}, &block)
|
||||||
name = relationship_name.to_sym
|
name = relationship_name.to_sym
|
||||||
singular_name = relationship_name.to_s.singularize
|
hash = create_relationship_hash(relationship_name, :has_many, options, block)
|
||||||
serializer_key = options[:serializer] || singular_name.to_sym
|
add_relationship(name, hash)
|
||||||
key = options[:key] || run_key_transform(relationship_name)
|
end
|
||||||
record_type = options[:record_type] || run_key_transform(singular_name)
|
|
||||||
relationship = {
|
def has_one(relationship_name, options = {}, &block)
|
||||||
key: key,
|
name = relationship_name.to_sym
|
||||||
|
hash = create_relationship_hash(relationship_name, :has_one, options, block)
|
||||||
|
add_relationship(name, hash)
|
||||||
|
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)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_relationship_hash(base_key, relationship_type, options, block)
|
||||||
|
name = base_key.to_sym
|
||||||
|
if relationship_type == :has_many
|
||||||
|
base_serialization_key = base_key.to_s.singularize
|
||||||
|
base_key_sym = base_serialization_key.to_sym
|
||||||
|
id_postfix = '_ids'
|
||||||
|
else
|
||||||
|
base_serialization_key = base_key
|
||||||
|
base_key_sym = name
|
||||||
|
id_postfix = '_id'
|
||||||
|
end
|
||||||
|
{
|
||||||
|
key: options[:key] || run_key_transform(base_key),
|
||||||
name: name,
|
name: name,
|
||||||
id_method_name: options[:id_method_name] || (singular_name + '_ids').to_sym,
|
id_method_name: options[:id_method_name] || "#{base_serialization_key}#{id_postfix}".to_sym,
|
||||||
record_type: record_type,
|
record_type: options[:record_type] || run_key_transform(base_key_sym),
|
||||||
object_method_name: options[:object_method_name] || name,
|
object_method_name: options[:object_method_name] || name,
|
||||||
serializer: compute_serializer_name(serializer_key),
|
object_block: block,
|
||||||
relationship_type: :has_many,
|
serializer: compute_serializer_name(options[:serializer] || base_key_sym),
|
||||||
|
relationship_type: relationship_type,
|
||||||
cached: options[:cached] || false,
|
cached: options[:cached] || false,
|
||||||
polymorphic: fetch_polymorphic_option(options)
|
polymorphic: fetch_polymorphic_option(options)
|
||||||
}
|
}
|
||||||
add_relationship(name, relationship)
|
|
||||||
end
|
|
||||||
|
|
||||||
def belongs_to(relationship_name, options = {})
|
|
||||||
name = relationship_name.to_sym
|
|
||||||
serializer_key = options[:serializer] || relationship_name.to_sym
|
|
||||||
key = options[:key] || run_key_transform(relationship_name)
|
|
||||||
record_type = options[:record_type] || run_key_transform(relationship_name)
|
|
||||||
add_relationship(name, {
|
|
||||||
key: key,
|
|
||||||
name: name,
|
|
||||||
id_method_name: options[:id_method_name] || (relationship_name.to_s + '_id').to_sym,
|
|
||||||
record_type: record_type,
|
|
||||||
object_method_name: options[:object_method_name] || name,
|
|
||||||
serializer: compute_serializer_name(serializer_key),
|
|
||||||
relationship_type: :belongs_to,
|
|
||||||
cached: options[:cached] || true,
|
|
||||||
polymorphic: fetch_polymorphic_option(options)
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
def has_one(relationship_name, options = {})
|
|
||||||
name = relationship_name.to_sym
|
|
||||||
serializer_key = options[:serializer] || name
|
|
||||||
key = options[:key] || run_key_transform(relationship_name)
|
|
||||||
record_type = options[:record_type] || run_key_transform(relationship_name)
|
|
||||||
add_relationship(name, {
|
|
||||||
key: key,
|
|
||||||
name: name,
|
|
||||||
id_method_name: options[:id_method_name] || (relationship_name.to_s + '_id').to_sym,
|
|
||||||
record_type: record_type,
|
|
||||||
object_method_name: options[:object_method_name] || name,
|
|
||||||
serializer: compute_serializer_name(serializer_key),
|
|
||||||
relationship_type: :has_one,
|
|
||||||
cached: options[:cached] || false,
|
|
||||||
polymorphic: fetch_polymorphic_option(options)
|
|
||||||
})
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def compute_serializer_name(serializer_key)
|
def compute_serializer_name(serializer_key)
|
||||||
|
return serializer_key unless serializer_key.is_a? Symbol
|
||||||
namespace = self.name.gsub(/()?\w+Serializer$/, '')
|
namespace = self.name.gsub(/()?\w+Serializer$/, '')
|
||||||
serializer_name = serializer_key.to_s.classify + 'Serializer'
|
serializer_name = serializer_key.to_s.classify + 'Serializer'
|
||||||
return (namespace + serializer_name).to_sym if namespace.present?
|
(namespace + serializer_name).to_sym
|
||||||
(serializer_key.to_s.classify + 'Serializer').to_sym
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_polymorphic_option(options)
|
def fetch_polymorphic_option(options)
|
||||||
@ -231,6 +228,27 @@ module FastJsonapi
|
|||||||
return option if option.respond_to? :keys
|
return option if option.respond_to? :keys
|
||||||
{}
|
{}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def link(link_name, link_method_name = nil, &block)
|
||||||
|
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
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate_includes!(includes)
|
||||||
|
return if includes.blank?
|
||||||
|
|
||||||
|
includes.detect do |include_item|
|
||||||
|
klass = self
|
||||||
|
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
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -4,6 +4,8 @@ require 'active_support/concern'
|
|||||||
require 'fast_jsonapi/multi_to_json'
|
require 'fast_jsonapi/multi_to_json'
|
||||||
|
|
||||||
module FastJsonapi
|
module FastJsonapi
|
||||||
|
MandatoryField = Class.new(StandardError)
|
||||||
|
|
||||||
module SerializationCore
|
module SerializationCore
|
||||||
extend ActiveSupport::Concern
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
@ -13,16 +15,23 @@ module FastJsonapi
|
|||||||
:relationships_to_serialize,
|
:relationships_to_serialize,
|
||||||
:cachable_relationships_to_serialize,
|
:cachable_relationships_to_serialize,
|
||||||
:uncachable_relationships_to_serialize,
|
:uncachable_relationships_to_serialize,
|
||||||
|
:transform_method,
|
||||||
:record_type,
|
:record_type,
|
||||||
:record_id,
|
:record_id,
|
||||||
:cache_length,
|
:cache_length,
|
||||||
:cached
|
:race_condition_ttl,
|
||||||
|
:cached,
|
||||||
|
:data_links
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class_methods do
|
class_methods do
|
||||||
def id_hash(id, record_type)
|
def id_hash(id, record_type, default_return=false)
|
||||||
return { id: id.to_s, type: record_type } if id.present?
|
if id.present?
|
||||||
|
{ id: id.to_s, type: record_type }
|
||||||
|
else
|
||||||
|
default_return ? { id: nil, type: record_type } : nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def ids_hash(ids, record_type)
|
def ids_hash(ids, record_type)
|
||||||
@ -33,19 +42,18 @@ module FastJsonapi
|
|||||||
def id_hash_from_record(record, record_types)
|
def id_hash_from_record(record, record_types)
|
||||||
# memoize the record type within the record_types dictionary, then assigning to record_type:
|
# 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
|
record_type = record_types[record.class] ||= record.class.name.underscore.to_sym
|
||||||
{ id: record.id.to_s, type: record_type }
|
id_hash(record.id, record_type)
|
||||||
end
|
end
|
||||||
|
|
||||||
def ids_hash_from_record_and_relationship(record, relationship)
|
def ids_hash_from_record_and_relationship(record, relationship, params = {})
|
||||||
polymorphic = relationship[:polymorphic]
|
polymorphic = relationship[:polymorphic]
|
||||||
|
|
||||||
return ids_hash(
|
return ids_hash(
|
||||||
record.public_send(relationship[:id_method_name]),
|
fetch_id(record, relationship, params),
|
||||||
relationship[:record_type]
|
relationship[:record_type]
|
||||||
) unless polymorphic
|
) unless polymorphic
|
||||||
|
|
||||||
object_method_name = relationship.fetch(:object_method_name, relationship[:name])
|
return unless associated_object = fetch_associated_object(record, relationship, params)
|
||||||
return unless associated_object = record.send(object_method_name)
|
|
||||||
|
|
||||||
return associated_object.map do |object|
|
return associated_object.map do |object|
|
||||||
id_hash_from_record object, polymorphic
|
id_hash_from_record object, polymorphic
|
||||||
@ -54,69 +62,131 @@ module FastJsonapi
|
|||||||
id_hash_from_record associated_object, polymorphic
|
id_hash_from_record associated_object, polymorphic
|
||||||
end
|
end
|
||||||
|
|
||||||
def attributes_hash(record)
|
def links_hash(record, params = {})
|
||||||
attributes_to_serialize.each_with_object({}) do |(key, method), attr_hash|
|
data_links.each_with_object({}) do |(key, method), link_hash|
|
||||||
attr_hash[key] = method.is_a?(Proc) ? method.call(record) : record.public_send(method)
|
link_hash[key] = if method.is_a?(Proc)
|
||||||
|
method.arity == 1 ? method.call(record) : method.call(record, params)
|
||||||
|
else
|
||||||
|
record.public_send(method)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def relationships_hash(record, relationships = nil)
|
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
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def relationships_hash(record, relationships = nil, params = {})
|
||||||
relationships = relationships_to_serialize if relationships.nil?
|
relationships = relationships_to_serialize if relationships.nil?
|
||||||
|
|
||||||
relationships.each_with_object({}) do |(_k, relationship), hash|
|
relationships.each_with_object({}) do |(_k, relationship), hash|
|
||||||
name = relationship[:key]
|
name = relationship[:key]
|
||||||
empty_case = relationship[:relationship_type] == :has_many ? [] : nil
|
empty_case = relationship[:relationship_type] == :has_many ? [] : nil
|
||||||
hash[name] = {
|
hash[name] = {
|
||||||
data: ids_hash_from_record_and_relationship(record, relationship) || empty_case
|
data: ids_hash_from_record_and_relationship(record, relationship, params) || empty_case
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def record_hash(record)
|
def record_hash(record, params = {})
|
||||||
if cached
|
if cached
|
||||||
record_hash = Rails.cache.fetch(record.cache_key, expires_in: cache_length) do
|
record_hash = Rails.cache.fetch(record.cache_key, expires_in: cache_length, race_condition_ttl: race_condition_ttl) do
|
||||||
id = record_id ? record.send(record_id) : record.id
|
temp_hash = id_hash(id_from_record(record), record_type, true)
|
||||||
temp_hash = id_hash(id, record_type) || { id: nil, type: record_type }
|
temp_hash[:attributes] = attributes_hash(record, params) if attributes_to_serialize.present?
|
||||||
temp_hash[:attributes] = attributes_hash(record) if attributes_to_serialize.present?
|
|
||||||
temp_hash[:relationships] = {}
|
temp_hash[:relationships] = {}
|
||||||
temp_hash[:relationships] = relationships_hash(record, cachable_relationships_to_serialize) if cachable_relationships_to_serialize.present?
|
temp_hash[:relationships] = relationships_hash(record, cachable_relationships_to_serialize, params) if cachable_relationships_to_serialize.present?
|
||||||
|
temp_hash[:links] = links_hash(record, params) if data_links.present?
|
||||||
temp_hash
|
temp_hash
|
||||||
end
|
end
|
||||||
record_hash[:relationships] = record_hash[:relationships].merge(relationships_hash(record, uncachable_relationships_to_serialize)) if uncachable_relationships_to_serialize.present?
|
record_hash[:relationships] = record_hash[:relationships].merge(relationships_hash(record, uncachable_relationships_to_serialize, params)) if uncachable_relationships_to_serialize.present?
|
||||||
record_hash
|
record_hash
|
||||||
else
|
else
|
||||||
id = record_id ? record.send(record_id) : record.id
|
record_hash = id_hash(id_from_record(record), record_type, true)
|
||||||
record_hash = id_hash(id, record_type) || { id: nil, type: record_type }
|
record_hash[:attributes] = attributes_hash(record, params) if attributes_to_serialize.present?
|
||||||
record_hash[:attributes] = attributes_hash(record) if attributes_to_serialize.present?
|
record_hash[:relationships] = relationships_hash(record, nil, params) if relationships_to_serialize.present?
|
||||||
record_hash[:relationships] = relationships_hash(record) if relationships_to_serialize.present?
|
record_hash[:links] = links_hash(record, params) if data_links.present?
|
||||||
record_hash
|
record_hash
|
||||||
end
|
end
|
||||||
end
|
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
|
||||||
|
end
|
||||||
|
|
||||||
# Override #to_json for alternative implementation
|
# Override #to_json for alternative implementation
|
||||||
def to_json(payload)
|
def to_json(payload)
|
||||||
FastJsonapi::MultiToJson.to_json(payload) if payload.present?
|
FastJsonapi::MultiToJson.to_json(payload) if payload.present?
|
||||||
end
|
end
|
||||||
|
|
||||||
# includes handler
|
def parse_include_item(include_item)
|
||||||
|
return [include_item.to_sym] unless include_item.to_s.include?('.')
|
||||||
|
include_item.to_s.split('.').map { |item| item.to_sym }
|
||||||
|
end
|
||||||
|
|
||||||
def get_included_records(record, includes_list, known_included_objects)
|
def remaining_items(items)
|
||||||
includes_list.each_with_object([]) do |item, included_records|
|
return unless items.size > 1
|
||||||
object_method_name = @relationships_to_serialize[item][:object_method_name]
|
|
||||||
record_type = @relationships_to_serialize[item][:record_type]
|
items_copy = items.dup
|
||||||
serializer = @relationships_to_serialize[item][:serializer].to_s.constantize
|
items_copy.delete_at(0)
|
||||||
relationship_type = @relationships_to_serialize[item][:relationship_type]
|
[items_copy.join('.').to_sym]
|
||||||
included_objects = record.send(object_method_name)
|
end
|
||||||
next if included_objects.blank?
|
|
||||||
included_objects = [included_objects] unless relationship_type == :has_many
|
# includes handler
|
||||||
included_objects.each do |inc_obj|
|
def get_included_records(record, includes_list, known_included_objects, params = {})
|
||||||
code = "#{record_type}_#{inc_obj.id}"
|
return unless includes_list.present?
|
||||||
next if known_included_objects.key?(code)
|
|
||||||
known_included_objects[code] = inc_obj
|
includes_list.sort.each_with_object([]) do |include_item, included_records|
|
||||||
included_records << serializer.record_hash(inc_obj)
|
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]
|
||||||
|
|
||||||
|
included_objects = fetch_associated_object(record, @relationships_to_serialize[item], 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)
|
||||||
|
included_records.concat(serializer_records) unless serializer_records.empty?
|
||||||
|
end
|
||||||
|
|
||||||
|
code = "#{record_type}_#{inc_obj.id}"
|
||||||
|
next if known_included_objects.key?(code)
|
||||||
|
|
||||||
|
known_included_objects[code] = inc_obj
|
||||||
|
included_records << serializer.record_hash(inc_obj, params)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
end
|
end
|
||||||
|
3
lib/fast_jsonapi/version.rb
Normal file
3
lib/fast_jsonapi/version.rb
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
module FastJsonapi
|
||||||
|
VERSION = "1.2"
|
||||||
|
end
|
@ -0,0 +1,14 @@
|
|||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe FastJsonapi::ObjectSerializer do
|
||||||
|
|
||||||
|
context 'instrument' do
|
||||||
|
context 'skylight' do
|
||||||
|
# skip for normal runs because this could alter some
|
||||||
|
# other test by insterting the instrumentation
|
||||||
|
xit 'make sure requiring skylight normalizers works' do
|
||||||
|
require 'fast_jsonapi/instrumentation/skylight'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
118
spec/lib/object_serializer_attribute_param_spec.rb
Normal file
118
spec/lib/object_serializer_attribute_param_spec.rb
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe FastJsonapi::ObjectSerializer do
|
||||||
|
include_context 'movie class'
|
||||||
|
|
||||||
|
context "params option" do
|
||||||
|
let(:hash) { serializer.serializable_hash }
|
||||||
|
|
||||||
|
before(:context) do
|
||||||
|
class Movie
|
||||||
|
def viewed?(user)
|
||||||
|
user.viewed.include?(id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class MovieSerializer
|
||||||
|
attribute :viewed do |movie, params|
|
||||||
|
params ? movie.viewed?(params[:user]) : false
|
||||||
|
end
|
||||||
|
|
||||||
|
attribute :no_param_attribute do |movie|
|
||||||
|
"no-param-attribute"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
User = Struct.new(:viewed)
|
||||||
|
end
|
||||||
|
|
||||||
|
after(:context) do
|
||||||
|
Object.send(:remove_const, User) if Object.constants.include?(User)
|
||||||
|
end
|
||||||
|
|
||||||
|
context "enforces a hash only params" do
|
||||||
|
let(:params) { User.new([]) }
|
||||||
|
|
||||||
|
it "fails when creating a serializer with an object as params" do
|
||||||
|
expect(-> { MovieSerializer.new(movie, {params: User.new([])}) }).to raise_error(ArgumentError)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "succeeds creating a serializer with a hash" do
|
||||||
|
expect(-> { MovieSerializer.new(movie, {params: {current_user: User.new([])}}) }).not_to raise_error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "passing params to the serializer" do
|
||||||
|
let(:params) { {user: User.new([movie.id])} }
|
||||||
|
let(:options_with_params) { {params: params} }
|
||||||
|
|
||||||
|
context "with a single record" do
|
||||||
|
let(:serializer) { MovieSerializer.new(movie, options_with_params) }
|
||||||
|
|
||||||
|
it "handles attributes that use params" do
|
||||||
|
expect(hash[:data][:attributes][:viewed]).to eq(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "handles attributes that don't use params" do
|
||||||
|
expect(hash[:data][:attributes][:no_param_attribute]).to eq("no-param-attribute")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with a list of records" do
|
||||||
|
let(:movies) { build_movies(3) }
|
||||||
|
let(:user) { User.new(movies.map { |m| [true, false].sample ? m.id : nil }.compact) }
|
||||||
|
let(:params) { {user: user} }
|
||||||
|
let(:serializer) { MovieSerializer.new(movies, options_with_params) }
|
||||||
|
|
||||||
|
it "has 3 items" do
|
||||||
|
hash[:data].length == 3
|
||||||
|
end
|
||||||
|
|
||||||
|
it "handles passing params to a list of resources" do
|
||||||
|
param_attribute_values = hash[:data].map { |data| [data[:id], data[:attributes][:viewed]] }
|
||||||
|
expected_values = movies.map { |m| [m.id.to_s, user.viewed.include?(m.id)] }
|
||||||
|
|
||||||
|
expect(param_attribute_values).to eq(expected_values)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "handles attributes without params" do
|
||||||
|
no_param_attribute_values = hash[:data].map { |data| data[:attributes][:no_param_attribute] }
|
||||||
|
expected_values = (1..3).map { "no-param-attribute" }
|
||||||
|
|
||||||
|
expect(no_param_attribute_values).to eq(expected_values)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "without passing params to the serializer" do
|
||||||
|
context "with a single movie" do
|
||||||
|
let(:serializer) { MovieSerializer.new(movie) }
|
||||||
|
|
||||||
|
it "handles param attributes" do
|
||||||
|
expect(hash[:data][:attributes][:viewed]).to eq(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "handles attributes that don't use params" do
|
||||||
|
expect(hash[:data][:attributes][:no_param_attribute]).to eq("no-param-attribute")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with multiple movies" do
|
||||||
|
let(:serializer) { MovieSerializer.new(build_movies(3)) }
|
||||||
|
|
||||||
|
it "handles attributes with params" do
|
||||||
|
param_attribute_values = hash[:data].map { |data| data[:attributes][:viewed] }
|
||||||
|
|
||||||
|
expect(param_attribute_values).to eq([false, false, false])
|
||||||
|
end
|
||||||
|
|
||||||
|
it "handles attributes that don't use params" do
|
||||||
|
no_param_attribute_values = hash[:data].map { |data| data[:attributes][:no_param_attribute] }
|
||||||
|
expected_attribute_values = (1..3).map { "no-param-attribute" }
|
||||||
|
|
||||||
|
expect(no_param_attribute_values).to eq(expected_attribute_values)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -13,6 +13,8 @@ describe FastJsonapi::ObjectSerializer do
|
|||||||
it 'returns correct hash when serializable_hash is called' do
|
it 'returns correct hash when serializable_hash is called' do
|
||||||
options = {}
|
options = {}
|
||||||
options[:meta] = { total: 2 }
|
options[:meta] = { total: 2 }
|
||||||
|
options[:links] = { self: 'self' }
|
||||||
|
|
||||||
options[:include] = [:actors]
|
options[:include] = [:actors]
|
||||||
serializable_hash = CachingMovieSerializer.new([movie, movie], options).serializable_hash
|
serializable_hash = CachingMovieSerializer.new([movie, movie], options).serializable_hash
|
||||||
|
|
||||||
@ -21,6 +23,7 @@ describe FastJsonapi::ObjectSerializer do
|
|||||||
expect(serializable_hash[:data][0][:attributes].length).to eq 2
|
expect(serializable_hash[:data][0][:attributes].length).to eq 2
|
||||||
|
|
||||||
expect(serializable_hash[:meta]).to be_instance_of(Hash)
|
expect(serializable_hash[:meta]).to be_instance_of(Hash)
|
||||||
|
expect(serializable_hash[:links]).to be_instance_of(Hash)
|
||||||
|
|
||||||
expect(serializable_hash[:included]).to be_instance_of(Array)
|
expect(serializable_hash[:included]).to be_instance_of(Array)
|
||||||
expect(serializable_hash[:included][0]).to be_instance_of(Hash)
|
expect(serializable_hash[:included][0]).to be_instance_of(Hash)
|
||||||
@ -30,6 +33,7 @@ describe FastJsonapi::ObjectSerializer do
|
|||||||
|
|
||||||
expect(serializable_hash[:data]).to be_instance_of(Hash)
|
expect(serializable_hash[:data]).to be_instance_of(Hash)
|
||||||
expect(serializable_hash[:meta]).to be nil
|
expect(serializable_hash[:meta]).to be nil
|
||||||
|
expect(serializable_hash[:links]).to be nil
|
||||||
expect(serializable_hash[:included]).to be nil
|
expect(serializable_hash[:included]).to be nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -4,73 +4,342 @@ describe FastJsonapi::ObjectSerializer do
|
|||||||
|
|
||||||
include_context 'movie class'
|
include_context 'movie class'
|
||||||
|
|
||||||
context 'when testing class methods of object serializer' do
|
describe '#has_many' do
|
||||||
|
subject(:relationship) { serializer.relationships_to_serialize[:roles] }
|
||||||
|
|
||||||
before(:example) do
|
before do
|
||||||
|
serializer.has_many *children
|
||||||
|
end
|
||||||
|
|
||||||
|
after do
|
||||||
|
serializer.relationships_to_serialize = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with namespace' do
|
||||||
|
let(:serializer) { AppName::V1::MovieSerializer }
|
||||||
|
let(:children) { [:roles] }
|
||||||
|
|
||||||
|
context 'with overrides' do
|
||||||
|
let(:children) { [:roles, id_method_name: :roles_only_ids, record_type: :super_role] }
|
||||||
|
|
||||||
|
it_behaves_like 'returning correct relationship hash', :'AppName::V1::RoleSerializer', :roles_only_ids, :super_role
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'without overrides' do
|
||||||
|
let(:children) { [:roles] }
|
||||||
|
|
||||||
|
it_behaves_like 'returning correct relationship hash', :'AppName::V1::RoleSerializer', :role_ids, :role
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'without namespace' do
|
||||||
|
let(:serializer) { MovieSerializer }
|
||||||
|
|
||||||
|
context 'with overrides' do
|
||||||
|
let(:children) { [:roles, id_method_name: :roles_only_ids, record_type: :super_role] }
|
||||||
|
|
||||||
|
it_behaves_like 'returning correct relationship hash', :'RoleSerializer', :roles_only_ids, :super_role
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'without overrides' do
|
||||||
|
let(:children) { [:roles] }
|
||||||
|
|
||||||
|
it_behaves_like 'returning correct relationship hash', :'RoleSerializer', :role_ids, :role
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#has_many with block' do
|
||||||
|
before do
|
||||||
|
MovieSerializer.has_many :awards do |movie|
|
||||||
|
movie.actors.map(&:awards).flatten
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
after do
|
||||||
|
MovieSerializer.relationships_to_serialize.delete(:awards)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'awards is not included' do
|
||||||
|
subject(:hash) { MovieSerializer.new(movie).serializable_hash }
|
||||||
|
|
||||||
|
it 'returns correct hash' do
|
||||||
|
expect(hash[:data][:relationships][:awards][:data].length).to eq(6)
|
||||||
|
expect(hash[:data][:relationships][:awards][:data][0]).to eq({ id: '9', type: :award })
|
||||||
|
expect(hash[:data][:relationships][:awards][:data][-1]).to eq({ id: '28', type: :award })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'state is included' do
|
||||||
|
subject(:hash) { MovieSerializer.new(movie, include: [:awards]).serializable_hash }
|
||||||
|
|
||||||
|
it 'returns correct hash' do
|
||||||
|
expect(hash[:included].length).to eq 6
|
||||||
|
expect(hash[:included][0][:id]).to eq '9'
|
||||||
|
expect(hash[:included][0][:type]).to eq :award
|
||||||
|
expect(hash[:included][0][:attributes]).to eq({ id: 9, title: 'Test Award 9' })
|
||||||
|
expect(hash[:included][0][:relationships]).to eq({ actor: { data: { id: '1', type: :actor } } })
|
||||||
|
expect(hash[:included][-1][:id]).to eq '28'
|
||||||
|
expect(hash[:included][-1][:type]).to eq :award
|
||||||
|
expect(hash[:included][-1][:attributes]).to eq({ id: 28, title: 'Test Award 28' })
|
||||||
|
expect(hash[:included][-1][:relationships]).to eq({ actor: { data: { id: '3', type: :actor } } })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#belongs_to' do
|
||||||
|
subject(:relationship) { MovieSerializer.relationships_to_serialize[:area] }
|
||||||
|
|
||||||
|
before do
|
||||||
|
MovieSerializer.belongs_to *parent
|
||||||
|
end
|
||||||
|
|
||||||
|
after do
|
||||||
MovieSerializer.relationships_to_serialize = {}
|
MovieSerializer.relationships_to_serialize = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns correct relationship hash for a has_many relationship' do
|
context 'with overrides' do
|
||||||
MovieSerializer.has_many :roles
|
let(:parent) { [:area, id_method_name: :blah_id, record_type: :awesome_area, serializer: :my_area] }
|
||||||
relationship = MovieSerializer.relationships_to_serialize[:roles]
|
|
||||||
expect(relationship).to be_instance_of(Hash)
|
it_behaves_like 'returning correct relationship hash', :'MyAreaSerializer', :blah_id, :awesome_area
|
||||||
expect(relationship.keys).to all(be_instance_of(Symbol))
|
|
||||||
expect(relationship[:id_method_name]).to end_with '_ids'
|
|
||||||
expect(relationship[:record_type]).to eq 'roles'.singularize.to_sym
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns correct relationship hash for a has_many relationship with overrides' do
|
context 'without overrides' do
|
||||||
MovieSerializer.has_many :roles, id_method_name: :roles_only_ids, record_type: :super_role
|
let(:parent) { [:area] }
|
||||||
relationship = MovieSerializer.relationships_to_serialize[:roles]
|
|
||||||
expect(relationship[:id_method_name]).to be :roles_only_ids
|
it_behaves_like 'returning correct relationship hash', :'AreaSerializer', :area_id, :area
|
||||||
expect(relationship[:record_type]).to be :super_role
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#belongs_to with block' do
|
||||||
|
before do
|
||||||
|
ActorSerializer.belongs_to :state do |actor|
|
||||||
|
actor.agency.state
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns correct relationship hash for a belongs_to relationship' do
|
after do
|
||||||
MovieSerializer.belongs_to :area
|
ActorSerializer.relationships_to_serialize.delete(:actorc)
|
||||||
relationship = MovieSerializer.relationships_to_serialize[:area]
|
|
||||||
expect(relationship).to be_instance_of(Hash)
|
|
||||||
expect(relationship.keys).to all(be_instance_of(Symbol))
|
|
||||||
expect(relationship[:id_method_name]).to end_with '_id'
|
|
||||||
expect(relationship[:record_type]).to eq 'area'.singularize.to_sym
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns correct relationship hash for a belongs_to relationship with overrides' do
|
context 'state is not included' do
|
||||||
MovieSerializer.has_many :area, id_method_name: :blah_id, record_type: :awesome_area, serializer: :my_area
|
subject(:hash) { ActorSerializer.new(actor).serializable_hash }
|
||||||
relationship = MovieSerializer.relationships_to_serialize[:area]
|
|
||||||
expect(relationship[:id_method_name]).to be :blah_id
|
it 'returns correct hash' do
|
||||||
expect(relationship[:record_type]).to be :awesome_area
|
expect(hash[:data][:relationships][:state][:data]).to eq({ id: '1', type: :state })
|
||||||
expect(relationship[:serializer]).to be :MyAreaSerializer
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns correct relationship hash for a has_one relationship' do
|
context 'state is included' do
|
||||||
MovieSerializer.has_one :area
|
subject(:hash) { ActorSerializer.new(actor, include: [:state]).serializable_hash }
|
||||||
relationship = MovieSerializer.relationships_to_serialize[:area]
|
|
||||||
expect(relationship).to be_instance_of(Hash)
|
it 'returns correct hash' do
|
||||||
expect(relationship.keys).to all(be_instance_of(Symbol))
|
expect(hash[:included].length).to eq 1
|
||||||
expect(relationship[:id_method_name]).to end_with '_id'
|
expect(hash[:included][0][:id]).to eq '1'
|
||||||
expect(relationship[:record_type]).to eq 'area'.singularize.to_sym
|
expect(hash[:included][0][:type]).to eq :state
|
||||||
|
expect(hash[:included][0][:attributes]).to eq({ id: 1, name: 'Test State 1' })
|
||||||
|
expect(hash[:included][0][:relationships]).to eq({ agency: { data: [{ id: '432', type: :agency }] } })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#has_one' do
|
||||||
|
subject(:relationship) { MovieSerializer.relationships_to_serialize[:area] }
|
||||||
|
|
||||||
|
before do
|
||||||
|
MovieSerializer.has_one *partner
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns correct relationship hash for a has_one relationship with overrides' do
|
after do
|
||||||
MovieSerializer.has_one :area, id_method_name: :blah_id, record_type: :awesome_area
|
MovieSerializer.relationships_to_serialize = {}
|
||||||
relationship = MovieSerializer.relationships_to_serialize[:area]
|
|
||||||
expect(relationship[:id_method_name]).to be :blah_id
|
|
||||||
expect(relationship[:record_type]).to be :awesome_area
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns serializer name correctly with namespaces' do
|
context 'with overrides' do
|
||||||
AppName::V1::MovieSerializer.has_many :area, id_method_name: :blah_id
|
let(:partner) { [:area, id_method_name: :blah_id, record_type: :awesome_area, serializer: :my_area] }
|
||||||
relationship = AppName::V1::MovieSerializer.relationships_to_serialize[:area]
|
|
||||||
expect(relationship[:serializer]).to be :'AppName::V1::AreaSerializer'
|
it_behaves_like 'returning correct relationship hash', :'MyAreaSerializer', :blah_id, :awesome_area
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'without overrides' do
|
||||||
|
let(:partner) { [:area] }
|
||||||
|
|
||||||
|
it_behaves_like 'returning correct relationship hash', :'AreaSerializer', :area_id, :area
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#set_id' do
|
||||||
|
subject(:serializable_hash) { MovieSerializer.new(resource).serializable_hash }
|
||||||
|
|
||||||
|
before do
|
||||||
|
MovieSerializer.set_id :owner_id
|
||||||
|
end
|
||||||
|
|
||||||
|
after do
|
||||||
|
MovieSerializer.set_id nil
|
||||||
|
end
|
||||||
|
|
||||||
|
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
|
||||||
|
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
|
||||||
|
|
||||||
|
describe '#use_hyphen' do
|
||||||
|
subject { MovieSerializer.use_hyphen }
|
||||||
|
|
||||||
|
after do
|
||||||
|
MovieSerializer.transform_method = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'sets the correct transform_method when use_hyphen is used' do
|
it 'sets the correct transform_method when use_hyphen is used' do
|
||||||
MovieSerializer.use_hyphen
|
warning_message = "DEPRECATION WARNING: use_hyphen is deprecated and will be removed from fast_jsonapi 2.0 use (set_key_transform :dash) instead\n"
|
||||||
warning_message = 'DEPRECATION WARNING: use_hyphen is deprecated and will be removed from fast_jsonapi 2.0 use (set_key_transform :dash) instead'
|
expect { subject }.to output(warning_message).to_stderr
|
||||||
expect { MovieSerializer.use_hyphen }.to output.to_stderr
|
|
||||||
expect(MovieSerializer.instance_variable_get(:@transform_method)).to eq :dasherize
|
expect(MovieSerializer.instance_variable_get(:@transform_method)).to eq :dasherize
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#attribute' do
|
||||||
|
subject(:serializable_hash) { MovieSerializer.new(movie).serializable_hash }
|
||||||
|
|
||||||
|
context 'with block' do
|
||||||
|
before do
|
||||||
|
movie.release_year = 2008
|
||||||
|
MovieSerializer.attribute :title_with_year do |record|
|
||||||
|
"#{record.name} (#{record.release_year})"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
after do
|
||||||
|
MovieSerializer.attributes_to_serialize.delete(:title_with_year)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns correct hash when serializable_hash is called' do
|
||||||
|
expect(serializable_hash[:data][:attributes][:name]).to eq movie.name
|
||||||
|
expect(serializable_hash[:data][:attributes][:title_with_year]).to eq "#{movie.name} (#{movie.release_year})"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#link' do
|
||||||
|
subject(:serializable_hash) { MovieSerializer.new(movie).serializable_hash }
|
||||||
|
|
||||||
|
after do
|
||||||
|
MovieSerializer.data_links = {}
|
||||||
|
ActorSerializer.data_links = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with block calling instance method on serializer' do
|
||||||
|
before do
|
||||||
|
MovieSerializer.link(:self) do |movie_object|
|
||||||
|
movie_object.url
|
||||||
|
end
|
||||||
|
end
|
||||||
|
let(:url) { "http://movies.com/#{movie.id}" }
|
||||||
|
|
||||||
|
it 'returns correct hash when serializable_hash is called' do
|
||||||
|
expect(serializable_hash[:data][:links][:self]).to eq url
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with block and param' do
|
||||||
|
before do
|
||||||
|
MovieSerializer.link(:public_url) do |movie_object|
|
||||||
|
"http://movies.com/#{movie_object.id}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
let(:url) { "http://movies.com/#{movie.id}" }
|
||||||
|
|
||||||
|
it 'returns correct hash when serializable_hash is called' do
|
||||||
|
expect(serializable_hash[:data][:links][:public_url]).to eq url
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with method' do
|
||||||
|
before do
|
||||||
|
MovieSerializer.link(:object_id, :id)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns correct hash when serializable_hash is called' do
|
||||||
|
expect(serializable_hash[:data][:links][:object_id]).to eq movie.id
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with method and convention' do
|
||||||
|
before do
|
||||||
|
MovieSerializer.link(:url)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns correct hash when serializable_hash is called' do
|
||||||
|
expect(serializable_hash[:data][:links][:url]).to eq movie.url
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#key_transform' do
|
||||||
|
subject(:hash) { movie_serializer_class.new([movie, movie], include: [:movie_type]).serializable_hash }
|
||||||
|
|
||||||
|
let(:movie_serializer_class) { "#{key_transform}_movie_serializer".classify.constantize }
|
||||||
|
|
||||||
|
before(:context) do
|
||||||
|
[:dash, :camel, :camel_lower, :underscore].each do |key_transform|
|
||||||
|
movie_serializer_name = "#{key_transform}_movie_serializer".classify
|
||||||
|
movie_type_serializer_name = "#{key_transform}_movie_type_serializer".classify
|
||||||
|
# https://stackoverflow.com/questions/4113479/dynamic-class-definition-with-a-class-name
|
||||||
|
movie_serializer_class = Object.const_set(movie_serializer_name, Class.new)
|
||||||
|
# https://rubymonk.com/learning/books/5-metaprogramming-ruby-ascent/chapters/24-eval/lessons/67-instance-eval
|
||||||
|
movie_serializer_class.instance_eval do
|
||||||
|
include FastJsonapi::ObjectSerializer
|
||||||
|
set_type :movie
|
||||||
|
set_key_transform key_transform
|
||||||
|
attributes :name, :release_year
|
||||||
|
has_many :actors
|
||||||
|
belongs_to :owner, record_type: :user
|
||||||
|
belongs_to :movie_type, serializer: "#{key_transform}_movie_type".to_sym
|
||||||
|
end
|
||||||
|
movie_type_serializer_class = Object.const_set(movie_type_serializer_name, Class.new)
|
||||||
|
movie_type_serializer_class.instance_eval do
|
||||||
|
include FastJsonapi::ObjectSerializer
|
||||||
|
set_key_transform key_transform
|
||||||
|
set_type :movie_type
|
||||||
|
attributes :name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when key_transform is dash' do
|
||||||
|
let(:key_transform) { :dash }
|
||||||
|
|
||||||
|
it_behaves_like 'returning key transformed hash', :'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
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when key_transform is camel_lower' do
|
||||||
|
let(:key_transform) { :camel_lower }
|
||||||
|
|
||||||
|
it_behaves_like 'returning key transformed hash', :movieType, :releaseYear
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when key_transform is underscore' do
|
||||||
|
let(:key_transform) { :underscore }
|
||||||
|
|
||||||
|
it_behaves_like 'returning key transformed hash', :movie_type, :release_year
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
163
spec/lib/object_serializer_inheritance_spec.rb
Normal file
163
spec/lib/object_serializer_inheritance_spec.rb
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe FastJsonapi::ObjectSerializer do
|
||||||
|
|
||||||
|
after(:all) do
|
||||||
|
classes_to_remove = %i[
|
||||||
|
User
|
||||||
|
UserSerializer
|
||||||
|
Country
|
||||||
|
CountrySerializer
|
||||||
|
Employee
|
||||||
|
EmployeeSerializer
|
||||||
|
Photo
|
||||||
|
PhotoSerializer
|
||||||
|
EmployeeAccount
|
||||||
|
]
|
||||||
|
classes_to_remove.each do |klass_name|
|
||||||
|
Object.send(:remove_const, klass_name) if Object.constants.include?(klass_name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class User
|
||||||
|
attr_accessor :id, :first_name, :last_name
|
||||||
|
|
||||||
|
attr_accessor :address_ids, :country_id
|
||||||
|
|
||||||
|
def photo
|
||||||
|
p = Photo.new
|
||||||
|
p.id = 1
|
||||||
|
p.user_id = id
|
||||||
|
p
|
||||||
|
end
|
||||||
|
|
||||||
|
def photo_id
|
||||||
|
1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class UserSerializer
|
||||||
|
include FastJsonapi::ObjectSerializer
|
||||||
|
set_type :user
|
||||||
|
attributes :first_name, :last_name
|
||||||
|
|
||||||
|
attribute :full_name do |user, params|
|
||||||
|
"#{user.first_name} #{user.last_name}"
|
||||||
|
end
|
||||||
|
|
||||||
|
has_many :addresses, cached: true
|
||||||
|
belongs_to :country
|
||||||
|
has_one :photo
|
||||||
|
end
|
||||||
|
|
||||||
|
class Photo
|
||||||
|
attr_accessor :id, :user_id
|
||||||
|
end
|
||||||
|
|
||||||
|
class PhotoSerializer
|
||||||
|
include FastJsonapi::ObjectSerializer
|
||||||
|
attributes :id, :name
|
||||||
|
end
|
||||||
|
|
||||||
|
class Country
|
||||||
|
attr_accessor :id, :name
|
||||||
|
end
|
||||||
|
|
||||||
|
class CountrySerializer
|
||||||
|
include FastJsonapi::ObjectSerializer
|
||||||
|
attributes :name
|
||||||
|
end
|
||||||
|
|
||||||
|
class EmployeeAccount
|
||||||
|
attr_accessor :id, :employee_id
|
||||||
|
end
|
||||||
|
|
||||||
|
class Employee < User
|
||||||
|
attr_accessor :id, :location, :compensation
|
||||||
|
|
||||||
|
def account
|
||||||
|
a = EmployeeAccount.new
|
||||||
|
a.id = 1
|
||||||
|
a.employee_id = id
|
||||||
|
a
|
||||||
|
end
|
||||||
|
|
||||||
|
def account_id
|
||||||
|
1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class EmployeeSerializer < UserSerializer
|
||||||
|
include FastJsonapi::ObjectSerializer
|
||||||
|
attributes :location
|
||||||
|
attributes :compensation
|
||||||
|
|
||||||
|
has_one :account
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when testing inheritance of attributes' do
|
||||||
|
|
||||||
|
it 'includes parent attributes' do
|
||||||
|
subclass_attributes = EmployeeSerializer.attributes_to_serialize
|
||||||
|
superclass_attributes = UserSerializer.attributes_to_serialize
|
||||||
|
expect(subclass_attributes).to include(superclass_attributes)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns inherited attribute with a block correctly' do
|
||||||
|
e = Employee.new
|
||||||
|
e.id = 1
|
||||||
|
e.first_name = 'S'
|
||||||
|
e.last_name = 'K'
|
||||||
|
attributes_hash = EmployeeSerializer.new(e).serializable_hash[:data][:attributes]
|
||||||
|
expect(attributes_hash).to include(full_name: 'S K')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'includes child attributes' do
|
||||||
|
expect(EmployeeSerializer.attributes_to_serialize[:location]).to eq(:location)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'doesnt change parent class attributes' do
|
||||||
|
EmployeeSerializer
|
||||||
|
expect(UserSerializer.attributes_to_serialize).not_to have_key(:location)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when testing inheritance of relationship' do
|
||||||
|
it 'includes parent relationships' do
|
||||||
|
subclass_relationships = EmployeeSerializer.relationships_to_serialize
|
||||||
|
superclass_relationships = UserSerializer.relationships_to_serialize
|
||||||
|
expect(subclass_relationships).to include(superclass_relationships)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns inherited relationship correctly' do
|
||||||
|
e = Employee.new
|
||||||
|
e.country_id = 1
|
||||||
|
relationships_hash = EmployeeSerializer.new(e).serializable_hash[:data][:relationships][:country]
|
||||||
|
expect(relationships_hash).to include(data: { id: "1", type: :country })
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'includes child relationships' do
|
||||||
|
expect(EmployeeSerializer.relationships_to_serialize.keys).to include(:account)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'doesnt change parent class attributes' do
|
||||||
|
EmployeeSerializer
|
||||||
|
expect(UserSerializer.relationships_to_serialize.keys).not_to include(:account)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'includes parent cached relationships' do
|
||||||
|
subclass_relationships = EmployeeSerializer.cachable_relationships_to_serialize
|
||||||
|
superclass_relationships = UserSerializer.cachable_relationships_to_serialize
|
||||||
|
expect(subclass_relationships).to include(superclass_relationships)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when test inheritence of other attributes' do
|
||||||
|
|
||||||
|
it 'inherits the tranform method' do
|
||||||
|
EmployeeSerializer
|
||||||
|
expect(UserSerializer.transform_method).to eq EmployeeSerializer.transform_method
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
@ -1,80 +0,0 @@
|
|||||||
require 'spec_helper'
|
|
||||||
|
|
||||||
describe FastJsonapi::ObjectSerializer do
|
|
||||||
include_context 'movie class'
|
|
||||||
include_context 'ams movie class'
|
|
||||||
|
|
||||||
before(:context) do
|
|
||||||
[:dash, :camel, :camel_lower, :underscore].each do |transform_type|
|
|
||||||
movie_serializer_name = "#{transform_type}_movie_serializer".classify
|
|
||||||
movie_type_serializer_name = "#{transform_type}_movie_type_serializer".classify
|
|
||||||
# https://stackoverflow.com/questions/4113479/dynamic-class-definition-with-a-class-name
|
|
||||||
movie_serializer_class = Object.const_set(
|
|
||||||
movie_serializer_name,
|
|
||||||
Class.new {
|
|
||||||
}
|
|
||||||
)
|
|
||||||
# https://rubymonk.com/learning/books/5-metaprogramming-ruby-ascent/chapters/24-eval/lessons/67-instance-eval
|
|
||||||
movie_serializer_class.instance_eval do
|
|
||||||
include FastJsonapi::ObjectSerializer
|
|
||||||
set_type :movie
|
|
||||||
set_key_transform transform_type
|
|
||||||
attributes :name, :release_year
|
|
||||||
has_many :actors
|
|
||||||
belongs_to :owner, record_type: :user
|
|
||||||
belongs_to :movie_type
|
|
||||||
end
|
|
||||||
movie_type_serializer_class = Object.const_set(
|
|
||||||
movie_type_serializer_name,
|
|
||||||
Class.new {
|
|
||||||
}
|
|
||||||
)
|
|
||||||
movie_type_serializer_class.instance_eval do
|
|
||||||
include FastJsonapi::ObjectSerializer
|
|
||||||
set_key_transform transform_type
|
|
||||||
set_type :movie_type
|
|
||||||
attributes :name
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when using dashes for word separation in the JSON API members' do
|
|
||||||
it 'returns correct hash when serializable_hash is called' do
|
|
||||||
serializable_hash = DashMovieSerializer.new([movie, movie]).serializable_hash
|
|
||||||
expect(serializable_hash[:data].length).to eq 2
|
|
||||||
expect(serializable_hash[:data][0][:relationships].length).to eq 3
|
|
||||||
expect(serializable_hash[:data][0][:relationships]).to have_key('movie-type'.to_sym)
|
|
||||||
expect(serializable_hash[:data][0][:attributes].length).to eq 2
|
|
||||||
expect(serializable_hash[:data][0][:attributes]).to have_key("release-year".to_sym)
|
|
||||||
|
|
||||||
serializable_hash = DashMovieSerializer.new(movie_struct).serializable_hash
|
|
||||||
expect(serializable_hash[:data][:relationships].length).to eq 3
|
|
||||||
expect(serializable_hash[:data][:relationships]).to have_key('movie-type'.to_sym)
|
|
||||||
expect(serializable_hash[:data][:attributes].length).to eq 2
|
|
||||||
expect(serializable_hash[:data][:attributes]).to have_key('release-year'.to_sym)
|
|
||||||
expect(serializable_hash[:data][:id]).to eq movie_struct.id.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns type hypenated when trying to serializing a class with multiple words' do
|
|
||||||
movie_type = MovieType.new
|
|
||||||
movie_type.id = 3
|
|
||||||
movie_type.name = "x"
|
|
||||||
serializable_hash = DashMovieTypeSerializer.new(movie_type).serializable_hash
|
|
||||||
expect(serializable_hash[:data][:type].to_sym).to eq 'movie-type'.to_sym
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when using other key transforms' do
|
|
||||||
[:camel, :camel_lower, :underscore, :dash].each do |transform_type|
|
|
||||||
it "returns same thing as ams when using #{transform_type}" do
|
|
||||||
ams_movie = build_ams_movies(1).first
|
|
||||||
movie = build_movies(1).first
|
|
||||||
movie_serializer_class = "#{transform_type}_movie_serializer".classify.constantize
|
|
||||||
our_json = movie_serializer_class.new([movie]).serialized_json
|
|
||||||
ams_json = ActiveModelSerializers::SerializableResource.new([ams_movie], key_transform: transform_type).to_json
|
|
||||||
expect(our_json.length).to eq (ams_json.length)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
@ -137,7 +137,6 @@ describe FastJsonapi::ObjectSerializer, performance: true do
|
|||||||
# json
|
# json
|
||||||
expect(json_benchmarks[:fast_jsonapi][:json].length).to eq json_benchmarks[:ams][:json].length
|
expect(json_benchmarks[:fast_jsonapi][:json].length).to eq json_benchmarks[:ams][:json].length
|
||||||
json_speed_up = json_benchmarks[:ams][:time] / json_benchmarks[:fast_jsonapi][:time]
|
json_speed_up = json_benchmarks[:ams][:time] / json_benchmarks[:fast_jsonapi][:time]
|
||||||
expect(json_speed_up).to be >= SERIALIZERS[:ams][:speed_factor]
|
|
||||||
|
|
||||||
# hash
|
# hash
|
||||||
hash_speed_up = hash_benchmarks[:ams][:time] / hash_benchmarks[:fast_jsonapi][:time]
|
hash_speed_up = hash_benchmarks[:ams][:time] / hash_benchmarks[:fast_jsonapi][:time]
|
||||||
@ -174,7 +173,6 @@ describe FastJsonapi::ObjectSerializer, performance: true do
|
|||||||
# json
|
# json
|
||||||
expect(json_benchmarks[:fast_jsonapi][:json].length).to eq json_benchmarks[:ams][:json].length
|
expect(json_benchmarks[:fast_jsonapi][:json].length).to eq json_benchmarks[:ams][:json].length
|
||||||
json_speed_up = json_benchmarks[:ams][:time] / json_benchmarks[:fast_jsonapi][:time]
|
json_speed_up = json_benchmarks[:ams][:time] / json_benchmarks[:fast_jsonapi][:time]
|
||||||
expect(json_speed_up).to be >= SERIALIZERS[:ams][:speed_factor]
|
|
||||||
|
|
||||||
# hash
|
# hash
|
||||||
hash_speed_up = hash_benchmarks[:ams][:time] / hash_benchmarks[:fast_jsonapi][:time]
|
hash_speed_up = hash_benchmarks[:ams][:time] / hash_benchmarks[:fast_jsonapi][:time]
|
||||||
@ -209,7 +207,6 @@ describe FastJsonapi::ObjectSerializer, performance: true do
|
|||||||
# json
|
# json
|
||||||
expect(json_benchmarks[:fast_jsonapi][:json].length).to eq json_benchmarks[:ams][:json].length
|
expect(json_benchmarks[:fast_jsonapi][:json].length).to eq json_benchmarks[:ams][:json].length
|
||||||
json_speed_up = json_benchmarks[:ams][:time] / json_benchmarks[:fast_jsonapi][:time]
|
json_speed_up = json_benchmarks[:ams][:time] / json_benchmarks[:fast_jsonapi][:time]
|
||||||
expect(json_speed_up).to be >= SERIALIZERS[:ams][:speed_factor]
|
|
||||||
|
|
||||||
# hash
|
# hash
|
||||||
hash_speed_up = hash_benchmarks[:ams][:time] / hash_benchmarks[:fast_jsonapi][:time]
|
hash_speed_up = hash_benchmarks[:ams][:time] / hash_benchmarks[:fast_jsonapi][:time]
|
||||||
|
63
spec/lib/object_serializer_relationship_param_spec.rb
Normal file
63
spec/lib/object_serializer_relationship_param_spec.rb
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe FastJsonapi::ObjectSerializer do
|
||||||
|
include_context 'movie class'
|
||||||
|
|
||||||
|
context "params option" do
|
||||||
|
let(:hash) { serializer.serializable_hash }
|
||||||
|
|
||||||
|
before(:context) do
|
||||||
|
class MovieSerializer
|
||||||
|
has_many :agencies do |movie, params|
|
||||||
|
movie.actors.map(&:agency) if params[:authorized]
|
||||||
|
end
|
||||||
|
|
||||||
|
belongs_to :primary_agency do |movie, params|
|
||||||
|
movie.actors.map(&:agency)[0] if params[:authorized]
|
||||||
|
end
|
||||||
|
|
||||||
|
belongs_to :secondary_agency do |movie|
|
||||||
|
movie.actors.map(&:agency)[1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "passing params to the serializer" do
|
||||||
|
let(:params) { {authorized: true} }
|
||||||
|
let(:options_with_params) { {params: params} }
|
||||||
|
|
||||||
|
context "with a single record" do
|
||||||
|
let(:serializer) { MovieSerializer.new(movie, options_with_params) }
|
||||||
|
|
||||||
|
it "handles relationships that use params" do
|
||||||
|
ids = hash[:data][:relationships][:agencies][:data].map{|a| a[:id]}
|
||||||
|
ids.map!(&:to_i)
|
||||||
|
expect(ids).to eq [0,1,2]
|
||||||
|
end
|
||||||
|
|
||||||
|
it "handles relationships that don't use params" do
|
||||||
|
expect(hash[:data][:relationships][:secondary_agency][:data]).to include({id: 1.to_s})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with a list of records" do
|
||||||
|
let(:movies) { build_movies(3) }
|
||||||
|
let(:params) { {authorized: true} }
|
||||||
|
let(:serializer) { MovieSerializer.new(movies, options_with_params) }
|
||||||
|
|
||||||
|
it "handles relationship params when passing params to a list of resources" do
|
||||||
|
relationships_hashes = hash[:data].map{|a| a[:relationships][:agencies][:data]}.uniq.flatten
|
||||||
|
expect(relationships_hashes.map{|a| a[:id].to_i}).to contain_exactly 0,1,2
|
||||||
|
|
||||||
|
uniq_count = hash[:data].map{|a| a[:relationships][:primary_agency] }.uniq.count
|
||||||
|
expect(uniq_count).to eq 1
|
||||||
|
end
|
||||||
|
|
||||||
|
it "handles relationships without params" do
|
||||||
|
uniq_count = hash[:data].map{|a| a[:relationships][:secondary_agency] }.uniq.count
|
||||||
|
expect(uniq_count).to eq 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -1,30 +0,0 @@
|
|||||||
require 'spec_helper'
|
|
||||||
|
|
||||||
describe FastJsonapi::ObjectSerializer do
|
|
||||||
include_context 'movie class'
|
|
||||||
|
|
||||||
context 'when setting id' do
|
|
||||||
subject(:serializable_hash) { MovieSerializer.new(resource).serializable_hash }
|
|
||||||
|
|
||||||
before(:all) do
|
|
||||||
MovieSerializer.set_id :owner_id
|
|
||||||
end
|
|
||||||
|
|
||||||
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
|
|
||||||
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
|
|
||||||
end
|
|
@ -2,19 +2,22 @@ require 'spec_helper'
|
|||||||
|
|
||||||
describe FastJsonapi::ObjectSerializer do
|
describe FastJsonapi::ObjectSerializer do
|
||||||
include_context 'movie class'
|
include_context 'movie class'
|
||||||
|
include_context 'group class'
|
||||||
|
|
||||||
context 'when testing instance methods of object serializer' do
|
context 'when testing instance methods of object serializer' do
|
||||||
it 'returns correct hash when serializable_hash is called' do
|
it 'returns correct hash when serializable_hash is called' do
|
||||||
options = {}
|
options = {}
|
||||||
options[:meta] = { total: 2 }
|
options[:meta] = { total: 2 }
|
||||||
|
options[:links] = { self: 'self' }
|
||||||
options[:include] = [:actors]
|
options[:include] = [:actors]
|
||||||
serializable_hash = MovieSerializer.new([movie, movie], options).serializable_hash
|
serializable_hash = MovieSerializer.new([movie, movie], options).serializable_hash
|
||||||
|
|
||||||
expect(serializable_hash[:data].length).to eq 2
|
expect(serializable_hash[:data].length).to eq 2
|
||||||
expect(serializable_hash[:data][0][:relationships].length).to eq 3
|
expect(serializable_hash[:data][0][:relationships].length).to eq 4
|
||||||
expect(serializable_hash[:data][0][:attributes].length).to eq 2
|
expect(serializable_hash[:data][0][:attributes].length).to eq 2
|
||||||
|
|
||||||
expect(serializable_hash[:meta]).to be_instance_of(Hash)
|
expect(serializable_hash[:meta]).to be_instance_of(Hash)
|
||||||
|
expect(serializable_hash[:links]).to be_instance_of(Hash)
|
||||||
|
|
||||||
expect(serializable_hash[:included]).to be_instance_of(Array)
|
expect(serializable_hash[:included]).to be_instance_of(Array)
|
||||||
expect(serializable_hash[:included][0]).to be_instance_of(Hash)
|
expect(serializable_hash[:included][0]).to be_instance_of(Hash)
|
||||||
@ -24,9 +27,34 @@ describe FastJsonapi::ObjectSerializer do
|
|||||||
|
|
||||||
expect(serializable_hash[:data]).to be_instance_of(Hash)
|
expect(serializable_hash[:data]).to be_instance_of(Hash)
|
||||||
expect(serializable_hash[:meta]).to be nil
|
expect(serializable_hash[:meta]).to be nil
|
||||||
|
expect(serializable_hash[:links]).to be nil
|
||||||
expect(serializable_hash[:included]).to be nil
|
expect(serializable_hash[:included]).to be nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'returns correct nested includes when serializable_hash is called' do
|
||||||
|
# 3 actors, 3 agencies
|
||||||
|
include_object_total = 6
|
||||||
|
|
||||||
|
options = {}
|
||||||
|
options[:include] = [:actors, :'actors.agency']
|
||||||
|
serializable_hash = MovieSerializer.new([movie], options).serializable_hash
|
||||||
|
|
||||||
|
expect(serializable_hash[:included]).to be_instance_of(Array)
|
||||||
|
expect(serializable_hash[:included].length).to eq include_object_total
|
||||||
|
(0..include_object_total-1).each do |include|
|
||||||
|
expect(serializable_hash[:included][include]).to be_instance_of(Hash)
|
||||||
|
end
|
||||||
|
|
||||||
|
options[:include] = [:'actors.agency']
|
||||||
|
serializable_hash = MovieSerializer.new([movie], options).serializable_hash
|
||||||
|
|
||||||
|
expect(serializable_hash[:included]).to be_instance_of(Array)
|
||||||
|
expect(serializable_hash[:included].length).to eq include_object_total
|
||||||
|
(0..include_object_total-1).each do |include|
|
||||||
|
expect(serializable_hash[:included][include]).to be_instance_of(Hash)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
it 'returns correct number of records when serialized_json is called for an array' do
|
it 'returns correct number of records when serialized_json is called for an array' do
|
||||||
options = {}
|
options = {}
|
||||||
options[:meta] = { total: 2 }
|
options[:meta] = { total: 2 }
|
||||||
@ -123,6 +151,107 @@ describe FastJsonapi::ObjectSerializer do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'nested includes' do
|
||||||
|
it 'has_many to belongs_to: returns correct nested includes when serializable_hash is called' do
|
||||||
|
# 3 actors, 3 agencies
|
||||||
|
include_object_total = 6
|
||||||
|
|
||||||
|
options = {}
|
||||||
|
options[:include] = [:actors, :'actors.agency']
|
||||||
|
serializable_hash = MovieSerializer.new([movie], options).serializable_hash
|
||||||
|
|
||||||
|
expect(serializable_hash[:included]).to be_instance_of(Array)
|
||||||
|
expect(serializable_hash[:included].length).to eq include_object_total
|
||||||
|
(0..include_object_total-1).each do |include|
|
||||||
|
expect(serializable_hash[:included][include]).to be_instance_of(Hash)
|
||||||
|
end
|
||||||
|
|
||||||
|
options[:include] = [:'actors.agency']
|
||||||
|
serializable_hash = MovieSerializer.new([movie], options).serializable_hash
|
||||||
|
|
||||||
|
expect(serializable_hash[:included]).to be_instance_of(Array)
|
||||||
|
expect(serializable_hash[:included].length).to eq include_object_total
|
||||||
|
(0..include_object_total-1).each do |include|
|
||||||
|
expect(serializable_hash[:included][include]).to be_instance_of(Hash)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it '`has_many` to `belongs_to` to `belongs_to` - returns correct nested includes when serializable_hash is called' do
|
||||||
|
# 3 actors, 3 agencies, 1 state
|
||||||
|
include_object_total = 7
|
||||||
|
|
||||||
|
options = {}
|
||||||
|
options[:include] = [:actors, :'actors.agency', :'actors.agency.state']
|
||||||
|
serializable_hash = MovieSerializer.new([movie], options).serializable_hash
|
||||||
|
|
||||||
|
expect(serializable_hash[:included]).to be_instance_of(Array)
|
||||||
|
expect(serializable_hash[:included].length).to eq include_object_total
|
||||||
|
|
||||||
|
actors_serialized = serializable_hash[:included].find_all { |included| included[:type] == :actor }.map { |included| included[:id].to_i }
|
||||||
|
agencies_serialized = serializable_hash[:included].find_all { |included| included[:type] == :agency }.map { |included| included[:id].to_i }
|
||||||
|
states_serialized = serializable_hash[:included].find_all { |included| included[:type] == :state }.map { |included| included[:id].to_i }
|
||||||
|
|
||||||
|
movie.actors.each do |actor|
|
||||||
|
expect(actors_serialized).to include(actor.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
agencies = movie.actors.map(&:agency).uniq
|
||||||
|
agencies.each do |agency|
|
||||||
|
expect(agencies_serialized).to include(agency.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
states = agencies.map(&:state).uniq
|
||||||
|
states.each do |state|
|
||||||
|
expect(states_serialized).to include(state.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'has_many => has_one returns correct nested includes when serializable_hash is called' do
|
||||||
|
options = {}
|
||||||
|
options[:include] = [:movies, :'movies.advertising_campaign']
|
||||||
|
serializable_hash = MovieTypeSerializer.new([movie_type], options).serializable_hash
|
||||||
|
|
||||||
|
movies_serialized = serializable_hash[:included].find_all { |included| included[:type] == :movie }.map { |included| included[:id].to_i }
|
||||||
|
advertising_campaigns_serialized = serializable_hash[:included].find_all { |included| included[:type] == :advertising_campaign }.map { |included| included[:id].to_i }
|
||||||
|
|
||||||
|
movies = movie_type.movies
|
||||||
|
movies.each do |movie|
|
||||||
|
expect(movies_serialized).to include(movie.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
advertising_campaigns = movies.map(&:advertising_campaign)
|
||||||
|
advertising_campaigns.each do |advertising_campaign|
|
||||||
|
expect(advertising_campaigns_serialized).to include(advertising_campaign.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'belongs_to: returns correct nested includes when nested attributes are nil when serializable_hash is called' do
|
||||||
|
class Movie
|
||||||
|
def advertising_campaign
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
options = {}
|
||||||
|
options[:include] = [:movies, :'movies.advertising_campaign']
|
||||||
|
|
||||||
|
serializable_hash = MovieTypeSerializer.new([movie_type], options).serializable_hash
|
||||||
|
|
||||||
|
movies_serialized = serializable_hash[:included].find_all { |included| included[:type] == :movie }.map { |included| included[:id].to_i }
|
||||||
|
|
||||||
|
movies = movie_type.movies
|
||||||
|
movies.each do |movie|
|
||||||
|
expect(movies_serialized).to include(movie.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'polymorphic throws an error that polymorphic is not supported' do
|
||||||
|
options = {}
|
||||||
|
options[:include] = [:groupees]
|
||||||
|
expect(-> { GroupSerializer.new([group], options)}).to raise_error(NotImplementedError)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'when testing included do block of object serializer' do
|
context 'when testing included do block of object serializer' do
|
||||||
it 'should set default_type based on serializer class name' do
|
it 'should set default_type based on serializer class name' do
|
||||||
class BlahSerializer
|
class BlahSerializer
|
||||||
@ -154,4 +283,23 @@ describe FastJsonapi::ObjectSerializer do
|
|||||||
expect(V1::BlahSerializer.record_type).to be :blah
|
expect(V1::BlahSerializer.record_type).to be :blah
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when serializing included, serialize any links' do
|
||||||
|
before do
|
||||||
|
ActorSerializer.link(:self) do |actor_object|
|
||||||
|
actor_object.url
|
||||||
|
end
|
||||||
|
end
|
||||||
|
subject(:serializable_hash) do
|
||||||
|
options = {}
|
||||||
|
options[:include] = [:actors]
|
||||||
|
MovieSerializer.new(movie, options).serializable_hash
|
||||||
|
end
|
||||||
|
let(:actor) { movie.actors.first }
|
||||||
|
let(:url) { "http://movies.com/actors/#{actor.id}" }
|
||||||
|
|
||||||
|
it 'returns correct hash when serializable_hash is called' do
|
||||||
|
expect(serializable_hash[:included][0][:links][:self]).to eq url
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -7,14 +7,16 @@ describe FastJsonapi::ObjectSerializer do
|
|||||||
it 'returns correct hash when serializable_hash is called' do
|
it 'returns correct hash when serializable_hash is called' do
|
||||||
options = {}
|
options = {}
|
||||||
options[:meta] = { total: 2 }
|
options[:meta] = { total: 2 }
|
||||||
|
options[:links] = { self: 'self' }
|
||||||
options[:include] = [:actors]
|
options[:include] = [:actors]
|
||||||
serializable_hash = MovieSerializer.new([movie_struct, movie_struct], options).serializable_hash
|
serializable_hash = MovieSerializer.new([movie_struct, movie_struct], options).serializable_hash
|
||||||
|
|
||||||
expect(serializable_hash[:data].length).to eq 2
|
expect(serializable_hash[:data].length).to eq 2
|
||||||
expect(serializable_hash[:data][0][:relationships].length).to eq 3
|
expect(serializable_hash[:data][0][:relationships].length).to eq 4
|
||||||
expect(serializable_hash[:data][0][:attributes].length).to eq 2
|
expect(serializable_hash[:data][0][:attributes].length).to eq 2
|
||||||
|
|
||||||
expect(serializable_hash[:meta]).to be_instance_of(Hash)
|
expect(serializable_hash[:meta]).to be_instance_of(Hash)
|
||||||
|
expect(serializable_hash[:links]).to be_instance_of(Hash)
|
||||||
|
|
||||||
expect(serializable_hash[:included]).to be_instance_of(Array)
|
expect(serializable_hash[:included]).to be_instance_of(Array)
|
||||||
expect(serializable_hash[:included][0]).to be_instance_of(Hash)
|
expect(serializable_hash[:included][0]).to be_instance_of(Hash)
|
||||||
@ -24,8 +26,16 @@ describe FastJsonapi::ObjectSerializer do
|
|||||||
|
|
||||||
expect(serializable_hash[:data]).to be_instance_of(Hash)
|
expect(serializable_hash[:data]).to be_instance_of(Hash)
|
||||||
expect(serializable_hash[:meta]).to be nil
|
expect(serializable_hash[:meta]).to be nil
|
||||||
|
expect(serializable_hash[:links]).to be nil
|
||||||
expect(serializable_hash[:included]).to be nil
|
expect(serializable_hash[:included]).to be nil
|
||||||
expect(serializable_hash[:data][:id]).to eq movie_struct.id.to_s
|
expect(serializable_hash[:data][:id]).to eq movie_struct.id.to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'struct without id' do
|
||||||
|
it 'returns correct hash when serializable_hash is called' do
|
||||||
|
serializer = MovieWithoutIdStructSerializer.new(movie_struct_without_id)
|
||||||
|
expect { serializer.serializable_hash }.to raise_error(FastJsonapi::MandatoryField)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
require 'spec_helper'
|
|
||||||
|
|
||||||
describe FastJsonapi::ObjectSerializer do
|
|
||||||
include_context 'movie class'
|
|
||||||
|
|
||||||
context 'when including attribute blocks' do
|
|
||||||
it 'returns correct hash when serializable_hash is called' do
|
|
||||||
serializable_hash = MovieSerializerWithAttributeBlock.new([movie]).serializable_hash
|
|
||||||
expect(serializable_hash[:data][0][:attributes][:name]).to eq movie.name
|
|
||||||
expect(serializable_hash[:data][0][:attributes][:title_with_year]).to eq "#{movie.name} (#{movie.release_year})"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -18,7 +18,7 @@ describe FastJsonapi::ObjectSerializer do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it 'returns the correct hash when ids_hash_from_record_and_relationship is called for a polymorphic association' do
|
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, polymorphic: {} }
|
relationship = { name: :groupees, relationship_type: :has_many, object_method_name: :groupees, polymorphic: {} }
|
||||||
results = GroupSerializer.send :ids_hash_from_record_and_relationship, group, relationship
|
results = GroupSerializer.send :ids_hash_from_record_and_relationship, group, relationship
|
||||||
expect(results).to include({ id: "1", type: :person }, { id: "2", type: :group })
|
expect(results).to include({ id: "1", type: :person }, { id: "2", type: :group })
|
||||||
end
|
end
|
||||||
@ -70,7 +70,7 @@ describe FastJsonapi::ObjectSerializer do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it 'returns correct hash when record_hash is called' do
|
it 'returns correct hash when record_hash is called' do
|
||||||
record_hash = MovieSerializer.send(:record_hash, movie)
|
record_hash = MovieSerializer.send(:record_hash, movie, nil)
|
||||||
expect(record_hash[:id]).to eq movie.id.to_s
|
expect(record_hash[:id]).to eq movie.id.to_s
|
||||||
expect(record_hash[:type]).to eq MovieSerializer.record_type
|
expect(record_hash[:type]).to eq MovieSerializer.record_type
|
||||||
expect(record_hash).to have_key(:attributes) if MovieSerializer.attributes_to_serialize.present?
|
expect(record_hash).to have_key(:attributes) if MovieSerializer.attributes_to_serialize.present?
|
||||||
@ -82,7 +82,7 @@ describe FastJsonapi::ObjectSerializer do
|
|||||||
known_included_objects = {}
|
known_included_objects = {}
|
||||||
included_records = []
|
included_records = []
|
||||||
[movie, movie].each do |record|
|
[movie, movie].each do |record|
|
||||||
included_records.concat MovieSerializer.send(:get_included_records, record, includes_list, known_included_objects)
|
included_records.concat MovieSerializer.send(:get_included_records, record, includes_list, known_included_objects, nil)
|
||||||
end
|
end
|
||||||
expect(included_records.size).to eq 3
|
expect(included_records.size).to eq 3
|
||||||
end
|
end
|
||||||
|
@ -4,12 +4,55 @@ RSpec.shared_context 'ams movie class' do
|
|||||||
class AMSModel < ActiveModelSerializers::Model
|
class AMSModel < ActiveModelSerializers::Model
|
||||||
derive_attributes_from_names_and_fix_accessors
|
derive_attributes_from_names_and_fix_accessors
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class AMSMovieType < AMSModel
|
||||||
|
attributes :id, :name, :movies
|
||||||
|
end
|
||||||
class AMSMovie < AMSModel
|
class AMSMovie < AMSModel
|
||||||
attributes :id, :name, :release_year, :actors, :owner, :movie_type
|
attributes :id, :name, :release_year, :actors, :owner, :movie_type, :advertising_campaign
|
||||||
|
|
||||||
|
def movie_type
|
||||||
|
mt = AMSMovieType.new
|
||||||
|
mt.id = 1
|
||||||
|
mt.name = 'Episode'
|
||||||
|
mt.movies = [self]
|
||||||
|
mt
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class AMSAdvertisingCampaign < AMSModel
|
||||||
|
attributes :id, :name, :movie
|
||||||
|
end
|
||||||
|
|
||||||
|
class AMSAward < AMSModel
|
||||||
|
attributes :id, :title, :actor
|
||||||
|
end
|
||||||
|
|
||||||
|
class AMSAgency < AMSModel
|
||||||
|
attributes :id, :name, :actors
|
||||||
end
|
end
|
||||||
|
|
||||||
class AMSActor < AMSModel
|
class AMSActor < AMSModel
|
||||||
attributes :id, :name, :email
|
attributes :id, :name, :email, :agency, :awards, :agency_id
|
||||||
|
def agency
|
||||||
|
AMSAgency.new.tap do |a|
|
||||||
|
a.id = agency_id
|
||||||
|
a.name = "Test Agency #{agency_id}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def award_ids
|
||||||
|
[id * 9, id * 9 + 1]
|
||||||
|
end
|
||||||
|
|
||||||
|
def awards
|
||||||
|
award_ids.map do |i|
|
||||||
|
AMSAward.new.tap do |a|
|
||||||
|
a.id = i
|
||||||
|
a.title = "Test Award #{i}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class AMSUser < AMSModel
|
class AMSUser < AMSModel
|
||||||
@ -19,9 +62,22 @@ RSpec.shared_context 'ams movie class' do
|
|||||||
attributes :id, :name
|
attributes :id, :name
|
||||||
end
|
end
|
||||||
# serializers
|
# serializers
|
||||||
|
class AMSAwardSerializer < ActiveModel::Serializer
|
||||||
|
type 'award'
|
||||||
|
attributes :id, :title
|
||||||
|
belongs_to :actor
|
||||||
|
end
|
||||||
|
class AMSAgencySerializer < ActiveModel::Serializer
|
||||||
|
type 'agency'
|
||||||
|
attributes :id, :name
|
||||||
|
belongs_to :state
|
||||||
|
has_many :actors
|
||||||
|
end
|
||||||
class AMSActorSerializer < ActiveModel::Serializer
|
class AMSActorSerializer < ActiveModel::Serializer
|
||||||
type 'actor'
|
type 'actor'
|
||||||
attributes :name, :email
|
attributes :name, :email
|
||||||
|
belongs_to :agency, serializer: ::AMSAgencySerializer
|
||||||
|
has_many :awards, serializer: ::AMSAwardSerializer
|
||||||
end
|
end
|
||||||
class AMSUserSerializer < ActiveModel::Serializer
|
class AMSUserSerializer < ActiveModel::Serializer
|
||||||
type 'user'
|
type 'user'
|
||||||
@ -30,6 +86,11 @@ RSpec.shared_context 'ams movie class' do
|
|||||||
class AMSMovieTypeSerializer < ActiveModel::Serializer
|
class AMSMovieTypeSerializer < ActiveModel::Serializer
|
||||||
type 'movie_type'
|
type 'movie_type'
|
||||||
attributes :name
|
attributes :name
|
||||||
|
has_many :movies
|
||||||
|
end
|
||||||
|
class AMSAdvertisingCampaignSerializer < ActiveModel::Serializer
|
||||||
|
type 'advertising_campaign'
|
||||||
|
attributes :name
|
||||||
end
|
end
|
||||||
class AMSMovieSerializer < ActiveModel::Serializer
|
class AMSMovieSerializer < ActiveModel::Serializer
|
||||||
type 'movie'
|
type 'movie'
|
||||||
@ -37,6 +98,7 @@ RSpec.shared_context 'ams movie class' do
|
|||||||
has_many :actors
|
has_many :actors
|
||||||
has_one :owner
|
has_one :owner
|
||||||
belongs_to :movie_type
|
belongs_to :movie_type
|
||||||
|
has_one :advertising_campaign
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -53,6 +115,7 @@ RSpec.shared_context 'ams movie class' do
|
|||||||
a.id = i + 1
|
a.id = i + 1
|
||||||
a.name = "Test #{a.id}"
|
a.name = "Test #{a.id}"
|
||||||
a.email = "test#{a.id}@test.com"
|
a.email = "test#{a.id}@test.com"
|
||||||
|
a.agency_id = i
|
||||||
a
|
a
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -70,6 +133,13 @@ RSpec.shared_context 'ams movie class' do
|
|||||||
ams_movie_type
|
ams_movie_type
|
||||||
end
|
end
|
||||||
|
|
||||||
|
let(:ams_advertising_campaign) do
|
||||||
|
campaign = AMSAdvertisingCampaign.new
|
||||||
|
campaign.id = 1
|
||||||
|
campaign.name = "Movie is incredible!!"
|
||||||
|
campaign
|
||||||
|
end
|
||||||
|
|
||||||
def build_ams_movies(count)
|
def build_ams_movies(count)
|
||||||
count.times.map do |i|
|
count.times.map do |i|
|
||||||
m = AMSMovie.new
|
m = AMSMovie.new
|
||||||
@ -78,6 +148,7 @@ RSpec.shared_context 'ams movie class' do
|
|||||||
m.actors = ams_actors
|
m.actors = ams_actors
|
||||||
m.owner = ams_user
|
m.owner = ams_user
|
||||||
m.movie_type = ams_movie_type
|
m.movie_type = ams_movie_type
|
||||||
|
m.advertising_campaign = ams_advertising_campaign
|
||||||
m
|
m
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -26,21 +26,7 @@ RSpec.shared_context 'group class' do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Person and Group struct
|
||||||
# Namespaced PersonSerializer
|
|
||||||
before(:context) do
|
|
||||||
# namespaced model stub
|
|
||||||
module AppName
|
|
||||||
module V1
|
|
||||||
class PersonSerializer
|
|
||||||
include FastJsonapi::ObjectSerializer
|
|
||||||
# to test if compute_serializer_name works
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Movie and Actor struct
|
|
||||||
before(:context) do
|
before(:context) do
|
||||||
PersonStruct = Struct.new(
|
PersonStruct = Struct.new(
|
||||||
:id, :first_name, :last_name
|
:id, :first_name, :last_name
|
||||||
@ -57,7 +43,6 @@ RSpec.shared_context 'group class' do
|
|||||||
PersonSerializer
|
PersonSerializer
|
||||||
Group
|
Group
|
||||||
GroupSerializer
|
GroupSerializer
|
||||||
AppName::V1::PersonSerializer
|
|
||||||
PersonStruct
|
PersonStruct
|
||||||
GroupStruct
|
GroupStruct
|
||||||
]
|
]
|
||||||
@ -66,25 +51,6 @@ RSpec.shared_context 'group class' do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:group_struct) do
|
|
||||||
group = GroupStruct.new
|
|
||||||
group[:id] = 1
|
|
||||||
group[:name] = 'Group 1'
|
|
||||||
group[:groupees] = []
|
|
||||||
|
|
||||||
person = PersonStruct.new
|
|
||||||
person[:id] = 1
|
|
||||||
person[:last_name] = "Last Name 1"
|
|
||||||
person[:first_name] = "First Name 1"
|
|
||||||
|
|
||||||
child_group = GroupStruct.new
|
|
||||||
child_group[:id] = 2
|
|
||||||
child_group[:name] = 'Group 2'
|
|
||||||
|
|
||||||
group.groupees = [person, child_group]
|
|
||||||
group
|
|
||||||
end
|
|
||||||
|
|
||||||
let(:group) do
|
let(:group) do
|
||||||
group = Group.new
|
group = Group.new
|
||||||
group.id = 1
|
group.id = 1
|
||||||
|
@ -13,11 +13,12 @@ RSpec.shared_context 'movie class' do
|
|||||||
:movie_type_id
|
:movie_type_id
|
||||||
|
|
||||||
def actors
|
def actors
|
||||||
actor_ids.map do |id|
|
actor_ids.map.with_index do |id, i|
|
||||||
a = Actor.new
|
a = Actor.new
|
||||||
a.id = id
|
a.id = id
|
||||||
a.name = "Test #{a.id}"
|
a.name = "Test #{a.id}"
|
||||||
a.email = "test#{a.id}@test.com"
|
a.email = "test#{a.id}@test.com"
|
||||||
|
a.agency_id = i
|
||||||
a
|
a
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -26,20 +27,107 @@ RSpec.shared_context 'movie class' do
|
|||||||
mt = MovieType.new
|
mt = MovieType.new
|
||||||
mt.id = movie_type_id
|
mt.id = movie_type_id
|
||||||
mt.name = 'Episode'
|
mt.name = 'Episode'
|
||||||
|
mt.movie_ids = [id]
|
||||||
mt
|
mt
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def advertising_campaign_id
|
||||||
|
1
|
||||||
|
end
|
||||||
|
|
||||||
|
def advertising_campaign
|
||||||
|
ac = AdvertisingCampaign.new
|
||||||
|
ac.id = 1
|
||||||
|
ac.movie_id = id
|
||||||
|
ac.name = "Movie #{name} is incredible!!"
|
||||||
|
ac
|
||||||
|
end
|
||||||
|
|
||||||
def cache_key
|
def cache_key
|
||||||
"#{id}"
|
"#{id}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def url
|
||||||
|
"http://movies.com/#{id}"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class Actor
|
class Actor
|
||||||
attr_accessor :id, :name, :email
|
attr_accessor :id, :name, :email, :agency_id
|
||||||
|
|
||||||
|
def agency
|
||||||
|
Agency.new.tap do |a|
|
||||||
|
a.id = agency_id
|
||||||
|
a.name = "Test Agency #{agency_id}"
|
||||||
|
a.state_id = 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def awards
|
||||||
|
award_ids.map do |i|
|
||||||
|
Award.new.tap do |a|
|
||||||
|
a.id = i
|
||||||
|
a.title = "Test Award #{i}"
|
||||||
|
a.actor_id = id
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def award_ids
|
||||||
|
[id * 9, id * 9 + 1]
|
||||||
|
end
|
||||||
|
|
||||||
|
def url
|
||||||
|
"http://movies.com/actors/#{id}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class AdvertisingCampaign
|
||||||
|
attr_accessor :id, :name, :movie_id
|
||||||
|
end
|
||||||
|
|
||||||
|
class Agency
|
||||||
|
attr_accessor :id, :name, :state_id
|
||||||
|
|
||||||
|
def state
|
||||||
|
State.new.tap do |s|
|
||||||
|
s.id = state_id
|
||||||
|
s.name = "Test State #{state_id}"
|
||||||
|
s.agency_ids = [id]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Award
|
||||||
|
attr_accessor :id, :title, :actor_id
|
||||||
|
end
|
||||||
|
|
||||||
|
class State
|
||||||
|
attr_accessor :id, :name, :agency_ids
|
||||||
end
|
end
|
||||||
|
|
||||||
class MovieType
|
class MovieType
|
||||||
attr_accessor :id, :name
|
attr_accessor :id, :name, :movie_ids
|
||||||
|
|
||||||
|
def movies
|
||||||
|
movie_ids.map.with_index do |id, i|
|
||||||
|
m = Movie.new
|
||||||
|
m.id = 232
|
||||||
|
m.name = 'test movie'
|
||||||
|
m.actor_ids = [1, 2, 3]
|
||||||
|
m.owner_id = 3
|
||||||
|
m.movie_type_id = 1
|
||||||
|
m
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Agency
|
||||||
|
attr_accessor :id, :name, :actor_ids
|
||||||
|
end
|
||||||
|
|
||||||
|
class Agency
|
||||||
|
attr_accessor :id, :name, :actor_ids
|
||||||
end
|
end
|
||||||
|
|
||||||
class Supplier
|
class Supplier
|
||||||
@ -67,6 +155,12 @@ RSpec.shared_context 'movie class' do
|
|||||||
has_many :actors
|
has_many :actors
|
||||||
belongs_to :owner, record_type: :user
|
belongs_to :owner, record_type: :user
|
||||||
belongs_to :movie_type
|
belongs_to :movie_type
|
||||||
|
has_one :advertising_campaign
|
||||||
|
end
|
||||||
|
|
||||||
|
class MovieWithoutIdStructSerializer
|
||||||
|
include FastJsonapi::ObjectSerializer
|
||||||
|
attributes :name, :release_year
|
||||||
end
|
end
|
||||||
|
|
||||||
class CachingMovieSerializer
|
class CachingMovieSerializer
|
||||||
@ -95,12 +189,41 @@ RSpec.shared_context 'movie class' do
|
|||||||
include FastJsonapi::ObjectSerializer
|
include FastJsonapi::ObjectSerializer
|
||||||
set_type :actor
|
set_type :actor
|
||||||
attributes :name, :email
|
attributes :name, :email
|
||||||
|
belongs_to :agency
|
||||||
|
has_many :awards
|
||||||
|
belongs_to :agency
|
||||||
|
end
|
||||||
|
|
||||||
|
class AgencySerializer
|
||||||
|
include FastJsonapi::ObjectSerializer
|
||||||
|
attributes :id, :name
|
||||||
|
belongs_to :state
|
||||||
|
has_many :actors
|
||||||
|
end
|
||||||
|
|
||||||
|
class AwardSerializer
|
||||||
|
include FastJsonapi::ObjectSerializer
|
||||||
|
attributes :id, :title
|
||||||
|
belongs_to :actor
|
||||||
|
end
|
||||||
|
|
||||||
|
class StateSerializer
|
||||||
|
include FastJsonapi::ObjectSerializer
|
||||||
|
attributes :id, :name
|
||||||
|
has_many :agency
|
||||||
|
end
|
||||||
|
|
||||||
|
class AdvertisingCampaignSerializer
|
||||||
|
include FastJsonapi::ObjectSerializer
|
||||||
|
attributes :id, :name
|
||||||
|
belongs_to :movie
|
||||||
end
|
end
|
||||||
|
|
||||||
class MovieTypeSerializer
|
class MovieTypeSerializer
|
||||||
include FastJsonapi::ObjectSerializer
|
include FastJsonapi::ObjectSerializer
|
||||||
set_type :movie_type
|
set_type :movie_type
|
||||||
attributes :name
|
attributes :name
|
||||||
|
has_many :movies
|
||||||
end
|
end
|
||||||
|
|
||||||
class MovieSerializerWithAttributeBlock
|
class MovieSerializerWithAttributeBlock
|
||||||
@ -112,6 +235,21 @@ RSpec.shared_context 'movie class' do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class MovieSerializerWithAttributeBlock
|
||||||
|
include FastJsonapi::ObjectSerializer
|
||||||
|
set_type :movie
|
||||||
|
attributes :name, :release_year
|
||||||
|
attribute :title_with_year do |record|
|
||||||
|
"#{record.name} (#{record.release_year})"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class AgencySerializer
|
||||||
|
include FastJsonapi::ObjectSerializer
|
||||||
|
attributes :id, :name
|
||||||
|
has_many :actors
|
||||||
|
end
|
||||||
|
|
||||||
class SupplierSerializer
|
class SupplierSerializer
|
||||||
include FastJsonapi::ObjectSerializer
|
include FastJsonapi::ObjectSerializer
|
||||||
set_type :supplier
|
set_type :supplier
|
||||||
@ -149,10 +287,13 @@ RSpec.shared_context 'movie class' do
|
|||||||
:actors,
|
:actors,
|
||||||
:owner_id,
|
:owner_id,
|
||||||
:owner,
|
:owner,
|
||||||
:movie_type_id
|
:movie_type_id,
|
||||||
|
:advertising_campaign_id
|
||||||
)
|
)
|
||||||
|
|
||||||
ActorStruct = Struct.new(:id, :name, :email)
|
ActorStruct = Struct.new(:id, :name, :email, :agency_id, :award_ids)
|
||||||
|
MovieWithoutIdStruct = Struct.new(:name, :release_year)
|
||||||
|
AgencyStruct = Struct.new(:id, :name, :actor_ids)
|
||||||
end
|
end
|
||||||
|
|
||||||
after(:context) do
|
after(:context) do
|
||||||
@ -163,11 +304,17 @@ RSpec.shared_context 'movie class' do
|
|||||||
ActorSerializer
|
ActorSerializer
|
||||||
MovieType
|
MovieType
|
||||||
MovieTypeSerializer
|
MovieTypeSerializer
|
||||||
MovieSerializerWithAttributeBlock
|
|
||||||
AppName::V1::MovieSerializer
|
AppName::V1::MovieSerializer
|
||||||
MovieStruct
|
MovieStruct
|
||||||
ActorStruct
|
ActorStruct
|
||||||
|
MovieWithoutIdStruct
|
||||||
HyphenMovieSerializer
|
HyphenMovieSerializer
|
||||||
|
MovieWithoutIdStructSerializer
|
||||||
|
Agency
|
||||||
|
AgencyStruct
|
||||||
|
AgencySerializer
|
||||||
|
AdvertisingCampaign
|
||||||
|
AdvertisingCampaignSerializer
|
||||||
]
|
]
|
||||||
classes_to_remove.each do |klass_name|
|
classes_to_remove.each do |klass_name|
|
||||||
Object.send(:remove_const, klass_name) if Object.constants.include?(klass_name)
|
Object.send(:remove_const, klass_name) if Object.constants.include?(klass_name)
|
||||||
@ -176,10 +323,12 @@ RSpec.shared_context 'movie class' do
|
|||||||
|
|
||||||
let(:movie_struct) do
|
let(:movie_struct) do
|
||||||
|
|
||||||
|
agency = AgencyStruct
|
||||||
|
|
||||||
actors = []
|
actors = []
|
||||||
|
|
||||||
3.times.each do |id|
|
3.times.each do |id|
|
||||||
actors << ActorStruct.new(id, id.to_s, id.to_s)
|
actors << ActorStruct.new(id, id.to_s, id.to_s, id, [id])
|
||||||
end
|
end
|
||||||
|
|
||||||
m = MovieStruct.new
|
m = MovieStruct.new
|
||||||
@ -193,6 +342,10 @@ RSpec.shared_context 'movie class' do
|
|||||||
m
|
m
|
||||||
end
|
end
|
||||||
|
|
||||||
|
let(:movie_struct_without_id) do
|
||||||
|
MovieWithoutIdStruct.new('struct without id', 2018)
|
||||||
|
end
|
||||||
|
|
||||||
let(:movie) do
|
let(:movie) do
|
||||||
m = Movie.new
|
m = Movie.new
|
||||||
m.id = 232
|
m.id = 232
|
||||||
@ -203,6 +356,25 @@ RSpec.shared_context 'movie class' do
|
|||||||
m
|
m
|
||||||
end
|
end
|
||||||
|
|
||||||
|
let(:actor) do
|
||||||
|
Actor.new.tap do |a|
|
||||||
|
a.id = 234
|
||||||
|
a.name = 'test actor'
|
||||||
|
a.email = 'test@test.com'
|
||||||
|
a.agency_id = 432
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:movie_type) do
|
||||||
|
movie
|
||||||
|
|
||||||
|
mt = MovieType.new
|
||||||
|
mt.id = movie.movie_type_id
|
||||||
|
mt.name = 'Foreign Thriller'
|
||||||
|
mt.movie_ids = [movie.id]
|
||||||
|
mt
|
||||||
|
end
|
||||||
|
|
||||||
let(:supplier) do
|
let(:supplier) do
|
||||||
s = Supplier.new
|
s = Supplier.new
|
||||||
s.id = 1
|
s.id = 1
|
||||||
|
@ -0,0 +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
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
RSpec.shared_examples 'returning key transformed hash' do |movie_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)
|
||||||
|
end
|
||||||
|
end
|
@ -1,3 +1,4 @@
|
|||||||
|
require 'active_record'
|
||||||
require 'fast_jsonapi'
|
require 'fast_jsonapi'
|
||||||
require 'rspec-benchmark'
|
require 'rspec-benchmark'
|
||||||
require 'byebug'
|
require 'byebug'
|
||||||
@ -7,6 +8,7 @@ require 'jsonapi/serializable'
|
|||||||
require 'jsonapi-serializers'
|
require 'jsonapi-serializers'
|
||||||
|
|
||||||
Dir[File.dirname(__FILE__) + '/shared/contexts/*.rb'].each {|file| require file }
|
Dir[File.dirname(__FILE__) + '/shared/contexts/*.rb'].each {|file| require file }
|
||||||
|
Dir[File.dirname(__FILE__) + '/shared/examples/*.rb'].each {|file| require file }
|
||||||
|
|
||||||
RSpec.configure do |config|
|
RSpec.configure do |config|
|
||||||
config.include RSpec::Benchmark::Matchers
|
config.include RSpec::Benchmark::Matchers
|
||||||
|
Loading…
x
Reference in New Issue
Block a user