Moved the adapter and adapter folder to active_model_serializers folder and changed the module namespace

Changed the namespace in adapters and folder to active_model_serializers from active_model::serializer

Changed namespace of adapters in serializers and other folders

Moved adapter_for_test file to active_model_serializers folder and changed namespace of adapter inside the test file

Require ActiveSupport's string/inflections

We depend on string/inflections to define String#underscore.

Refactor JsonApi adapter to avoid redundant computations.

Update readme.md to link to v0.10.0.rc4

changed namespace of adapter folder testcases

Changed all namespaces of adapter under active_moder_serializers

Namespaced IncludeTree which is from serializer module, so needed to namespace it properly

Fixed wrong namsepacing of fieldset

namespace change in deserializer json_api

Fixed the namespace for collection serializer when used inside adapter, changed namespace for adapter to new namespace which I had forgotten previously

Modified logging test and adapter test cases to make the testcases pass

Changed the yardoc links,as old links are not taking to documentation pages,proper links for 0.10,0.9 and 0.8 in rubydoc

Rubocop errors are fixed by underscore naming unused variables

Moved the require of adapter to serializable resource

Remoeved adapter dependency inside serializer and added warning to Serializer::adapter method

Fixed frament cache test which is calling Serializer.adapter

Changed the name of lookup_adapter_from_config to configured_adapter

Changed the docs which will show the new namespace of adapters

Rubocop fix
This commit is contained in:
bobba surendranath chowdary 2016-02-04 11:49:27 +05:30 committed by Benjamin Fleischer
parent f5ec8ed9d4
commit 252f9c4ae9
61 changed files with 2693 additions and 2764 deletions

View File

