Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
ffa25cda0c | ||
|
f4ad6bc2ac |
93
README.md
93
README.md
@ -29,6 +29,7 @@ Fast JSON API serialized 250 records in 3.01 ms
|
||||
* [Key Transforms](#key-transforms)
|
||||
* [Collection Serialization](#collection-serialization)
|
||||
* [Caching](#caching)
|
||||
* [Params](#params)
|
||||
* [Contributing](#contributing)
|
||||
|
||||
|
||||
@ -173,7 +174,7 @@ By default, attributes are read directly from the model property of the same nam
|
||||
```ruby
|
||||
class MovieSerializer
|
||||
include FastJsonapi::ObjectSerializer
|
||||
|
||||
|
||||
attribute :name
|
||||
end
|
||||
```
|
||||
@ -183,9 +184,9 @@ Custom attributes that must be serialized but do not exist on the model can be d
|
||||
```ruby
|
||||
class MovieSerializer
|
||||
include FastJsonapi::ObjectSerializer
|
||||
|
||||
|
||||
attributes :name, :year
|
||||
|
||||
|
||||
attribute :name_with_year do |object|
|
||||
"#{object.name} (#{object.year})"
|
||||
end
|
||||
@ -197,21 +198,51 @@ The block syntax can also be used to override the property on the object:
|
||||
```ruby
|
||||
class MovieSerializer
|
||||
include FastJsonapi::ObjectSerializer
|
||||
|
||||
|
||||
attribute :name do |object|
|
||||
"#{object.name} Part 2"
|
||||
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
|
||||
|
||||
Support for top-level included member through ` options[:include] `.
|
||||
Support for top-level and nested included associations through ` options[:include] `.
|
||||
|
||||
```ruby
|
||||
options = {}
|
||||
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
|
||||
```
|
||||
|
||||
@ -219,11 +250,17 @@ MovieSerializer.new([movie, movie], options).serialized_json
|
||||
|
||||
```ruby
|
||||
options[:meta] = { total: 2 }
|
||||
options[:links] = {
|
||||
self: '...',
|
||||
next: '...',
|
||||
prev: '...'
|
||||
}
|
||||
hash = MovieSerializer.new([movie, movie], options).serializable_hash
|
||||
json_string = MovieSerializer.new([movie, movie], options).serialized_json
|
||||
```
|
||||
|
||||
### Caching
|
||||
Requires a `cache_key` method be defined on model:
|
||||
|
||||
```ruby
|
||||
class MovieSerializer
|
||||
@ -234,17 +271,56 @@ class MovieSerializer
|
||||
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
|
||||
|
||||
Option | Purpose | Example
|
||||
------------ | ------------- | -------------
|
||||
set_type | Type name of Object | ```set_type :movie ```
|
||||
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 ```
|
||||
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```
|
||||
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
|
||||
|
||||
@ -304,4 +380,3 @@ rspec spec --tag performance:true
|
||||
Join the Netflix Studio Engineering team and help us build gems like this!
|
||||
|
||||
* [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.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.metadata = { "allowed_push_host" => "https://rubygems.org" } if gem.respond_to? :metadata=
|
||||
gem.require_paths = ["lib"]
|
||||
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.email = ""
|
||||
gem.extra_rdoc_files = [
|
||||
|
@ -1,8 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
begin
|
||||
require 'active_record'
|
||||
|
||||
if defined?(::ActiveRecord)
|
||||
::ActiveRecord::Associations::Builder::HasOne.class_eval do
|
||||
# Based on
|
||||
# https://github.com/rails/rails/blob/master/activerecord/lib/active_record/associations/builder/collection_association.rb#L50
|
||||
@ -12,11 +10,11 @@ begin
|
||||
name = reflection.name
|
||||
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
||||
def #{name}_id
|
||||
# if an attribute is already defined with this methods name we should just use it
|
||||
return read_attribute(__method__) if has_attribute?(__method__)
|
||||
association(:#{name}).reader.try(:id)
|
||||
end
|
||||
CODE
|
||||
end
|
||||
end
|
||||
rescue LoadError
|
||||
# active_record can't be loaded so we shouldn't try to monkey-patch it.
|
||||
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'
|
||||
|
||||
module FastJsonapi
|
||||
module Instrumentation
|
||||
module Skylight
|
||||
module Normalizers
|
||||
class SerializableHash < Skylight::Normalizers::Normalizer
|
||||
class SerializableHash < SKYLIGHT_NORMALIZER_BASE_CLASS
|
||||
|
||||
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'
|
||||
|
||||
module FastJsonapi
|
||||
module Instrumentation
|
||||
module Skylight
|
||||
module Normalizers
|
||||
class SerializedJson < Skylight::Normalizers::Normalizer
|
||||
class SerializedJson < SKYLIGHT_NORMALIZER_BASE_CLASS
|
||||
|
||||
register FastJsonapi::ObjectSerializer::SERIALIZED_JSON_NOTIFICATION
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'logger'
|
||||
|
||||
# Usage:
|
||||
# class Movie
|
||||
# def to_json(payload)
|
||||
|
@ -10,8 +10,8 @@ module FastJsonapi
|
||||
extend ActiveSupport::Concern
|
||||
include SerializationCore
|
||||
|
||||
SERIALIZABLE_HASH_NOTIFICATION = 'render.fast_jsonapi.serializable_hash'.freeze
|
||||
SERIALIZED_JSON_NOTIFICATION = 'render.fast_jsonapi.serialized_json'.freeze
|
||||
SERIALIZABLE_HASH_NOTIFICATION = 'render.fast_jsonapi.serializable_hash'
|
||||
SERIALIZED_JSON_NOTIFICATION = 'render.fast_jsonapi.serialized_json'
|
||||
|
||||
included do
|
||||
# Set record_type based on the name of the serializer class
|
||||
@ -34,11 +34,12 @@ module FastJsonapi
|
||||
def hash_for_one_record
|
||||
serializable_hash = { data: nil }
|
||||
serializable_hash[:meta] = @meta if @meta.present?
|
||||
serializable_hash[:links] = @links if @links.present?
|
||||
|
||||
return serializable_hash unless @resource
|
||||
|
||||
serializable_hash[:data] = self.class.record_hash(@resource)
|
||||
serializable_hash[:included] = self.class.get_included_records(@resource, @includes, @known_included_objects) if @includes.present?
|
||||
serializable_hash[:data] = self.class.record_hash(@resource, @params)
|
||||
serializable_hash[:included] = self.class.get_included_records(@resource, @includes, @known_included_objects, @params) if @includes.present?
|
||||
serializable_hash
|
||||
end
|
||||
|
||||
@ -48,13 +49,14 @@ module FastJsonapi
|
||||
data = []
|
||||
included = []
|
||||
@resource.each do |record|
|
||||
data << self.class.record_hash(record)
|
||||
included.concat self.class.get_included_records(record, @includes, @known_included_objects) if @includes.present?
|
||||
data << self.class.record_hash(record, @params)
|
||||
included.concat self.class.get_included_records(record, @includes, @known_included_objects, @params) if @includes.present?
|
||||
end
|
||||
|
||||
serializable_hash[:data] = data
|
||||
serializable_hash[:included] = included if @includes.present?
|
||||
serializable_hash[:meta] = @meta if @meta.present?
|
||||
serializable_hash[:links] = @links if @links.present?
|
||||
serializable_hash
|
||||
end
|
||||
|
||||
@ -69,20 +71,13 @@ module FastJsonapi
|
||||
|
||||
@known_included_objects = {}
|
||||
@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?
|
||||
@includes = options[:include].delete_if(&:blank?).map(&:to_sym)
|
||||
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"
|
||||
self.class.validate_includes!(@includes)
|
||||
end
|
||||
end
|
||||
|
||||
@ -91,6 +86,20 @@ module FastJsonapi
|
||||
end
|
||||
|
||||
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
|
||||
return @reflected_record_type if defined?(@reflected_record_type)
|
||||
|
||||
@ -108,11 +117,11 @@ module FastJsonapi
|
||||
dash: :dasherize,
|
||||
underscore: :underscore
|
||||
}
|
||||
@transform_method = mapping[transform_name.to_sym]
|
||||
self.transform_method = mapping[transform_name.to_sym]
|
||||
end
|
||||
|
||||
def run_key_transform(input)
|
||||
if @transform_method.present?
|
||||
if self.transform_method.present?
|
||||
input.to_s.send(*@transform_method).to_sym
|
||||
else
|
||||
input.to_sym
|
||||
@ -135,6 +144,7 @@ module FastJsonapi
|
||||
def cache_options(cache_options)
|
||||
self.cached = cache_options[:enabled] || false
|
||||
self.cache_length = cache_options[:cache_length] || 5.minutes
|
||||
self.race_condition_ttl = cache_options[:race_condition_ttl] || 5.seconds
|
||||
end
|
||||
|
||||
def attributes(*attributes_list, &block)
|
||||
@ -162,67 +172,54 @@ module FastJsonapi
|
||||
self.relationships_to_serialize[name] = relationship
|
||||
end
|
||||
|
||||
def has_many(relationship_name, options = {})
|
||||
def has_many(relationship_name, options = {}, &block)
|
||||
name = relationship_name.to_sym
|
||||
singular_name = relationship_name.to_s.singularize
|
||||
serializer_key = options[:serializer] || singular_name.to_sym
|
||||
key = options[:key] || run_key_transform(relationship_name)
|
||||
record_type = options[:record_type] || run_key_transform(singular_name)
|
||||
relationship = {
|
||||
key: key,
|
||||
hash = create_relationship_hash(relationship_name, :has_many, options, block)
|
||||
add_relationship(name, hash)
|
||||
end
|
||||
|
||||
def has_one(relationship_name, options = {}, &block)
|
||||
name = relationship_name.to_sym
|
||||
hash = create_relationship_hash(relationship_name, :has_one, options, block)
|
||||
add_relationship(name, hash)
|
||||
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,
|
||||
id_method_name: options[:id_method_name] || (singular_name + '_ids').to_sym,
|
||||
record_type: record_type,
|
||||
id_method_name: options[:id_method_name] || "#{base_serialization_key}#{id_postfix}".to_sym,
|
||||
record_type: options[:record_type] || run_key_transform(base_key_sym),
|
||||
object_method_name: options[:object_method_name] || name,
|
||||
serializer: compute_serializer_name(serializer_key),
|
||||
relationship_type: :has_many,
|
||||
object_block: block,
|
||||
serializer: compute_serializer_name(options[:serializer] || base_key_sym),
|
||||
relationship_type: relationship_type,
|
||||
cached: options[:cached] || false,
|
||||
polymorphic: fetch_polymorphic_option(options)
|
||||
}
|
||||
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
|
||||
|
||||
def compute_serializer_name(serializer_key)
|
||||
return serializer_key unless serializer_key.is_a? Symbol
|
||||
namespace = self.name.gsub(/()?\w+Serializer$/, '')
|
||||
serializer_name = serializer_key.to_s.classify + 'Serializer'
|
||||
return (namespace + serializer_name).to_sym if namespace.present?
|
||||
(serializer_key.to_s.classify + 'Serializer').to_sym
|
||||
(namespace + serializer_name).to_sym
|
||||
end
|
||||
|
||||
def fetch_polymorphic_option(options)
|
||||
@ -231,6 +228,27 @@ module FastJsonapi
|
||||
return option if option.respond_to? :keys
|
||||
{}
|
||||
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
|
||||
|
@ -4,6 +4,8 @@ require 'active_support/concern'
|
||||
require 'fast_jsonapi/multi_to_json'
|
||||
|
||||
module FastJsonapi
|
||||
MandatoryField = Class.new(StandardError)
|
||||
|
||||
module SerializationCore
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
@ -13,16 +15,23 @@ module FastJsonapi
|
||||
:relationships_to_serialize,
|
||||
:cachable_relationships_to_serialize,
|
||||
:uncachable_relationships_to_serialize,
|
||||
:transform_method,
|
||||
:record_type,
|
||||
:record_id,
|
||||
:cache_length,
|
||||
:cached
|
||||
:race_condition_ttl,
|
||||
:cached,
|
||||
:data_links
|
||||
end
|
||||
end
|
||||
|
||||
class_methods do
|
||||
def id_hash(id, record_type)
|
||||
return { id: id.to_s, type: record_type } if id.present?
|
||||
def id_hash(id, record_type, default_return=false)
|
||||
if id.present?
|
||||
{ id: id.to_s, type: record_type }
|
||||
else
|
||||
default_return ? { id: nil, type: record_type } : nil
|
||||
end
|
||||
end
|
||||
|
||||
def ids_hash(ids, record_type)
|
||||
@ -33,19 +42,18 @@ module FastJsonapi
|
||||
def id_hash_from_record(record, record_types)
|
||||
# memoize the record type within the record_types dictionary, then assigning to record_type:
|
||||
record_type = record_types[record.class] ||= record.class.name.underscore.to_sym
|
||||
{ id: record.id.to_s, type: record_type }
|
||||
id_hash(record.id, record_type)
|
||||
end
|
||||
|
||||
def ids_hash_from_record_and_relationship(record, relationship)
|
||||
def ids_hash_from_record_and_relationship(record, relationship, params = {})
|
||||
polymorphic = relationship[:polymorphic]
|
||||
|
||||
return ids_hash(
|
||||
record.public_send(relationship[:id_method_name]),
|
||||
fetch_id(record, relationship, params),
|
||||
relationship[:record_type]
|
||||
) unless polymorphic
|
||||
|
||||
object_method_name = relationship.fetch(:object_method_name, relationship[:name])
|
||||
return unless associated_object = record.send(object_method_name)
|
||||
return unless associated_object = fetch_associated_object(record, relationship, params)
|
||||
|
||||
return associated_object.map do |object|
|
||||
id_hash_from_record object, polymorphic
|
||||
@ -54,69 +62,131 @@ module FastJsonapi
|
||||
id_hash_from_record associated_object, polymorphic
|
||||
end
|
||||
|
||||
def attributes_hash(record)
|
||||
attributes_to_serialize.each_with_object({}) do |(key, method), attr_hash|
|
||||
attr_hash[key] = method.is_a?(Proc) ? method.call(record) : record.public_send(method)
|
||||
def links_hash(record, params = {})
|
||||
data_links.each_with_object({}) do |(key, method), link_hash|
|
||||
link_hash[key] = if method.is_a?(Proc)
|
||||
method.arity == 1 ? method.call(record) : method.call(record, params)
|
||||
else
|
||||
record.public_send(method)
|
||||
end
|
||||
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.each_with_object({}) do |(_k, relationship), hash|
|
||||
name = relationship[:key]
|
||||
empty_case = relationship[:relationship_type] == :has_many ? [] : nil
|
||||
hash[name] = {
|
||||
data: ids_hash_from_record_and_relationship(record, relationship) || empty_case
|
||||
data: ids_hash_from_record_and_relationship(record, relationship, params) || empty_case
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def record_hash(record)
|
||||
def record_hash(record, params = {})
|
||||
if cached
|
||||
record_hash = Rails.cache.fetch(record.cache_key, expires_in: cache_length) do
|
||||
id = record_id ? record.send(record_id) : record.id
|
||||
temp_hash = id_hash(id, record_type) || { id: nil, type: record_type }
|
||||
temp_hash[:attributes] = attributes_hash(record) if attributes_to_serialize.present?
|
||||
record_hash = Rails.cache.fetch(record.cache_key, expires_in: cache_length, race_condition_ttl: race_condition_ttl) do
|
||||
temp_hash = id_hash(id_from_record(record), record_type, true)
|
||||
temp_hash[:attributes] = attributes_hash(record, params) if attributes_to_serialize.present?
|
||||
temp_hash[: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
|
||||
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
|
||||
else
|
||||
id = record_id ? record.send(record_id) : record.id
|
||||
record_hash = id_hash(id, record_type) || { id: nil, type: record_type }
|
||||
record_hash[:attributes] = attributes_hash(record) if attributes_to_serialize.present?
|
||||
record_hash[:relationships] = relationships_hash(record) if relationships_to_serialize.present?
|
||||
record_hash = id_hash(id_from_record(record), record_type, true)
|
||||
record_hash[:attributes] = attributes_hash(record, params) if attributes_to_serialize.present?
|
||||
record_hash[:relationships] = relationships_hash(record, nil, params) if relationships_to_serialize.present?
|
||||
record_hash[:links] = links_hash(record, params) if data_links.present?
|
||||
record_hash
|
||||
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
|
||||
def to_json(payload)
|
||||
FastJsonapi::MultiToJson.to_json(payload) if payload.present?
|
||||
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)
|
||||
includes_list.each_with_object([]) do |item, included_records|
|
||||
object_method_name = @relationships_to_serialize[item][:object_method_name]
|
||||
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 = record.send(object_method_name)
|
||||
next if included_objects.blank?
|
||||
included_objects = [included_objects] unless relationship_type == :has_many
|
||||
included_objects.each do |inc_obj|
|
||||
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)
|
||||
def remaining_items(items)
|
||||
return unless items.size > 1
|
||||
|
||||
items_copy = items.dup
|
||||
items_copy.delete_at(0)
|
||||
[items_copy.join('.').to_sym]
|
||||
end
|
||||
|
||||
# includes handler
|
||||
def get_included_records(record, includes_list, known_included_objects, params = {})
|
||||
return unless includes_list.present?
|
||||
|
||||
includes_list.sort.each_with_object([]) do |include_item, included_records|
|
||||
items = parse_include_item(include_item)
|
||||
items.each do |item|
|
||||
next unless relationships_to_serialize && relationships_to_serialize[item]
|
||||
raise NotImplementedError if @relationships_to_serialize[item][:polymorphic].is_a?(Hash)
|
||||
record_type = @relationships_to_serialize[item][:record_type]
|
||||
serializer = @relationships_to_serialize[item][:serializer].to_s.constantize
|
||||
relationship_type = @relationships_to_serialize[item][:relationship_type]
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
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
|
||||
options = {}
|
||||
options[:meta] = { total: 2 }
|
||||
options[:links] = { self: 'self' }
|
||||
|
||||
options[:include] = [:actors]
|
||||
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[: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][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[:meta]).to be nil
|
||||
expect(serializable_hash[:links]).to be nil
|
||||
expect(serializable_hash[:included]).to be nil
|
||||
end
|
||||
|
||||
|
@ -4,73 +4,342 @@ describe FastJsonapi::ObjectSerializer do
|
||||
|
||||
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 = {}
|
||||
end
|
||||
|
||||
it 'returns correct relationship hash for a has_many relationship' do
|
||||
MovieSerializer.has_many :roles
|
||||
relationship = MovieSerializer.relationships_to_serialize[:roles]
|
||||
expect(relationship).to be_instance_of(Hash)
|
||||
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
|
||||
context 'with overrides' do
|
||||
let(:parent) { [:area, id_method_name: :blah_id, record_type: :awesome_area, serializer: :my_area] }
|
||||
|
||||
it_behaves_like 'returning correct relationship hash', :'MyAreaSerializer', :blah_id, :awesome_area
|
||||
end
|
||||
|
||||
it 'returns correct relationship hash for a has_many relationship with overrides' do
|
||||
MovieSerializer.has_many :roles, id_method_name: :roles_only_ids, record_type: :super_role
|
||||
relationship = MovieSerializer.relationships_to_serialize[:roles]
|
||||
expect(relationship[:id_method_name]).to be :roles_only_ids
|
||||
expect(relationship[:record_type]).to be :super_role
|
||||
context 'without overrides' do
|
||||
let(:parent) { [:area] }
|
||||
|
||||
it_behaves_like 'returning correct relationship hash', :'AreaSerializer', :area_id, :area
|
||||
end
|
||||
end
|
||||
|
||||
describe '#belongs_to with block' do
|
||||
before do
|
||||
ActorSerializer.belongs_to :state do |actor|
|
||||
actor.agency.state
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns correct relationship hash for a belongs_to relationship' do
|
||||
MovieSerializer.belongs_to :area
|
||||
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
|
||||
after do
|
||||
ActorSerializer.relationships_to_serialize.delete(:actorc)
|
||||
end
|
||||
|
||||
it 'returns correct relationship hash for a belongs_to relationship with overrides' do
|
||||
MovieSerializer.has_many :area, id_method_name: :blah_id, record_type: :awesome_area, serializer: :my_area
|
||||
relationship = MovieSerializer.relationships_to_serialize[:area]
|
||||
expect(relationship[:id_method_name]).to be :blah_id
|
||||
expect(relationship[:record_type]).to be :awesome_area
|
||||
expect(relationship[:serializer]).to be :MyAreaSerializer
|
||||
context 'state is not included' do
|
||||
subject(:hash) { ActorSerializer.new(actor).serializable_hash }
|
||||
|
||||
it 'returns correct hash' do
|
||||
expect(hash[:data][:relationships][:state][:data]).to eq({ id: '1', type: :state })
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns correct relationship hash for a has_one relationship' do
|
||||
MovieSerializer.has_one :area
|
||||
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
|
||||
context 'state is included' do
|
||||
subject(:hash) { ActorSerializer.new(actor, include: [:state]).serializable_hash }
|
||||
|
||||
it 'returns correct hash' do
|
||||
expect(hash[:included].length).to eq 1
|
||||
expect(hash[:included][0][:id]).to eq '1'
|
||||
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
|
||||
|
||||
it 'returns correct relationship hash for a has_one relationship with overrides' do
|
||||
MovieSerializer.has_one :area, id_method_name: :blah_id, record_type: :awesome_area
|
||||
relationship = MovieSerializer.relationships_to_serialize[:area]
|
||||
expect(relationship[:id_method_name]).to be :blah_id
|
||||
expect(relationship[:record_type]).to be :awesome_area
|
||||
after do
|
||||
MovieSerializer.relationships_to_serialize = {}
|
||||
end
|
||||
|
||||
it 'returns serializer name correctly with namespaces' do
|
||||
AppName::V1::MovieSerializer.has_many :area, id_method_name: :blah_id
|
||||
relationship = AppName::V1::MovieSerializer.relationships_to_serialize[:area]
|
||||
expect(relationship[:serializer]).to be :'AppName::V1::AreaSerializer'
|
||||
context 'with overrides' do
|
||||
let(:partner) { [:area, id_method_name: :blah_id, record_type: :awesome_area, serializer: :my_area] }
|
||||
|
||||
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
|
||||
|
||||
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'
|
||||
expect { MovieSerializer.use_hyphen }.to output.to_stderr
|
||||
warning_message = "DEPRECATION WARNING: use_hyphen is deprecated and will be removed from fast_jsonapi 2.0 use (set_key_transform :dash) instead\n"
|
||||
expect { subject }.to output(warning_message).to_stderr
|
||||
expect(MovieSerializer.instance_variable_get(:@transform_method)).to eq :dasherize
|
||||
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
|
||||
|
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
|
||||
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]
|
||||
expect(json_speed_up).to be >= SERIALIZERS[:ams][:speed_factor]
|
||||
|
||||
# hash
|
||||
hash_speed_up = hash_benchmarks[:ams][:time] / hash_benchmarks[:fast_jsonapi][:time]
|
||||
@ -174,7 +173,6 @@ describe FastJsonapi::ObjectSerializer, performance: true do
|
||||
# json
|
||||
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]
|
||||
expect(json_speed_up).to be >= SERIALIZERS[:ams][:speed_factor]
|
||||
|
||||
# hash
|
||||
hash_speed_up = hash_benchmarks[:ams][:time] / hash_benchmarks[:fast_jsonapi][:time]
|
||||
@ -209,7 +207,6 @@ describe FastJsonapi::ObjectSerializer, performance: true do
|
||||
# json
|
||||
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]
|
||||
expect(json_speed_up).to be >= SERIALIZERS[:ams][:speed_factor]
|
||||
|
||||
# hash
|
||||
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
|
||||
include_context 'movie class'
|
||||
include_context 'group class'
|
||||
|
||||
context 'when testing instance methods of object serializer' do
|
||||
it 'returns correct hash when serializable_hash is called' do
|
||||
options = {}
|
||||
options[:meta] = { total: 2 }
|
||||
options[:links] = { self: 'self' }
|
||||
options[:include] = [:actors]
|
||||
serializable_hash = MovieSerializer.new([movie, movie], options).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].length).to eq 4
|
||||
expect(serializable_hash[:data][0][:attributes].length).to eq 2
|
||||
|
||||
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][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[:meta]).to be nil
|
||||
expect(serializable_hash[:links]).to be nil
|
||||
expect(serializable_hash[:included]).to be nil
|
||||
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
|
||||
options = {}
|
||||
options[:meta] = { total: 2 }
|
||||
@ -123,6 +151,107 @@ describe FastJsonapi::ObjectSerializer do
|
||||
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
|
||||
it 'should set default_type based on serializer class name' do
|
||||
class BlahSerializer
|
||||
@ -154,4 +283,23 @@ describe FastJsonapi::ObjectSerializer do
|
||||
expect(V1::BlahSerializer.record_type).to be :blah
|
||||
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
|
||||
|
@ -7,14 +7,16 @@ describe FastJsonapi::ObjectSerializer do
|
||||
it 'returns correct hash when serializable_hash is called' do
|
||||
options = {}
|
||||
options[:meta] = { total: 2 }
|
||||
options[:links] = { self: 'self' }
|
||||
options[:include] = [:actors]
|
||||
serializable_hash = MovieSerializer.new([movie_struct, movie_struct], options).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].length).to eq 4
|
||||
expect(serializable_hash[:data][0][:attributes].length).to eq 2
|
||||
|
||||
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][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[:meta]).to be nil
|
||||
expect(serializable_hash[:links]).to be nil
|
||||
expect(serializable_hash[:included]).to be nil
|
||||
expect(serializable_hash[:data][:id]).to eq movie_struct.id.to_s
|
||||
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
|
||||
|
@ -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
|
||||
|
||||
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
|
||||
expect(results).to include({ id: "1", type: :person }, { id: "2", type: :group })
|
||||
end
|
||||
@ -70,7 +70,7 @@ describe FastJsonapi::ObjectSerializer do
|
||||
end
|
||||
|
||||
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[:type]).to eq MovieSerializer.record_type
|
||||
expect(record_hash).to have_key(:attributes) if MovieSerializer.attributes_to_serialize.present?
|
||||
@ -82,7 +82,7 @@ describe FastJsonapi::ObjectSerializer do
|
||||
known_included_objects = {}
|
||||
included_records = []
|
||||
[movie, movie].each do |record|
|
||||
included_records.concat MovieSerializer.send(:get_included_records, record, includes_list, known_included_objects)
|
||||
included_records.concat MovieSerializer.send(:get_included_records, record, includes_list, known_included_objects, nil)
|
||||
end
|
||||
expect(included_records.size).to eq 3
|
||||
end
|
||||
|
@ -4,12 +4,55 @@ RSpec.shared_context 'ams movie class' do
|
||||
class AMSModel < ActiveModelSerializers::Model
|
||||
derive_attributes_from_names_and_fix_accessors
|
||||
end
|
||||
|
||||
class AMSMovieType < AMSModel
|
||||
attributes :id, :name, :movies
|
||||
end
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
class AMSUser < AMSModel
|
||||
@ -19,9 +62,22 @@ RSpec.shared_context 'ams movie class' do
|
||||
attributes :id, :name
|
||||
end
|
||||
# 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
|
||||
type 'actor'
|
||||
attributes :name, :email
|
||||
belongs_to :agency, serializer: ::AMSAgencySerializer
|
||||
has_many :awards, serializer: ::AMSAwardSerializer
|
||||
end
|
||||
class AMSUserSerializer < ActiveModel::Serializer
|
||||
type 'user'
|
||||
@ -30,6 +86,11 @@ RSpec.shared_context 'ams movie class' do
|
||||
class AMSMovieTypeSerializer < ActiveModel::Serializer
|
||||
type 'movie_type'
|
||||
attributes :name
|
||||
has_many :movies
|
||||
end
|
||||
class AMSAdvertisingCampaignSerializer < ActiveModel::Serializer
|
||||
type 'advertising_campaign'
|
||||
attributes :name
|
||||
end
|
||||
class AMSMovieSerializer < ActiveModel::Serializer
|
||||
type 'movie'
|
||||
@ -37,6 +98,7 @@ RSpec.shared_context 'ams movie class' do
|
||||
has_many :actors
|
||||
has_one :owner
|
||||
belongs_to :movie_type
|
||||
has_one :advertising_campaign
|
||||
end
|
||||
end
|
||||
|
||||
@ -53,6 +115,7 @@ RSpec.shared_context 'ams movie class' do
|
||||
a.id = i + 1
|
||||
a.name = "Test #{a.id}"
|
||||
a.email = "test#{a.id}@test.com"
|
||||
a.agency_id = i
|
||||
a
|
||||
end
|
||||
end
|
||||
@ -70,6 +133,13 @@ RSpec.shared_context 'ams movie class' do
|
||||
ams_movie_type
|
||||
end
|
||||
|
||||
let(:ams_advertising_campaign) do
|
||||
campaign = AMSAdvertisingCampaign.new
|
||||
campaign.id = 1
|
||||
campaign.name = "Movie is incredible!!"
|
||||
campaign
|
||||
end
|
||||
|
||||
def build_ams_movies(count)
|
||||
count.times.map do |i|
|
||||
m = AMSMovie.new
|
||||
@ -78,6 +148,7 @@ RSpec.shared_context 'ams movie class' do
|
||||
m.actors = ams_actors
|
||||
m.owner = ams_user
|
||||
m.movie_type = ams_movie_type
|
||||
m.advertising_campaign = ams_advertising_campaign
|
||||
m
|
||||
end
|
||||
end
|
||||
|
@ -26,21 +26,7 @@ RSpec.shared_context 'group class' do
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# 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
|
||||
# Person and Group struct
|
||||
before(:context) do
|
||||
PersonStruct = Struct.new(
|
||||
:id, :first_name, :last_name
|
||||
@ -57,7 +43,6 @@ RSpec.shared_context 'group class' do
|
||||
PersonSerializer
|
||||
Group
|
||||
GroupSerializer
|
||||
AppName::V1::PersonSerializer
|
||||
PersonStruct
|
||||
GroupStruct
|
||||
]
|
||||
@ -66,25 +51,6 @@ RSpec.shared_context 'group class' do
|
||||
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
|
||||
group = Group.new
|
||||
group.id = 1
|
||||
|
@ -5,19 +5,20 @@ RSpec.shared_context 'movie class' do
|
||||
# models
|
||||
class Movie
|
||||
attr_accessor :id,
|
||||
:name,
|
||||
:name,
|
||||
:release_year,
|
||||
:director,
|
||||
:actor_ids,
|
||||
:owner_id,
|
||||
:actor_ids,
|
||||
:owner_id,
|
||||
:movie_type_id
|
||||
|
||||
def actors
|
||||
actor_ids.map do |id|
|
||||
actor_ids.map.with_index do |id, i|
|
||||
a = Actor.new
|
||||
a.id = id
|
||||
a.name = "Test #{a.id}"
|
||||
a.email = "test#{a.id}@test.com"
|
||||
a.agency_id = i
|
||||
a
|
||||
end
|
||||
end
|
||||
@ -26,20 +27,107 @@ RSpec.shared_context 'movie class' do
|
||||
mt = MovieType.new
|
||||
mt.id = movie_type_id
|
||||
mt.name = 'Episode'
|
||||
mt.movie_ids = [id]
|
||||
mt
|
||||
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
|
||||
"#{id}"
|
||||
end
|
||||
|
||||
def url
|
||||
"http://movies.com/#{id}"
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
class Supplier
|
||||
@ -67,6 +155,12 @@ RSpec.shared_context 'movie class' do
|
||||
has_many :actors
|
||||
belongs_to :owner, record_type: :user
|
||||
belongs_to :movie_type
|
||||
has_one :advertising_campaign
|
||||
end
|
||||
|
||||
class MovieWithoutIdStructSerializer
|
||||
include FastJsonapi::ObjectSerializer
|
||||
attributes :name, :release_year
|
||||
end
|
||||
|
||||
class CachingMovieSerializer
|
||||
@ -95,12 +189,41 @@ RSpec.shared_context 'movie class' do
|
||||
include FastJsonapi::ObjectSerializer
|
||||
set_type :actor
|
||||
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
|
||||
|
||||
class MovieTypeSerializer
|
||||
include FastJsonapi::ObjectSerializer
|
||||
set_type :movie_type
|
||||
attributes :name
|
||||
has_many :movies
|
||||
end
|
||||
|
||||
class MovieSerializerWithAttributeBlock
|
||||
@ -112,6 +235,21 @@ RSpec.shared_context 'movie class' do
|
||||
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
|
||||
include FastJsonapi::ObjectSerializer
|
||||
set_type :supplier
|
||||
@ -142,17 +280,20 @@ RSpec.shared_context 'movie class' do
|
||||
# Movie and Actor struct
|
||||
before(:context) do
|
||||
MovieStruct = Struct.new(
|
||||
:id,
|
||||
:name,
|
||||
:release_year,
|
||||
:actor_ids,
|
||||
:actors,
|
||||
:owner_id,
|
||||
:owner,
|
||||
:movie_type_id
|
||||
:id,
|
||||
:name,
|
||||
:release_year,
|
||||
:actor_ids,
|
||||
:actors,
|
||||
:owner_id,
|
||||
:owner,
|
||||
: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
|
||||
|
||||
after(:context) do
|
||||
@ -163,11 +304,17 @@ RSpec.shared_context 'movie class' do
|
||||
ActorSerializer
|
||||
MovieType
|
||||
MovieTypeSerializer
|
||||
MovieSerializerWithAttributeBlock
|
||||
AppName::V1::MovieSerializer
|
||||
MovieStruct
|
||||
ActorStruct
|
||||
MovieWithoutIdStruct
|
||||
HyphenMovieSerializer
|
||||
MovieWithoutIdStructSerializer
|
||||
Agency
|
||||
AgencyStruct
|
||||
AgencySerializer
|
||||
AdvertisingCampaign
|
||||
AdvertisingCampaignSerializer
|
||||
]
|
||||
classes_to_remove.each do |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
|
||||
|
||||
agency = AgencyStruct
|
||||
|
||||
actors = []
|
||||
|
||||
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
|
||||
|
||||
m = MovieStruct.new
|
||||
@ -193,6 +342,10 @@ RSpec.shared_context 'movie class' do
|
||||
m
|
||||
end
|
||||
|
||||
let(:movie_struct_without_id) do
|
||||
MovieWithoutIdStruct.new('struct without id', 2018)
|
||||
end
|
||||
|
||||
let(:movie) do
|
||||
m = Movie.new
|
||||
m.id = 232
|
||||
@ -203,6 +356,25 @@ RSpec.shared_context 'movie class' do
|
||||
m
|
||||
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
|
||||
s = Supplier.new
|
||||
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 'rspec-benchmark'
|
||||
require 'byebug'
|
||||
@ -7,6 +8,7 @@ require 'jsonapi/serializable'
|
||||
require 'jsonapi-serializers'
|
||||
|
||||
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|
|
||||
config.include RSpec::Benchmark::Matchers
|
||||
|
Loading…
x
Reference in New Issue
Block a user