mirror of
https://github.com/ditkrg/active_model_serializers.git
synced 2026-01-22 22:06:50 +00:00
Merge pull request #1535 from bf4/domitian-move-namespace-of-adapter-to-active-model-serializers
Moved the adapter and adapter folder to active_model_serializers folder and changed the module namespace
This commit is contained in:
commit
8a040052af
@ -222,7 +222,6 @@ Style/TrailingBlankLines:
|
||||
- 'test/adapter/null_test.rb'
|
||||
- 'test/serializers/cache_test.rb'
|
||||
- 'test/serializers/fieldset_test.rb'
|
||||
- 'test/support/stream_capture.rb'
|
||||
|
||||
# Offense count: 5
|
||||
# Cop supports --auto-correct.
|
||||
|
||||
@ -12,7 +12,9 @@
|
||||
- [](http://www.rubydoc.info/github/rails-api/active_model_serializers/v0.10.0.rc4)
|
||||
- [Guides](docs)
|
||||
- [0.9 (0-9-stable) Documentation](https://github.com/rails-api/active_model_serializers/tree/0-9-stable)
|
||||
- [](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)
|
||||
- [](http://www.rubydoc.info/github/rails-api/active_model_serializers/0-8-stable)
|
||||
|
||||
## About
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@ It should be set only once, preferably at initialization.
|
||||
For example:
|
||||
|
||||
```ruby
|
||||
ActiveModelSerializers.config.adapter = ActiveModel::Serializer::Adapter::JsonApi
|
||||
ActiveModelSerializers.config.adapter = ActiveModelSerializers::Adapter::JsonApi
|
||||
```
|
||||
|
||||
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.
|
||||
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:
|
||||
|
||||
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"`.
|
||||
|
||||
```ruby
|
||||
module Example
|
||||
class UsefulAdapter < ActiveModel::Serializer::Adapter::Base
|
||||
class UsefulAdapter < ActiveModelSerializers::Adapter::Base
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
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)`
|
||||
|
||||
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`.
|
||||
|
||||
```ruby
|
||||
class MyAdapter; end
|
||||
ActiveModel::Serializer::Adapter.register(:special_adapter, MyAdapter)
|
||||
ActiveModelSerializers::Adapter.register(:special_adapter, MyAdapter)
|
||||
```
|
||||
|
||||
### Looking up an adapter
|
||||
|
||||
| Method | Return value |
|
||||
| :------------ |:---------------|
|
||||
| `ActiveModel::Serializer::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` |
|
||||
| `ActiveModel::Serializer::Adapter.lookup(name_or_klass)` | The `adapter_class`, else raises an `ActiveModel::Serializer::Adapter::UnknownAdapter` error |
|
||||
| `ActiveModel::Serializer::Adapter.adapter_class(adapter)` | Delegates to `ActiveModel::Serializer::Adapter.lookup(adapter)` |
|
||||
| `ActiveModel::Serializer.adapter` | A convenience method for `ActiveModel::Serializer::Adapter.lookup(config.adapter)` |
|
||||
| `ActiveModelSerializers::Adapter.adapter_map` | A Hash of all known adapters `{ adapter_name => adapter_class }` |
|
||||
| `ActiveModelSerializers::Adapter.adapters` | A (sorted) Array of all known `adapter_names` |
|
||||
| `ActiveModelSerializers::Adapter.lookup(name_or_klass)` | The `adapter_class`, else raises an `ActiveModelSerializers::Adapter::UnknownAdapter` error |
|
||||
| `ActiveModelSerializers::Adapter.adapter_class(adapter)` | Delegates to `ActiveModelSerializers::Adapter.lookup(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.
|
||||
Helpfully, the Symbol or String is underscored, so that `get(:my_adapter)` and `get("MyAdapter")`
|
||||
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)
|
||||
|
||||
@ -17,7 +17,7 @@ Payload (example):
|
||||
```ruby
|
||||
{
|
||||
serializer: PostSerializer,
|
||||
adapter: ActiveModel::Serializer::Adapter::Attributes
|
||||
adapter: ActiveModelSerializers::Adapter::Attributes
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@ -70,7 +70,7 @@ at the first moment.
|
||||
## Renaming of class and modules
|
||||
|
||||
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 `ActiveModel::Serializer::Adapter::JsonApi`.
|
||||
Discussion of renaming existing classes / modules and JsonApi objects will
|
||||
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
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
require 'set'
|
||||
require 'active_model_serializers/adapter'
|
||||
module ActiveModel
|
||||
class SerializableResource
|
||||
ADAPTER_OPTION_KEYS = Set.new([:include, :fields, :adapter, :meta, :meta_key, :links])
|
||||
@ -30,7 +31,7 @@ module ActiveModel
|
||||
end
|
||||
|
||||
def adapter
|
||||
@adapter ||= ActiveModel::Serializer::Adapter.create(serializer_instance, adapter_opts)
|
||||
@adapter ||= ActiveModelSerializers::Adapter.create(serializer_instance, adapter_opts)
|
||||
end
|
||||
alias_method :adapter_instance, :adapter
|
||||
|
||||
|
||||
@ -23,7 +23,6 @@ module ActiveModel
|
||||
include Links
|
||||
include Meta
|
||||
include Type
|
||||
require 'active_model/serializer/adapter'
|
||||
|
||||
# @param resource [ActiveRecord::Base, ActiveModelSerializers::Model]
|
||||
# @return [ActiveModel::Serializer]
|
||||
@ -42,9 +41,11 @@ module ActiveModel
|
||||
end
|
||||
end
|
||||
|
||||
# @see ActiveModel::Serializer::Adapter.lookup
|
||||
# @see ActiveModelSerializers::Adapter.lookup
|
||||
# Deprecated
|
||||
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
|
||||
|
||||
# @api private
|
||||
|
||||
@ -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
|
||||
@ -1,100 +0,0 @@
|
||||
module ActiveModel
|
||||
class Serializer
|
||||
module Adapter
|
||||
class Attributes < Base
|
||||
def initialize(serializer, options = {})
|
||||
super
|
||||
@include_tree = IncludeTree.from_include_args(options[:include] || '*')
|
||||
@cached_attributes = options[:cache_attributes] || {}
|
||||
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)
|
||||
cache_attributes
|
||||
|
||||
serializer.map { |s| Attributes.new(s, instance_options).serializable_hash(options) }
|
||||
end
|
||||
|
||||
# Read cache from cache_store
|
||||
# @return [Hash]
|
||||
def cache_read_multi
|
||||
return {} if ActiveModelSerializers.config.cache_store.blank?
|
||||
|
||||
keys = CachedSerializer.object_cache_keys(serializer, @include_tree)
|
||||
|
||||
return {} if keys.blank?
|
||||
|
||||
ActiveModelSerializers.config.cache_store.read_multi(*keys)
|
||||
end
|
||||
|
||||
# Set @cached_attributes
|
||||
def cache_attributes
|
||||
return if @cached_attributes.present?
|
||||
|
||||
@cached_attributes = cache_read_multi
|
||||
end
|
||||
|
||||
# Get attributes from @cached_attributes
|
||||
# @return [Hash] cached attributes
|
||||
def cached_attributes(cached_serializer)
|
||||
return yield unless cached_serializer.cached?
|
||||
|
||||
@cached_attributes.fetch(cached_serializer.cache_key) { yield }
|
||||
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)
|
||||
cached_serializer = CachedSerializer.new(serializer)
|
||||
|
||||
cached_attributes(cached_serializer) do
|
||||
cached_serializer.cache_check(self) do
|
||||
serializer.attributes(options[:fields])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -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
|
||||
@ -1,79 +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 && (@klass._cache_only && !@klass._cache_except || !@klass._cache_only && @klass._cache_except)
|
||||
end
|
||||
|
||||
def cache_key
|
||||
return @cache_key if defined?(@cache_key)
|
||||
|
||||
parts = []
|
||||
parts << object_cache_key
|
||||
parts << @klass._cache_digest unless @klass._cache_options && @klass._cache_options[:skip_digest]
|
||||
@cache_key = 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
|
||||
|
||||
# find all cache_key for the collection_serializer
|
||||
# @param collection_serializer
|
||||
# @param include_tree
|
||||
# @return [Array] all cache_key of collection_serializer
|
||||
def self.object_cache_keys(serializers, include_tree)
|
||||
cache_keys = []
|
||||
|
||||
serializers.each do |serializer|
|
||||
cache_keys << object_cache_key(serializer)
|
||||
|
||||
serializer.associations(include_tree).each do |association|
|
||||
if association.serializer.respond_to?(:each)
|
||||
association.serializer.each do |sub_serializer|
|
||||
cache_keys << object_cache_key(sub_serializer)
|
||||
end
|
||||
else
|
||||
cache_keys << object_cache_key(association.serializer)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
cache_keys.compact.uniq
|
||||
end
|
||||
|
||||
# @return [String, nil] the cache_key of the serializer or nil
|
||||
def self.object_cache_key(serializer)
|
||||
return unless serializer.present? && serializer.object.present?
|
||||
|
||||
cached_serializer = new(serializer)
|
||||
cached_serializer.cached? ? cached_serializer.cache_key : nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1,115 +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)
|
||||
|
||||
# Preserve the type setting in the cached/non-cached serializer classes
|
||||
cached.constantize.type(klass._type)
|
||||
non_cached.constantize.type(klass._type)
|
||||
|
||||
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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -1,188 +0,0 @@
|
||||
module ActiveModel
|
||||
class Serializer
|
||||
module Adapter
|
||||
class JsonApi < Base
|
||||
extend ActiveSupport::Autoload
|
||||
autoload :PaginationLinks
|
||||
autoload :FragmentCache
|
||||
autoload :Link
|
||||
autoload :Meta
|
||||
autoload :Deserialization
|
||||
require 'active_model/serializer/adapter/json_api/api_objects'
|
||||
|
||||
# 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 ||= {}
|
||||
|
||||
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)
|
||||
ActiveModel::Serializer::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 = ApiObjects::ResourceIdentifier.new(serializer).as_json
|
||||
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 attributes_for(serializer, fields)
|
||||
serializer.attributes(fields).except(:id)
|
||||
end
|
||||
|
||||
def resource_object_for(serializer)
|
||||
resource_object = cache_check(serializer) do
|
||||
resource_object = ApiObjects::ResourceIdentifier.new(serializer).as_json
|
||||
|
||||
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
|
||||
|
||||
requested_associations = fieldset.fields_for(resource_object[:type]) || '*'
|
||||
relationships = relationships_for(serializer, requested_associations)
|
||||
resource_object[:relationships] = relationships if relationships.any?
|
||||
|
||||
links = links_for(serializer)
|
||||
resource_object[:links] = links if links.any?
|
||||
|
||||
meta = meta_for(serializer)
|
||||
resource_object[:meta] = meta unless meta.nil?
|
||||
|
||||
resource_object
|
||||
end
|
||||
|
||||
def relationships_for(serializer, requested_associations)
|
||||
include_tree = IncludeTree.from_include_args(requested_associations)
|
||||
serializer.associations(include_tree).each_with_object({}) do |association, hash|
|
||||
hash[association.key] = ApiObjects::Relationship.new(
|
||||
serializer,
|
||||
association.serializer,
|
||||
association.options,
|
||||
association.links,
|
||||
association.meta
|
||||
).as_json
|
||||
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
|
||||
|
||||
def meta_for(serializer)
|
||||
Meta.new(serializer).as_json
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -11,7 +11,7 @@ module ActiveModel
|
||||
@options = options
|
||||
@data = data_for(serializer, options)
|
||||
@links = links.each_with_object({}) do |(key, value), hash|
|
||||
hash[key] = Link.new(parent_serializer, value).as_json
|
||||
hash[key] = ActiveModelSerializers::Adapter::JsonApi::Link.new(parent_serializer, value).as_json
|
||||
end
|
||||
@meta = meta.respond_to?(:call) ? parent_serializer.instance_eval(&meta) : meta
|
||||
end
|
||||
|
||||
@ -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
|
||||
@ -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
|
||||
@ -1,45 +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 = {}
|
||||
hash[:href] = @href if @href
|
||||
hash[:meta] = @meta if @meta
|
||||
|
||||
hash
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
attr_reader :object, :scope
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -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
|
||||
@ -1,11 +0,0 @@
|
||||
module ActiveModel
|
||||
class Serializer
|
||||
module Adapter
|
||||
class Null < Base
|
||||
def serializable_hash(options = nil)
|
||||
{}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -9,6 +9,7 @@ module ActiveModelSerializers
|
||||
autoload :Deserialization
|
||||
autoload :Logging
|
||||
autoload :Test
|
||||
autoload :Adapter
|
||||
|
||||
class << self; attr_accessor :logger; end
|
||||
self.logger = ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new(STDOUT))
|
||||
|
||||
93
lib/active_model_serializers/adapter.rb
Normal file
93
lib/active_model_serializers/adapter.rb
Normal 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
|
||||
98
lib/active_model_serializers/adapter/attributes.rb
Normal file
98
lib/active_model_serializers/adapter/attributes.rb
Normal file
@ -0,0 +1,98 @@
|
||||
module ActiveModelSerializers
|
||||
module Adapter
|
||||
class Attributes < Base
|
||||
def initialize(serializer, options = {})
|
||||
super
|
||||
@include_tree = ActiveModel::Serializer::IncludeTree.from_include_args(options[:include] || '*')
|
||||
@cached_attributes = options[:cache_attributes] || {}
|
||||
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)
|
||||
cache_attributes
|
||||
|
||||
serializer.map { |s| Attributes.new(s, instance_options).serializable_hash(options) }
|
||||
end
|
||||
|
||||
# Read cache from cache_store
|
||||
# @return [Hash]
|
||||
def cache_read_multi
|
||||
return {} if ActiveModelSerializers.config.cache_store.blank?
|
||||
|
||||
keys = CachedSerializer.object_cache_keys(serializer, @include_tree)
|
||||
|
||||
return {} if keys.blank?
|
||||
|
||||
ActiveModelSerializers.config.cache_store.read_multi(*keys)
|
||||
end
|
||||
|
||||
# Set @cached_attributes
|
||||
def cache_attributes
|
||||
return if @cached_attributes.present?
|
||||
|
||||
@cached_attributes = cache_read_multi
|
||||
end
|
||||
|
||||
# Get attributes from @cached_attributes
|
||||
# @return [Hash] cached attributes
|
||||
def cached_attributes(cached_serializer)
|
||||
return yield unless cached_serializer.cached?
|
||||
|
||||
@cached_attributes.fetch(cached_serializer.cache_key) { yield }
|
||||
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)
|
||||
cached_serializer = CachedSerializer.new(serializer)
|
||||
|
||||
cached_attributes(cached_serializer) do
|
||||
cached_serializer.cache_check(self) do
|
||||
serializer.attributes(options[:fields])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
56
lib/active_model_serializers/adapter/base.rb
Normal file
56
lib/active_model_serializers/adapter/base.rb
Normal 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
|
||||
77
lib/active_model_serializers/adapter/cached_serializer.rb
Normal file
77
lib/active_model_serializers/adapter/cached_serializer.rb
Normal file
@ -0,0 +1,77 @@
|
||||
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 && (@klass._cache_only && !@klass._cache_except || !@klass._cache_only && @klass._cache_except)
|
||||
end
|
||||
|
||||
def cache_key
|
||||
return @cache_key if defined?(@cache_key)
|
||||
|
||||
parts = []
|
||||
parts << object_cache_key
|
||||
parts << @klass._cache_digest unless @klass._cache_options && @klass._cache_options[:skip_digest]
|
||||
@cache_key = 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
|
||||
|
||||
# find all cache_key for the collection_serializer
|
||||
# @param collection_serializer
|
||||
# @param include_tree
|
||||
# @return [Array] all cache_key of collection_serializer
|
||||
def self.object_cache_keys(serializers, include_tree)
|
||||
cache_keys = []
|
||||
|
||||
serializers.each do |serializer|
|
||||
cache_keys << object_cache_key(serializer)
|
||||
|
||||
serializer.associations(include_tree).each do |association|
|
||||
if association.serializer.respond_to?(:each)
|
||||
association.serializer.each do |sub_serializer|
|
||||
cache_keys << object_cache_key(sub_serializer)
|
||||
end
|
||||
else
|
||||
cache_keys << object_cache_key(association.serializer)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
cache_keys.compact.uniq
|
||||
end
|
||||
|
||||
# @return [String, nil] the cache_key of the serializer or nil
|
||||
def self.object_cache_key(serializer)
|
||||
return unless serializer.present? && serializer.object.present?
|
||||
|
||||
cached_serializer = new(serializer)
|
||||
cached_serializer.cached? ? cached_serializer.cache_key : nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
113
lib/active_model_serializers/adapter/fragment_cache.rb
Normal file
113
lib/active_model_serializers/adapter/fragment_cache.rb
Normal file
@ -0,0 +1,113 @@
|
||||
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)
|
||||
|
||||
# Preserve the type setting in the cached/non-cached serializer classes
|
||||
cached.constantize.type(klass._type)
|
||||
non_cached.constantize.type(klass._type)
|
||||
|
||||
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
|
||||
19
lib/active_model_serializers/adapter/json.rb
Normal file
19
lib/active_model_serializers/adapter/json.rb
Normal 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
|
||||
11
lib/active_model_serializers/adapter/json/fragment_cache.rb
Normal file
11
lib/active_model_serializers/adapter/json/fragment_cache.rb
Normal 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
|
||||
186
lib/active_model_serializers/adapter/json_api.rb
Normal file
186
lib/active_model_serializers/adapter/json_api.rb
Normal file
@ -0,0 +1,186 @@
|
||||
module ActiveModelSerializers
|
||||
module Adapter
|
||||
class JsonApi < Base
|
||||
extend ActiveSupport::Autoload
|
||||
autoload :PaginationLinks
|
||||
autoload :FragmentCache
|
||||
autoload :Link
|
||||
require 'active_model/serializer/adapter/json_api/meta'
|
||||
autoload :Deserialization
|
||||
require 'active_model/serializer/adapter/json_api/api_objects'
|
||||
|
||||
# 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 = ActiveModel::Serializer::Adapter::JsonApi::ApiObjects::ResourceIdentifier.new(serializer).as_json
|
||||
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 attributes_for(serializer, fields)
|
||||
serializer.attributes(fields).except(:id)
|
||||
end
|
||||
|
||||
def resource_object_for(serializer)
|
||||
resource_object = cache_check(serializer) do
|
||||
resource_object = ActiveModel::Serializer::Adapter::JsonApi::ApiObjects::ResourceIdentifier.new(serializer).as_json
|
||||
|
||||
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
|
||||
|
||||
requested_associations = fieldset.fields_for(resource_object[:type]) || '*'
|
||||
relationships = relationships_for(serializer, requested_associations)
|
||||
resource_object[:relationships] = relationships if relationships.any?
|
||||
|
||||
links = links_for(serializer)
|
||||
resource_object[:links] = links if links.any?
|
||||
|
||||
meta = meta_for(serializer)
|
||||
resource_object[:meta] = meta unless meta.nil?
|
||||
|
||||
resource_object
|
||||
end
|
||||
|
||||
def relationships_for(serializer, requested_associations)
|
||||
include_tree = ActiveModel::Serializer::IncludeTree.from_include_args(requested_associations)
|
||||
serializer.associations(include_tree).each_with_object({}) do |association, hash|
|
||||
hash[association.key] = ActiveModel::Serializer::Adapter::JsonApi::ApiObjects::Relationship.new(
|
||||
serializer,
|
||||
association.serializer,
|
||||
association.options,
|
||||
association.links,
|
||||
association.meta
|
||||
).as_json
|
||||
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
|
||||
|
||||
def meta_for(serializer)
|
||||
ActiveModel::Serializer::Adapter::JsonApi::Meta.new(serializer).as_json
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
205
lib/active_model_serializers/adapter/json_api/deserialization.rb
Normal file
205
lib/active_model_serializers/adapter/json_api/deserialization.rb
Normal 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
|
||||
@ -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
|
||||
43
lib/active_model_serializers/adapter/json_api/link.rb
Normal file
43
lib/active_model_serializers/adapter/json_api/link.rb
Normal file
@ -0,0 +1,43 @@
|
||||
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 = {}
|
||||
hash[:href] = @href if @href
|
||||
hash[:meta] = @meta if @meta
|
||||
|
||||
hash
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
attr_reader :object, :scope
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -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
|
||||
10
lib/active_model_serializers/adapter/null.rb
Normal file
10
lib/active_model_serializers/adapter/null.rb
Normal 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
|
||||
@ -3,11 +3,11 @@ module ActiveModelSerializers
|
||||
module_function
|
||||
|
||||
def jsonapi_parse(*args)
|
||||
ActiveModel::Serializer::Adapter::JsonApi::Deserialization.parse(*args)
|
||||
ActiveModelSerializers::Adapter::JsonApi::Deserialization.parse(*args)
|
||||
end
|
||||
|
||||
def jsonapi_parse!(*args)
|
||||
ActiveModel::Serializer::Adapter::JsonApi::Deserialization.parse!(*args)
|
||||
ActiveModelSerializers::Adapter::JsonApi::Deserialization.parse!(*args)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -3,7 +3,6 @@ require 'test_helper'
|
||||
module ActionController
|
||||
module Serialization
|
||||
class ImplicitSerializerTest < ActionController::TestCase
|
||||
include ActiveSupport::Testing::Stream
|
||||
class ImplicitSerializationTestController < ActionController::Base
|
||||
include SerializationTesting
|
||||
def render_using_implicit_serializer
|
||||
@ -46,7 +45,7 @@ module ActionController
|
||||
end
|
||||
|
||||
def render_array_using_implicit_serializer_and_links
|
||||
with_adapter ActiveModel::Serializer::Adapter::JsonApi do
|
||||
with_adapter ActiveModelSerializers::Adapter::JsonApi do
|
||||
@profiles = [
|
||||
Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1')
|
||||
]
|
||||
@ -438,9 +437,9 @@ module ActionController
|
||||
false
|
||||
end
|
||||
end.new
|
||||
assert_match(/adapter: false/, (capture(:stderr) do
|
||||
assert_output(nil, /adapter: false/) do
|
||||
controller.get_serializer(Profile.new)
|
||||
end))
|
||||
end
|
||||
end
|
||||
|
||||
def test_dont_warn_overridding_use_adapter_as_truthy_on_controller_instance
|
||||
@ -449,9 +448,9 @@ module ActionController
|
||||
true
|
||||
end
|
||||
end.new
|
||||
assert_equal '', (capture(:stderr) do
|
||||
assert_output(nil, '') do
|
||||
controller.get_serializer(Profile.new)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
def test_render_event_is_emmited
|
||||
|
||||
203
test/active_model_serializers/adapter_for_test.rb
Normal file
203
test/active_model_serializers/adapter_for_test.rb
Normal file
@ -0,0 +1,203 @@
|
||||
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_serializer_adapter_returns_configured__adapter
|
||||
assert_output(nil, /ActiveModelSerializers::configured_adapter/) do
|
||||
assert_equal ActiveModelSerializers::Adapter.configured_adapter, ActiveModel::Serializer.adapter
|
||||
end
|
||||
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_camelcased_symbol
|
||||
ActiveModelSerializers.config.adapter = :JsonApi
|
||||
|
||||
adapter = ActiveModelSerializers::Adapter.configured_adapter
|
||||
assert_equal ActiveModelSerializers::Adapter::JsonApi, adapter
|
||||
ensure
|
||||
ActiveModelSerializers.config.adapter = @previous_adapter
|
||||
end
|
||||
|
||||
def test_overwrite_adapter_with_string
|
||||
ActiveModelSerializers.config.adapter = 'json_api'
|
||||
|
||||
adapter = ActiveModelSerializers::Adapter.configured_adapter
|
||||
assert_equal ActiveModelSerializers::Adapter::JsonApi, adapter
|
||||
ensure
|
||||
ActiveModelSerializers.config.adapter = @previous_adapter
|
||||
end
|
||||
|
||||
def test_overwrite_adapter_with_a_camelcased_string
|
||||
ActiveModelSerializers.config.adapter = 'JsonApi'
|
||||
|
||||
adapter = ActiveModelSerializers::Adapter.configured_adapter
|
||||
assert_equal ActiveModelSerializers::Adapter::JsonApi, 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
|
||||
ensure
|
||||
ActiveModelSerializers.config.adapter = @previous_adapter
|
||||
end
|
||||
|
||||
def test_raises_exception_if_invalid_symbol_given
|
||||
ActiveModelSerializers.config.adapter = :unknown
|
||||
|
||||
assert_raises UnknownAdapterError do
|
||||
ActiveModelSerializers::Adapter.configured_adapter
|
||||
end
|
||||
ensure
|
||||
ActiveModelSerializers.config.adapter = @previous_adapter
|
||||
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
|
||||
ensure
|
||||
ActiveModelSerializers.config.adapter = @previous_adapter
|
||||
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
|
||||
@ -65,7 +65,7 @@ module ActiveModel
|
||||
|
||||
def test_logs_correct_adapter
|
||||
ActiveModel::SerializableResource.new(@post).serializable_hash
|
||||
assert_match(/ActiveModel::Serializer::Adapter::Attributes/, @logger.messages)
|
||||
assert_match(/ActiveModelSerializers::Adapter::Attributes/, @logger.messages)
|
||||
end
|
||||
|
||||
def test_logs_the_duration
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
require 'test_helper'
|
||||
module ActiveModel
|
||||
class Serializer
|
||||
module ActiveModelSerializers
|
||||
module Adapter
|
||||
class FragmentCacheTest < ActiveSupport::TestCase
|
||||
TypedRoleSerializer = Class.new(ActiveModel::Serializer) do
|
||||
@ -19,8 +18,8 @@ module ActiveModel
|
||||
@role.author = [@author]
|
||||
@role_serializer = RoleSerializer.new(@role)
|
||||
@spam_serializer = Spam::UnrelatedLinkSerializer.new(@spam)
|
||||
@role_hash = FragmentCache.new(RoleSerializer.adapter.new(@role_serializer), @role_serializer, {})
|
||||
@spam_hash = FragmentCache.new(Spam::UnrelatedLinkSerializer.adapter.new(@spam_serializer), @spam_serializer, {})
|
||||
@role_hash = FragmentCache.new(::ActiveModelSerializers::Adapter.configured_adapter.new(@role_serializer), @role_serializer, {})
|
||||
@spam_hash = FragmentCache.new(::ActiveModelSerializers::Adapter.configured_adapter.new(@spam_serializer), @spam_serializer, {})
|
||||
end
|
||||
|
||||
def test_fragment_fetch_with_virtual_attributes
|
||||
@ -47,4 +46,3 @@ module ActiveModel
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
require 'test_helper'
|
||||
|
||||
module ActiveModel
|
||||
class Serializer
|
||||
module ActiveModelSerializers
|
||||
module Adapter
|
||||
class Json
|
||||
class BelongsToTest < ActiveSupport::TestCase
|
||||
@ -19,7 +18,7 @@ module ActiveModel
|
||||
@anonymous_post.blog = nil
|
||||
|
||||
@serializer = CommentSerializer.new(@comment)
|
||||
@adapter = ActiveModel::Serializer::Adapter::Json.new(@serializer)
|
||||
@adapter = ActiveModelSerializers::Adapter::Json.new(@serializer)
|
||||
ActionController::Base.cache_store.clear
|
||||
end
|
||||
|
||||
@ -29,14 +28,14 @@ module ActiveModel
|
||||
|
||||
def test_include_nil_author
|
||||
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)
|
||||
end
|
||||
|
||||
def test_include_nil_author_with_specified_serializer
|
||||
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)
|
||||
end
|
||||
@ -44,4 +43,3 @@ module ActiveModel
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
require 'test_helper'
|
||||
|
||||
module ActiveModel
|
||||
class Serializer
|
||||
module ActiveModelSerializers
|
||||
module Adapter
|
||||
class Json
|
||||
class Collection < ActiveSupport::TestCase
|
||||
@ -23,8 +22,8 @@ module ActiveModel
|
||||
def test_with_serializer_option
|
||||
@blog.special_attribute = 'Special'
|
||||
@blog.articles = [@first_post, @second_post]
|
||||
serializer = CollectionSerializer.new([@blog], serializer: CustomBlogSerializer)
|
||||
adapter = ActiveModel::Serializer::Adapter::Json.new(serializer)
|
||||
serializer = ActiveModel::Serializer::CollectionSerializer.new([@blog], serializer: CustomBlogSerializer)
|
||||
adapter = ActiveModelSerializers::Adapter::Json.new(serializer)
|
||||
|
||||
expected = { blogs: [{
|
||||
id: 1,
|
||||
@ -35,8 +34,8 @@ module ActiveModel
|
||||
end
|
||||
|
||||
def test_include_multiple_posts
|
||||
serializer = CollectionSerializer.new([@first_post, @second_post])
|
||||
adapter = ActiveModel::Serializer::Adapter::Json.new(serializer)
|
||||
serializer = ActiveModel::Serializer::CollectionSerializer.new([@first_post, @second_post])
|
||||
adapter = ActiveModelSerializers::Adapter::Json.new(serializer)
|
||||
|
||||
expected = { posts: [{
|
||||
title: 'Hello!!',
|
||||
@ -70,15 +69,15 @@ module ActiveModel
|
||||
|
||||
def test_root_is_underscored
|
||||
virtual_value = VirtualValue.new(id: 1)
|
||||
serializer = CollectionSerializer.new([virtual_value])
|
||||
adapter = ActiveModel::Serializer::Adapter::Json.new(serializer)
|
||||
serializer = ActiveModel::Serializer::CollectionSerializer.new([virtual_value])
|
||||
adapter = ActiveModelSerializers::Adapter::Json.new(serializer)
|
||||
|
||||
assert_equal 1, adapter.serializable_hash[:virtual_values].length
|
||||
end
|
||||
|
||||
def test_include_option
|
||||
serializer = CollectionSerializer.new([@first_post, @second_post])
|
||||
adapter = ActiveModel::Serializer::Adapter::Json.new(serializer, include: '')
|
||||
serializer = ActiveModel::Serializer::CollectionSerializer.new([@first_post, @second_post])
|
||||
adapter = ActiveModelSerializers::Adapter::Json.new(serializer, include: '')
|
||||
actual = adapter.serializable_hash
|
||||
expected = { posts: [{ id: 1, title: 'Hello!!', body: 'Hello, world!!' },
|
||||
{ id: 2, title: 'New Post', body: 'Body' }] }
|
||||
@ -89,4 +88,3 @@ module ActiveModel
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
require 'test_helper'
|
||||
|
||||
module ActiveModel
|
||||
class Serializer
|
||||
module ActiveModelSerializers
|
||||
module Adapter
|
||||
class Json
|
||||
class HasManyTestTest < ActiveSupport::TestCase
|
||||
@ -23,7 +22,7 @@ module ActiveModel
|
||||
|
||||
def test_has_many
|
||||
serializer = PostSerializer.new(@post)
|
||||
adapter = ActiveModel::Serializer::Adapter::Json.new(serializer)
|
||||
adapter = ActiveModelSerializers::Adapter::Json.new(serializer)
|
||||
assert_equal([
|
||||
{ id: 1, body: 'ZOMG A COMMENT' },
|
||||
{ id: 2, body: 'ZOMG ANOTHER COMMENT' }
|
||||
@ -32,7 +31,7 @@ module ActiveModel
|
||||
|
||||
def test_has_many_with_no_serializer
|
||||
serializer = PostWithTagsSerializer.new(@post)
|
||||
adapter = ActiveModel::Serializer::Adapter::Json.new(serializer)
|
||||
adapter = ActiveModelSerializers::Adapter::Json.new(serializer)
|
||||
assert_equal({
|
||||
id: 42,
|
||||
tags: [
|
||||
@ -44,4 +43,3 @@ module ActiveModel
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
require 'test_helper'
|
||||
|
||||
module ActiveModel
|
||||
class Serializer
|
||||
module ActiveModelSerializers
|
||||
module Adapter
|
||||
class JsonApi
|
||||
class BelongsToTest < ActiveSupport::TestCase
|
||||
@ -27,7 +26,7 @@ module ActiveModel
|
||||
@author.posts = []
|
||||
|
||||
@serializer = CommentSerializer.new(@comment)
|
||||
@adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer)
|
||||
@adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer)
|
||||
ActionController::Base.cache_store.clear
|
||||
end
|
||||
|
||||
@ -38,7 +37,7 @@ module ActiveModel
|
||||
end
|
||||
|
||||
def test_includes_linked_post
|
||||
@adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, include: [:post])
|
||||
@adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer, include: [:post])
|
||||
expected = [{
|
||||
id: '42',
|
||||
type: 'posts',
|
||||
@ -56,7 +55,7 @@ module ActiveModel
|
||||
end
|
||||
|
||||
def test_limiting_linked_post_fields
|
||||
@adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, include: [:post], fields: { post: [:title, :comments, :blog, :author] })
|
||||
@adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer, include: [:post], fields: { post: [:title, :comments, :blog, :author] })
|
||||
expected = [{
|
||||
id: '42',
|
||||
type: 'posts',
|
||||
@ -74,14 +73,14 @@ module ActiveModel
|
||||
|
||||
def test_include_nil_author
|
||||
serializer = PostSerializer.new(@anonymous_post)
|
||||
adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer)
|
||||
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 = ActiveModel::Serializer::Adapter::JsonApi.new(serializer)
|
||||
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
|
||||
relationships = adapter.serializable_hash[:data][:relationships]
|
||||
expected = {
|
||||
writer: {
|
||||
@ -108,7 +107,7 @@ module ActiveModel
|
||||
|
||||
def test_include_linked_resources_with_type_name
|
||||
serializer = BlogSerializer.new(@blog)
|
||||
adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer, include: [:writer, :articles])
|
||||
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, include: [:writer, :articles])
|
||||
linked = adapter.serializable_hash[:included]
|
||||
expected = [
|
||||
{
|
||||
@ -154,4 +153,3 @@ module ActiveModel
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
require 'test_helper'
|
||||
|
||||
module ActiveModel
|
||||
class Serializer
|
||||
module ActiveModelSerializers
|
||||
module Adapter
|
||||
class JsonApi
|
||||
class CollectionTest < ActiveSupport::TestCase
|
||||
@ -19,8 +18,8 @@ module ActiveModel
|
||||
@second_post.author = @author
|
||||
@author.posts = [@first_post, @second_post]
|
||||
|
||||
@serializer = CollectionSerializer.new([@first_post, @second_post])
|
||||
@adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer)
|
||||
@serializer = ActiveModel::Serializer::CollectionSerializer.new([@first_post, @second_post])
|
||||
@adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer)
|
||||
ActionController::Base.cache_store.clear
|
||||
end
|
||||
|
||||
@ -94,4 +93,3 @@ module ActiveModel
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
require 'test_helper'
|
||||
|
||||
module ActiveModel
|
||||
class Serializer
|
||||
module ActiveModelSerializers
|
||||
module Adapter
|
||||
class JsonApi
|
||||
class FieldsTest < ActiveSupport::TestCase
|
||||
@ -86,4 +85,3 @@ module ActiveModel
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
require 'test_helper'
|
||||
|
||||
module ActiveModel
|
||||
class Serializer
|
||||
module ActiveModelSerializers
|
||||
module Adapter
|
||||
class JsonApi
|
||||
class HasManyEmbedIdsTest < ActiveSupport::TestCase
|
||||
@ -21,7 +20,7 @@ module ActiveModel
|
||||
@second_post.blog = nil
|
||||
|
||||
@serializer = AuthorSerializer.new(@author)
|
||||
@adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer)
|
||||
@adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer)
|
||||
end
|
||||
|
||||
def test_includes_comment_ids
|
||||
@ -42,4 +41,3 @@ module ActiveModel
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
require 'test_helper'
|
||||
|
||||
module ActiveModel
|
||||
class Serializer
|
||||
module ActiveModelSerializers
|
||||
module Adapter
|
||||
class JsonApi
|
||||
# Test 'has_many :assocs, serializer: AssocXSerializer'
|
||||
@ -22,7 +21,7 @@ module ActiveModel
|
||||
@post.blog = @blog
|
||||
|
||||
@serializer = PostPreviewSerializer.new(@post)
|
||||
@adapter = ActiveModel::Serializer::Adapter::JsonApi.new(
|
||||
@adapter = ActiveModelSerializers::Adapter::JsonApi.new(
|
||||
@serializer,
|
||||
include: [:comments, :author]
|
||||
)
|
||||
@ -95,4 +94,3 @@ module ActiveModel
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
require 'test_helper'
|
||||
|
||||
module ActiveModel
|
||||
class Serializer
|
||||
module ActiveModelSerializers
|
||||
module Adapter
|
||||
class JsonApi
|
||||
class HasManyTest < ActiveSupport::TestCase
|
||||
@ -30,7 +29,7 @@ module ActiveModel
|
||||
@tag = Tag.new(id: 1, name: '#hash_tag')
|
||||
@post.tags = [@tag]
|
||||
@serializer = PostSerializer.new(@post)
|
||||
@adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer)
|
||||
@adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer)
|
||||
|
||||
@virtual_value = VirtualValue.new(id: 1)
|
||||
end
|
||||
@ -42,7 +41,7 @@ module ActiveModel
|
||||
end
|
||||
|
||||
def test_includes_linked_comments
|
||||
@adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, include: [:comments])
|
||||
@adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer, include: [:comments])
|
||||
expected = [{
|
||||
id: '1',
|
||||
type: 'comments',
|
||||
@ -68,7 +67,7 @@ module ActiveModel
|
||||
end
|
||||
|
||||
def test_limit_fields_of_linked_comments
|
||||
@adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, include: [:comments], fields: { comment: [:id, :post, :author] })
|
||||
@adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer, include: [:comments], fields: { comment: [:id, :post, :author] })
|
||||
expected = [{
|
||||
id: '1',
|
||||
type: 'comments',
|
||||
@ -89,14 +88,14 @@ module ActiveModel
|
||||
|
||||
def test_no_include_linked_if_comments_is_empty
|
||||
serializer = PostSerializer.new(@post_without_comments)
|
||||
adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer)
|
||||
adapter = ActiveModelSerializers::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)
|
||||
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
|
||||
actual = adapter.serializable_hash[:data][:relationships][:articles]
|
||||
|
||||
expected = {
|
||||
@ -110,7 +109,7 @@ module ActiveModel
|
||||
|
||||
def test_has_many_with_no_serializer
|
||||
serializer = PostWithTagsSerializer.new(@post)
|
||||
adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer)
|
||||
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
|
||||
|
||||
assert_equal({
|
||||
data: {
|
||||
@ -125,7 +124,7 @@ module ActiveModel
|
||||
|
||||
def test_has_many_with_virtual_value
|
||||
serializer = VirtualValueSerializer.new(@virtual_value)
|
||||
adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer)
|
||||
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
|
||||
|
||||
assert_equal({
|
||||
data: {
|
||||
@ -142,4 +141,3 @@ module ActiveModel
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
require 'test_helper'
|
||||
|
||||
module ActiveModel
|
||||
class Serializer
|
||||
module ActiveModelSerializers
|
||||
module Adapter
|
||||
class JsonApi
|
||||
class HasOneTest < ActiveSupport::TestCase
|
||||
@ -28,7 +27,7 @@ module ActiveModel
|
||||
@virtual_value = VirtualValue.new(id: 1)
|
||||
|
||||
@serializer = AuthorSerializer.new(@author)
|
||||
@adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, include: [:bio, :posts])
|
||||
@adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer, include: [:bio, :posts])
|
||||
end
|
||||
|
||||
def test_includes_bio_id
|
||||
@ -38,7 +37,7 @@ module ActiveModel
|
||||
end
|
||||
|
||||
def test_includes_linked_bio
|
||||
@adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, include: [:bio])
|
||||
@adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer, include: [:bio])
|
||||
|
||||
expected = [
|
||||
{
|
||||
@ -59,7 +58,7 @@ module ActiveModel
|
||||
|
||||
def test_has_one_with_virtual_value
|
||||
serializer = VirtualValueSerializer.new(@virtual_value)
|
||||
adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer)
|
||||
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
|
||||
|
||||
expected = {
|
||||
data: {
|
||||
@ -78,4 +77,3 @@ module ActiveModel
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
require 'test_helper'
|
||||
|
||||
module ActiveModel
|
||||
class Serializer
|
||||
module ActiveModelSerializers
|
||||
module Adapter
|
||||
class JsonApiTest < ActiveSupport::TestCase
|
||||
def setup
|
||||
@ -20,7 +19,7 @@ module ActiveModel
|
||||
|
||||
def test_custom_keys
|
||||
serializer = PostWithCustomKeysSerializer.new(@post)
|
||||
adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer)
|
||||
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
|
||||
|
||||
assert_equal({
|
||||
reviews: { data: [
|
||||
@ -34,4 +33,3 @@ module ActiveModel
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -5,8 +5,7 @@ class NestedPostSerializer < ActiveModel::Serializer
|
||||
has_many :nested_posts
|
||||
end
|
||||
|
||||
module ActiveModel
|
||||
class Serializer
|
||||
module ActiveModelSerializers
|
||||
module Adapter
|
||||
class JsonApi
|
||||
class LinkedTest < ActiveSupport::TestCase
|
||||
@ -45,12 +44,12 @@ module ActiveModel
|
||||
end
|
||||
|
||||
def test_include_multiple_posts_and_linked_array
|
||||
serializer = CollectionSerializer.new([@first_post, @second_post])
|
||||
adapter = ActiveModel::Serializer::Adapter::JsonApi.new(
|
||||
serializer = ActiveModel::Serializer::CollectionSerializer.new([@first_post, @second_post])
|
||||
adapter = ActiveModelSerializers::Adapter::JsonApi.new(
|
||||
serializer,
|
||||
include: [:comments, author: [:bio]]
|
||||
)
|
||||
alt_adapter = ActiveModel::Serializer::Adapter::JsonApi.new(
|
||||
alt_adapter = ActiveModelSerializers::Adapter::JsonApi.new(
|
||||
serializer,
|
||||
include: [:comments, author: [:bio]]
|
||||
)
|
||||
@ -156,11 +155,11 @@ module ActiveModel
|
||||
|
||||
def test_include_multiple_posts_and_linked
|
||||
serializer = BioSerializer.new @bio1
|
||||
adapter = ActiveModel::Serializer::Adapter::JsonApi.new(
|
||||
adapter = ActiveModelSerializers::Adapter::JsonApi.new(
|
||||
serializer,
|
||||
include: [author: [:posts]]
|
||||
)
|
||||
alt_adapter = ActiveModel::Serializer::Adapter::JsonApi.new(
|
||||
alt_adapter = ActiveModelSerializers::Adapter::JsonApi.new(
|
||||
serializer,
|
||||
include: [author: [:posts]]
|
||||
)
|
||||
@ -212,7 +211,7 @@ module ActiveModel
|
||||
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)
|
||||
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
|
||||
relationships = adapter.serializable_hash[:data][:relationships]
|
||||
expected = {
|
||||
related: {
|
||||
@ -226,8 +225,8 @@ module ActiveModel
|
||||
end
|
||||
|
||||
def test_multiple_references_to_same_resource
|
||||
serializer = CollectionSerializer.new([@first_comment, @second_comment])
|
||||
adapter = ActiveModel::Serializer::Adapter::JsonApi.new(
|
||||
serializer = ActiveModel::Serializer::CollectionSerializer.new([@first_comment, @second_comment])
|
||||
adapter = ActiveModelSerializers::Adapter::JsonApi.new(
|
||||
serializer,
|
||||
include: [:post]
|
||||
)
|
||||
@ -260,7 +259,7 @@ module ActiveModel
|
||||
def test_nil_link_with_specified_serializer
|
||||
@first_post.author = nil
|
||||
serializer = PostPreviewSerializer.new(@first_post)
|
||||
adapter = ActiveModel::Serializer::Adapter::JsonApi.new(
|
||||
adapter = ActiveModelSerializers::Adapter::JsonApi.new(
|
||||
serializer,
|
||||
include: [:author]
|
||||
)
|
||||
@ -391,4 +390,3 @@ module ActiveModel
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
require 'test_helper'
|
||||
|
||||
module ActiveModel
|
||||
class Serializer
|
||||
module ActiveModelSerializers
|
||||
module Adapter
|
||||
class JsonApi
|
||||
class LinksTest < ActiveSupport::TestCase
|
||||
@ -83,4 +82,3 @@ module ActiveModel
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -4,8 +4,7 @@ require 'kaminari'
|
||||
require 'kaminari/hooks'
|
||||
::Kaminari::Hooks.init
|
||||
|
||||
module ActiveModel
|
||||
class Serializer
|
||||
module ActiveModelSerializers
|
||||
module Adapter
|
||||
class JsonApi
|
||||
class PaginationLinksTest < ActiveSupport::TestCase
|
||||
@ -112,4 +111,3 @@ module ActiveModel
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
require 'test_helper'
|
||||
module ActiveModel
|
||||
class Serializer
|
||||
module ActiveModelSerializers
|
||||
module Adapter
|
||||
class JsonApi
|
||||
module Deserialization
|
||||
@ -60,19 +59,19 @@ module ActiveModel
|
||||
end
|
||||
|
||||
def test_hash
|
||||
parsed_hash = ActiveModel::Serializer::Adapter::JsonApi::Deserialization.parse!(@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 = ActiveModel::Serializer::Adapter::JsonApi::Deserialization.parse!(@params)
|
||||
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 = ActiveModel::Serializer::Adapter::JsonApi::Deserialization.parse(p)
|
||||
parsed_hash = ActiveModelSerializers::Adapter::JsonApi::Deserialization.parse(p)
|
||||
assert_equal({}, parsed_hash)
|
||||
end
|
||||
end
|
||||
@ -80,13 +79,13 @@ module ActiveModel
|
||||
def test_illformed_payloads_unsafe
|
||||
@illformed_payloads.each do |p|
|
||||
assert_raises(InvalidDocument) do
|
||||
ActiveModel::Serializer::Adapter::JsonApi::Deserialization.parse!(p)
|
||||
ActiveModelSerializers::Adapter::JsonApi::Deserialization.parse!(p)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_filter_fields_only
|
||||
parsed_hash = ActiveModel::Serializer::Adapter::JsonApi::Deserialization.parse!(@hash, only: [:id, :title, :author])
|
||||
parsed_hash = ActiveModelSerializers::Adapter::JsonApi::Deserialization.parse!(@hash, only: [:id, :title, :author])
|
||||
expected = {
|
||||
id: 'zorglub',
|
||||
title: 'Ember Hamster',
|
||||
@ -96,7 +95,7 @@ module ActiveModel
|
||||
end
|
||||
|
||||
def test_filter_fields_except
|
||||
parsed_hash = ActiveModel::Serializer::Adapter::JsonApi::Deserialization.parse!(@hash, except: [:id, :title, :author])
|
||||
parsed_hash = ActiveModelSerializers::Adapter::JsonApi::Deserialization.parse!(@hash, except: [:id, :title, :author])
|
||||
expected = {
|
||||
src: 'http://example.com/images/productivity.png',
|
||||
photographer_id: '9',
|
||||
@ -106,7 +105,7 @@ module ActiveModel
|
||||
end
|
||||
|
||||
def test_keys
|
||||
parsed_hash = ActiveModel::Serializer::Adapter::JsonApi::Deserialization.parse!(@hash, keys: { author: :user, title: :post_title })
|
||||
parsed_hash = ActiveModelSerializers::Adapter::JsonApi::Deserialization.parse!(@hash, keys: { author: :user, title: :post_title })
|
||||
expected = {
|
||||
id: 'zorglub',
|
||||
post_title: 'Ember Hamster',
|
||||
@ -119,7 +118,7 @@ module ActiveModel
|
||||
end
|
||||
|
||||
def test_polymorphic
|
||||
parsed_hash = ActiveModel::Serializer::Adapter::JsonApi::Deserialization.parse!(@hash, polymorphic: [:photographer])
|
||||
parsed_hash = ActiveModelSerializers::Adapter::JsonApi::Deserialization.parse!(@hash, polymorphic: [:photographer])
|
||||
expected = {
|
||||
id: 'zorglub',
|
||||
title: 'Ember Hamster',
|
||||
@ -136,4 +135,3 @@ module ActiveModel
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
require 'test_helper'
|
||||
|
||||
module ActiveModel
|
||||
class Serializer
|
||||
module ActiveModelSerializers
|
||||
module Adapter
|
||||
class JsonApi
|
||||
class TopLevelJsonApiTest < ActiveSupport::TestCase
|
||||
@ -81,4 +80,3 @@ module ActiveModel
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
require 'test_helper'
|
||||
|
||||
module ActiveModel
|
||||
class Serializer
|
||||
module ActiveModelSerializers
|
||||
module Adapter
|
||||
class JsonTest < ActiveSupport::TestCase
|
||||
def setup
|
||||
@ -18,7 +17,7 @@ module ActiveModel
|
||||
@post.blog = @blog
|
||||
|
||||
@serializer = PostSerializer.new(@post)
|
||||
@adapter = ActiveModel::Serializer::Adapter::Json.new(@serializer)
|
||||
@adapter = ActiveModelSerializers::Adapter::Json.new(@serializer)
|
||||
end
|
||||
|
||||
def test_has_many
|
||||
@ -30,7 +29,7 @@ module ActiveModel
|
||||
|
||||
def test_custom_keys
|
||||
serializer = PostWithCustomKeysSerializer.new(@post)
|
||||
adapter = ActiveModel::Serializer::Adapter::Json.new(serializer)
|
||||
adapter = ActiveModelSerializers::Adapter::Json.new(serializer)
|
||||
|
||||
assert_equal({
|
||||
id: 1,
|
||||
@ -44,4 +43,3 @@ module ActiveModel
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
require 'test_helper'
|
||||
|
||||
module ActiveModel
|
||||
class Serializer
|
||||
module ActiveModelSerializers
|
||||
module Adapter
|
||||
class NullTest < ActiveSupport::TestCase
|
||||
def setup
|
||||
@ -21,5 +20,4 @@ module ActiveModel
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
require 'test_helper'
|
||||
|
||||
module ActiveModel
|
||||
class Serializer
|
||||
module ActiveModelSerializers
|
||||
class AdapterTest < ActiveSupport::TestCase
|
||||
def setup
|
||||
profile = Profile.new
|
||||
@serializer = ProfileSerializer.new(profile)
|
||||
@adapter = ActiveModel::Serializer::Adapter::Base.new(@serializer)
|
||||
@adapter = ActiveModelSerializers::Adapter::Base.new(@serializer)
|
||||
end
|
||||
|
||||
def test_serializable_hash_is_abstract_method
|
||||
@ -20,23 +19,22 @@ module ActiveModel
|
||||
end
|
||||
|
||||
def test_create_adapter
|
||||
adapter = ActiveModel::Serializer::Adapter.create(@serializer)
|
||||
assert_equal ActiveModel::Serializer::Adapter::Attributes, adapter.class
|
||||
adapter = ActiveModelSerializers::Adapter.create(@serializer)
|
||||
assert_equal ActiveModelSerializers::Adapter::Attributes, adapter.class
|
||||
end
|
||||
|
||||
def test_create_adapter_with_override
|
||||
adapter = ActiveModel::Serializer::Adapter.create(@serializer, { adapter: :json_api })
|
||||
assert_equal ActiveModel::Serializer::Adapter::JsonApi, adapter.class
|
||||
adapter = ActiveModelSerializers::Adapter.create(@serializer, { adapter: :json_api })
|
||||
assert_equal ActiveModelSerializers::Adapter::JsonApi, adapter.class
|
||||
end
|
||||
|
||||
def test_inflected_adapter_class_for_known_adapter
|
||||
ActiveSupport::Inflector.inflections(:en) { |inflect| inflect.acronym 'API' }
|
||||
klass = ActiveModel::Serializer::Adapter.adapter_class(:json_api)
|
||||
klass = ActiveModelSerializers::Adapter.adapter_class(:json_api)
|
||||
|
||||
ActiveSupport::Inflector.inflections.acronyms.clear
|
||||
|
||||
assert_equal ActiveModel::Serializer::Adapter::JsonApi, klass
|
||||
end
|
||||
assert_equal ActiveModelSerializers::Adapter::JsonApi, klass
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -6,11 +6,11 @@ module ActiveModel
|
||||
# Minitest.run_one_method isn't present in minitest 4
|
||||
if $minitest_version > 4 # rubocop:disable Style/GlobalVars
|
||||
class ArraySerializerTest < CollectionSerializerTest
|
||||
extend ActiveSupport::Testing::Stream
|
||||
extend Minitest::Assertions
|
||||
def self.run_one_method(*)
|
||||
stderr = (capture(:stderr) do
|
||||
_, stderr = capture_io do
|
||||
super
|
||||
end)
|
||||
end
|
||||
if stderr !~ /Calling deprecated ArraySerializer/
|
||||
fail Minitest::Assertion, stderr
|
||||
end
|
||||
@ -22,14 +22,13 @@ module ActiveModel
|
||||
end
|
||||
else
|
||||
class ArraySerializerTest < ActiveSupport::TestCase
|
||||
extend ActiveSupport::Testing::Stream
|
||||
def test_json_key_with_root_warns_when_using_array_serializer
|
||||
stderr = (capture(:stderr) do
|
||||
_, stderr = capture_io do
|
||||
comment = Comment.new
|
||||
post = Post.new
|
||||
serializer = ArraySerializer.new([comment, post])
|
||||
assert_equal 'comments', serializer.json_key
|
||||
end)
|
||||
end
|
||||
assert_match(/Calling deprecated ArraySerializer/, stderr)
|
||||
end
|
||||
end
|
||||
|
||||
@ -5,7 +5,7 @@ module ActiveModel
|
||||
def setup
|
||||
@resource = Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' })
|
||||
@serializer = ProfileSerializer.new(@resource)
|
||||
@adapter = ActiveModel::Serializer::Adapter.create(@serializer)
|
||||
@adapter = ActiveModelSerializers::Adapter.create(@serializer)
|
||||
@serializable_resource = ActiveModel::SerializableResource.new(@resource)
|
||||
end
|
||||
|
||||
|
||||
@ -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
|
||||
@ -14,14 +14,14 @@ module ActiveModel
|
||||
end
|
||||
|
||||
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)
|
||||
end
|
||||
|
||||
def test_attribute_inheritance_with_key
|
||||
inherited_klass = Class.new(AlternateBlogSerializer)
|
||||
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)
|
||||
end
|
||||
|
||||
@ -39,7 +39,7 @@ module ActiveModel
|
||||
attribute :name, key: :id
|
||||
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)
|
||||
end
|
||||
|
||||
@ -48,7 +48,7 @@ module ActiveModel
|
||||
attribute :name, key: :object
|
||||
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)
|
||||
end
|
||||
|
||||
@ -60,10 +60,10 @@ module ActiveModel
|
||||
attributes :type
|
||||
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)
|
||||
|
||||
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)
|
||||
end
|
||||
|
||||
|
||||
@ -1,11 +1,8 @@
|
||||
require 'test_helper'
|
||||
require 'tmpdir'
|
||||
require 'tempfile'
|
||||
module ActiveModel
|
||||
class Serializer
|
||||
module ActiveModelSerializers
|
||||
class CacheTest < ActiveSupport::TestCase
|
||||
include ActiveSupport::Testing::Stream
|
||||
|
||||
def setup
|
||||
ActionController::Base.cache_store.clear
|
||||
@comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')
|
||||
@ -150,10 +147,10 @@ module ActiveModel
|
||||
end
|
||||
|
||||
def test_object_cache_keys
|
||||
serializer = CollectionSerializer.new([@comment, @comment])
|
||||
include_tree = IncludeTree.from_include_args('*')
|
||||
serializer = ActiveModel::Serializer::CollectionSerializer.new([@comment, @comment])
|
||||
include_tree = ActiveModel::Serializer::IncludeTree.from_include_args('*')
|
||||
|
||||
actual = Serializer::Adapter::CachedSerializer.object_cache_keys(serializer, include_tree)
|
||||
actual = Adapter::CachedSerializer.object_cache_keys(serializer, include_tree)
|
||||
|
||||
assert_equal actual.size, 3
|
||||
assert actual.any? { |key| key == 'comment/1' }
|
||||
@ -162,12 +159,12 @@ module ActiveModel
|
||||
end
|
||||
|
||||
def test_cached_attributes
|
||||
serializer = CollectionSerializer.new([@comment, @comment])
|
||||
serializer = ActiveModel::Serializer::CollectionSerializer.new([@comment, @comment])
|
||||
|
||||
Timecop.freeze(Time.now) do
|
||||
render_object_with_cache(@comment)
|
||||
|
||||
attributes = ActiveModel::Serializer::Adapter::Attributes.new(serializer)
|
||||
attributes = Adapter::Attributes.new(serializer)
|
||||
attributes.send(:cache_attributes)
|
||||
cached_attributes = attributes.instance_variable_get(:@cached_attributes)
|
||||
|
||||
@ -223,10 +220,10 @@ module ActiveModel
|
||||
def test_warn_on_serializer_not_defined_in_file
|
||||
called = false
|
||||
serializer = Class.new(ActiveModel::Serializer)
|
||||
assert_match(/_cache_digest/, (capture(:stderr) do
|
||||
assert_output(nil, /_cache_digest/) do
|
||||
serializer.digest_caller_file('')
|
||||
called = true
|
||||
end))
|
||||
end
|
||||
assert called
|
||||
end
|
||||
|
||||
@ -237,5 +234,3 @@ module ActiveModel
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
require 'test_helper'
|
||||
module ActiveModel
|
||||
class Serializer
|
||||
module ActiveModelSerializers
|
||||
module Adapter
|
||||
class CachedSerializerTest < ActiveSupport::TestCase
|
||||
def test_cached_false_without_cache_store
|
||||
@ -74,8 +73,7 @@ module ActiveModel
|
||||
serializer._cache_options = nil
|
||||
yield serializer if block_given?
|
||||
serializer_instance = serializer.new(Object)
|
||||
ActiveModel::Serializer::Adapter::CachedSerializer.new(serializer_instance)
|
||||
end
|
||||
CachedSerializer.new(serializer_instance)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,50 +0,0 @@
|
||||
# Use cleaner stream testing interface from Rails 5 if available
|
||||
# see https://github.com/rails/rails/blob/29959eb59d/activesupport/lib/active_support/testing/stream.rb
|
||||
begin
|
||||
require 'active_support/testing/stream'
|
||||
rescue LoadError
|
||||
require 'tempfile'
|
||||
module ActiveSupport
|
||||
module Testing
|
||||
module Stream #:nodoc:
|
||||
private
|
||||
|
||||
def silence_stream(stream)
|
||||
old_stream = stream.dup
|
||||
stream.reopen(IO::NULL)
|
||||
stream.sync = true
|
||||
yield
|
||||
ensure
|
||||
stream.reopen(old_stream)
|
||||
old_stream.close
|
||||
end
|
||||
|
||||
def quietly
|
||||
silence_stream(STDOUT) do
|
||||
silence_stream(STDERR) do
|
||||
yield
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def capture(stream)
|
||||
stream = stream.to_s
|
||||
captured_stream = Tempfile.new(stream)
|
||||
stream_io = eval("$#{stream}") # rubocop:disable Lint/Eval
|
||||
origin_stream = stream_io.dup
|
||||
stream_io.reopen(captured_stream)
|
||||
|
||||
yield
|
||||
|
||||
stream_io.rewind
|
||||
return captured_stream.read
|
||||
ensure
|
||||
captured_stream.close
|
||||
captured_stream.unlink
|
||||
stream_io.reopen(origin_stream)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -43,8 +43,6 @@ end
|
||||
require 'minitest/reporters'
|
||||
Minitest::Reporters.use!
|
||||
|
||||
require 'support/stream_capture'
|
||||
|
||||
require 'support/rails_app'
|
||||
|
||||
require 'support/test_case'
|
||||
|
||||
Loading…
Reference in New Issue
Block a user