@ -3,6 +3,7 @@
Breaking changes: Breaking changes:
Features: Features:
Fixes: Fixes:
- [#1488](https://github.com/rails-api/active_model_serializers/pull/1488) Require ActiveSupport's string inflections (@nate00)
Misc: Misc:
### v0.10.0.rc4 (2016/01/27 11:00 +00:00) ### v0.10.0.rc4 (2016/01/27 11:00 +00:00)

View File

@ -8,10 +8,12 @@
## Documentation ## Documentation
- [0.10 (master) Documentation](https://github.com/rails-api/active_model_serializers/tree/master) - [0.10 (master) Documentation](https://github.com/rails-api/active_model_serializers/tree/master)
- [![API Docs](http://img.shields.io/badge/yard-docs-blue.svg)](http://www.rubydoc.info/github/rails-api/active_model_serializers) - [![API Docs](http://img.shields.io/badge/yard-docs-blue.svg)](http://www.rubydoc.info/github/rails-api/active_model_serializers/v0.10.0.rc4)
- [Guides](docs) - [Guides](docs)
- [0.9 (0-9-stable) Documentation](https://github.com/rails-api/active_model_serializers/tree/0-9-stable) - [0.9 (0-9-stable) Documentation](https://github.com/rails-api/active_model_serializers/tree/0-9-stable)
- [![API Docs](http://img.shields.io/badge/yard-docs-blue.svg)](http://www.rubydoc.info/github/rails-api/active_model_serializers/0-9-stable)
- [0.8 (0-8-stable) Documentation](https://github.com/rails-api/active_model_serializers/tree/0-8-stable) - [0.8 (0-8-stable) Documentation](https://github.com/rails-api/active_model_serializers/tree/0-8-stable)
- [![API Docs](http://img.shields.io/badge/yard-docs-blue.svg)](http://www.rubydoc.info/github/rails-api/active_model_serializers/0-8-stable)
## About ## About

View File

@ -11,7 +11,7 @@ It should be set only once, preferably at initialization.
For example: For example:
```ruby ```ruby
ActiveModelSerializers.config.adapter = ActiveModel::Serializer::Adapter::JsonApi ActiveModelSerializers.config.adapter = ActiveModelSerializers::Adapter::JsonApi
``` ```
or or
@ -117,46 +117,46 @@ The default adapter can be configured, as above, to use any class given to it.
An adapter may also be specified, e.g. when rendering, as a class or as a symbol. An adapter may also be specified, e.g. when rendering, as a class or as a symbol.
If a symbol, then the adapter must be, e.g. `:great_example`, If a symbol, then the adapter must be, e.g. `:great_example`,
`ActiveModel::Serializer::Adapter::GreatExample`, or registered. `ActiveModelSerializers::Adapter::GreatExample`, or registered.
There are two ways to register an adapter: There are two ways to register an adapter:
1) The simplest, is to subclass `ActiveModel::Serializer::Adapter::Base`, e.g. the below will 1) The simplest, is to subclass `ActiveModelSerializers::Adapter::Base`, e.g. the below will
register the `Example::UsefulAdapter` as `"example/useful_adapter"`. register the `Example::UsefulAdapter` as `"example/useful_adapter"`.
```ruby ```ruby
module Example module Example
class UsefulAdapter < ActiveModel::Serializer::Adapter::Base class UsefulAdapter < ActiveModelSerializers::Adapter::Base
end end
end end
``` ```
You'll notice that the name it registers is the underscored namespace and class. You'll notice that the name it registers is the underscored namespace and class.
Under the covers, when the `ActiveModel::Serializer::Adapter::Base` is subclassed, it registers Under the covers, when the `ActiveModelSerializers::Adapter::Base` is subclassed, it registers
the subclass as `register("example/useful_adapter", Example::UsefulAdapter)` the subclass as `register("example/useful_adapter", Example::UsefulAdapter)`
2) Any class can be registered as an adapter by calling `register` directly on the 2) Any class can be registered as an adapter by calling `register` directly on the
`ActiveModel::Serializer::Adapter` class. e.g., the below registers `MyAdapter` as `ActiveModelSerializers::Adapter` class. e.g., the below registers `MyAdapter` as
`:special_adapter`. `:special_adapter`.
```ruby ```ruby
class MyAdapter; end class MyAdapter; end
ActiveModel::Serializer::Adapter.register(:special_adapter, MyAdapter) ActiveModelSerializers::Adapter.register(:special_adapter, MyAdapter)
``` ```
### Looking up an adapter ### Looking up an adapter
| Method | Return value | | Method | Return value |
| :------------ |:---------------| | :------------ |:---------------|
| `ActiveModel::Serializer::Adapter.adapter_map` | A Hash of all known adapters `{ adapter_name => adapter_class }` | | `ActiveModelSerializers::Adapter.adapter_map` | A Hash of all known adapters `{ adapter_name => adapter_class }` |
| `ActiveModel::Serializer::Adapter.adapters` | A (sorted) Array of all known `adapter_names` | | `ActiveModelSerializers::Adapter.adapters` | A (sorted) Array of all known `adapter_names` |
| `ActiveModel::Serializer::Adapter.lookup(name_or_klass)` | The `adapter_class`, else raises an `ActiveModel::Serializer::Adapter::UnknownAdapter` error | | `ActiveModelSerializers::Adapter.lookup(name_or_klass)` | The `adapter_class`, else raises an `ActiveModelSerializers::Adapter::UnknownAdapter` error |
| `ActiveModel::Serializer::Adapter.adapter_class(adapter)` | Delegates to `ActiveModel::Serializer::Adapter.lookup(adapter)` | | `ActiveModelSerializers::Adapter.adapter_class(adapter)` | Delegates to `ActiveModelSerializers::Adapter.lookup(adapter)` |
| `ActiveModel::Serializer.adapter` | A convenience method for `ActiveModel::Serializer::Adapter.lookup(config.adapter)` | | `ActiveModelSerializers::Adapter.configured_adapter` | A convenience method for `ActiveModelSerializers::Adapter.lookup(config.adapter)` |
The registered adapter name is always a String, but may be looked up as a Symbol or String. The registered adapter name is always a String, but may be looked up as a Symbol or String.
Helpfully, the Symbol or String is underscored, so that `get(:my_adapter)` and `get("MyAdapter")` Helpfully, the Symbol or String is underscored, so that `get(:my_adapter)` and `get("MyAdapter")`
may both be used. may both be used.
For more information, see [the Adapter class on GitHub](https://github.com/rails-api/active_model_serializers/blob/master/lib/active_model/serializer/adapter.rb) For more information, see [the Adapter class on GitHub](https://github.com/rails-api/active_model_serializers/blob/master/lib/active_model_serializers/adapter.rb)

View File

@ -17,7 +17,7 @@ Payload (example):
```ruby ```ruby
{ {
serializer: PostSerializer, serializer: PostSerializer,
adapter: ActiveModel::Serializer::Adapter::Attributes adapter: ActiveModelSerializers::Adapter::Attributes
} }
``` ```

View File

@ -70,7 +70,7 @@ at the first moment.
## Renaming of class and modules ## Renaming of class and modules
When moving some content to the new namespace we can find some names that does When moving some content to the new namespace we can find some names that does
not make much sense like `ActiveModelSerializers::Serializer::Adapter::JsonApi`. not make much sense like `ActiveModelSerializers::Adapter::JsonApi`.
Discussion of renaming existing classes / modules and JsonApi objects will Discussion of renaming existing classes / modules and JsonApi objects will
happen in separate pull requests, and issues, and in the google doc happen in separate pull requests, and issues, and in the google doc
https://docs.google.com/document/d/1rcrJr0sVcazY2Opd_6Kmv1iIwuHbI84s1P_NzFn-05c/edit?usp=sharing https://docs.google.com/document/d/1rcrJr0sVcazY2Opd_6Kmv1iIwuHbI84s1P_NzFn-05c/edit?usp=sharing

View File

@ -1,4 +1,5 @@
require 'set' require 'set'
require 'active_model_serializers/adapter'
module ActiveModel module ActiveModel
class SerializableResource class SerializableResource
ADAPTER_OPTION_KEYS = Set.new([:include, :fields, :adapter, :meta, :meta_key, :links]) ADAPTER_OPTION_KEYS = Set.new([:include, :fields, :adapter, :meta, :meta_key, :links])
@ -30,7 +31,7 @@ module ActiveModel
end end
def adapter def adapter
@adapter ||= ActiveModel::Serializer::Adapter.create(serializer_instance, adapter_opts) @adapter ||= ActiveModelSerializers::Adapter.create(serializer_instance, adapter_opts)
end end
alias_method :adapter_instance, :adapter alias_method :adapter_instance, :adapter

View File

@ -21,7 +21,8 @@ module ActiveModel
include Caching include Caching
include Links include Links
include Type include Type
require 'active_model/serializer/adapter' # Deprecated
require 'active_model_serializers/adapter'
# @param resource [ActiveRecord::Base, ActiveModelSerializers::Model] # @param resource [ActiveRecord::Base, ActiveModelSerializers::Model]
# @return [ActiveModel::Serializer] # @return [ActiveModel::Serializer]
@ -40,9 +41,11 @@ module ActiveModel
end end
end end
# @see ActiveModel::Serializer::Adapter.lookup # @see ActiveModelSerializers::Adapter.lookup
# Deprecated
def self.adapter def self.adapter
ActiveModel::Serializer::Adapter.lookup(config.adapter) warn 'Calling adapter method in Serializer, please use the ActiveModelSerializers::configured_adapter'
ActiveModelSerializers::Adapter.lookup(config.adapter)
end end
# @api private # @api private

View File

@ -1,91 +0,0 @@
module ActiveModel
class Serializer
module Adapter
UnknownAdapterError = Class.new(ArgumentError)
ADAPTER_MAP = {}
private_constant :ADAPTER_MAP if defined?(private_constant)
require 'active_model/serializer/adapter/fragment_cache'
require 'active_model/serializer/adapter/cached_serializer'
class << self # All methods are class functions
def new(*args)
fail ArgumentError, 'Adapters inherit from Adapter::Base.' \
"Adapter.new called with args: '#{args.inspect}', from" \
"'caller[0]'."
end
def create(resource, options = {})
override = options.delete(:adapter)
klass = override ? adapter_class(override) : ActiveModel::Serializer.adapter
klass.new(resource, options)
end
# @see ActiveModel::Serializer::Adapter.lookup
def adapter_class(adapter)
ActiveModel::Serializer::Adapter.lookup(adapter)
end
# @return Hash<adapter_name, adapter_class>
def adapter_map
ADAPTER_MAP
end
# @return [Array<Symbol>] list of adapter names
def adapters
adapter_map.keys.sort
end
# Adds an adapter 'klass' with 'name' to the 'adapter_map'
# Names are stringified and underscored
# @param name [Symbol, String, Class] name of the registered adapter
# @param klass [Class] adapter class itself, optional if name is the class
# @example
# AMS::Adapter.register(:my_adapter, MyAdapter)
# @note The registered name strips out 'ActiveModel::Serializer::Adapter::'
# so that registering 'ActiveModel::Serializer::Adapter::Json' and
# 'Json' will both register as 'json'.
def register(name, klass = name)
name = name.to_s.gsub(/\AActiveModel::Serializer::Adapter::/, ''.freeze)
adapter_map.update(name.underscore => klass)
self
end
# @param adapter [String, Symbol, Class] name to fetch adapter by
# @return [ActiveModel::Serializer::Adapter] subclass of Adapter
# @raise [UnknownAdapterError]
def lookup(adapter)
# 1. return if is a class
return adapter if adapter.is_a?(Class)
adapter_name = adapter.to_s.underscore
# 2. return if registered
adapter_map.fetch(adapter_name) do
# 3. try to find adapter class from environment
adapter_class = find_by_name(adapter_name)
register(adapter_name, adapter_class)
adapter_class
end
rescue NameError, ArgumentError => e
failure_message =
"NameError: #{e.message}. Unknown adapter: #{adapter.inspect}. Valid adapters are: #{adapters}"
raise UnknownAdapterError, failure_message, e.backtrace
end
# @api private
def find_by_name(adapter_name)
adapter_name = adapter_name.to_s.classify.tr('API', 'Api')
"ActiveModel::Serializer::Adapter::#{adapter_name}".safe_constantize ||
"ActiveModel::Serializer::Adapter::#{adapter_name.pluralize}".safe_constantize or # rubocop:disable Style/AndOr
fail UnknownAdapterError
end
private :find_by_name
end
# Gotta be at the bottom to use the code above it :(
require 'active_model/serializer/adapter/base'
require 'active_model/serializer/adapter/null'
require 'active_model/serializer/adapter/attributes'
require 'active_model/serializer/adapter/json'
require 'active_model/serializer/adapter/json_api'
end
end
end

View File

@ -1,66 +0,0 @@
module ActiveModel
class Serializer
module Adapter
class Attributes < Base
def initialize(serializer, options = {})
super
@include_tree = IncludeTree.from_include_args(options[:include] || '*')
end
def serializable_hash(options = nil)
options ||= {}
if serializer.respond_to?(:each)
serializable_hash_for_collection(options)
else
serializable_hash_for_single_resource(options)
end
end
def fragment_cache(cached_hash, non_cached_hash)
Json::FragmentCache.new.fragment_cache(cached_hash, non_cached_hash)
end
private
def serializable_hash_for_collection(options)
serializer.map { |s| Attributes.new(s, instance_options).serializable_hash(options) }
end
def serializable_hash_for_single_resource(options)
resource = resource_object_for(options)
relationships = resource_relationships(options)
resource.merge!(relationships)
end
def resource_relationships(options)
relationships = {}
serializer.associations(@include_tree).each do |association|
relationships[association.key] = relationship_value_for(association, options)
end
relationships
end
def relationship_value_for(association, options)
return association.options[:virtual_value] if association.options[:virtual_value]
return unless association.serializer && association.serializer.object
opts = instance_options.merge(include: @include_tree[association.key])
Attributes.new(association.serializer, opts).serializable_hash(options)
end
# no-op: Attributes adapter does not include meta data, because it does not support root.
def include_meta(json)
json
end
def resource_object_for(options)
cache_check(serializer) do
serializer.attributes(options[:fields])
end
end
end
end
end
end

View File

@ -1,58 +0,0 @@
module ActiveModel
class Serializer
module Adapter
class Base
# Automatically register adapters when subclassing
def self.inherited(subclass)
ActiveModel::Serializer::Adapter.register(subclass)
end
attr_reader :serializer, :instance_options
def initialize(serializer, options = {})
@serializer = serializer
@instance_options = options
end
def serializable_hash(_options = nil)
fail NotImplementedError, 'This is an abstract method. Should be implemented at the concrete adapter.'
end
def as_json(options = nil)
hash = serializable_hash(options)
include_meta(hash)
hash
end
def fragment_cache(*_args)
fail NotImplementedError, 'This is an abstract method. Should be implemented at the concrete adapter.'
end
def cache_check(serializer)
CachedSerializer.new(serializer).cache_check(self) do
yield
end
end
private
def meta
instance_options.fetch(:meta, nil)
end
def meta_key
instance_options.fetch(:meta_key, 'meta'.freeze)
end
def root
serializer.json_key.to_sym if serializer.json_key
end
def include_meta(json)
json[meta_key] = meta if meta
json
end
end
end
end
end

View File

@ -1,45 +0,0 @@
module ActiveModel
class Serializer
module Adapter
class CachedSerializer
def initialize(serializer)
@cached_serializer = serializer
@klass = @cached_serializer.class
end
def cache_check(adapter_instance)
if cached?
@klass._cache.fetch(cache_key, @klass._cache_options) do
yield
end
elsif fragment_cached?
FragmentCache.new(adapter_instance, @cached_serializer, adapter_instance.instance_options).fetch
else
yield
end
end
def cached?
@klass._cache && !@klass._cache_only && !@klass._cache_except
end
def fragment_cached?
@klass._cache_only && !@klass._cache_except || !@klass._cache_only && @klass._cache_except
end
def cache_key
parts = []
parts << object_cache_key
parts << @klass._cache_digest unless @klass._cache_options && @klass._cache_options[:skip_digest]
parts.join('/')
end
def object_cache_key
object_time_safe = @cached_serializer.object.updated_at
object_time_safe = object_time_safe.strftime('%Y%m%d%H%M%S%9N') if object_time_safe.respond_to?(:strftime)
(@klass._cache_key) ? "#{@klass._cache_key}/#{@cached_serializer.object.id}-#{object_time_safe}" : @cached_serializer.object.cache_key
end
end
end
end
end

View File

@ -1,111 +0,0 @@
module ActiveModel
class Serializer
module Adapter
class FragmentCache
attr_reader :serializer
def initialize(adapter, serializer, options)
@instance_options = options
@adapter = adapter
@serializer = serializer
end
# TODO: Use Serializable::Resource
# TODO: call +constantize+ less
# 1. Create a CachedSerializer and NonCachedSerializer from the serializer class
# 2. Serialize the above two with the given adapter
# 3. Pass their serializations to the adapter +::fragment_cache+
def fetch
klass = serializer.class
# It will split the serializer into two, one that will be cached and one that will not
serializers = fragment_serializer(serializer.object.class.name, klass)
# Instantiate both serializers
cached_serializer = serializers[:cached].constantize.new(serializer.object)
non_cached_serializer = serializers[:non_cached].constantize.new(serializer.object)
cached_adapter = adapter.class.new(cached_serializer, instance_options)
non_cached_adapter = adapter.class.new(non_cached_serializer, instance_options)
# Get serializable hash from both
cached_hash = cached_adapter.serializable_hash
non_cached_hash = non_cached_adapter.serializable_hash
# Merge both results
adapter.fragment_cache(cached_hash, non_cached_hash)
end
protected
attr_reader :instance_options, :adapter
private
# Given a serializer class and a hash of its cached and non-cached serializers
# 1. Determine cached attributes from serializer class options
# 2. Add cached attributes to cached Serializer
# 3. Add non-cached attributes to non-cached Serializer
def cached_attributes(klass, serializers)
attributes = serializer.class._attributes
cached_attributes = (klass._cache_only) ? klass._cache_only : attributes.reject { |attr| klass._cache_except.include?(attr) }
non_cached_attributes = attributes - cached_attributes
cached_attributes.each do |attribute|
options = serializer.class._attributes_keys[attribute]
options ||= {}
# Add cached attributes to cached Serializer
serializers[:cached].constantize.attribute(attribute, options)
end
non_cached_attributes.each do |attribute|
options = serializer.class._attributes_keys[attribute]
options ||= {}
# Add non-cached attributes to non-cached Serializer
serializers[:non_cached].constantize.attribute(attribute, options)
end
end
# Given a resource name and its serializer's class
# 1. Dyanmically creates a CachedSerializer and NonCachedSerializer
# for a given class 'name'
# 2. Call
# CachedSerializer.cache(serializer._cache_options)
# CachedSerializer.fragmented(serializer)
# NontCachedSerializer.cache(serializer._cache_options)
# 3. Build a hash keyed to the +cached+ and +non_cached+ serializers
# 4. Call +cached_attributes+ on the serializer class and the above hash
# 5. Return the hash
#
# @example
# When +name+ is <tt>User::Admin</tt>
# creates the Serializer classes (if they don't exist).
# User_AdminCachedSerializer
# User_AdminNOnCachedSerializer
#
def fragment_serializer(name, klass)
cached = "#{to_valid_const_name(name)}CachedSerializer"
non_cached = "#{to_valid_const_name(name)}NonCachedSerializer"
Object.const_set cached, Class.new(ActiveModel::Serializer) unless Object.const_defined?(cached)
Object.const_set non_cached, Class.new(ActiveModel::Serializer) unless Object.const_defined?(non_cached)
klass._cache_options ||= {}
klass._cache_options[:key] = klass._cache_key if klass._cache_key
cached.constantize.cache(klass._cache_options)
cached.constantize.fragmented(serializer)
non_cached.constantize.fragmented(serializer)
serializers = { cached: cached, non_cached: non_cached }
cached_attributes(klass, serializers)
serializers
end
def to_valid_const_name(name)
name.gsub('::', '_')
end
end
end
end
end

View File

@ -1,21 +0,0 @@
module ActiveModel
class Serializer
module Adapter
class Json < Base
extend ActiveSupport::Autoload
autoload :FragmentCache
def serializable_hash(options = nil)
options ||= {}
{ root => Attributes.new(serializer, instance_options).serializable_hash(options) }
end
private
def fragment_cache(cached_hash, non_cached_hash)
ActiveModel::Serializer::Adapter::Json::FragmentCache.new.fragment_cache(cached_hash, non_cached_hash)
end
end
end
end
end

View File

@ -1,13 +0,0 @@
module ActiveModel
class Serializer
module Adapter
class Json
class FragmentCache
def fragment_cache(cached_hash, non_cached_hash)
non_cached_hash.merge cached_hash
end
end
end
end
end
end

View File

@ -1,223 +0,0 @@
module ActiveModel
class Serializer
module Adapter
class JsonApi < Base
extend ActiveSupport::Autoload
autoload :PaginationLinks
autoload :FragmentCache
autoload :Link
autoload :Deserialization
# TODO: if we like this abstraction and other API objects to it,
# then extract to its own file and require it.
module ApiObjects
module JsonApi
ActiveModelSerializers.config.jsonapi_version = '1.0'
ActiveModelSerializers.config.jsonapi_toplevel_meta = {}
# Make JSON API top-level jsonapi member opt-in
# ref: http://jsonapi.org/format/#document-top-level
ActiveModelSerializers.config.jsonapi_include_toplevel_object = false
module_function
def add!(hash)
hash.merge!(object) if include_object?
end
def include_object?
ActiveModelSerializers.config.jsonapi_include_toplevel_object
end
# TODO: see if we can cache this
def object
object = {
jsonapi: {
version: ActiveModelSerializers.config.jsonapi_version,
meta: ActiveModelSerializers.config.jsonapi_toplevel_meta
}
}
object[:jsonapi].reject! { |_, v| v.blank? }
object
end
end
end
def initialize(serializer, options = {})
super
@include_tree = IncludeTree.from_include_args(options[:include])
@fieldset = options[:fieldset] || ActiveModel::Serializer::Fieldset.new(options.delete(:fields))
end
def serializable_hash(options = nil)
options ||= {}
hash =
if serializer.respond_to?(:each)
serializable_hash_for_collection(options)
else
serializable_hash_for_single_resource
end
ApiObjects::JsonApi.add!(hash)
if instance_options[:links]
hash[:links] ||= {}
hash[:links].update(instance_options[:links])
end
hash
end
def fragment_cache(cached_hash, non_cached_hash)
root = false if instance_options.include?(:include)
ActiveModel::Serializer::Adapter::JsonApi::FragmentCache.new.fragment_cache(root, cached_hash, non_cached_hash)
end
protected
attr_reader :fieldset
private
def serializable_hash_for_collection(options)
hash = { data: [] }
included = []
serializer.each do |s|
result = self.class.new(s, instance_options.merge(fieldset: fieldset)).serializable_hash(options)
hash[:data] << result[:data]
next unless result[:included]
included |= result[:included]
end
included.delete_if { |resource| hash[:data].include?(resource) }
hash[:included] = included if included.any?
if serializer.paginated?
hash[:links] ||= {}
hash[:links].update(pagination_links_for(serializer, options))
end
hash
end
def serializable_hash_for_single_resource
primary_data = resource_object_for(serializer)
hash = { data: primary_data }
included = included_resources(@include_tree, [primary_data])
hash[:included] = included if included.any?
hash
end
def resource_identifier_type_for(serializer)
return serializer._type if serializer._type
if ActiveModelSerializers.config.jsonapi_resource_type == :singular
serializer.object.class.model_name.singular
else
serializer.object.class.model_name.plural
end
end
def resource_identifier_id_for(serializer)
if serializer.respond_to?(:id)
serializer.id
else
serializer.object.id
end
end
def resource_identifier_for(serializer)
type = resource_identifier_type_for(serializer)
id = resource_identifier_id_for(serializer)
{ id: id.to_s, type: type }
end
def attributes_for(serializer, fields)
serializer.attributes(fields).except(:id)
end
def resource_object_for(serializer)
resource_object = cache_check(serializer) do
resource_object = resource_identifier_for(serializer)
requested_fields = fieldset && fieldset.fields_for(resource_object[:type])
attributes = attributes_for(serializer, requested_fields)
resource_object[:attributes] = attributes if attributes.any?
resource_object
end
relationships = relationships_for(serializer)
resource_object[:relationships] = relationships if relationships.any?
links = links_for(serializer)
resource_object[:links] = links if links.any?
resource_object
end
def relationship_value_for(serializer, options = {})
if serializer.respond_to?(:each)
serializer.map { |s| resource_identifier_for(s) }
else
if options[:virtual_value]
options[:virtual_value]
elsif serializer && serializer.object
resource_identifier_for(serializer)
end
end
end
def relationships_for(serializer)
resource_type = resource_identifier_type_for(serializer)
requested_associations = fieldset.fields_for(resource_type) || '*'
include_tree = IncludeTree.from_include_args(requested_associations)
serializer.associations(include_tree).each_with_object({}) do |association, hash|
hash[association.key] = { data: relationship_value_for(association.serializer, association.options) }
end
end
def included_resources(include_tree, primary_data)
included = []
serializer.associations(include_tree).each do |association|
add_included_resources_for(association.serializer, include_tree[association.key], primary_data, included)
end
included
end
def add_included_resources_for(serializer, include_tree, primary_data, included)
if serializer.respond_to?(:each)
serializer.each { |s| add_included_resources_for(s, include_tree, primary_data, included) }
else
return unless serializer && serializer.object
resource_object = resource_object_for(serializer)
return if included.include?(resource_object) || primary_data.include?(resource_object)
included.push(resource_object)
serializer.associations(include_tree).each do |association|
add_included_resources_for(association.serializer, include_tree[association.key], primary_data, included)
end
end
end
def links_for(serializer)
serializer._links.each_with_object({}) do |(name, value), hash|
hash[name] = Link.new(serializer, value).as_json
end
end
def pagination_links_for(serializer, options)
JsonApi::PaginationLinks.new(serializer.object, options[:serialization_context]).serializable_hash(options)
end
end
end
end
end

View File

@ -1,207 +0,0 @@
module ActiveModel
class Serializer
module Adapter
class JsonApi
# NOTE(Experimental):
# This is an experimental feature. Both the interface and internals could be subject
# to changes.
module Deserialization
InvalidDocument = Class.new(ArgumentError)
module_function
# Transform a JSON API document, containing a single data object,
# into a hash that is ready for ActiveRecord::Base.new() and such.
# Raises InvalidDocument if the payload is not properly formatted.
#
# @param [Hash|ActionController::Parameters] document
# @param [Hash] options
# only: Array of symbols of whitelisted fields.
# except: Array of symbols of blacklisted fields.
# keys: Hash of translated keys (e.g. :author => :user).
# polymorphic: Array of symbols of polymorphic fields.
# @return [Hash]
#
# @example
# document = {
# data: {
# id: 1,
# type: 'post',
# attributes: {
# title: 'Title 1',
# date: '2015-12-20'
# },
# associations: {
# author: {
# data: {
# type: 'user',
# id: 2
# }
# },
# second_author: {
# data: nil
# },
# comments: {
# data: [{
# type: 'comment',
# id: 3
# },{
# type: 'comment',
# id: 4
# }]
# }
# }
# }
# }
#
# parse(document) #=>
# # {
# # title: 'Title 1',
# # date: '2015-12-20',
# # author_id: 2,
# # second_author_id: nil
# # comment_ids: [3, 4]
# # }
#
# parse(document, only: [:title, :date, :author],
# keys: { date: :published_at },
# polymorphic: [:author]) #=>
# # {
# # title: 'Title 1',
# # published_at: '2015-12-20',
# # author_id: '2',
# # author_type: 'people'
# # }
#
def parse!(document, options = {})
parse(document, options) do |invalid_payload, reason|
fail InvalidDocument, "Invalid payload (#{reason}): #{invalid_payload}"
end
end
# Same as parse!, but returns an empty hash instead of raising InvalidDocument
# on invalid payloads.
def parse(document, options = {})
document = document.dup.permit!.to_h if document.is_a?(ActionController::Parameters)
validate_payload(document) do |invalid_document, reason|
yield invalid_document, reason if block_given?
return {}
end
primary_data = document['data']
attributes = primary_data['attributes'] || {}
attributes['id'] = primary_data['id'] if primary_data['id']
relationships = primary_data['relationships'] || {}
filter_fields(attributes, options)
filter_fields(relationships, options)
hash = {}
hash.merge!(parse_attributes(attributes, options))
hash.merge!(parse_relationships(relationships, options))
hash
end
# Checks whether a payload is compliant with the JSON API spec.
#
# @api private
# rubocop:disable Metrics/CyclomaticComplexity
def validate_payload(payload)
unless payload.is_a?(Hash)
yield payload, 'Expected hash'
return
end
primary_data = payload['data']
unless primary_data.is_a?(Hash)
yield payload, { data: 'Expected hash' }
return
end
attributes = primary_data['attributes'] || {}
unless attributes.is_a?(Hash)
yield payload, { data: { attributes: 'Expected hash or nil' } }
return
end
relationships = primary_data['relationships'] || {}
unless relationships.is_a?(Hash)
yield payload, { data: { relationships: 'Expected hash or nil' } }
return
end
relationships.each do |(key, value)|
unless value.is_a?(Hash) && value.key?('data')
yield payload, { data: { relationships: { key => 'Expected hash with :data key' } } }
end
end
end
# rubocop:enable Metrics/CyclomaticComplexity
# @api private
def filter_fields(fields, options)
if (only = options[:only])
fields.slice!(*Array(only).map(&:to_s))
elsif (except = options[:except])
fields.except!(*Array(except).map(&:to_s))
end
end
# @api private
def field_key(field, options)
(options[:keys] || {}).fetch(field.to_sym, field).to_sym
end
# @api private
def parse_attributes(attributes, options)
attributes
.map { |(k, v)| { field_key(k, options) => v } }
.reduce({}, :merge)
end
# Given an association name, and a relationship data attribute, build a hash
# mapping the corresponding ActiveRecord attribute to the corresponding value.
#
# @example
# parse_relationship(:comments, [{ 'id' => '1', 'type' => 'comments' },
# { 'id' => '2', 'type' => 'comments' }],
# {})
# # => { :comment_ids => ['1', '2'] }
# parse_relationship(:author, { 'id' => '1', 'type' => 'users' }, {})
# # => { :author_id => '1' }
# parse_relationship(:author, nil, {})
# # => { :author_id => nil }
# @param [Symbol] assoc_name
# @param [Hash] assoc_data
# @param [Hash] options
# @return [Hash{Symbol, Object}]
#
# @api private
def parse_relationship(assoc_name, assoc_data, options)
prefix_key = field_key(assoc_name, options).to_s.singularize
hash =
if assoc_data.is_a?(Array)
{ "#{prefix_key}_ids".to_sym => assoc_data.map { |ri| ri['id'] } }
else
{ "#{prefix_key}_id".to_sym => assoc_data ? assoc_data['id'] : nil }
end
polymorphic = (options[:polymorphic] || []).include?(assoc_name.to_sym)
hash.merge!("#{prefix_key}_type".to_sym => assoc_data['type']) if polymorphic
hash
end
# @api private
def parse_relationships(relationships, options)
relationships
.map { |(k, v)| parse_relationship(k, v['data'], options) }
.reduce({}, :merge)
end
end
end
end
end
end

View File

@ -1,21 +0,0 @@
module ActiveModel
class Serializer
module Adapter
class JsonApi
class FragmentCache
def fragment_cache(root, cached_hash, non_cached_hash)
hash = {}
core_cached = cached_hash.first
core_non_cached = non_cached_hash.first
no_root_cache = cached_hash.delete_if { |key, value| key == core_cached[0] }
no_root_non_cache = non_cached_hash.delete_if { |key, value| key == core_non_cached[0] }
cached_resource = (core_cached[1]) ? core_cached[1].deep_merge(core_non_cached[1]) : core_non_cached[1]
hash = (root) ? { root => cached_resource } : cached_resource
hash.deep_merge no_root_non_cache.deep_merge no_root_cache
end
end
end
end
end
end

View File

@ -1,44 +0,0 @@
module ActiveModel
class Serializer
module Adapter
class JsonApi
class Link
def initialize(serializer, value)
@object = serializer.object
@scope = serializer.scope
# Use the return value of the block unless it is nil.
if value.respond_to?(:call)
@value = instance_eval(&value)
else
@value = value
end
end
def href(value)
@href = value
nil
end
def meta(value)
@meta = value
nil
end
def as_json
return @value if @value
hash = { href: @href }
hash.merge!(meta: @meta) if @meta
hash
end
protected
attr_reader :object, :scope
end
end
end
end
end

View File

@ -1,58 +0,0 @@
module ActiveModel
class Serializer
module Adapter
class JsonApi < Base
class PaginationLinks
FIRST_PAGE = 1
attr_reader :collection, :context
def initialize(collection, context)
@collection = collection
@context = context
end
def serializable_hash(options = {})
pages_from.each_with_object({}) do |(key, value), hash|
params = query_parameters.merge(page: { number: value, size: collection.size }).to_query
hash[key] = "#{url(options)}?#{params}"
end
end
private
def pages_from
return {} if collection.total_pages == FIRST_PAGE
{}.tap do |pages|
pages[:self] = collection.current_page
unless collection.current_page == FIRST_PAGE
pages[:first] = FIRST_PAGE
pages[:prev] = collection.current_page - FIRST_PAGE
end
unless collection.current_page == collection.total_pages
pages[:next] = collection.current_page + FIRST_PAGE
pages[:last] = collection.total_pages
end
end
end
def url(options)
@url ||= options.fetch(:links, {}).fetch(:self, nil) || request_url
end
def request_url
@request_url ||= context.request_url
end
def query_parameters
@query_parameters ||= context.query_parameters
end
end
end
end
end
end

View File

@ -1,11 +0,0 @@
module ActiveModel
class Serializer
module Adapter
class Null < Base
def serializable_hash(options = nil)
{}
end
end
end
end
end

View File

@ -1,6 +1,7 @@
require 'active_model' require 'active_model'
require 'active_support' require 'active_support'
require 'active_support/core_ext/object/with_options' require 'active_support/core_ext/object/with_options'
require 'active_support/core_ext/string/inflections'
module ActiveModelSerializers module ActiveModelSerializers
extend ActiveSupport::Autoload extend ActiveSupport::Autoload
autoload :Model autoload :Model

View File

@ -0,0 +1,93 @@
module ActiveModelSerializers
module Adapter
UnknownAdapterError = Class.new(ArgumentError)
ADAPTER_MAP = {}
private_constant :ADAPTER_MAP if defined?(private_constant)
require 'active_model_serializers/adapter/fragment_cache'
require 'active_model_serializers/adapter/cached_serializer'
class << self # All methods are class functions
def new(*args)
fail ArgumentError, 'Adapters inherit from Adapter::Base.' \
"Adapter.new called with args: '#{args.inspect}', from" \
"'caller[0]'."
end
def configured_adapter
lookup(ActiveModelSerializers.config.adapter)
end
def create(resource, options = {})
override = options.delete(:adapter)
klass = override ? adapter_class(override) : configured_adapter
klass.new(resource, options)
end
# @see ActiveModelSerializers::Adapter.lookup
def adapter_class(adapter)
ActiveModelSerializers::Adapter.lookup(adapter)
end
# @return Hash<adapter_name, adapter_class>
def adapter_map
ADAPTER_MAP
end
# @return [Array<Symbol>] list of adapter names
def adapters
adapter_map.keys.sort
end
# Adds an adapter 'klass' with 'name' to the 'adapter_map'
# Names are stringified and underscored
# @param name [Symbol, String, Class] name of the registered adapter
# @param klass [Class] adapter class itself, optional if name is the class
# @example
# AMS::Adapter.register(:my_adapter, MyAdapter)
# @note The registered name strips out 'ActiveModelSerializers::Adapter::'
# so that registering 'ActiveModelSerializers::Adapter::Json' and
# 'Json' will both register as 'json'.
def register(name, klass = name)
name = name.to_s.gsub(/\AActiveModelSerializers::Adapter::/, ''.freeze)
adapter_map.update(name.underscore => klass)
self
end
# @param adapter [String, Symbol, Class] name to fetch adapter by
# @return [ActiveModelSerializers::Adapter] subclass of Adapter
# @raise [UnknownAdapterError]
def lookup(adapter)
# 1. return if is a class
return adapter if adapter.is_a?(Class)
adapter_name = adapter.to_s.underscore
# 2. return if registered
adapter_map.fetch(adapter_name) do
# 3. try to find adapter class from environment
adapter_class = find_by_name(adapter_name)
register(adapter_name, adapter_class)
adapter_class
end
rescue NameError, ArgumentError => e
failure_message =
"NameError: #{e.message}. Unknown adapter: #{adapter.inspect}. Valid adapters are: #{adapters}"
raise UnknownAdapterError, failure_message, e.backtrace
end
# @api private
def find_by_name(adapter_name)
adapter_name = adapter_name.to_s.classify.tr('API', 'Api')
"ActiveModelSerializers::Adapter::#{adapter_name}".safe_constantize ||
"ActiveModelSerializers::Adapter::#{adapter_name.pluralize}".safe_constantize or # rubocop:disable Style/AndOr
fail UnknownAdapterError
end
private :find_by_name
end
# Gotta be at the bottom to use the code above it :(
require 'active_model_serializers/adapter/base'
require 'active_model_serializers/adapter/null'
require 'active_model_serializers/adapter/attributes'
require 'active_model_serializers/adapter/json'
require 'active_model_serializers/adapter/json_api'
end
end

View File

@ -0,0 +1,64 @@
module ActiveModelSerializers
module Adapter
class Attributes < Base
def initialize(serializer, options = {})
super
@include_tree = ActiveModel::Serializer::IncludeTree.from_include_args(options[:include] || '*')
end
def serializable_hash(options = nil)
options ||= {}
if serializer.respond_to?(:each)
serializable_hash_for_collection(options)
else
serializable_hash_for_single_resource(options)
end
end
def fragment_cache(cached_hash, non_cached_hash)
Json::FragmentCache.new.fragment_cache(cached_hash, non_cached_hash)
end
private
def serializable_hash_for_collection(options)
serializer.map { |s| Attributes.new(s, instance_options).serializable_hash(options) }
end
def serializable_hash_for_single_resource(options)
resource = resource_object_for(options)
relationships = resource_relationships(options)
resource.merge!(relationships)
end
def resource_relationships(options)
relationships = {}
serializer.associations(@include_tree).each do |association|
relationships[association.key] = relationship_value_for(association, options)
end
relationships
end
def relationship_value_for(association, options)
return association.options[:virtual_value] if association.options[:virtual_value]
return unless association.serializer && association.serializer.object
opts = instance_options.merge(include: @include_tree[association.key])
Attributes.new(association.serializer, opts).serializable_hash(options)
end
# no-op: Attributes adapter does not include meta data, because it does not support root.
def include_meta(json)
json
end
def resource_object_for(options)
cache_check(serializer) do
serializer.attributes(options[:fields])
end
end
end
end
end

View File

@ -0,0 +1,56 @@
module ActiveModelSerializers
module Adapter
class Base
# Automatically register adapters when subclassing
def self.inherited(subclass)
ActiveModelSerializers::Adapter.register(subclass)
end
attr_reader :serializer, :instance_options
def initialize(serializer, options = {})
@serializer = serializer
@instance_options = options
end
def serializable_hash(_options = nil)
fail NotImplementedError, 'This is an abstract method. Should be implemented at the concrete adapter.'
end
def as_json(options = nil)
hash = serializable_hash(options)
include_meta(hash)
hash
end
def fragment_cache(*_args)
fail NotImplementedError, 'This is an abstract method. Should be implemented at the concrete adapter.'
end
def cache_check(serializer)
CachedSerializer.new(serializer).cache_check(self) do
yield
end
end
private
def meta
instance_options.fetch(:meta, nil)
end
def meta_key
instance_options.fetch(:meta_key, 'meta'.freeze)
end
def root
serializer.json_key.to_sym if serializer.json_key
end
def include_meta(json)
json[meta_key] = meta if meta
json
end
end
end
end

View File

@ -0,0 +1,43 @@
module ActiveModelSerializers
module Adapter
class CachedSerializer
def initialize(serializer)
@cached_serializer = serializer
@klass = @cached_serializer.class
end
def cache_check(adapter_instance)
if cached?
@klass._cache.fetch(cache_key, @klass._cache_options) do
yield
end
elsif fragment_cached?
FragmentCache.new(adapter_instance, @cached_serializer, adapter_instance.instance_options).fetch
else
yield
end
end
def cached?
@klass._cache && !@klass._cache_only && !@klass._cache_except
end
def fragment_cached?
@klass._cache_only && !@klass._cache_except || !@klass._cache_only && @klass._cache_except
end
def cache_key
parts = []
parts << object_cache_key
parts << @klass._cache_digest unless @klass._cache_options && @klass._cache_options[:skip_digest]
parts.join('/')
end
def object_cache_key
object_time_safe = @cached_serializer.object.updated_at
object_time_safe = object_time_safe.strftime('%Y%m%d%H%M%S%9N') if object_time_safe.respond_to?(:strftime)
(@klass._cache_key) ? "#{@klass._cache_key}/#{@cached_serializer.object.id}-#{object_time_safe}" : @cached_serializer.object.cache_key
end
end
end
end

View File

@ -0,0 +1,109 @@
module ActiveModelSerializers
module Adapter
class FragmentCache
attr_reader :serializer
def initialize(adapter, serializer, options)
@instance_options = options
@adapter = adapter
@serializer = serializer
end
# TODO: Use Serializable::Resource
# TODO: call +constantize+ less
# 1. Create a CachedSerializer and NonCachedSerializer from the serializer class
# 2. Serialize the above two with the given adapter
# 3. Pass their serializations to the adapter +::fragment_cache+
def fetch
klass = serializer.class
# It will split the serializer into two, one that will be cached and one that will not
serializers = fragment_serializer(serializer.object.class.name, klass)
# Instantiate both serializers
cached_serializer = serializers[:cached].constantize.new(serializer.object)
non_cached_serializer = serializers[:non_cached].constantize.new(serializer.object)
cached_adapter = adapter.class.new(cached_serializer, instance_options)
non_cached_adapter = adapter.class.new(non_cached_serializer, instance_options)
# Get serializable hash from both
cached_hash = cached_adapter.serializable_hash
non_cached_hash = non_cached_adapter.serializable_hash
# Merge both results
adapter.fragment_cache(cached_hash, non_cached_hash)
end
protected
attr_reader :instance_options, :adapter
private
# Given a serializer class and a hash of its cached and non-cached serializers
# 1. Determine cached attributes from serializer class options
# 2. Add cached attributes to cached Serializer
# 3. Add non-cached attributes to non-cached Serializer
def cached_attributes(klass, serializers)
attributes = serializer.class._attributes
cached_attributes = (klass._cache_only) ? klass._cache_only : attributes.reject { |attr| klass._cache_except.include?(attr) }
non_cached_attributes = attributes - cached_attributes
cached_attributes.each do |attribute|
options = serializer.class._attributes_keys[attribute]
options ||= {}
# Add cached attributes to cached Serializer
serializers[:cached].constantize.attribute(attribute, options)
end
non_cached_attributes.each do |attribute|
options = serializer.class._attributes_keys[attribute]
options ||= {}
# Add non-cached attributes to non-cached Serializer
serializers[:non_cached].constantize.attribute(attribute, options)
end
end
# Given a resource name and its serializer's class
# 1. Dyanmically creates a CachedSerializer and NonCachedSerializer
# for a given class 'name'
# 2. Call
# CachedSerializer.cache(serializer._cache_options)
# CachedSerializer.fragmented(serializer)
# NontCachedSerializer.cache(serializer._cache_options)
# 3. Build a hash keyed to the +cached+ and +non_cached+ serializers
# 4. Call +cached_attributes+ on the serializer class and the above hash
# 5. Return the hash
#
# @example
# When +name+ is <tt>User::Admin</tt>
# creates the Serializer classes (if they don't exist).
# User_AdminCachedSerializer
# User_AdminNOnCachedSerializer
#
def fragment_serializer(name, klass)
cached = "#{to_valid_const_name(name)}CachedSerializer"
non_cached = "#{to_valid_const_name(name)}NonCachedSerializer"
Object.const_set cached, Class.new(ActiveModel::Serializer) unless Object.const_defined?(cached)
Object.const_set non_cached, Class.new(ActiveModel::Serializer) unless Object.const_defined?(non_cached)
klass._cache_options ||= {}
klass._cache_options[:key] = klass._cache_key if klass._cache_key
cached.constantize.cache(klass._cache_options)
cached.constantize.fragmented(serializer)
non_cached.constantize.fragmented(serializer)
serializers = { cached: cached, non_cached: non_cached }
cached_attributes(klass, serializers)
serializers
end
def to_valid_const_name(name)
name.gsub('::', '_')
end
end
end
end

View File

@ -0,0 +1,19 @@
module ActiveModelSerializers
module Adapter
class Json < Base
extend ActiveSupport::Autoload
autoload :FragmentCache
def serializable_hash(options = nil)
options ||= {}
{ root => Attributes.new(serializer, instance_options).serializable_hash(options) }
end
private
def fragment_cache(cached_hash, non_cached_hash)
ActiveModelSerializers::Adapter::Json::FragmentCache.new.fragment_cache(cached_hash, non_cached_hash)
end
end
end
end

View File

@ -0,0 +1,11 @@
module ActiveModelSerializers
module Adapter
class Json
class FragmentCache
def fragment_cache(cached_hash, non_cached_hash)
non_cached_hash.merge cached_hash
end
end
end
end
end

View File

@ -0,0 +1,208 @@
module ActiveModelSerializers
module Adapter
class JsonApi < Base
extend ActiveSupport::Autoload
autoload :PaginationLinks
autoload :FragmentCache
autoload :Link
autoload :Deserialization
# TODO: if we like this abstraction and other API objects to it,
# then extract to its own file and require it.
module ApiObjects
module JsonApi
ActiveModelSerializers.config.jsonapi_version = '1.0'
ActiveModelSerializers.config.jsonapi_toplevel_meta = {}
# Make JSON API top-level jsonapi member opt-in
# ref: http://jsonapi.org/format/#document-top-level
ActiveModelSerializers.config.jsonapi_include_toplevel_object = false
module_function
def add!(hash)
hash.merge!(object) if include_object?
end
def include_object?
ActiveModelSerializers.config.jsonapi_include_toplevel_object
end
# TODO: see if we can cache this
def object
object = {
jsonapi: {
version: ActiveModelSerializers.config.jsonapi_version,
meta: ActiveModelSerializers.config.jsonapi_toplevel_meta
}
}
object[:jsonapi].reject! { |_, v| v.blank? }
object
end
end
end
def initialize(serializer, options = {})
super
@include_tree = ActiveModel::Serializer::IncludeTree.from_include_args(options[:include])
@fieldset = options[:fieldset] || ActiveModel::Serializer::Fieldset.new(options.delete(:fields))
end
def serializable_hash(options = nil)
options ||= {}
is_collection = serializer.respond_to?(:each)
serializers = is_collection ? serializer : [serializer]
primary_data, included = resource_objects_for(serializers)
hash = {}
hash[:data] = is_collection ? primary_data : primary_data[0]
hash[:included] = included if included.any?
ApiObjects::JsonApi.add!(hash)
if instance_options[:links]
hash[:links] ||= {}
hash[:links].update(instance_options[:links])
end
if is_collection && serializer.paginated?
hash[:links] ||= {}
hash[:links].update(pagination_links_for(serializer, options))
end
hash
end
def fragment_cache(cached_hash, non_cached_hash)
root = false if instance_options.include?(:include)
ActiveModelSerializers::Adapter::JsonApi::FragmentCache.new.fragment_cache(root, cached_hash, non_cached_hash)
end
protected
attr_reader :fieldset
private
def resource_objects_for(serializers)
@primary = []
@included = []
@resource_identifiers = Set.new
serializers.each { |serializer| process_resource(serializer, true) }
serializers.each { |serializer| process_relationships(serializer, @include_tree) }
[@primary, @included]
end
def process_resource(serializer, primary)
resource_identifier = resource_identifier_for(serializer)
return false unless @resource_identifiers.add?(resource_identifier)
resource_object = resource_object_for(serializer)
if primary
@primary << resource_object
else
@included << resource_object
end
true
end
def process_relationships(serializer, include_tree)
serializer.associations(include_tree).each do |association|
process_relationship(association.serializer, include_tree[association.key])
end
end
def process_relationship(serializer, include_tree)
if serializer.respond_to?(:each)
serializer.each { |s| process_relationship(s, include_tree) }
return
end
return unless serializer && serializer.object
return unless process_resource(serializer, false)
process_relationships(serializer, include_tree)
end
def resource_identifier_type_for(serializer)
return serializer._type if serializer._type
if ActiveModelSerializers.config.jsonapi_resource_type == :singular
serializer.object.class.model_name.singular
else
serializer.object.class.model_name.plural
end
end
def resource_identifier_id_for(serializer)
if serializer.respond_to?(:id)
serializer.id
else
serializer.object.id
end
end
def resource_identifier_for(serializer)
type = resource_identifier_type_for(serializer)
id = resource_identifier_id_for(serializer)
{ id: id.to_s, type: type }
end
def attributes_for(serializer, fields)
serializer.attributes(fields).except(:id)
end
def resource_object_for(serializer)
resource_object = cache_check(serializer) do
resource_object = resource_identifier_for(serializer)
requested_fields = fieldset && fieldset.fields_for(resource_object[:type])
attributes = attributes_for(serializer, requested_fields)
resource_object[:attributes] = attributes if attributes.any?
resource_object
end
relationships = relationships_for(serializer)
resource_object[:relationships] = relationships if relationships.any?
links = links_for(serializer)
resource_object[:links] = links if links.any?
resource_object
end
def relationship_value_for(serializer, options = {})
if serializer.respond_to?(:each)
serializer.map { |s| resource_identifier_for(s) }
else
if options[:virtual_value]
options[:virtual_value]
elsif serializer && serializer.object
resource_identifier_for(serializer)
end
end
end
def relationships_for(serializer)
resource_type = resource_identifier_type_for(serializer)
requested_associations = fieldset.fields_for(resource_type) || '*'
include_tree = ActiveModel::Serializer::IncludeTree.from_include_args(requested_associations)
serializer.associations(include_tree).each_with_object({}) do |association, hash|
hash[association.key] = { data: relationship_value_for(association.serializer, association.options) }
end
end
def links_for(serializer)
serializer._links.each_with_object({}) do |(name, value), hash|
hash[name] = Link.new(serializer, value).as_json
end
end
def pagination_links_for(serializer, options)
JsonApi::PaginationLinks.new(serializer.object, options[:serialization_context]).serializable_hash(options)
end
end
end
end

View File

@ -0,0 +1,205 @@
module ActiveModelSerializers
module Adapter
class JsonApi
# NOTE(Experimental):
# This is an experimental feature. Both the interface and internals could be subject
# to changes.
module Deserialization
InvalidDocument = Class.new(ArgumentError)
module_function
# Transform a JSON API document, containing a single data object,
# into a hash that is ready for ActiveRecord::Base.new() and such.
# Raises InvalidDocument if the payload is not properly formatted.
#
# @param [Hash|ActionController::Parameters] document
# @param [Hash] options
# only: Array of symbols of whitelisted fields.
# except: Array of symbols of blacklisted fields.
# keys: Hash of translated keys (e.g. :author => :user).
# polymorphic: Array of symbols of polymorphic fields.
# @return [Hash]
#
# @example
# document = {
# data: {
# id: 1,
# type: 'post',
# attributes: {
# title: 'Title 1',
# date: '2015-12-20'
# },
# associations: {
# author: {
# data: {
# type: 'user',
# id: 2
# }
# },
# second_author: {
# data: nil
# },
# comments: {
# data: [{
# type: 'comment',
# id: 3
# },{
# type: 'comment',
# id: 4
# }]
# }
# }
# }
# }
#
# parse(document) #=>
# # {
# # title: 'Title 1',
# # date: '2015-12-20',
# # author_id: 2,
# # second_author_id: nil
# # comment_ids: [3, 4]
# # }
#
# parse(document, only: [:title, :date, :author],
# keys: { date: :published_at },
# polymorphic: [:author]) #=>
# # {
# # title: 'Title 1',
# # published_at: '2015-12-20',
# # author_id: '2',
# # author_type: 'people'
# # }
#
def parse!(document, options = {})
parse(document, options) do |invalid_payload, reason|
fail InvalidDocument, "Invalid payload (#{reason}): #{invalid_payload}"
end
end
# Same as parse!, but returns an empty hash instead of raising InvalidDocument
# on invalid payloads.
def parse(document, options = {})
document = document.dup.permit!.to_h if document.is_a?(ActionController::Parameters)
validate_payload(document) do |invalid_document, reason|
yield invalid_document, reason if block_given?
return {}
end
primary_data = document['data']
attributes = primary_data['attributes'] || {}
attributes['id'] = primary_data['id'] if primary_data['id']
relationships = primary_data['relationships'] || {}
filter_fields(attributes, options)
filter_fields(relationships, options)
hash = {}
hash.merge!(parse_attributes(attributes, options))
hash.merge!(parse_relationships(relationships, options))
hash
end
# Checks whether a payload is compliant with the JSON API spec.
#
# @api private
# rubocop:disable Metrics/CyclomaticComplexity
def validate_payload(payload)
unless payload.is_a?(Hash)
yield payload, 'Expected hash'
return
end
primary_data = payload['data']
unless primary_data.is_a?(Hash)
yield payload, { data: 'Expected hash' }
return
end
attributes = primary_data['attributes'] || {}
unless attributes.is_a?(Hash)
yield payload, { data: { attributes: 'Expected hash or nil' } }
return
end
relationships = primary_data['relationships'] || {}
unless relationships.is_a?(Hash)
yield payload, { data: { relationships: 'Expected hash or nil' } }
return
end
relationships.each do |(key, value)|
unless value.is_a?(Hash) && value.key?('data')
yield payload, { data: { relationships: { key => 'Expected hash with :data key' } } }
end
end
end
# rubocop:enable Metrics/CyclomaticComplexity
# @api private
def filter_fields(fields, options)
if (only = options[:only])
fields.slice!(*Array(only).map(&:to_s))
elsif (except = options[:except])
fields.except!(*Array(except).map(&:to_s))
end
end
# @api private
def field_key(field, options)
(options[:keys] || {}).fetch(field.to_sym, field).to_sym
end
# @api private
def parse_attributes(attributes, options)
attributes
.map { |(k, v)| { field_key(k, options) => v } }
.reduce({}, :merge)
end
# Given an association name, and a relationship data attribute, build a hash
# mapping the corresponding ActiveRecord attribute to the corresponding value.
#
# @example
# parse_relationship(:comments, [{ 'id' => '1', 'type' => 'comments' },
# { 'id' => '2', 'type' => 'comments' }],
# {})
# # => { :comment_ids => ['1', '2'] }
# parse_relationship(:author, { 'id' => '1', 'type' => 'users' }, {})
# # => { :author_id => '1' }
# parse_relationship(:author, nil, {})
# # => { :author_id => nil }
# @param [Symbol] assoc_name
# @param [Hash] assoc_data
# @param [Hash] options
# @return [Hash{Symbol, Object}]
#
# @api private
def parse_relationship(assoc_name, assoc_data, options)
prefix_key = field_key(assoc_name, options).to_s.singularize
hash =
if assoc_data.is_a?(Array)
{ "#{prefix_key}_ids".to_sym => assoc_data.map { |ri| ri['id'] } }
else
{ "#{prefix_key}_id".to_sym => assoc_data ? assoc_data['id'] : nil }
end
polymorphic = (options[:polymorphic] || []).include?(assoc_name.to_sym)
hash.merge!("#{prefix_key}_type".to_sym => assoc_data['type']) if polymorphic
hash
end
# @api private
def parse_relationships(relationships, options)
relationships
.map { |(k, v)| parse_relationship(k, v['data'], options) }
.reduce({}, :merge)
end
end
end
end
end

View File

@ -0,0 +1,18 @@
module ActiveModelSerializers
module Adapter
class JsonApi
class FragmentCache
def fragment_cache(root, cached_hash, non_cached_hash)
core_cached = cached_hash.first
core_non_cached = non_cached_hash.first
no_root_cache = cached_hash.delete_if { |key, _value| key == core_cached[0] }
no_root_non_cache = non_cached_hash.delete_if { |key, _value| key == core_non_cached[0] }
cached_resource = (core_cached[1]) ? core_cached[1].deep_merge(core_non_cached[1]) : core_non_cached[1]
hash = (root) ? { root => cached_resource } : cached_resource
hash.deep_merge no_root_non_cache.deep_merge no_root_cache
end
end
end
end
end

View File

@ -0,0 +1,42 @@
module ActiveModelSerializers
module Adapter
class JsonApi
class Link
def initialize(serializer, value)
@object = serializer.object
@scope = serializer.scope
# Use the return value of the block unless it is nil.
if value.respond_to?(:call)
@value = instance_eval(&value)
else
@value = value
end
end
def href(value)
@href = value
nil
end
def meta(value)
@meta = value
nil
end
def as_json
return @value if @value
hash = { href: @href }
hash.merge!(meta: @meta) if @meta
hash
end
protected
attr_reader :object, :scope
end
end
end
end

View File

@ -0,0 +1,56 @@
module ActiveModelSerializers
module Adapter
class JsonApi < Base
class PaginationLinks
FIRST_PAGE = 1
attr_reader :collection, :context
def initialize(collection, context)
@collection = collection
@context = context
end
def serializable_hash(options = {})
pages_from.each_with_object({}) do |(key, value), hash|
params = query_parameters.merge(page: { number: value, size: collection.size }).to_query
hash[key] = "#{url(options)}?#{params}"
end
end
private
def pages_from
return {} if collection.total_pages == FIRST_PAGE
{}.tap do |pages|
pages[:self] = collection.current_page
unless collection.current_page == FIRST_PAGE
pages[:first] = FIRST_PAGE
pages[:prev] = collection.current_page - FIRST_PAGE
end
unless collection.current_page == collection.total_pages
pages[:next] = collection.current_page + FIRST_PAGE
pages[:last] = collection.total_pages
end
end
end
def url(options)
@url ||= options.fetch(:links, {}).fetch(:self, nil) || request_url
end
def request_url
@request_url ||= context.request_url
end
def query_parameters
@query_parameters ||= context.query_parameters
end
end
end
end
end

View File

@ -0,0 +1,10 @@
module ActiveModelSerializers
module Adapter
class Null < Base
# Since options param is not being used, underscored naming of the param
def serializable_hash(_options = nil)
{}
end
end
end
end

View File

@ -3,11 +3,11 @@ module ActiveModelSerializers
module_function module_function
def jsonapi_parse(*args) def jsonapi_parse(*args)
ActiveModel::Serializer::Adapter::JsonApi::Deserialization.parse(*args) ActiveModelSerializers::Adapter::JsonApi::Deserialization.parse(*args)
end end
def jsonapi_parse!(*args) def jsonapi_parse!(*args)
ActiveModel::Serializer::Adapter::JsonApi::Deserialization.parse!(*args) ActiveModelSerializers::Adapter::JsonApi::Deserialization.parse!(*args)
end end
end end
end end

View File

@ -0,0 +1,164 @@
module ActiveModelSerializers
class AdapterForTest < ActiveSupport::TestCase
UnknownAdapterError = ::ActiveModelSerializers::Adapter::UnknownAdapterError
def setup
@previous_adapter = ActiveModelSerializers.config.adapter
end
def teardown
ActiveModelSerializers.config.adapter = @previous_adapter
end
def test_returns_default_adapter
adapter = ActiveModelSerializers::Adapter.configured_adapter
assert_equal ActiveModelSerializers::Adapter::Attributes, adapter
end
def test_overwrite_adapter_with_symbol
ActiveModelSerializers.config.adapter = :null
adapter = ActiveModelSerializers::Adapter.configured_adapter
assert_equal ActiveModelSerializers::Adapter::Null, adapter
ensure
ActiveModelSerializers.config.adapter = @previous_adapter
end
def test_overwrite_adapter_with_class
ActiveModelSerializers.config.adapter = ActiveModelSerializers::Adapter::Null
adapter = ActiveModelSerializers::Adapter.configured_adapter
assert_equal ActiveModelSerializers::Adapter::Null, adapter
end
def test_raises_exception_if_invalid_symbol_given
ActiveModelSerializers.config.adapter = :unknown
assert_raises UnknownAdapterError do
ActiveModelSerializers::Adapter.configured_adapter
end
end
def test_raises_exception_if_it_does_not_know_hot_to_infer_adapter
ActiveModelSerializers.config.adapter = 42
assert_raises UnknownAdapterError do
ActiveModelSerializers::Adapter.configured_adapter
end
end
def test_adapter_class_for_known_adapter
klass = ActiveModelSerializers::Adapter.adapter_class(:json_api)
assert_equal ActiveModelSerializers::Adapter::JsonApi, klass
end
def test_adapter_class_for_unknown_adapter
assert_raises UnknownAdapterError do
ActiveModelSerializers::Adapter.adapter_class(:json_simple)
end
end
def test_adapter_map
expected_adapter_map = {
'null'.freeze => ActiveModelSerializers::Adapter::Null,
'json'.freeze => ActiveModelSerializers::Adapter::Json,
'attributes'.freeze => ActiveModelSerializers::Adapter::Attributes,
'json_api'.freeze => ActiveModelSerializers::Adapter::JsonApi
}
actual = ActiveModelSerializers::Adapter.adapter_map
assert_equal actual, expected_adapter_map
end
def test_adapters
assert_equal ActiveModelSerializers::Adapter.adapters.sort, [
'attributes'.freeze,
'json'.freeze,
'json_api'.freeze,
'null'.freeze
]
end
def test_lookup_adapter_by_string_name
assert_equal ActiveModelSerializers::Adapter.lookup('json'.freeze), ActiveModelSerializers::Adapter::Json
end
def test_lookup_adapter_by_symbol_name
assert_equal ActiveModelSerializers::Adapter.lookup(:json), ActiveModelSerializers::Adapter::Json
end
def test_lookup_adapter_by_class
klass = ActiveModelSerializers::Adapter::Json
assert_equal ActiveModelSerializers::Adapter.lookup(klass), klass
end
def test_lookup_adapter_from_environment_registers_adapter
ActiveModelSerializers::Adapter.const_set(:AdapterFromEnvironment, Class.new)
klass = ::ActiveModelSerializers::Adapter::AdapterFromEnvironment
name = 'adapter_from_environment'.freeze
assert_equal ActiveModelSerializers::Adapter.lookup(name), klass
assert ActiveModelSerializers::Adapter.adapters.include?(name)
ensure
ActiveModelSerializers::Adapter.adapter_map.delete(name)
ActiveModelSerializers::Adapter.send(:remove_const, :AdapterFromEnvironment)
end
def test_lookup_adapter_for_unknown_name
assert_raises UnknownAdapterError do
ActiveModelSerializers::Adapter.lookup(:json_simple)
end
end
def test_adapter
assert_equal ActiveModelSerializers.config.adapter, :attributes
assert_equal ActiveModelSerializers::Adapter.configured_adapter, ActiveModelSerializers::Adapter::Attributes
end
def test_register_adapter
new_adapter_name = :foo
new_adapter_klass = Class.new
ActiveModelSerializers::Adapter.register(new_adapter_name, new_adapter_klass)
assert ActiveModelSerializers::Adapter.adapters.include?('foo'.freeze)
assert ActiveModelSerializers::Adapter.lookup(:foo), new_adapter_klass
ensure
ActiveModelSerializers::Adapter.adapter_map.delete(new_adapter_name.to_s)
end
def test_inherited_adapter_hooks_register_adapter
Object.const_set(:MyAdapter, Class.new)
my_adapter = MyAdapter
ActiveModelSerializers::Adapter::Base.inherited(my_adapter)
assert_equal ActiveModelSerializers::Adapter.lookup(:my_adapter), my_adapter
ensure
ActiveModelSerializers::Adapter.adapter_map.delete('my_adapter'.freeze)
Object.send(:remove_const, :MyAdapter)
end
def test_inherited_adapter_hooks_register_namespaced_adapter
Object.const_set(:MyNamespace, Module.new)
MyNamespace.const_set(:MyAdapter, Class.new)
my_adapter = MyNamespace::MyAdapter
ActiveModelSerializers::Adapter::Base.inherited(my_adapter)
assert_equal ActiveModelSerializers::Adapter.lookup(:'my_namespace/my_adapter'), my_adapter
ensure
ActiveModelSerializers::Adapter.adapter_map.delete('my_namespace/my_adapter'.freeze)
MyNamespace.send(:remove_const, :MyAdapter)
Object.send(:remove_const, :MyNamespace)
end
def test_inherited_adapter_hooks_register_subclass_of_registered_adapter
Object.const_set(:MyAdapter, Class.new)
my_adapter = MyAdapter
Object.const_set(:MySubclassedAdapter, Class.new(MyAdapter))
my_subclassed_adapter = MySubclassedAdapter
ActiveModelSerializers::Adapter::Base.inherited(my_adapter)
ActiveModelSerializers::Adapter::Base.inherited(my_subclassed_adapter)
assert_equal ActiveModelSerializers::Adapter.lookup(:my_adapter), my_adapter
assert_equal ActiveModelSerializers::Adapter.lookup(:my_subclassed_adapter), my_subclassed_adapter
ensure
ActiveModelSerializers::Adapter.adapter_map.delete('my_adapter'.freeze)
ActiveModelSerializers::Adapter.adapter_map.delete('my_subclassed_adapter'.freeze)
Object.send(:remove_const, :MyAdapter)
Object.send(:remove_const, :MySubclassedAdapter)
end
end
end

View File

@ -65,7 +65,7 @@ module ActiveModel
def test_logs_correct_adapter def test_logs_correct_adapter
ActiveModel::SerializableResource.new(@post).serializable_hash ActiveModel::SerializableResource.new(@post).serializable_hash
assert_match(/ActiveModel::Serializer::Adapter::Attributes/, @logger.messages) assert_match(/ActiveModelSerializers::Adapter::Attributes/, @logger.messages)
end end
def test_logs_the_duration def test_logs_the_duration

View File

@ -1,36 +1,34 @@
require 'test_helper' require 'test_helper'
module ActiveModel module ActiveModelSerializers
class Serializer module Adapter
module Adapter class FragmentCacheTest < ActiveSupport::TestCase
class FragmentCacheTest < ActiveSupport::TestCase def setup
def setup super
super @spam = Spam::UnrelatedLink.new(id: 'spam-id-1')
@spam = Spam::UnrelatedLink.new(id: 'spam-id-1') @author = Author.new(name: 'Joao M. D. Moura')
@author = Author.new(name: 'Joao M. D. Moura') @role = Role.new(name: 'Great Author', description: nil)
@role = Role.new(name: 'Great Author', description: nil) @role.author = [@author]
@role.author = [@author] @role_serializer = RoleSerializer.new(@role)
@role_serializer = RoleSerializer.new(@role) @spam_serializer = Spam::UnrelatedLinkSerializer.new(@spam)
@spam_serializer = Spam::UnrelatedLinkSerializer.new(@spam) @role_hash = FragmentCache.new(::ActiveModelSerializers::Adapter.configured_adapter.new(@role_serializer), @role_serializer, {})
@role_hash = FragmentCache.new(RoleSerializer.adapter.new(@role_serializer), @role_serializer, {}) @spam_hash = FragmentCache.new(::ActiveModelSerializers::Adapter.configured_adapter.new(@spam_serializer), @spam_serializer, {})
@spam_hash = FragmentCache.new(Spam::UnrelatedLinkSerializer.adapter.new(@spam_serializer), @spam_serializer, {}) end
end
def test_fragment_fetch_with_virtual_attributes def test_fragment_fetch_with_virtual_attributes
expected_result = { expected_result = {
id: @role.id, id: @role.id,
description: @role.description, description: @role.description,
slug: "#{@role.name}-#{@role.id}", slug: "#{@role.name}-#{@role.id}",
name: @role.name name: @role.name
} }
assert_equal(@role_hash.fetch, expected_result) assert_equal(@role_hash.fetch, expected_result)
end end
def test_fragment_fetch_with_namespaced_object def test_fragment_fetch_with_namespaced_object
expected_result = { expected_result = {
id: @spam.id id: @spam.id
} }
assert_equal(@spam_hash.fetch, expected_result) assert_equal(@spam_hash.fetch, expected_result)
end
end end
end end
end end

View File

@ -1,45 +1,43 @@
require 'test_helper' require 'test_helper'
module ActiveModel module ActiveModelSerializers
class Serializer module Adapter
module Adapter class Json
class Json class BelongsToTest < ActiveSupport::TestCase
class BelongsToTest < ActiveSupport::TestCase def setup
def setup @post = Post.new(id: 42, title: 'New Post', body: 'Body')
@post = Post.new(id: 42, title: 'New Post', body: 'Body') @anonymous_post = Post.new(id: 43, title: 'Hello!!', body: 'Hello, world!!')
@anonymous_post = Post.new(id: 43, title: 'Hello!!', body: 'Hello, world!!') @comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')
@comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') @post.comments = [@comment]
@post.comments = [@comment] @anonymous_post.comments = []
@anonymous_post.comments = [] @comment.post = @post
@comment.post = @post @comment.author = nil
@comment.author = nil @anonymous_post.author = nil
@anonymous_post.author = nil @blog = Blog.new(id: 1, name: 'My Blog!!')
@blog = Blog.new(id: 1, name: 'My Blog!!') @post.blog = @blog
@post.blog = @blog @anonymous_post.blog = nil
@anonymous_post.blog = nil
@serializer = CommentSerializer.new(@comment) @serializer = CommentSerializer.new(@comment)
@adapter = ActiveModel::Serializer::Adapter::Json.new(@serializer) @adapter = ActiveModelSerializers::Adapter::Json.new(@serializer)
ActionController::Base.cache_store.clear ActionController::Base.cache_store.clear
end end
def test_includes_post def test_includes_post
assert_equal({ id: 42, title: 'New Post', body: 'Body' }, @adapter.serializable_hash[:comment][:post]) assert_equal({ id: 42, title: 'New Post', body: 'Body' }, @adapter.serializable_hash[:comment][:post])
end end
def test_include_nil_author def test_include_nil_author
serializer = PostSerializer.new(@anonymous_post) serializer = PostSerializer.new(@anonymous_post)
adapter = ActiveModel::Serializer::Adapter::Json.new(serializer) adapter = ActiveModelSerializers::Adapter::Json.new(serializer)
assert_equal({ post: { title: 'Hello!!', body: 'Hello, world!!', id: 43, comments: [], blog: { id: 999, name: 'Custom blog' }, author: nil } }, adapter.serializable_hash) assert_equal({ post: { title: 'Hello!!', body: 'Hello, world!!', id: 43, comments: [], blog: { id: 999, name: 'Custom blog' }, author: nil } }, adapter.serializable_hash)
end end
def test_include_nil_author_with_specified_serializer def test_include_nil_author_with_specified_serializer
serializer = PostPreviewSerializer.new(@anonymous_post) serializer = PostPreviewSerializer.new(@anonymous_post)
adapter = ActiveModel::Serializer::Adapter::Json.new(serializer) adapter = ActiveModelSerializers::Adapter::Json.new(serializer)
assert_equal({ post: { title: 'Hello!!', body: 'Hello, world!!', id: 43, comments: [], author: nil } }, adapter.serializable_hash) assert_equal({ post: { title: 'Hello!!', body: 'Hello, world!!', id: 43, comments: [], author: nil } }, adapter.serializable_hash)
end
end end
end end
end end

View File

@ -1,90 +1,88 @@
require 'test_helper' require 'test_helper'
module ActiveModel module ActiveModelSerializers
class Serializer module Adapter
module Adapter class Json
class Json class Collection < ActiveSupport::TestCase
class Collection < ActiveSupport::TestCase def setup
def setup @author = Author.new(id: 1, name: 'Steve K.')
@author = Author.new(id: 1, name: 'Steve K.') @first_post = Post.new(id: 1, title: 'Hello!!', body: 'Hello, world!!')
@first_post = Post.new(id: 1, title: 'Hello!!', body: 'Hello, world!!') @second_post = Post.new(id: 2, title: 'New Post', body: 'Body')
@second_post = Post.new(id: 2, title: 'New Post', body: 'Body') @first_post.comments = []
@first_post.comments = [] @second_post.comments = []
@second_post.comments = [] @first_post.author = @author
@first_post.author = @author @second_post.author = @author
@second_post.author = @author @blog = Blog.new(id: 1, name: 'My Blog!!')
@blog = Blog.new(id: 1, name: 'My Blog!!') @first_post.blog = @blog
@first_post.blog = @blog @second_post.blog = nil
@second_post.blog = nil
ActionController::Base.cache_store.clear ActionController::Base.cache_store.clear
end end
def test_with_serializer_option def test_with_serializer_option
@blog.special_attribute = 'Special' @blog.special_attribute = 'Special'
@blog.articles = [@first_post, @second_post] @blog.articles = [@first_post, @second_post]
serializer = CollectionSerializer.new([@blog], serializer: CustomBlogSerializer) serializer = ActiveModel::Serializer::CollectionSerializer.new([@blog], serializer: CustomBlogSerializer)
adapter = ActiveModel::Serializer::Adapter::Json.new(serializer) adapter = ActiveModelSerializers::Adapter::Json.new(serializer)
expected = { blogs: [{ expected = { blogs: [{
id: 1,
special_attribute: 'Special',
articles: [{ id: 1, title: 'Hello!!', body: 'Hello, world!!' }, { id: 2, title: 'New Post', body: 'Body' }]
}] }
assert_equal expected, adapter.serializable_hash
end
def test_include_multiple_posts
serializer = ActiveModel::Serializer::CollectionSerializer.new([@first_post, @second_post])
adapter = ActiveModelSerializers::Adapter::Json.new(serializer)
expected = { posts: [{
title: 'Hello!!',
body: 'Hello, world!!',
id: 1,
comments: [],
author: {
id: 1, id: 1,
special_attribute: 'Special', name: 'Steve K.'
articles: [{ id: 1, title: 'Hello!!', body: 'Hello, world!!' }, { id: 2, title: 'New Post', body: 'Body' }] },
}] } blog: {
assert_equal expected, adapter.serializable_hash id: 999,
end name: 'Custom blog'
}
def test_include_multiple_posts }, {
serializer = CollectionSerializer.new([@first_post, @second_post]) title: 'New Post',
adapter = ActiveModel::Serializer::Adapter::Json.new(serializer) body: 'Body',
id: 2,
expected = { posts: [{ comments: [],
title: 'Hello!!', author: {
body: 'Hello, world!!',
id: 1, id: 1,
comments: [], name: 'Steve K.'
author: { },
id: 1, blog: {
name: 'Steve K.' id: 999,
}, name: 'Custom blog'
blog: { }
id: 999, }] }
name: 'Custom blog' assert_equal expected, adapter.serializable_hash
} end
}, {
title: 'New Post',
body: 'Body',
id: 2,
comments: [],
author: {
id: 1,
name: 'Steve K.'
},
blog: {
id: 999,
name: 'Custom blog'
}
}] }
assert_equal expected, adapter.serializable_hash
end
def test_root_is_underscored def test_root_is_underscored
virtual_value = VirtualValue.new(id: 1) virtual_value = VirtualValue.new(id: 1)
serializer = CollectionSerializer.new([virtual_value]) serializer = ActiveModel::Serializer::CollectionSerializer.new([virtual_value])
adapter = ActiveModel::Serializer::Adapter::Json.new(serializer) adapter = ActiveModelSerializers::Adapter::Json.new(serializer)
assert_equal 1, adapter.serializable_hash[:virtual_values].length assert_equal 1, adapter.serializable_hash[:virtual_values].length
end end
def test_include_option def test_include_option
serializer = CollectionSerializer.new([@first_post, @second_post]) serializer = ActiveModel::Serializer::CollectionSerializer.new([@first_post, @second_post])
adapter = ActiveModel::Serializer::Adapter::Json.new(serializer, include: '') adapter = ActiveModelSerializers::Adapter::Json.new(serializer, include: '')
actual = adapter.serializable_hash actual = adapter.serializable_hash
expected = { posts: [{ id: 1, title: 'Hello!!', body: 'Hello, world!!' }, expected = { posts: [{ id: 1, title: 'Hello!!', body: 'Hello, world!!' },
{ id: 2, title: 'New Post', body: 'Body' }] } { id: 2, title: 'New Post', body: 'Body' }] }
assert_equal(expected, actual) assert_equal(expected, actual)
end
end end
end end
end end

View File

@ -1,45 +1,43 @@
require 'test_helper' require 'test_helper'
module ActiveModel module ActiveModelSerializers
class Serializer module Adapter
module Adapter class Json
class Json class HasManyTestTest < ActiveSupport::TestCase
class HasManyTestTest < ActiveSupport::TestCase def setup
def setup ActionController::Base.cache_store.clear
ActionController::Base.cache_store.clear @author = Author.new(id: 1, name: 'Steve K.')
@author = Author.new(id: 1, name: 'Steve K.') @post = Post.new(id: 42, title: 'New Post', body: 'Body')
@post = Post.new(id: 42, title: 'New Post', body: 'Body') @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')
@first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT')
@second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT') @post.comments = [@first_comment, @second_comment]
@post.comments = [@first_comment, @second_comment] @post.author = @author
@post.author = @author @first_comment.post = @post
@first_comment.post = @post @second_comment.post = @post
@second_comment.post = @post @blog = Blog.new(id: 1, name: 'My Blog!!')
@blog = Blog.new(id: 1, name: 'My Blog!!') @post.blog = @blog
@post.blog = @blog @tag = Tag.new(id: 1, name: '#hash_tag')
@tag = Tag.new(id: 1, name: '#hash_tag') @post.tags = [@tag]
@post.tags = [@tag] end
end
def test_has_many def test_has_many
serializer = PostSerializer.new(@post) serializer = PostSerializer.new(@post)
adapter = ActiveModel::Serializer::Adapter::Json.new(serializer) adapter = ActiveModelSerializers::Adapter::Json.new(serializer)
assert_equal([ assert_equal([
{ id: 1, body: 'ZOMG A COMMENT' }, { id: 1, body: 'ZOMG A COMMENT' },
{ id: 2, body: 'ZOMG ANOTHER COMMENT' } { id: 2, body: 'ZOMG ANOTHER COMMENT' }
], adapter.serializable_hash[:post][:comments]) ], adapter.serializable_hash[:post][:comments])
end end
def test_has_many_with_no_serializer def test_has_many_with_no_serializer
serializer = PostWithTagsSerializer.new(@post) serializer = PostWithTagsSerializer.new(@post)
adapter = ActiveModel::Serializer::Adapter::Json.new(serializer) adapter = ActiveModelSerializers::Adapter::Json.new(serializer)
assert_equal({ assert_equal({
id: 42, id: 42,
tags: [ tags: [
{ 'id' => 1, 'name' => '#hash_tag' } { 'id' => 1, 'name' => '#hash_tag' }
] ]
}.to_json, adapter.serializable_hash[:post].to_json) }.to_json, adapter.serializable_hash[:post].to_json)
end
end end
end end
end end

View File

@ -1,155 +1,153 @@
require 'test_helper' require 'test_helper'
module ActiveModel module ActiveModelSerializers
class Serializer module Adapter
module Adapter class JsonApi
class JsonApi class BelongsToTest < ActiveSupport::TestCase
class BelongsToTest < ActiveSupport::TestCase def setup
def setup @author = Author.new(id: 1, name: 'Steve K.')
@author = Author.new(id: 1, name: 'Steve K.') @author.bio = nil
@author.bio = nil @author.roles = []
@author.roles = [] @blog = Blog.new(id: 23, name: 'AMS Blog')
@blog = Blog.new(id: 23, name: 'AMS Blog') @post = Post.new(id: 42, title: 'New Post', body: 'Body')
@post = Post.new(id: 42, title: 'New Post', body: 'Body') @anonymous_post = Post.new(id: 43, title: 'Hello!!', body: 'Hello, world!!')
@anonymous_post = Post.new(id: 43, title: 'Hello!!', body: 'Hello, world!!') @comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')
@comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') @post.comments = [@comment]
@post.comments = [@comment] @post.blog = @blog
@post.blog = @blog @anonymous_post.comments = []
@anonymous_post.comments = [] @anonymous_post.blog = nil
@anonymous_post.blog = nil @comment.post = @post
@comment.post = @post @comment.author = nil
@comment.author = nil @post.author = @author
@post.author = @author @anonymous_post.author = nil
@anonymous_post.author = nil @blog = Blog.new(id: 1, name: 'My Blog!!')
@blog = Blog.new(id: 1, name: 'My Blog!!') @blog.writer = @author
@blog.writer = @author @blog.articles = [@post, @anonymous_post]
@blog.articles = [@post, @anonymous_post] @author.posts = []
@author.posts = []
@serializer = CommentSerializer.new(@comment) @serializer = CommentSerializer.new(@comment)
@adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer) @adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer)
ActionController::Base.cache_store.clear ActionController::Base.cache_store.clear
end end
def test_includes_post_id def test_includes_post_id
expected = { data: { type: 'posts', id: '42' } } expected = { data: { type: 'posts', id: '42' } }
assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:post]) assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:post])
end end
def test_includes_linked_post def test_includes_linked_post
@adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, include: [:post]) @adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer, include: [:post])
expected = [{ expected = [{
id: '42',
type: 'posts',
attributes: {
title: 'New Post',
body: 'Body',
},
relationships: {
comments: { data: [{ type: 'comments', id: '1' }] },
blog: { data: { type: 'blogs', id: '999' } },
author: { data: { type: 'authors', id: '1' } }
}
}]
assert_equal expected, @adapter.serializable_hash[:included]
end
def test_limiting_linked_post_fields
@adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer, include: [:post], fields: { post: [:title, :comments, :blog, :author] })
expected = [{
id: '42',
type: 'posts',
attributes: {
title: 'New Post'
},
relationships: {
comments: { data: [{ type: 'comments', id: '1' }] },
blog: { data: { type: 'blogs', id: '999' } },
author: { data: { type: 'authors', id: '1' } }
}
}]
assert_equal expected, @adapter.serializable_hash[:included]
end
def test_include_nil_author
serializer = PostSerializer.new(@anonymous_post)
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
assert_equal({ comments: { data: [] }, blog: { data: { type: 'blogs', id: '999' } }, author: { data: nil } }, adapter.serializable_hash[:data][:relationships])
end
def test_include_type_for_association_when_different_than_name
serializer = BlogSerializer.new(@blog)
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
relationships = adapter.serializable_hash[:data][:relationships]
expected = {
writer: {
data: {
type: 'authors',
id: '1'
}
},
articles: {
data: [
{
type: 'posts',
id: '42'
},
{
type: 'posts',
id: '43'
}
]
}
}
assert_equal expected, relationships
end
def test_include_linked_resources_with_type_name
serializer = BlogSerializer.new(@blog)
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, include: [:writer, :articles])
linked = adapter.serializable_hash[:included]
expected = [
{
id: '1',
type: 'authors',
attributes: {
name: 'Steve K.'
},
relationships: {
posts: { data: [] },
roles: { data: [] },
bio: { data: nil }
}
}, {
id: '42', id: '42',
type: 'posts', type: 'posts',
attributes: { attributes: {
title: 'New Post', title: 'New Post',
body: 'Body', body: 'Body'
}, },
relationships: { relationships: {
comments: { data: [{ type: 'comments', id: '1' }] }, comments: { data: [{ type: 'comments', id: '1' }] },
blog: { data: { type: 'blogs', id: '999' } }, blog: { data: { type: 'blogs', id: '999' } },
author: { data: { type: 'authors', id: '1' } } author: { data: { type: 'authors', id: '1' } }
} }
}] }, {
assert_equal expected, @adapter.serializable_hash[:included] id: '43',
end
def test_limiting_linked_post_fields
@adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, include: [:post], fields: { post: [:title, :comments, :blog, :author] })
expected = [{
id: '42',
type: 'posts', type: 'posts',
attributes: { attributes: {
title: 'New Post' title: 'Hello!!',
body: 'Hello, world!!'
}, },
relationships: { relationships: {
comments: { data: [{ type: 'comments', id: '1' }] }, comments: { data: [] },
blog: { data: { type: 'blogs', id: '999' } }, blog: { data: { type: 'blogs', id: '999' } },
author: { data: { type: 'authors', id: '1' } } author: { data: nil }
}
}]
assert_equal expected, @adapter.serializable_hash[:included]
end
def test_include_nil_author
serializer = PostSerializer.new(@anonymous_post)
adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer)
assert_equal({ comments: { data: [] }, blog: { data: { type: 'blogs', id: '999' } }, author: { data: nil } }, adapter.serializable_hash[:data][:relationships])
end
def test_include_type_for_association_when_different_than_name
serializer = BlogSerializer.new(@blog)
adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer)
relationships = adapter.serializable_hash[:data][:relationships]
expected = {
writer: {
data: {
type: 'authors',
id: '1'
}
},
articles: {
data: [
{
type: 'posts',
id: '42'
},
{
type: 'posts',
id: '43'
}
]
} }
} }
assert_equal expected, relationships ]
end assert_equal expected, linked
def test_include_linked_resources_with_type_name
serializer = BlogSerializer.new(@blog)
adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer, include: [:writer, :articles])
linked = adapter.serializable_hash[:included]
expected = [
{
id: '1',
type: 'authors',
attributes: {
name: 'Steve K.'
},
relationships: {
posts: { data: [] },
roles: { data: [] },
bio: { data: nil }
}
}, {
id: '42',
type: 'posts',
attributes: {
title: 'New Post',
body: 'Body'
},
relationships: {
comments: { data: [{ type: 'comments', id: '1' }] },
blog: { data: { type: 'blogs', id: '999' } },
author: { data: { type: 'authors', id: '1' } }
}
}, {
id: '43',
type: 'posts',
attributes: {
title: 'Hello!!',
body: 'Hello, world!!'
},
relationships: {
comments: { data: [] },
blog: { data: { type: 'blogs', id: '999' } },
author: { data: nil }
}
}
]
assert_equal expected, linked
end
end end
end end
end end

View File

@ -1,95 +1,93 @@
require 'test_helper' require 'test_helper'
module ActiveModel module ActiveModelSerializers
class Serializer module Adapter
module Adapter class JsonApi
class JsonApi class CollectionTest < ActiveSupport::TestCase
class CollectionTest < ActiveSupport::TestCase def setup
def setup @author = Author.new(id: 1, name: 'Steve K.')
@author = Author.new(id: 1, name: 'Steve K.') @author.bio = nil
@author.bio = nil @blog = Blog.new(id: 23, name: 'AMS Blog')
@blog = Blog.new(id: 23, name: 'AMS Blog') @first_post = Post.new(id: 1, title: 'Hello!!', body: 'Hello, world!!')
@first_post = Post.new(id: 1, title: 'Hello!!', body: 'Hello, world!!') @second_post = Post.new(id: 2, title: 'New Post', body: 'Body')
@second_post = Post.new(id: 2, title: 'New Post', body: 'Body') @first_post.comments = []
@first_post.comments = [] @second_post.comments = []
@second_post.comments = [] @first_post.blog = @blog
@first_post.blog = @blog @second_post.blog = nil
@second_post.blog = nil @first_post.author = @author
@first_post.author = @author @second_post.author = @author
@second_post.author = @author @author.posts = [@first_post, @second_post]
@author.posts = [@first_post, @second_post]
@serializer = CollectionSerializer.new([@first_post, @second_post]) @serializer = ActiveModel::Serializer::CollectionSerializer.new([@first_post, @second_post])
@adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer) @adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer)
ActionController::Base.cache_store.clear ActionController::Base.cache_store.clear
end end
def test_include_multiple_posts def test_include_multiple_posts
expected = [ expected = [
{ {
id: '1', id: '1',
type: 'posts', type: 'posts',
attributes: { attributes: {
title: 'Hello!!', title: 'Hello!!',
body: 'Hello, world!!' body: 'Hello, world!!'
},
relationships: {
comments: { data: [] },
blog: { data: { type: 'blogs', id: '999' } },
author: { data: { type: 'authors', id: '1' } }
}
}, },
{ relationships: {
id: '2', comments: { data: [] },
type: 'posts', blog: { data: { type: 'blogs', id: '999' } },
attributes: { author: { data: { type: 'authors', id: '1' } }
title: 'New Post',
body: 'Body'
},
relationships: {
comments: { data: [] },
blog: { data: { type: 'blogs', id: '999' } },
author: { data: { type: 'authors', id: '1' } }
}
} }
] },
{
assert_equal(expected, @adapter.serializable_hash[:data]) id: '2',
end type: 'posts',
attributes: {
def test_limiting_fields title: 'New Post',
actual = ActiveModel::SerializableResource.new( body: 'Body'
[@first_post, @second_post], adapter: :json_api,
fields: { posts: %w(title comments blog author) })
.serializable_hash
expected = [
{
id: '1',
type: 'posts',
attributes: {
title: 'Hello!!'
},
relationships: {
comments: { data: [] },
blog: { data: { type: 'blogs', id: '999' } },
author: { data: { type: 'authors', id: '1' } }
}
}, },
{ relationships: {
id: '2', comments: { data: [] },
type: 'posts', blog: { data: { type: 'blogs', id: '999' } },
attributes: { author: { data: { type: 'authors', id: '1' } }
title: 'New Post'
},
relationships: {
comments: { data: [] },
blog: { data: { type: 'blogs', id: '999' } },
author: { data: { type: 'authors', id: '1' } }
}
} }
] }
assert_equal(expected, actual[:data]) ]
end
assert_equal(expected, @adapter.serializable_hash[:data])
end
def test_limiting_fields
actual = ActiveModel::SerializableResource.new(
[@first_post, @second_post], adapter: :json_api,
fields: { posts: %w(title comments blog author) })
.serializable_hash
expected = [
{
id: '1',
type: 'posts',
attributes: {
title: 'Hello!!'
},
relationships: {
comments: { data: [] },
blog: { data: { type: 'blogs', id: '999' } },
author: { data: { type: 'authors', id: '1' } }
}
},
{
id: '2',
type: 'posts',
attributes: {
title: 'New Post'
},
relationships: {
comments: { data: [] },
blog: { data: { type: 'blogs', id: '999' } },
author: { data: { type: 'authors', id: '1' } }
}
}
]
assert_equal(expected, actual[:data])
end end
end end
end end

View File

@ -1,87 +1,85 @@
require 'test_helper' require 'test_helper'
module ActiveModel module ActiveModelSerializers
class Serializer module Adapter
module Adapter class JsonApi
class JsonApi class FieldsTest < ActiveSupport::TestCase
class FieldsTest < ActiveSupport::TestCase Post = Class.new(::Model)
Post = Class.new(::Model) class PostSerializer < ActiveModel::Serializer
class PostSerializer < ActiveModel::Serializer type 'posts'
type 'posts' attributes :title, :body
attributes :title, :body belongs_to :author
belongs_to :author has_many :comments
has_many :comments end
end
Author = Class.new(::Model) Author = Class.new(::Model)
class AuthorSerializer < ActiveModel::Serializer class AuthorSerializer < ActiveModel::Serializer
type 'authors' type 'authors'
attributes :name, :birthday attributes :name, :birthday
end end
Comment = Class.new(::Model) Comment = Class.new(::Model)
class CommentSerializer < ActiveModel::Serializer class CommentSerializer < ActiveModel::Serializer
type 'comments' type 'comments'
attributes :body attributes :body
belongs_to :author belongs_to :author
end end
def setup def setup
@author = Author.new(id: 1, name: 'Lucas', birthday: '10.01.1990') @author = Author.new(id: 1, name: 'Lucas', birthday: '10.01.1990')
@comment1 = Comment.new(id: 7, body: 'cool', author: @author) @comment1 = Comment.new(id: 7, body: 'cool', author: @author)
@comment2 = Comment.new(id: 12, body: 'awesome', author: @author) @comment2 = Comment.new(id: 12, body: 'awesome', author: @author)
@post = Post.new(id: 1337, title: 'Title 1', body: 'Body 1', @post = Post.new(id: 1337, title: 'Title 1', body: 'Body 1',
author: @author, comments: [@comment1, @comment2]) author: @author, comments: [@comment1, @comment2])
@comment1.post = @post @comment1.post = @post
@comment2.post = @post @comment2.post = @post
end end
def test_fields_attributes def test_fields_attributes
fields = { posts: [:title] } fields = { posts: [:title] }
hash = serializable(@post, adapter: :json_api, fields: fields).serializable_hash hash = serializable(@post, adapter: :json_api, fields: fields).serializable_hash
expected = { expected = {
title: 'Title 1' title: 'Title 1'
} }
assert_equal(expected, hash[:data][:attributes]) assert_equal(expected, hash[:data][:attributes])
end end
def test_fields_relationships def test_fields_relationships
fields = { posts: [:author] } fields = { posts: [:author] }
hash = serializable(@post, adapter: :json_api, fields: fields).serializable_hash hash = serializable(@post, adapter: :json_api, fields: fields).serializable_hash
expected = { expected = {
author: { author: {
data: { data: {
type: 'authors', type: 'authors',
id: '1' id: '1'
}
} }
} }
}
assert_equal(expected, hash[:data][:relationships]) assert_equal(expected, hash[:data][:relationships])
end end
def test_fields_included def test_fields_included
fields = { posts: [:author], comments: [:body] } fields = { posts: [:author], comments: [:body] }
hash = serializable(@post, adapter: :json_api, fields: fields, include: 'comments').serializable_hash hash = serializable(@post, adapter: :json_api, fields: fields, include: 'comments').serializable_hash
expected = [ expected = [
{ {
type: 'comments', type: 'comments',
id: '7', id: '7',
attributes: { attributes: {
body: 'cool' body: 'cool'
}
}, {
type: 'comments',
id: '12',
attributes: {
body: 'awesome'
}
} }
] }, {
type: 'comments',
id: '12',
attributes: {
body: 'awesome'
}
}
]
assert_equal(expected, hash[:included]) assert_equal(expected, hash[:included])
end
end end
end end
end end

View File

@ -1,43 +1,41 @@
require 'test_helper' require 'test_helper'
module ActiveModel module ActiveModelSerializers
class Serializer module Adapter
module Adapter class JsonApi
class JsonApi class HasManyEmbedIdsTest < ActiveSupport::TestCase
class HasManyEmbedIdsTest < ActiveSupport::TestCase def setup
def setup @author = Author.new(name: 'Steve K.')
@author = Author.new(name: 'Steve K.') @author.bio = nil
@author.bio = nil @author.roles = nil
@author.roles = nil @first_post = Post.new(id: 1, title: 'Hello!!', body: 'Hello, world!!')
@first_post = Post.new(id: 1, title: 'Hello!!', body: 'Hello, world!!') @second_post = Post.new(id: 2, title: 'New Post', body: 'Body')
@second_post = Post.new(id: 2, title: 'New Post', body: 'Body') @author.posts = [@first_post, @second_post]
@author.posts = [@first_post, @second_post] @first_post.author = @author
@first_post.author = @author @second_post.author = @author
@second_post.author = @author @first_post.comments = []
@first_post.comments = [] @second_post.comments = []
@second_post.comments = [] @blog = Blog.new(id: 23, name: 'AMS Blog')
@blog = Blog.new(id: 23, name: 'AMS Blog') @first_post.blog = @blog
@first_post.blog = @blog @second_post.blog = nil
@second_post.blog = nil
@serializer = AuthorSerializer.new(@author) @serializer = AuthorSerializer.new(@author)
@adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer) @adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer)
end end
def test_includes_comment_ids def test_includes_comment_ids
expected = { expected = {
data: [ data: [
{ type: 'posts', id: '1' }, { type: 'posts', id: '1' },
{ type: 'posts', id: '2' } { type: 'posts', id: '2' }
] ]
} }
assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:posts]) assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:posts])
end end
def test_no_includes_linked_comments def test_no_includes_linked_comments
assert_nil @adapter.serializable_hash[:linked] assert_nil @adapter.serializable_hash[:linked]
end
end end
end end
end end

View File

@ -1,96 +1,94 @@
require 'test_helper' require 'test_helper'
module ActiveModel module ActiveModelSerializers
class Serializer module Adapter
module Adapter class JsonApi
class JsonApi # Test 'has_many :assocs, serializer: AssocXSerializer'
# Test 'has_many :assocs, serializer: AssocXSerializer' class HasManyExplicitSerializerTest < ActiveSupport::TestCase
class HasManyExplicitSerializerTest < ActiveSupport::TestCase def setup
def setup @post = Post.new(title: 'New Post', body: 'Body')
@post = Post.new(title: 'New Post', body: 'Body') @author = Author.new(name: 'Jane Blogger')
@author = Author.new(name: 'Jane Blogger') @author.posts = [@post]
@author.posts = [@post] @post.author = @author
@post.author = @author @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')
@first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT')
@second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT') @post.comments = [@first_comment, @second_comment]
@post.comments = [@first_comment, @second_comment] @first_comment.post = @post
@first_comment.post = @post @first_comment.author = nil
@first_comment.author = nil @second_comment.post = @post
@second_comment.post = @post @second_comment.author = nil
@second_comment.author = nil @blog = Blog.new(id: 23, name: 'AMS Blog')
@blog = Blog.new(id: 23, name: 'AMS Blog') @post.blog = @blog
@post.blog = @blog
@serializer = PostPreviewSerializer.new(@post) @serializer = PostPreviewSerializer.new(@post)
@adapter = ActiveModel::Serializer::Adapter::JsonApi.new( @adapter = ActiveModelSerializers::Adapter::JsonApi.new(
@serializer, @serializer,
include: [:comments, :author] include: [:comments, :author]
) )
end end
def test_includes_comment_ids def test_includes_comment_ids
expected = { expected = {
data: [ data: [
{ type: 'comments', id: '1' }, { type: 'comments', id: '1' },
{ type: 'comments', id: '2' } { type: 'comments', id: '2' }
]
}
assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:comments])
end
def test_includes_linked_data
# If CommentPreviewSerializer is applied correctly the body text will not be present in the output
expected = [
{
id: '1',
type: 'comments',
relationships: {
post: { data: { type: 'posts', id: @post.id.to_s } }
}
},
{
id: '2',
type: 'comments',
relationships: {
post: { data: { type: 'posts', id: @post.id.to_s } }
}
},
{
id: @author.id.to_s,
type: 'authors',
relationships: {
posts: { data: [{ type: 'posts', id: @post.id.to_s }] }
}
}
] ]
}
assert_equal(expected, @adapter.serializable_hash[:included]) assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:comments])
end end
def test_includes_author_id def test_includes_linked_data
expected = { # If CommentPreviewSerializer is applied correctly the body text will not be present in the output
data: { type: 'authors', id: @author.id.to_s } expected = [
{
id: '1',
type: 'comments',
relationships: {
post: { data: { type: 'posts', id: @post.id.to_s } }
}
},
{
id: '2',
type: 'comments',
relationships: {
post: { data: { type: 'posts', id: @post.id.to_s } }
}
},
{
id: @author.id.to_s,
type: 'authors',
relationships: {
posts: { data: [{ type: 'posts', id: @post.id.to_s }] }
}
} }
]
assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:author]) assert_equal(expected, @adapter.serializable_hash[:included])
end end
def test_explicit_serializer_with_null_resource def test_includes_author_id
@post.author = nil expected = {
data: { type: 'authors', id: @author.id.to_s }
}
expected = { data: nil } assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:author])
end
assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:author]) def test_explicit_serializer_with_null_resource
end @post.author = nil
def test_explicit_serializer_with_null_collection expected = { data: nil }
@post.comments = []
expected = { data: [] } assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:author])
end
assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:comments]) def test_explicit_serializer_with_null_collection
end @post.comments = []
expected = { data: [] }
assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:comments])
end end
end end
end end

View File

@ -1,143 +1,141 @@
require 'test_helper' require 'test_helper'
module ActiveModel module ActiveModelSerializers
class Serializer module Adapter
module Adapter class JsonApi
class JsonApi class HasManyTest < ActiveSupport::TestCase
class HasManyTest < ActiveSupport::TestCase def setup
def setup ActionController::Base.cache_store.clear
ActionController::Base.cache_store.clear @author = Author.new(id: 1, name: 'Steve K.')
@author = Author.new(id: 1, name: 'Steve K.') @author.posts = []
@author.posts = [] @author.bio = nil
@author.bio = nil @post = Post.new(id: 1, title: 'New Post', body: 'Body')
@post = Post.new(id: 1, title: 'New Post', body: 'Body') @post_without_comments = Post.new(id: 2, title: 'Second Post', body: 'Second')
@post_without_comments = Post.new(id: 2, title: 'Second Post', body: 'Second') @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')
@first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') @first_comment.author = nil
@first_comment.author = nil @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT')
@second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT') @second_comment.author = nil
@second_comment.author = nil @post.comments = [@first_comment, @second_comment]
@post.comments = [@first_comment, @second_comment] @post_without_comments.comments = []
@post_without_comments.comments = [] @first_comment.post = @post
@first_comment.post = @post @second_comment.post = @post
@second_comment.post = @post @post.author = @author
@post.author = @author @post_without_comments.author = nil
@post_without_comments.author = nil @blog = Blog.new(id: 1, name: 'My Blog!!')
@blog = Blog.new(id: 1, name: 'My Blog!!') @blog.writer = @author
@blog.writer = @author @blog.articles = [@post]
@blog.articles = [@post] @post.blog = @blog
@post.blog = @blog @post_without_comments.blog = nil
@post_without_comments.blog = nil @tag = Tag.new(id: 1, name: '#hash_tag')
@tag = Tag.new(id: 1, name: '#hash_tag') @post.tags = [@tag]
@post.tags = [@tag] @serializer = PostSerializer.new(@post)
@serializer = PostSerializer.new(@post) @adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer)
@adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer)
@virtual_value = VirtualValue.new(id: 1) @virtual_value = VirtualValue.new(id: 1)
end end
def test_includes_comment_ids def test_includes_comment_ids
expected = { data: [{ type: 'comments', id: '1' }, { type: 'comments', id: '2' }] } expected = { data: [{ type: 'comments', id: '1' }, { type: 'comments', id: '2' }] }
assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:comments]) assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:comments])
end end
def test_includes_linked_comments def test_includes_linked_comments
@adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, include: [:comments]) @adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer, include: [:comments])
expected = [{ expected = [{
id: '1', id: '1',
type: 'comments', type: 'comments',
attributes: { attributes: {
body: 'ZOMG A COMMENT' body: 'ZOMG A COMMENT'
}, },
relationships: { relationships: {
post: { data: { type: 'posts', id: '1' } }, post: { data: { type: 'posts', id: '1' } },
author: { data: nil } author: { data: nil }
}
}, {
id: '2',
type: 'comments',
attributes: {
body: 'ZOMG ANOTHER COMMENT'
},
relationships: {
post: { data: { type: 'posts', id: '1' } },
author: { data: nil }
}
}]
assert_equal expected, @adapter.serializable_hash[:included]
end
def test_limit_fields_of_linked_comments
@adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, include: [:comments], fields: { comment: [:id, :post, :author] })
expected = [{
id: '1',
type: 'comments',
relationships: {
post: { data: { type: 'posts', id: '1' } },
author: { data: nil }
}
}, {
id: '2',
type: 'comments',
relationships: {
post: { data: { type: 'posts', id: '1' } },
author: { data: nil }
}
}]
assert_equal expected, @adapter.serializable_hash[:included]
end
def test_no_include_linked_if_comments_is_empty
serializer = PostSerializer.new(@post_without_comments)
adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer)
assert_nil adapter.serializable_hash[:linked]
end
def test_include_type_for_association_when_different_than_name
serializer = BlogSerializer.new(@blog)
adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer)
actual = adapter.serializable_hash[:data][:relationships][:articles]
expected = {
data: [{
type: 'posts',
id: '1'
}]
} }
assert_equal expected, actual }, {
end id: '2',
type: 'comments',
attributes: {
body: 'ZOMG ANOTHER COMMENT'
},
relationships: {
post: { data: { type: 'posts', id: '1' } },
author: { data: nil }
}
}]
assert_equal expected, @adapter.serializable_hash[:included]
end
def test_has_many_with_no_serializer def test_limit_fields_of_linked_comments
serializer = PostWithTagsSerializer.new(@post) @adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer, include: [:comments], fields: { comment: [:id, :post, :author] })
adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer) expected = [{
id: '1',
type: 'comments',
relationships: {
post: { data: { type: 'posts', id: '1' } },
author: { data: nil }
}
}, {
id: '2',
type: 'comments',
relationships: {
post: { data: { type: 'posts', id: '1' } },
author: { data: nil }
}
}]
assert_equal expected, @adapter.serializable_hash[:included]
end
assert_equal({ def test_no_include_linked_if_comments_is_empty
data: { serializer = PostSerializer.new(@post_without_comments)
id: '1', adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
type: 'posts',
relationships: { assert_nil adapter.serializable_hash[:linked]
tags: { data: [@tag.as_json] } end
}
def test_include_type_for_association_when_different_than_name
serializer = BlogSerializer.new(@blog)
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
actual = adapter.serializable_hash[:data][:relationships][:articles]
expected = {
data: [{
type: 'posts',
id: '1'
}]
}
assert_equal expected, actual
end
def test_has_many_with_no_serializer
serializer = PostWithTagsSerializer.new(@post)
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
assert_equal({
data: {
id: '1',
type: 'posts',
relationships: {
tags: { data: [@tag.as_json] }
} }
}, adapter.serializable_hash) }
end }, adapter.serializable_hash)
end
def test_has_many_with_virtual_value def test_has_many_with_virtual_value
serializer = VirtualValueSerializer.new(@virtual_value) serializer = VirtualValueSerializer.new(@virtual_value)
adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer) adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
assert_equal({ assert_equal({
data: { data: {
id: '1', id: '1',
type: 'virtual_values', type: 'virtual_values',
relationships: { relationships: {
maker: { data: { id: 1 } }, maker: { data: { id: 1 } },
reviews: { data: [{ id: 1 }, { id: 2 }] } reviews: { data: [{ id: 1 }, { id: 2 }] }
}
} }
}, adapter.serializable_hash) }
end }, adapter.serializable_hash)
end end
end end
end end

View File

@ -1,79 +1,77 @@
require 'test_helper' require 'test_helper'
module ActiveModel module ActiveModelSerializers
class Serializer module Adapter
module Adapter class JsonApi
class JsonApi class HasOneTest < ActiveSupport::TestCase
class HasOneTest < ActiveSupport::TestCase def setup
def setup @author = Author.new(id: 1, name: 'Steve K.')
@author = Author.new(id: 1, name: 'Steve K.') @bio = Bio.new(id: 43, content: 'AMS Contributor')
@bio = Bio.new(id: 43, content: 'AMS Contributor') @author.bio = @bio
@author.bio = @bio @bio.author = @author
@bio.author = @author @post = Post.new(id: 42, title: 'New Post', body: 'Body')
@post = Post.new(id: 42, title: 'New Post', body: 'Body') @anonymous_post = Post.new(id: 43, title: 'Hello!!', body: 'Hello, world!!')
@anonymous_post = Post.new(id: 43, title: 'Hello!!', body: 'Hello, world!!') @comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')
@comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') @post.comments = [@comment]
@post.comments = [@comment] @anonymous_post.comments = []
@anonymous_post.comments = [] @comment.post = @post
@comment.post = @post @comment.author = nil
@comment.author = nil @post.author = @author
@post.author = @author @anonymous_post.author = nil
@anonymous_post.author = nil @blog = Blog.new(id: 1, name: 'My Blog!!')
@blog = Blog.new(id: 1, name: 'My Blog!!') @blog.writer = @author
@blog.writer = @author @blog.articles = [@post, @anonymous_post]
@blog.articles = [@post, @anonymous_post] @author.posts = []
@author.posts = [] @author.roles = []
@author.roles = []
@virtual_value = VirtualValue.new(id: 1) @virtual_value = VirtualValue.new(id: 1)
@serializer = AuthorSerializer.new(@author) @serializer = AuthorSerializer.new(@author)
@adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, include: [:bio, :posts]) @adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer, include: [:bio, :posts])
end end
def test_includes_bio_id def test_includes_bio_id
expected = { data: { type: 'bios', id: '43' } } expected = { data: { type: 'bios', id: '43' } }
assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:bio]) assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:bio])
end end
def test_includes_linked_bio def test_includes_linked_bio
@adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, include: [:bio]) @adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer, include: [:bio])
expected = [ expected = [
{ {
id: '43', id: '43',
type: 'bios', type: 'bios',
attributes: { attributes: {
content: 'AMS Contributor', content: 'AMS Contributor',
rating: nil rating: nil
}, },
relationships: { relationships: {
author: { data: { type: 'authors', id: '1' } } author: { data: { type: 'authors', id: '1' } }
}
}
]
assert_equal(expected, @adapter.serializable_hash[:included])
end
def test_has_one_with_virtual_value
serializer = VirtualValueSerializer.new(@virtual_value)
adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer)
expected = {
data: {
id: '1',
type: 'virtual_values',
relationships: {
maker: { data: { id: 1 } },
reviews: { data: [{ id: 1 }, { id: 2 }] }
}
} }
} }
]
assert_equal(expected, adapter.serializable_hash) assert_equal(expected, @adapter.serializable_hash[:included])
end end
def test_has_one_with_virtual_value
serializer = VirtualValueSerializer.new(@virtual_value)
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
expected = {
data: {
id: '1',
type: 'virtual_values',
relationships: {
maker: { data: { id: 1 } },
reviews: { data: [{ id: 1 }, { id: 2 }] }
}
}
}
assert_equal(expected, adapter.serializable_hash)
end end
end end
end end

View File

@ -1,36 +1,34 @@
require 'test_helper' require 'test_helper'
module ActiveModel module ActiveModelSerializers
class Serializer module Adapter
module Adapter class JsonApiTest < ActiveSupport::TestCase
class JsonApiTest < ActiveSupport::TestCase def setup
def setup ActionController::Base.cache_store.clear
ActionController::Base.cache_store.clear @author = Author.new(id: 1, name: 'Steve K.')
@author = Author.new(id: 1, name: 'Steve K.') @post = Post.new(id: 1, title: 'New Post', body: 'Body')
@post = Post.new(id: 1, title: 'New Post', body: 'Body') @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')
@first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT')
@second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT') @post.comments = [@first_comment, @second_comment]
@post.comments = [@first_comment, @second_comment] @first_comment.post = @post
@first_comment.post = @post @second_comment.post = @post
@second_comment.post = @post @post.author = @author
@post.author = @author @blog = Blog.new(id: 1, name: 'My Blog!!')
@blog = Blog.new(id: 1, name: 'My Blog!!') @post.blog = @blog
@post.blog = @blog end
end
def test_custom_keys def test_custom_keys
serializer = PostWithCustomKeysSerializer.new(@post) serializer = PostWithCustomKeysSerializer.new(@post)
adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer) adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
assert_equal({ assert_equal({
reviews: { data: [ reviews: { data: [
{ type: 'comments', id: '1' }, { type: 'comments', id: '1' },
{ type: 'comments', id: '2' } { type: 'comments', id: '2' }
] }, ] },
writer: { data: { type: 'authors', id: '1' } }, writer: { data: { type: 'authors', id: '1' } },
site: { data: { type: 'blogs', id: '1' } } site: { data: { type: 'blogs', id: '1' } }
}, adapter.serializable_hash[:data][:relationships]) }, adapter.serializable_hash[:data][:relationships])
end
end end
end end
end end

View File

@ -5,168 +5,106 @@ class NestedPostSerializer < ActiveModel::Serializer
has_many :nested_posts has_many :nested_posts
end end
module ActiveModel module ActiveModelSerializers
class Serializer module Adapter
module Adapter class JsonApi
class JsonApi class LinkedTest < ActiveSupport::TestCase
class LinkedTest < ActiveSupport::TestCase def setup
def setup @author1 = Author.new(id: 1, name: 'Steve K.')
@author1 = Author.new(id: 1, name: 'Steve K.') @author2 = Author.new(id: 2, name: 'Tenderlove')
@author2 = Author.new(id: 2, name: 'Tenderlove') @bio1 = Bio.new(id: 1, content: 'AMS Contributor')
@bio1 = Bio.new(id: 1, content: 'AMS Contributor') @bio2 = Bio.new(id: 2, content: 'Rails Contributor')
@bio2 = Bio.new(id: 2, content: 'Rails Contributor') @first_post = Post.new(id: 10, title: 'Hello!!', body: 'Hello, world!!')
@first_post = Post.new(id: 10, title: 'Hello!!', body: 'Hello, world!!') @second_post = Post.new(id: 20, title: 'New Post', body: 'Body')
@second_post = Post.new(id: 20, title: 'New Post', body: 'Body') @third_post = Post.new(id: 30, title: 'Yet Another Post', body: 'Body')
@third_post = Post.new(id: 30, title: 'Yet Another Post', body: 'Body') @blog = Blog.new({ name: 'AMS Blog' })
@blog = Blog.new({ name: 'AMS Blog' }) @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')
@first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT')
@second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT') @first_post.blog = @blog
@first_post.blog = @blog @second_post.blog = @blog
@second_post.blog = @blog @third_post.blog = nil
@third_post.blog = nil @first_post.comments = [@first_comment, @second_comment]
@first_post.comments = [@first_comment, @second_comment] @second_post.comments = []
@second_post.comments = [] @third_post.comments = []
@third_post.comments = [] @first_post.author = @author1
@first_post.author = @author1 @second_post.author = @author2
@second_post.author = @author2 @third_post.author = @author1
@third_post.author = @author1 @first_comment.post = @first_post
@first_comment.post = @first_post @first_comment.author = nil
@first_comment.author = nil @second_comment.post = @first_post
@second_comment.post = @first_post @second_comment.author = nil
@second_comment.author = nil @author1.posts = [@first_post, @third_post]
@author1.posts = [@first_post, @third_post] @author1.bio = @bio1
@author1.bio = @bio1 @author1.roles = []
@author1.roles = [] @author2.posts = [@second_post]
@author2.posts = [@second_post] @author2.bio = @bio2
@author2.bio = @bio2 @author2.roles = []
@author2.roles = [] @bio1.author = @author1
@bio1.author = @author1 @bio2.author = @author2
@bio2.author = @author2 end
end
def test_include_multiple_posts_and_linked_array def test_include_multiple_posts_and_linked_array
serializer = CollectionSerializer.new([@first_post, @second_post]) serializer = ActiveModel::Serializer::CollectionSerializer.new([@first_post, @second_post])
adapter = ActiveModel::Serializer::Adapter::JsonApi.new( adapter = ActiveModelSerializers::Adapter::JsonApi.new(
serializer, serializer,
include: [:comments, author: [:bio]] include: [:comments, author: [:bio]]
) )
alt_adapter = ActiveModel::Serializer::Adapter::JsonApi.new( alt_adapter = ActiveModelSerializers::Adapter::JsonApi.new(
serializer, serializer,
include: [:comments, author: [:bio]] include: [:comments, author: [:bio]]
) )
expected = { expected = {
data: [ data: [
{
id: '10',
type: 'posts',
attributes: {
title: 'Hello!!',
body: 'Hello, world!!'
},
relationships: {
comments: { data: [{ type: 'comments', id: '1' }, { type: 'comments', id: '2' }] },
blog: { data: { type: 'blogs', id: '999' } },
author: { data: { type: 'authors', id: '1' } }
}
},
{
id: '20',
type: 'posts',
attributes: {
title: 'New Post',
body: 'Body'
},
relationships: {
comments: { data: [] },
blog: { data: { type: 'blogs', id: '999' } },
author: { data: { type: 'authors', id: '2' } }
}
}
],
included: [
{
id: '1',
type: 'comments',
attributes: {
body: 'ZOMG A COMMENT'
},
relationships: {
post: { data: { type: 'posts', id: '10' } },
author: { data: nil }
}
}, {
id: '2',
type: 'comments',
attributes: {
body: 'ZOMG ANOTHER COMMENT',
},
relationships: {
post: { data: { type: 'posts', id: '10' } },
author: { data: nil }
}
}, {
id: '1',
type: 'authors',
attributes: {
name: 'Steve K.'
},
relationships: {
posts: { data: [{ type: 'posts', id: '10' }, { type: 'posts', id: '30' }] },
roles: { data: [] },
bio: { data: { type: 'bios', id: '1' } }
}
}, {
id: '1',
type: 'bios',
attributes: {
content: 'AMS Contributor',
rating: nil
},
relationships: {
author: { data: { type: 'authors', id: '1' } }
}
}, {
id: '2',
type: 'authors',
attributes: {
name: 'Tenderlove'
},
relationships: {
posts: { data: [{ type: 'posts', id: '20' }] },
roles: { data: [] },
bio: { data: { type: 'bios', id: '2' } }
}
}, {
id: '2',
type: 'bios',
attributes: {
rating: nil,
content: 'Rails Contributor',
},
relationships: {
author: { data: { type: 'authors', id: '2' } }
}
}
]
}
assert_equal expected, adapter.serializable_hash
assert_equal expected, alt_adapter.serializable_hash
end
def test_include_multiple_posts_and_linked
serializer = BioSerializer.new @bio1
adapter = ActiveModel::Serializer::Adapter::JsonApi.new(
serializer,
include: [author: [:posts]]
)
alt_adapter = ActiveModel::Serializer::Adapter::JsonApi.new(
serializer,
include: [author: [:posts]]
)
expected = [
{ {
id: '10',
type: 'posts',
attributes: {
title: 'Hello!!',
body: 'Hello, world!!'
},
relationships: {
comments: { data: [{ type: 'comments', id: '1' }, { type: 'comments', id: '2' }] },
blog: { data: { type: 'blogs', id: '999' } },
author: { data: { type: 'authors', id: '1' } }
}
},
{
id: '20',
type: 'posts',
attributes: {
title: 'New Post',
body: 'Body'
},
relationships: {
comments: { data: [] },
blog: { data: { type: 'blogs', id: '999' } },
author: { data: { type: 'authors', id: '2' } }
}
}
],
included: [
{
id: '1',
type: 'comments',
attributes: {
body: 'ZOMG A COMMENT'
},
relationships: {
post: { data: { type: 'posts', id: '10' } },
author: { data: nil }
}
}, {
id: '2',
type: 'comments',
attributes: {
body: 'ZOMG ANOTHER COMMENT',
},
relationships: {
post: { data: { type: 'posts', id: '10' } },
author: { data: nil }
}
}, {
id: '1', id: '1',
type: 'authors', type: 'authors',
attributes: { attributes: {
@ -178,215 +116,275 @@ module ActiveModel
bio: { data: { type: 'bios', id: '1' } } bio: { data: { type: 'bios', id: '1' } }
} }
}, { }, {
id: '10', id: '1',
type: 'posts', type: 'bios',
attributes: { attributes: {
title: 'Hello!!', content: 'AMS Contributor',
body: 'Hello, world!!' rating: nil
}, },
relationships: { relationships: {
comments: { data: [{ type: 'comments', id: '1' }, { type: 'comments', id: '2' }] },
blog: { data: { type: 'blogs', id: '999' } },
author: { data: { type: 'authors', id: '1' } } author: { data: { type: 'authors', id: '1' } }
} }
}, { }, {
id: '30', id: '2',
type: 'posts', type: 'authors',
attributes: { attributes: {
title: 'Yet Another Post', name: 'Tenderlove'
body: 'Body'
}, },
relationships: { relationships: {
comments: { data: [] }, posts: { data: [{ type: 'posts', id: '20' }] },
blog: { data: { type: 'blogs', id: '999' } }, roles: { data: [] },
author: { data: { type: 'authors', id: '1' } } bio: { data: { type: 'bios', id: '2' } }
}
}, {
id: '2',
type: 'bios',
attributes: {
rating: nil,
content: 'Rails Contributor',
},
relationships: {
author: { data: { type: 'authors', id: '2' } }
} }
} }
] ]
}
assert_equal expected, adapter.serializable_hash[:included] assert_equal expected, adapter.serializable_hash
assert_equal expected, alt_adapter.serializable_hash[:included] assert_equal expected, alt_adapter.serializable_hash
end
def test_underscore_model_namespace_for_linked_resource_type
spammy_post = Post.new(id: 123)
spammy_post.related = [Spam::UnrelatedLink.new(id: 456)]
serializer = SpammyPostSerializer.new(spammy_post)
adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer)
relationships = adapter.serializable_hash[:data][:relationships]
expected = {
related: {
data: [{
type: 'spam_unrelated_links',
id: '456'
}]
}
}
assert_equal expected, relationships
end
def test_multiple_references_to_same_resource
serializer = CollectionSerializer.new([@first_comment, @second_comment])
adapter = ActiveModel::Serializer::Adapter::JsonApi.new(
serializer,
include: [:post]
)
expected = [
{
id: '10',
type: 'posts',
attributes: {
title: 'Hello!!',
body: 'Hello, world!!'
},
relationships: {
comments: {
data: [{ type: 'comments', id: '1' }, { type: 'comments', id: '2' }]
},
blog: {
data: { type: 'blogs', id: '999' }
},
author: {
data: { type: 'authors', id: '1' }
}
}
}
]
assert_equal expected, adapter.serializable_hash[:included]
end
def test_nil_link_with_specified_serializer
@first_post.author = nil
serializer = PostPreviewSerializer.new(@first_post)
adapter = ActiveModel::Serializer::Adapter::JsonApi.new(
serializer,
include: [:author]
)
expected = {
data: {
id: '10',
type: 'posts',
attributes: {
title: 'Hello!!',
body: 'Hello, world!!'
},
relationships: {
comments: { data: [{ type: 'comments', id: '1' }, { type: 'comments', id: '2' }] },
author: { data: nil }
}
}
}
assert_equal expected, adapter.serializable_hash
end
end end
class NoDuplicatesTest < ActiveSupport::TestCase def test_include_multiple_posts_and_linked
Post = Class.new(::Model) serializer = BioSerializer.new @bio1
Author = Class.new(::Model) adapter = ActiveModelSerializers::Adapter::JsonApi.new(
serializer,
include: [author: [:posts]]
)
alt_adapter = ActiveModelSerializers::Adapter::JsonApi.new(
serializer,
include: [author: [:posts]]
)
class PostSerializer < ActiveModel::Serializer expected = [
type 'posts' {
belongs_to :author id: '1',
end type: 'authors',
attributes: {
class AuthorSerializer < ActiveModel::Serializer name: 'Steve K.'
type 'authors'
has_many :posts
end
def setup
@author = Author.new(id: 1, posts: [], roles: [], bio: nil)
@post1 = Post.new(id: 1, author: @author)
@post2 = Post.new(id: 2, author: @author)
@author.posts << @post1
@author.posts << @post2
@nestedpost1 = ::NestedPost.new(id: 1, nested_posts: [])
@nestedpost2 = ::NestedPost.new(id: 2, nested_posts: [])
@nestedpost1.nested_posts << @nestedpost1
@nestedpost1.nested_posts << @nestedpost2
@nestedpost2.nested_posts << @nestedpost1
@nestedpost2.nested_posts << @nestedpost2
end
def test_no_duplicates
hash = ActiveModel::SerializableResource.new(@post1, adapter: :json_api,
include: '*.*')
.serializable_hash
expected = [
{
type: 'authors', id: '1',
relationships: {
posts: {
data: [
{ type: 'posts', id: '1' },
{ type: 'posts', id: '2' }
]
}
}
}, },
{
type: 'posts', id: '2',
relationships: {
author: {
data: { type: 'authors', id: '1' }
}
}
}
]
assert_equal(expected, hash[:included])
end
def test_no_duplicates_collection
hash = ActiveModel::SerializableResource.new(
[@post1, @post2], adapter: :json_api,
include: '*.*')
.serializable_hash
expected = [
{
type: 'authors', id: '1',
relationships: {
posts: {
data: [
{ type: 'posts', id: '1' },
{ type: 'posts', id: '2' }
]
}
}
}
]
assert_equal(expected, hash[:included])
end
def test_no_duplicates_global
hash = ActiveModel::SerializableResource.new(
@nestedpost1,
adapter: :json_api,
include: '*').serializable_hash
expected = [
type: 'nested_posts', id: '2',
relationships: { relationships: {
nested_posts: { posts: { data: [{ type: 'posts', id: '10' }, { type: 'posts', id: '30' }] },
roles: { data: [] },
bio: { data: { type: 'bios', id: '1' } }
}
}, {
id: '10',
type: 'posts',
attributes: {
title: 'Hello!!',
body: 'Hello, world!!'
},
relationships: {
comments: { data: [{ type: 'comments', id: '1' }, { type: 'comments', id: '2' }] },
blog: { data: { type: 'blogs', id: '999' } },
author: { data: { type: 'authors', id: '1' } }
}
}, {
id: '30',
type: 'posts',
attributes: {
title: 'Yet Another Post',
body: 'Body'
},
relationships: {
comments: { data: [] },
blog: { data: { type: 'blogs', id: '999' } },
author: { data: { type: 'authors', id: '1' } }
}
}
]
assert_equal expected, adapter.serializable_hash[:included]
assert_equal expected, alt_adapter.serializable_hash[:included]
end
def test_underscore_model_namespace_for_linked_resource_type
spammy_post = Post.new(id: 123)
spammy_post.related = [Spam::UnrelatedLink.new(id: 456)]
serializer = SpammyPostSerializer.new(spammy_post)
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
relationships = adapter.serializable_hash[:data][:relationships]
expected = {
related: {
data: [{
type: 'spam_unrelated_links',
id: '456'
}]
}
}
assert_equal expected, relationships
end
def test_multiple_references_to_same_resource
serializer = ActiveModel::Serializer::CollectionSerializer.new([@first_comment, @second_comment])
adapter = ActiveModelSerializers::Adapter::JsonApi.new(
serializer,
include: [:post]
)
expected = [
{
id: '10',
type: 'posts',
attributes: {
title: 'Hello!!',
body: 'Hello, world!!'
},
relationships: {
comments: {
data: [{ type: 'comments', id: '1' }, { type: 'comments', id: '2' }]
},
blog: {
data: { type: 'blogs', id: '999' }
},
author: {
data: { type: 'authors', id: '1' }
}
}
}
]
assert_equal expected, adapter.serializable_hash[:included]
end
def test_nil_link_with_specified_serializer
@first_post.author = nil
serializer = PostPreviewSerializer.new(@first_post)
adapter = ActiveModelSerializers::Adapter::JsonApi.new(
serializer,
include: [:author]
)
expected = {
data: {
id: '10',
type: 'posts',
attributes: {
title: 'Hello!!',
body: 'Hello, world!!'
},
relationships: {
comments: { data: [{ type: 'comments', id: '1' }, { type: 'comments', id: '2' }] },
author: { data: nil }
}
}
}
assert_equal expected, adapter.serializable_hash
end
end
class NoDuplicatesTest < ActiveSupport::TestCase
Post = Class.new(::Model)
Author = Class.new(::Model)
class PostSerializer < ActiveModel::Serializer
type 'posts'
belongs_to :author
end
class AuthorSerializer < ActiveModel::Serializer
type 'authors'
has_many :posts
end
def setup
@author = Author.new(id: 1, posts: [], roles: [], bio: nil)
@post1 = Post.new(id: 1, author: @author)
@post2 = Post.new(id: 2, author: @author)
@author.posts << @post1
@author.posts << @post2
@nestedpost1 = ::NestedPost.new(id: 1, nested_posts: [])
@nestedpost2 = ::NestedPost.new(id: 2, nested_posts: [])
@nestedpost1.nested_posts << @nestedpost1
@nestedpost1.nested_posts << @nestedpost2
@nestedpost2.nested_posts << @nestedpost1
@nestedpost2.nested_posts << @nestedpost2
end
def test_no_duplicates
hash = ActiveModel::SerializableResource.new(@post1, adapter: :json_api,
include: '*.*')
.serializable_hash
expected = [
{
type: 'authors', id: '1',
relationships: {
posts: {
data: [ data: [
{ type: 'nested_posts', id: '1' }, { type: 'posts', id: '1' },
{ type: 'nested_posts', id: '2' } { type: 'posts', id: '2' }
] ]
} }
} }
] },
assert_equal(expected, hash[:included]) {
end type: 'posts', id: '2',
relationships: {
author: {
data: { type: 'authors', id: '1' }
}
}
}
]
assert_equal(expected, hash[:included])
end
def test_no_duplicates_collection_global def test_no_duplicates_collection
hash = ActiveModel::SerializableResource.new( hash = ActiveModel::SerializableResource.new(
[@nestedpost1, @nestedpost2], [@post1, @post2], adapter: :json_api,
adapter: :json_api, include: '*.*')
include: '*').serializable_hash .serializable_hash
assert_nil(hash[:included]) expected = [
end {
type: 'authors', id: '1',
relationships: {
posts: {
data: [
{ type: 'posts', id: '1' },
{ type: 'posts', id: '2' }
]
}
}
}
]
assert_equal(expected, hash[:included])
end
def test_no_duplicates_global
hash = ActiveModel::SerializableResource.new(
@nestedpost1,
adapter: :json_api,
include: '*').serializable_hash
expected = [
type: 'nested_posts', id: '2',
relationships: {
nested_posts: {
data: [
{ type: 'nested_posts', id: '1' },
{ type: 'nested_posts', id: '2' }
]
}
}
]
assert_equal(expected, hash[:included])
end
def test_no_duplicates_collection_global
hash = ActiveModel::SerializableResource.new(
[@nestedpost1, @nestedpost2],
adapter: :json_api,
include: '*').serializable_hash
assert_nil(hash[:included])
end end
end end
end end

View File

@ -1,66 +1,64 @@
require 'test_helper' require 'test_helper'
module ActiveModel module ActiveModelSerializers
class Serializer module Adapter
module Adapter class JsonApi
class JsonApi class LinksTest < ActiveSupport::TestCase
class LinksTest < ActiveSupport::TestCase LinkAuthor = Class.new(::Model)
LinkAuthor = Class.new(::Model) class LinkAuthorSerializer < ActiveModel::Serializer
class LinkAuthorSerializer < ActiveModel::Serializer link :self do
link :self do href "//example.com/link_author/#{object.id}"
href "//example.com/link_author/#{object.id}" meta stuff: 'value'
meta stuff: 'value'
end
link :other, '//example.com/resource'
link :yet_another do
"//example.com/resource/#{object.id}"
end
end end
def setup link :other, '//example.com/resource'
@post = Post.new(id: 1337, comments: [], author: nil)
@author = LinkAuthor.new(id: 1337)
end
def test_toplevel_links link :yet_another do
hash = ActiveModel::SerializableResource.new( "//example.com/resource/#{object.id}"
@post, end
adapter: :json_api, end
links: {
self: { def setup
href: '//example.com/posts', @post = Post.new(id: 1337, comments: [], author: nil)
meta: { @author = LinkAuthor.new(id: 1337)
stuff: 'value' end
}
} def test_toplevel_links
}).serializable_hash hash = ActiveModel::SerializableResource.new(
expected = { @post,
adapter: :json_api,
links: {
self: { self: {
href: '//example.com/posts', href: '//example.com/posts',
meta: { meta: {
stuff: 'value' stuff: 'value'
} }
} }
}).serializable_hash
expected = {
self: {
href: '//example.com/posts',
meta: {
stuff: 'value'
}
} }
assert_equal(expected, hash[:links]) }
end assert_equal(expected, hash[:links])
end
def test_resource_links def test_resource_links
hash = serializable(@author, adapter: :json_api).serializable_hash hash = serializable(@author, adapter: :json_api).serializable_hash
expected = { expected = {
self: { self: {
href: '//example.com/link_author/1337', href: '//example.com/link_author/1337',
meta: { meta: {
stuff: 'value' stuff: 'value'
} }
}, },
other: '//example.com/resource', other: '//example.com/resource',
yet_another: '//example.com/resource/1337' yet_another: '//example.com/resource/1337'
} }
assert_equal(expected, hash[:data][:links]) assert_equal(expected, hash[:data][:links])
end
end end
end end
end end

View File

@ -4,110 +4,108 @@ require 'kaminari'
require 'kaminari/hooks' require 'kaminari/hooks'
::Kaminari::Hooks.init ::Kaminari::Hooks.init
module ActiveModel module ActiveModelSerializers
class Serializer module Adapter
module Adapter class JsonApi
class JsonApi class PaginationLinksTest < ActiveSupport::TestCase
class PaginationLinksTest < ActiveSupport::TestCase URI = 'http://example.com'
URI = 'http://example.com'
def setup def setup
ActionController::Base.cache_store.clear ActionController::Base.cache_store.clear
@array = [ @array = [
Profile.new({ id: 1, name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }), Profile.new({ id: 1, name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }),
Profile.new({ id: 2, name: 'Name 2', description: 'Description 2', comments: 'Comments 2' }), Profile.new({ id: 2, name: 'Name 2', description: 'Description 2', comments: 'Comments 2' }),
Profile.new({ id: 3, name: 'Name 3', description: 'Description 3', comments: 'Comments 3' }) Profile.new({ id: 3, name: 'Name 3', description: 'Description 3', comments: 'Comments 3' })
]
end
def mock_request(query_parameters = {}, original_url = URI)
context = Minitest::Mock.new
context.expect(:request_url, original_url)
context.expect(:query_parameters, query_parameters)
@options = {}
@options[:serialization_context] = context
end
def load_adapter(paginated_collection, options = {})
options = options.merge(adapter: :json_api)
ActiveModel::SerializableResource.new(paginated_collection, options)
end
def using_kaminari
Kaminari.paginate_array(@array).page(2).per(1)
end
def using_will_paginate
@array.paginate(page: 2, per_page: 1)
end
def data
{ data: [
{ id: '1', type: 'profiles', attributes: { name: 'Name 1', description: 'Description 1' } },
{ id: '2', type: 'profiles', attributes: { name: 'Name 2', description: 'Description 2' } },
{ id: '3', type: 'profiles', attributes: { name: 'Name 3', description: 'Description 3' } }
] ]
end }
end
def mock_request(query_parameters = {}, original_url = URI) def links
context = Minitest::Mock.new {
context.expect(:request_url, original_url) links: {
context.expect(:query_parameters, query_parameters) self: "#{URI}?page%5Bnumber%5D=2&page%5Bsize%5D=1",
@options = {} first: "#{URI}?page%5Bnumber%5D=1&page%5Bsize%5D=1",
@options[:serialization_context] = context prev: "#{URI}?page%5Bnumber%5D=1&page%5Bsize%5D=1",
end next: "#{URI}?page%5Bnumber%5D=3&page%5Bsize%5D=1",
last: "#{URI}?page%5Bnumber%5D=3&page%5Bsize%5D=1"
def load_adapter(paginated_collection, options = {})
options = options.merge(adapter: :json_api)
ActiveModel::SerializableResource.new(paginated_collection, options)
end
def using_kaminari
Kaminari.paginate_array(@array).page(2).per(1)
end
def using_will_paginate
@array.paginate(page: 2, per_page: 1)
end
def data
{ data: [
{ id: '1', type: 'profiles', attributes: { name: 'Name 1', description: 'Description 1' } },
{ id: '2', type: 'profiles', attributes: { name: 'Name 2', description: 'Description 2' } },
{ id: '3', type: 'profiles', attributes: { name: 'Name 3', description: 'Description 3' } }
]
} }
}
end
def expected_response_without_pagination_links
data
end
def expected_response_with_pagination_links
{}.tap do |hash|
hash[:data] = [data.values.flatten.second]
hash.merge! links
end end
end
def links def expected_response_with_pagination_links_and_additional_params
{ new_links = links[:links].each_with_object({}) { |(key, value), hash| hash[key] = "#{value}&test=test" }
links: { {}.tap do |hash|
self: "#{URI}?page%5Bnumber%5D=2&page%5Bsize%5D=1", hash[:data] = [data.values.flatten.second]
first: "#{URI}?page%5Bnumber%5D=1&page%5Bsize%5D=1", hash.merge! links: new_links
prev: "#{URI}?page%5Bnumber%5D=1&page%5Bsize%5D=1",
next: "#{URI}?page%5Bnumber%5D=3&page%5Bsize%5D=1",
last: "#{URI}?page%5Bnumber%5D=3&page%5Bsize%5D=1"
}
}
end end
end
def expected_response_without_pagination_links def test_pagination_links_using_kaminari
data adapter = load_adapter(using_kaminari)
end
def expected_response_with_pagination_links mock_request
{}.tap do |hash| assert_equal expected_response_with_pagination_links, adapter.serializable_hash(@options)
hash[:data] = [data.values.flatten.second] end
hash.merge! links
end
end
def expected_response_with_pagination_links_and_additional_params def test_pagination_links_using_will_paginate
new_links = links[:links].each_with_object({}) { |(key, value), hash| hash[key] = "#{value}&test=test" } adapter = load_adapter(using_will_paginate)
{}.tap do |hash|
hash[:data] = [data.values.flatten.second]
hash.merge! links: new_links
end
end
def test_pagination_links_using_kaminari mock_request
adapter = load_adapter(using_kaminari) assert_equal expected_response_with_pagination_links, adapter.serializable_hash(@options)
end
mock_request def test_pagination_links_with_additional_params
assert_equal expected_response_with_pagination_links, adapter.serializable_hash(@options) adapter = load_adapter(using_will_paginate)
end
def test_pagination_links_using_will_paginate mock_request({ test: 'test' })
adapter = load_adapter(using_will_paginate) assert_equal expected_response_with_pagination_links_and_additional_params,
adapter.serializable_hash(@options)
end
mock_request def test_not_showing_pagination_links
assert_equal expected_response_with_pagination_links, adapter.serializable_hash(@options) adapter = load_adapter(@array)
end
def test_pagination_links_with_additional_params assert_equal expected_response_without_pagination_links, adapter.serializable_hash
adapter = load_adapter(using_will_paginate)
mock_request({ test: 'test' })
assert_equal expected_response_with_pagination_links_and_additional_params,
adapter.serializable_hash(@options)
end
def test_not_showing_pagination_links
adapter = load_adapter(@array)
assert_equal expected_response_without_pagination_links, adapter.serializable_hash
end
end end
end end
end end

View File

@ -1,136 +1,134 @@
require 'test_helper' require 'test_helper'
module ActiveModel module ActiveModelSerializers
class Serializer module Adapter
module Adapter class JsonApi
class JsonApi module Deserialization
module Deserialization class ParseTest < Minitest::Test
class ParseTest < Minitest::Test def setup
def setup @hash = {
@hash = { 'data' => {
'data' => { 'type' => 'photos',
'type' => 'photos', 'id' => 'zorglub',
'id' => 'zorglub', 'attributes' => {
'attributes' => { 'title' => 'Ember Hamster',
'title' => 'Ember Hamster', 'src' => 'http://example.com/images/productivity.png'
'src' => 'http://example.com/images/productivity.png' },
'relationships' => {
'author' => {
'data' => nil
}, },
'relationships' => { 'photographer' => {
'author' => { 'data' => { 'type' => 'people', 'id' => '9' }
'data' => nil },
}, 'comments' => {
'photographer' => { 'data' => [
'data' => { 'type' => 'people', 'id' => '9' } { 'type' => 'comments', 'id' => '1' },
}, { 'type' => 'comments', 'id' => '2' }
'comments' => { ]
'data' => [
{ 'type' => 'comments', 'id' => '1' },
{ 'type' => 'comments', 'id' => '2' }
]
}
} }
} }
} }
@params = ActionController::Parameters.new(@hash) }
@expected = { @params = ActionController::Parameters.new(@hash)
id: 'zorglub', @expected = {
title: 'Ember Hamster', id: 'zorglub',
src: 'http://example.com/images/productivity.png', title: 'Ember Hamster',
author_id: nil, src: 'http://example.com/images/productivity.png',
photographer_id: '9', author_id: nil,
comment_ids: %w(1 2) photographer_id: '9',
} comment_ids: %w(1 2)
}
@illformed_payloads = [nil, @illformed_payloads = [nil,
{}, {},
{ {
'data' => nil 'data' => nil
}, { }, {
'data' => { 'attributes' => [] } 'data' => { 'attributes' => [] }
}, { }, {
'data' => { 'relationships' => [] } 'data' => { 'relationships' => [] }
}, { }, {
'data' => { 'data' => {
'relationships' => { 'rel' => nil } 'relationships' => { 'rel' => nil }
} }
}, { }, {
'data' => { 'data' => {
'relationships' => { 'rel' => {} } 'relationships' => { 'rel' => {} }
} }
}] }]
end
def test_hash
parsed_hash = ActiveModelSerializers::Adapter::JsonApi::Deserialization.parse!(@hash)
assert_equal(@expected, parsed_hash)
end
def test_actioncontroller_parameters
assert_equal(false, @params.permitted?)
parsed_hash = ActiveModelSerializers::Adapter::JsonApi::Deserialization.parse!(@params)
assert_equal(@expected, parsed_hash)
end
def test_illformed_payloads_safe
@illformed_payloads.each do |p|
parsed_hash = ActiveModelSerializers::Adapter::JsonApi::Deserialization.parse(p)
assert_equal({}, parsed_hash)
end end
end
def test_hash def test_illformed_payloads_unsafe
parsed_hash = ActiveModel::Serializer::Adapter::JsonApi::Deserialization.parse!(@hash) @illformed_payloads.each do |p|
assert_equal(@expected, parsed_hash) assert_raises(InvalidDocument) do
end ActiveModelSerializers::Adapter::JsonApi::Deserialization.parse!(p)
def test_actioncontroller_parameters
assert_equal(false, @params.permitted?)
parsed_hash = ActiveModel::Serializer::Adapter::JsonApi::Deserialization.parse!(@params)
assert_equal(@expected, parsed_hash)
end
def test_illformed_payloads_safe
@illformed_payloads.each do |p|
parsed_hash = ActiveModel::Serializer::Adapter::JsonApi::Deserialization.parse(p)
assert_equal({}, parsed_hash)
end end
end end
end
def test_illformed_payloads_unsafe def test_filter_fields_only
@illformed_payloads.each do |p| parsed_hash = ActiveModelSerializers::Adapter::JsonApi::Deserialization.parse!(@hash, only: [:id, :title, :author])
assert_raises(InvalidDocument) do expected = {
ActiveModel::Serializer::Adapter::JsonApi::Deserialization.parse!(p) id: 'zorglub',
end title: 'Ember Hamster',
end author_id: nil
end }
assert_equal(expected, parsed_hash)
end
def test_filter_fields_only def test_filter_fields_except
parsed_hash = ActiveModel::Serializer::Adapter::JsonApi::Deserialization.parse!(@hash, only: [:id, :title, :author]) parsed_hash = ActiveModelSerializers::Adapter::JsonApi::Deserialization.parse!(@hash, except: [:id, :title, :author])
expected = { expected = {
id: 'zorglub', src: 'http://example.com/images/productivity.png',
title: 'Ember Hamster', photographer_id: '9',
author_id: nil comment_ids: %w(1 2)
} }
assert_equal(expected, parsed_hash) assert_equal(expected, parsed_hash)
end end
def test_filter_fields_except def test_keys
parsed_hash = ActiveModel::Serializer::Adapter::JsonApi::Deserialization.parse!(@hash, except: [:id, :title, :author]) parsed_hash = ActiveModelSerializers::Adapter::JsonApi::Deserialization.parse!(@hash, keys: { author: :user, title: :post_title })
expected = { expected = {
src: 'http://example.com/images/productivity.png', id: 'zorglub',
photographer_id: '9', post_title: 'Ember Hamster',
comment_ids: %w(1 2) src: 'http://example.com/images/productivity.png',
} user_id: nil,
assert_equal(expected, parsed_hash) photographer_id: '9',
end comment_ids: %w(1 2)
}
assert_equal(expected, parsed_hash)
end
def test_keys def test_polymorphic
parsed_hash = ActiveModel::Serializer::Adapter::JsonApi::Deserialization.parse!(@hash, keys: { author: :user, title: :post_title }) parsed_hash = ActiveModelSerializers::Adapter::JsonApi::Deserialization.parse!(@hash, polymorphic: [:photographer])
expected = { expected = {
id: 'zorglub', id: 'zorglub',
post_title: 'Ember Hamster', title: 'Ember Hamster',
src: 'http://example.com/images/productivity.png', src: 'http://example.com/images/productivity.png',
user_id: nil, author_id: nil,
photographer_id: '9', photographer_id: '9',
comment_ids: %w(1 2) photographer_type: 'people',
} comment_ids: %w(1 2)
assert_equal(expected, parsed_hash) }
end assert_equal(expected, parsed_hash)
def test_polymorphic
parsed_hash = ActiveModel::Serializer::Adapter::JsonApi::Deserialization.parse!(@hash, polymorphic: [:photographer])
expected = {
id: 'zorglub',
title: 'Ember Hamster',
src: 'http://example.com/images/productivity.png',
author_id: nil,
photographer_id: '9',
photographer_type: 'people',
comment_ids: %w(1 2)
}
assert_equal(expected, parsed_hash)
end
end end
end end
end end

View File

@ -1,69 +1,67 @@
require 'test_helper' require 'test_helper'
module ActiveModel module ActiveModelSerializers
class Serializer module Adapter
module Adapter class JsonApi
class JsonApi class ResourceTypeConfigTest < ActiveSupport::TestCase
class ResourceTypeConfigTest < ActiveSupport::TestCase class ProfileTypeSerializer < ActiveModel::Serializer
class ProfileTypeSerializer < ActiveModel::Serializer attributes :name
attributes :name type 'profile'
type 'profile' end
end
def setup def setup
@author = Author.new(id: 1, name: 'Steve K.') @author = Author.new(id: 1, name: 'Steve K.')
@author.bio = nil @author.bio = nil
@author.roles = [] @author.roles = []
@blog = Blog.new(id: 23, name: 'AMS Blog') @blog = Blog.new(id: 23, name: 'AMS Blog')
@post = Post.new(id: 42, title: 'New Post', body: 'Body') @post = Post.new(id: 42, title: 'New Post', body: 'Body')
@anonymous_post = Post.new(id: 43, title: 'Hello!!', body: 'Hello, world!!') @anonymous_post = Post.new(id: 43, title: 'Hello!!', body: 'Hello, world!!')
@comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') @comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')
@post.comments = [@comment] @post.comments = [@comment]
@post.blog = @blog @post.blog = @blog
@anonymous_post.comments = [] @anonymous_post.comments = []
@anonymous_post.blog = nil @anonymous_post.blog = nil
@comment.post = @post @comment.post = @post
@comment.author = nil @comment.author = nil
@post.author = @author @post.author = @author
@anonymous_post.author = nil @anonymous_post.author = nil
@blog = Blog.new(id: 1, name: 'My Blog!!') @blog = Blog.new(id: 1, name: 'My Blog!!')
@blog.writer = @author @blog.writer = @author
@blog.articles = [@post, @anonymous_post] @blog.articles = [@post, @anonymous_post]
@author.posts = [] @author.posts = []
end end
def with_jsonapi_resource_type type def with_jsonapi_resource_type type
old_type = ActiveModelSerializers.config.jsonapi_resource_type old_type = ActiveModelSerializers.config.jsonapi_resource_type
ActiveModelSerializers.config.jsonapi_resource_type = type ActiveModelSerializers.config.jsonapi_resource_type = type
yield yield
ensure ensure
ActiveModelSerializers.config.jsonapi_resource_type = old_type ActiveModelSerializers.config.jsonapi_resource_type = old_type
end end
def test_config_plural def test_config_plural
with_jsonapi_resource_type :plural do with_jsonapi_resource_type :plural do
hash = serializable(@comment, adapter: :json_api).serializable_hash hash = serializable(@comment, adapter: :json_api).serializable_hash
assert_equal('comments', hash[:data][:type]) assert_equal('comments', hash[:data][:type])
end
end end
end
def test_config_singular def test_config_singular
with_jsonapi_resource_type :singular do with_jsonapi_resource_type :singular do
hash = serializable(@comment, adapter: :json_api).serializable_hash hash = serializable(@comment, adapter: :json_api).serializable_hash
assert_equal('comment', hash[:data][:type]) assert_equal('comment', hash[:data][:type])
end
end end
end
def test_explicit_type_value def test_explicit_type_value
hash = serializable(@author, serializer: ProfileTypeSerializer, adapter: :json_api).serializable_hash hash = serializable(@author, serializer: ProfileTypeSerializer, adapter: :json_api).serializable_hash
assert_equal('profile', hash.fetch(:data).fetch(:type)) assert_equal('profile', hash.fetch(:data).fetch(:type))
end end
private private
def serializable(resource, options = {}) def serializable(resource, options = {})
ActiveModel::SerializableResource.new(resource, options) ActiveModel::SerializableResource.new(resource, options)
end
end end
end end
end end

View File

@ -1,82 +1,80 @@
require 'test_helper' require 'test_helper'
module ActiveModel module ActiveModelSerializers
class Serializer module Adapter
module Adapter class JsonApi
class JsonApi class TopLevelJsonApiTest < ActiveSupport::TestCase
class TopLevelJsonApiTest < ActiveSupport::TestCase def setup
def setup @author = Author.new(id: 1, name: 'Steve K.')
@author = Author.new(id: 1, name: 'Steve K.') @author.bio = nil
@author.bio = nil @author.roles = []
@author.roles = [] @blog = Blog.new(id: 23, name: 'AMS Blog')
@blog = Blog.new(id: 23, name: 'AMS Blog') @post = Post.new(id: 42, title: 'New Post', body: 'Body')
@post = Post.new(id: 42, title: 'New Post', body: 'Body') @anonymous_post = Post.new(id: 43, title: 'Hello!!', body: 'Hello, world!!')
@anonymous_post = Post.new(id: 43, title: 'Hello!!', body: 'Hello, world!!') @comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')
@comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') @post.comments = [@comment]
@post.comments = [@comment] @post.blog = @blog
@post.blog = @blog @anonymous_post.comments = []
@anonymous_post.comments = [] @anonymous_post.blog = nil
@anonymous_post.blog = nil @comment.post = @post
@comment.post = @post @comment.author = nil
@comment.author = nil @post.author = @author
@post.author = @author @anonymous_post.author = nil
@anonymous_post.author = nil @blog = Blog.new(id: 1, name: 'My Blog!!')
@blog = Blog.new(id: 1, name: 'My Blog!!') @blog.writer = @author
@blog.writer = @author @blog.articles = [@post, @anonymous_post]
@blog.articles = [@post, @anonymous_post] @author.posts = []
@author.posts = [] end
end
def test_toplevel_jsonapi_defaults_to_false def test_toplevel_jsonapi_defaults_to_false
assert_equal config.fetch(:jsonapi_include_toplevel_object), false assert_equal config.fetch(:jsonapi_include_toplevel_object), false
end end
def test_disable_toplevel_jsonapi def test_disable_toplevel_jsonapi
with_config(jsonapi_include_toplevel_object: false) do with_config(jsonapi_include_toplevel_object: false) do
hash = serialize(@post) hash = serialize(@post)
assert_nil(hash[:jsonapi]) assert_nil(hash[:jsonapi])
end
end end
end
def test_enable_toplevel_jsonapi def test_enable_toplevel_jsonapi
with_config(jsonapi_include_toplevel_object: true) do with_config(jsonapi_include_toplevel_object: true) do
hash = serialize(@post) hash = serialize(@post)
refute_nil(hash[:jsonapi]) refute_nil(hash[:jsonapi])
end
end end
end
def test_default_toplevel_jsonapi_version def test_default_toplevel_jsonapi_version
with_config(jsonapi_include_toplevel_object: true) do with_config(jsonapi_include_toplevel_object: true) do
hash = serialize(@post) hash = serialize(@post)
assert_equal('1.0', hash[:jsonapi][:version]) assert_equal('1.0', hash[:jsonapi][:version])
end
end end
end
def test_toplevel_jsonapi_no_meta def test_toplevel_jsonapi_no_meta
with_config(jsonapi_include_toplevel_object: true) do with_config(jsonapi_include_toplevel_object: true) do
hash = serialize(@post) hash = serialize(@post)
assert_nil(hash[:jsonapi][:meta]) assert_nil(hash[:jsonapi][:meta])
end
end end
end
def test_toplevel_jsonapi_meta def test_toplevel_jsonapi_meta
new_config = { new_config = {
jsonapi_include_toplevel_object: true, jsonapi_include_toplevel_object: true,
jsonapi_toplevel_meta: { jsonapi_toplevel_meta: {
'copyright' => 'Copyright 2015 Example Corp.' 'copyright' => 'Copyright 2015 Example Corp.'
}
} }
with_config(new_config) do }
hash = serialize(@post) with_config(new_config) do
assert_equal(new_config[:jsonapi_toplevel_meta], hash.fetch(:jsonapi).fetch(:meta)) hash = serialize(@post)
end assert_equal(new_config[:jsonapi_toplevel_meta], hash.fetch(:jsonapi).fetch(:meta))
end end
end
private private
def serialize(resource, options = {}) def serialize(resource, options = {})
serializable(resource, { adapter: :json_api }.merge!(options)).serializable_hash serializable(resource, { adapter: :json_api }.merge!(options)).serializable_hash
end
end end
end end
end end

View File

@ -1,46 +1,44 @@
require 'test_helper' require 'test_helper'
module ActiveModel module ActiveModelSerializers
class Serializer module Adapter
module Adapter class JsonTest < ActiveSupport::TestCase
class JsonTest < ActiveSupport::TestCase def setup
def setup ActionController::Base.cache_store.clear
ActionController::Base.cache_store.clear @author = Author.new(id: 1, name: 'Steve K.')
@author = Author.new(id: 1, name: 'Steve K.') @post = Post.new(id: 1, title: 'New Post', body: 'Body')
@post = Post.new(id: 1, title: 'New Post', body: 'Body') @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')
@first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT')
@second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT') @post.comments = [@first_comment, @second_comment]
@post.comments = [@first_comment, @second_comment] @first_comment.post = @post
@first_comment.post = @post @second_comment.post = @post
@second_comment.post = @post @post.author = @author
@post.author = @author @blog = Blog.new(id: 1, name: 'My Blog!!')
@blog = Blog.new(id: 1, name: 'My Blog!!') @post.blog = @blog
@post.blog = @blog
@serializer = PostSerializer.new(@post) @serializer = PostSerializer.new(@post)
@adapter = ActiveModel::Serializer::Adapter::Json.new(@serializer) @adapter = ActiveModelSerializers::Adapter::Json.new(@serializer)
end end
def test_has_many def test_has_many
assert_equal([ assert_equal([
{ id: 1, body: 'ZOMG A COMMENT' }, { id: 1, body: 'ZOMG A COMMENT' },
{ id: 2, body: 'ZOMG ANOTHER COMMENT' } { id: 2, body: 'ZOMG ANOTHER COMMENT' }
], @adapter.serializable_hash[:post][:comments]) ], @adapter.serializable_hash[:post][:comments])
end end
def test_custom_keys def test_custom_keys
serializer = PostWithCustomKeysSerializer.new(@post) serializer = PostWithCustomKeysSerializer.new(@post)
adapter = ActiveModel::Serializer::Adapter::Json.new(serializer) adapter = ActiveModelSerializers::Adapter::Json.new(serializer)
assert_equal({ assert_equal({
id: 1, id: 1,
reviews: [{ id: 1, body: 'ZOMG A COMMENT' }, reviews: [{ id: 1, body: 'ZOMG A COMMENT' },
{ id: 2, body: 'ZOMG ANOTHER COMMENT' } { id: 2, body: 'ZOMG ANOTHER COMMENT' }
], ],
writer: { id: 1, name: 'Steve K.' }, writer: { id: 1, name: 'Steve K.' },
site: { id: 1, name: 'My Blog!!' } site: { id: 1, name: 'My Blog!!' }
}, adapter.serializable_hash[:post]) }, adapter.serializable_hash[:post])
end
end end
end end
end end

View File

@ -1,23 +1,21 @@
require 'test_helper' require 'test_helper'
module ActiveModel module ActiveModelSerializers
class Serializer module Adapter
module Adapter class NullTest < ActiveSupport::TestCase
class NullTest < ActiveSupport::TestCase def setup
def setup profile = Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' })
profile = Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }) serializer = ProfileSerializer.new(profile)
serializer = ProfileSerializer.new(profile)
@adapter = Null.new(serializer) @adapter = Null.new(serializer)
end end
def test_serializable_hash def test_serializable_hash
assert_equal({}, @adapter.serializable_hash) assert_equal({}, @adapter.serializable_hash)
end end
def test_it_returns_empty_json def test_it_returns_empty_json
assert_equal('{}', @adapter.to_json) assert_equal('{}', @adapter.to_json)
end
end end
end end
end end

View File

@ -1,42 +1,40 @@
require 'test_helper' require 'test_helper'
module ActiveModel module ActiveModelSerializers
class Serializer class AdapterTest < ActiveSupport::TestCase
class AdapterTest < ActiveSupport::TestCase def setup
def setup profile = Profile.new
profile = Profile.new @serializer = ProfileSerializer.new(profile)
@serializer = ProfileSerializer.new(profile) @adapter = ActiveModelSerializers::Adapter::Base.new(@serializer)
@adapter = ActiveModel::Serializer::Adapter::Base.new(@serializer) end
def test_serializable_hash_is_abstract_method
assert_raises(NotImplementedError) do
@adapter.serializable_hash(only: [:name])
end end
end
def test_serializable_hash_is_abstract_method def test_serializer
assert_raises(NotImplementedError) do assert_equal @serializer, @adapter.serializer
@adapter.serializable_hash(only: [:name]) end
end
end
def test_serializer def test_create_adapter
assert_equal @serializer, @adapter.serializer adapter = ActiveModelSerializers::Adapter.create(@serializer)
end assert_equal ActiveModelSerializers::Adapter::Attributes, adapter.class
end
def test_create_adapter def test_create_adapter_with_override
adapter = ActiveModel::Serializer::Adapter.create(@serializer) adapter = ActiveModelSerializers::Adapter.create(@serializer, { adapter: :json_api })
assert_equal ActiveModel::Serializer::Adapter::Attributes, adapter.class assert_equal ActiveModelSerializers::Adapter::JsonApi, adapter.class
end end
def test_create_adapter_with_override def test_inflected_adapter_class_for_known_adapter
adapter = ActiveModel::Serializer::Adapter.create(@serializer, { adapter: :json_api }) ActiveSupport::Inflector.inflections(:en) { |inflect| inflect.acronym 'API' }
assert_equal ActiveModel::Serializer::Adapter::JsonApi, adapter.class klass = ActiveModelSerializers::Adapter.adapter_class(:json_api)
end
def test_inflected_adapter_class_for_known_adapter ActiveSupport::Inflector.inflections.acronyms.clear
ActiveSupport::Inflector.inflections(:en) { |inflect| inflect.acronym 'API' }
klass = ActiveModel::Serializer::Adapter.adapter_class(:json_api)
ActiveSupport::Inflector.inflections.acronyms.clear assert_equal ActiveModelSerializers::Adapter::JsonApi, klass
assert_equal ActiveModel::Serializer::Adapter::JsonApi, klass
end
end end
end end
end end

View File

@ -5,7 +5,7 @@ module ActiveModel
def setup def setup
@resource = Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }) @resource = Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' })
@serializer = ProfileSerializer.new(@resource) @serializer = ProfileSerializer.new(@resource)
@adapter = ActiveModel::Serializer::Adapter.create(@serializer) @adapter = ActiveModelSerializers::Adapter.create(@serializer)
@serializable_resource = ActiveModel::SerializableResource.new(@resource) @serializable_resource = ActiveModel::SerializableResource.new(@resource)
end end

View File

@ -1,166 +0,0 @@
module ActiveModel
class Serializer
class AdapterForTest < ActiveSupport::TestCase
UnknownAdapterError = ::ActiveModel::Serializer::Adapter::UnknownAdapterError
def setup
@previous_adapter = ActiveModelSerializers.config.adapter
end
def teardown
ActiveModelSerializers.config.adapter = @previous_adapter
end
def test_returns_default_adapter
adapter = ActiveModel::Serializer.adapter
assert_equal ActiveModel::Serializer::Adapter::Attributes, adapter
end
def test_overwrite_adapter_with_symbol
ActiveModelSerializers.config.adapter = :null
adapter = ActiveModel::Serializer.adapter
assert_equal ActiveModel::Serializer::Adapter::Null, adapter
ensure
ActiveModelSerializers.config.adapter = @previous_adapter
end
def test_overwrite_adapter_with_class
ActiveModelSerializers.config.adapter = ActiveModel::Serializer::Adapter::Null
adapter = ActiveModel::Serializer.adapter
assert_equal ActiveModel::Serializer::Adapter::Null, adapter
end
def test_raises_exception_if_invalid_symbol_given
ActiveModelSerializers.config.adapter = :unknown
assert_raises UnknownAdapterError do
ActiveModel::Serializer.adapter
end
end
def test_raises_exception_if_it_does_not_know_hot_to_infer_adapter
ActiveModelSerializers.config.adapter = 42
assert_raises UnknownAdapterError do
ActiveModel::Serializer.adapter
end
end
def test_adapter_class_for_known_adapter
klass = ActiveModel::Serializer::Adapter.adapter_class(:json_api)
assert_equal ActiveModel::Serializer::Adapter::JsonApi, klass
end
def test_adapter_class_for_unknown_adapter
assert_raises UnknownAdapterError do
ActiveModel::Serializer::Adapter.adapter_class(:json_simple)
end
end
def test_adapter_map
expected_adapter_map = {
'null'.freeze => ActiveModel::Serializer::Adapter::Null,
'json'.freeze => ActiveModel::Serializer::Adapter::Json,
'attributes'.freeze => ActiveModel::Serializer::Adapter::Attributes,
'json_api'.freeze => ActiveModel::Serializer::Adapter::JsonApi
}
actual = ActiveModel::Serializer::Adapter.adapter_map
assert_equal actual, expected_adapter_map
end
def test_adapters
assert_equal ActiveModel::Serializer::Adapter.adapters.sort, [
'attributes'.freeze,
'json'.freeze,
'json_api'.freeze,
'null'.freeze
]
end
def test_lookup_adapter_by_string_name
assert_equal ActiveModel::Serializer::Adapter.lookup('json'.freeze), ActiveModel::Serializer::Adapter::Json
end
def test_lookup_adapter_by_symbol_name
assert_equal ActiveModel::Serializer::Adapter.lookup(:json), ActiveModel::Serializer::Adapter::Json
end
def test_lookup_adapter_by_class
klass = ActiveModel::Serializer::Adapter::Json
assert_equal ActiveModel::Serializer::Adapter.lookup(klass), klass
end
def test_lookup_adapter_from_environment_registers_adapter
ActiveModel::Serializer::Adapter.const_set(:AdapterFromEnvironment, Class.new)
klass = ::ActiveModel::Serializer::Adapter::AdapterFromEnvironment
name = 'adapter_from_environment'.freeze
assert_equal ActiveModel::Serializer::Adapter.lookup(name), klass
assert ActiveModel::Serializer::Adapter.adapters.include?(name)
ensure
ActiveModel::Serializer::Adapter.adapter_map.delete(name)
ActiveModel::Serializer::Adapter.send(:remove_const, :AdapterFromEnvironment)
end
def test_lookup_adapter_for_unknown_name
assert_raises UnknownAdapterError do
ActiveModel::Serializer::Adapter.lookup(:json_simple)
end
end
def test_adapter
assert_equal ActiveModelSerializers.config.adapter, :attributes
assert_equal ActiveModel::Serializer.adapter, ActiveModel::Serializer::Adapter::Attributes
end
def test_register_adapter
new_adapter_name = :foo
new_adapter_klass = Class.new
ActiveModel::Serializer::Adapter.register(new_adapter_name, new_adapter_klass)
assert ActiveModel::Serializer::Adapter.adapters.include?('foo'.freeze)
assert ActiveModel::Serializer::Adapter.lookup(:foo), new_adapter_klass
ensure
ActiveModel::Serializer::Adapter.adapter_map.delete(new_adapter_name.to_s)
end
def test_inherited_adapter_hooks_register_adapter
Object.const_set(:MyAdapter, Class.new)
my_adapter = MyAdapter
ActiveModel::Serializer::Adapter::Base.inherited(my_adapter)
assert_equal ActiveModel::Serializer::Adapter.lookup(:my_adapter), my_adapter
ensure
ActiveModel::Serializer::Adapter.adapter_map.delete('my_adapter'.freeze)
Object.send(:remove_const, :MyAdapter)
end
def test_inherited_adapter_hooks_register_namespaced_adapter
Object.const_set(:MyNamespace, Module.new)
MyNamespace.const_set(:MyAdapter, Class.new)
my_adapter = MyNamespace::MyAdapter
ActiveModel::Serializer::Adapter::Base.inherited(my_adapter)
assert_equal ActiveModel::Serializer::Adapter.lookup(:'my_namespace/my_adapter'), my_adapter
ensure
ActiveModel::Serializer::Adapter.adapter_map.delete('my_namespace/my_adapter'.freeze)
MyNamespace.send(:remove_const, :MyAdapter)
Object.send(:remove_const, :MyNamespace)
end
def test_inherited_adapter_hooks_register_subclass_of_registered_adapter
Object.const_set(:MyAdapter, Class.new)
my_adapter = MyAdapter
Object.const_set(:MySubclassedAdapter, Class.new(MyAdapter))
my_subclassed_adapter = MySubclassedAdapter
ActiveModel::Serializer::Adapter::Base.inherited(my_adapter)
ActiveModel::Serializer::Adapter::Base.inherited(my_subclassed_adapter)
assert_equal ActiveModel::Serializer::Adapter.lookup(:my_adapter), my_adapter
assert_equal ActiveModel::Serializer::Adapter.lookup(:my_subclassed_adapter), my_subclassed_adapter
ensure
ActiveModel::Serializer::Adapter.adapter_map.delete('my_adapter'.freeze)
ActiveModel::Serializer::Adapter.adapter_map.delete('my_subclassed_adapter'.freeze)
Object.send(:remove_const, :MyAdapter)
Object.send(:remove_const, :MySubclassedAdapter)
end
end
end
end

View File

@ -14,14 +14,14 @@ module ActiveModel
end end
def test_json_serializable_hash def test_json_serializable_hash
adapter = ActiveModel::Serializer::Adapter::Json.new(@blog_serializer) adapter = ActiveModelSerializers::Adapter::Json.new(@blog_serializer)
assert_equal({ blog: { id: 1, title: 'AMS Hints' } }, adapter.serializable_hash) assert_equal({ blog: { id: 1, title: 'AMS Hints' } }, adapter.serializable_hash)
end end
def test_attribute_inheritance_with_key def test_attribute_inheritance_with_key
inherited_klass = Class.new(AlternateBlogSerializer) inherited_klass = Class.new(AlternateBlogSerializer)
blog_serializer = inherited_klass.new(@blog) blog_serializer = inherited_klass.new(@blog)
adapter = ActiveModel::Serializer::Adapter::Attributes.new(blog_serializer) adapter = ActiveModelSerializers::Adapter::Attributes.new(blog_serializer)
assert_equal({ :id => 1, :title => 'AMS Hints' }, adapter.serializable_hash) assert_equal({ :id => 1, :title => 'AMS Hints' }, adapter.serializable_hash)
end end
@ -39,7 +39,7 @@ module ActiveModel
attribute :name, key: :id attribute :name, key: :id
end end
adapter = ActiveModel::Serializer::Adapter::Json.new(serializer.new(@blog)) adapter = ActiveModelSerializers::Adapter::Json.new(serializer.new(@blog))
assert_equal({ blog: { id: 'AMS Hints' } }, adapter.serializable_hash) assert_equal({ blog: { id: 'AMS Hints' } }, adapter.serializable_hash)
end end
@ -48,7 +48,7 @@ module ActiveModel
attribute :name, key: :object attribute :name, key: :object
end end
adapter = ActiveModel::Serializer::Adapter::Json.new(serializer.new(@blog)) adapter = ActiveModelSerializers::Adapter::Json.new(serializer.new(@blog))
assert_equal({ blog: { object: 'AMS Hints' } }, adapter.serializable_hash) assert_equal({ blog: { object: 'AMS Hints' } }, adapter.serializable_hash)
end end
@ -60,10 +60,10 @@ module ActiveModel
attributes :type attributes :type
end end
adapter = ActiveModel::Serializer::Adapter::Json.new(attribute_serializer.new(@blog)) adapter = ActiveModelSerializers::Adapter::Json.new(attribute_serializer.new(@blog))
assert_equal({ blog: { type: 1 } }, adapter.serializable_hash) assert_equal({ blog: { type: 1 } }, adapter.serializable_hash)
adapter = ActiveModel::Serializer::Adapter::Json.new(attributes_serializer.new(@blog)) adapter = ActiveModelSerializers::Adapter::Json.new(attributes_serializer.new(@blog))
assert_equal({ blog: { type: 'stuff' } }, adapter.serializable_hash) assert_equal({ blog: { type: 'stuff' } }, adapter.serializable_hash)
end end