From 252f9c4ae932e6280dfe68605d495b208fe22ba7 Mon Sep 17 00:00:00 2001 From: bobba surendranath chowdary Date: Thu, 4 Feb 2016 11:49:27 +0530 Subject: [PATCH 1/3] Moved the adapter and adapter folder to active_model_serializers folder and changed the module namespace Changed the namespace in adapters and folder to active_model_serializers from active_model::serializer Changed namespace of adapters in serializers and other folders Moved adapter_for_test file to active_model_serializers folder and changed namespace of adapter inside the test file Require ActiveSupport's string/inflections We depend on string/inflections to define String#underscore. Refactor JsonApi adapter to avoid redundant computations. Update readme.md to link to v0.10.0.rc4 changed namespace of adapter folder testcases Changed all namespaces of adapter under active_moder_serializers Namespaced IncludeTree which is from serializer module, so needed to namespace it properly Fixed wrong namsepacing of fieldset namespace change in deserializer json_api Fixed the namespace for collection serializer when used inside adapter, changed namespace for adapter to new namespace which I had forgotten previously Modified logging test and adapter test cases to make the testcases pass Changed the yardoc links,as old links are not taking to documentation pages,proper links for 0.10,0.9 and 0.8 in rubydoc Rubocop errors are fixed by underscore naming unused variables Moved the require of adapter to serializable resource Remoeved adapter dependency inside serializer and added warning to Serializer::adapter method Fixed frament cache test which is calling Serializer.adapter Changed the name of lookup_adapter_from_config to configured_adapter Changed the docs which will show the new namespace of adapters Rubocop fix --- CHANGELOG.md | 1 + README.md | 4 +- docs/general/adapters.md | 26 +- docs/general/instrumentation.md | 2 +- docs/rfcs/0000-namespace.md | 2 +- lib/active_model/serializable_resource.rb | 3 +- lib/active_model/serializer.rb | 9 +- lib/active_model/serializer/adapter.rb | 91 --- .../serializer/adapter/attributes.rb | 66 -- lib/active_model/serializer/adapter/base.rb | 58 -- .../serializer/adapter/cached_serializer.rb | 45 -- .../serializer/adapter/fragment_cache.rb | 111 --- lib/active_model/serializer/adapter/json.rb | 21 - .../serializer/adapter/json/fragment_cache.rb | 13 - .../serializer/adapter/json_api.rb | 223 ------ .../adapter/json_api/deserialization.rb | 207 ------ .../adapter/json_api/fragment_cache.rb | 21 - .../serializer/adapter/json_api/link.rb | 44 -- .../adapter/json_api/pagination_links.rb | 58 -- lib/active_model/serializer/adapter/null.rb | 11 - lib/active_model_serializers.rb | 1 + lib/active_model_serializers/adapter.rb | 93 +++ .../adapter/attributes.rb | 64 ++ lib/active_model_serializers/adapter/base.rb | 56 ++ .../adapter/cached_serializer.rb | 43 ++ .../adapter/fragment_cache.rb | 109 +++ lib/active_model_serializers/adapter/json.rb | 19 + .../adapter/json/fragment_cache.rb | 11 + .../adapter/json_api.rb | 208 ++++++ .../adapter/json_api/deserialization.rb | 205 ++++++ .../adapter/json_api/fragment_cache.rb | 18 + .../adapter/json_api/link.rb | 42 ++ .../adapter/json_api/pagination_links.rb | 56 ++ lib/active_model_serializers/adapter/null.rb | 10 + .../deserialization.rb | 4 +- .../adapter_for_test.rb | 164 +++++ test/active_model_serializers/logging_test.rb | 2 +- test/adapter/fragment_cache_test.rb | 58 +- test/adapter/json/belongs_to_test.rb | 66 +- test/adapter/json/collection_test.rb | 148 ++-- test/adapter/json/has_many_test.rb | 74 +- test/adapter/json_api/belongs_to_test.rb | 256 ++++--- test/adapter/json_api/collection_test.rb | 162 +++-- test/adapter/json_api/fields_test.rb | 138 ++-- .../json_api/has_many_embed_ids_test.rb | 66 +- .../has_many_explicit_serializer_test.rb | 152 ++--- test/adapter/json_api/has_many_test.rb | 244 ++++--- test/adapter/json_api/has_one_test.rb | 120 ++-- test/adapter/json_api/json_api_test.rb | 58 +- test/adapter/json_api/linked_test.rb | 640 +++++++++--------- test/adapter/json_api/links_test.rb | 94 ++- .../adapter/json_api/pagination_links_test.rb | 166 +++-- test/adapter/json_api/parse_test.rb | 230 ++++--- .../json_api/resource_type_config_test.rb | 108 ++- .../adapter/json_api/toplevel_jsonapi_test.rb | 124 ++-- test/adapter/json_test.rb | 72 +- test/adapter/null_test.rb | 28 +- test/adapter_test.rb | 56 +- test/serializable_resource_test.rb | 2 +- test/serializers/adapter_for_test.rb | 166 ----- test/serializers/attribute_test.rb | 12 +- 61 files changed, 2645 insertions(+), 2716 deletions(-) delete mode 100644 lib/active_model/serializer/adapter.rb delete mode 100644 lib/active_model/serializer/adapter/attributes.rb delete mode 100644 lib/active_model/serializer/adapter/base.rb delete mode 100644 lib/active_model/serializer/adapter/cached_serializer.rb delete mode 100644 lib/active_model/serializer/adapter/fragment_cache.rb delete mode 100644 lib/active_model/serializer/adapter/json.rb delete mode 100644 lib/active_model/serializer/adapter/json/fragment_cache.rb delete mode 100644 lib/active_model/serializer/adapter/json_api.rb delete mode 100644 lib/active_model/serializer/adapter/json_api/deserialization.rb delete mode 100644 lib/active_model/serializer/adapter/json_api/fragment_cache.rb delete mode 100644 lib/active_model/serializer/adapter/json_api/link.rb delete mode 100644 lib/active_model/serializer/adapter/json_api/pagination_links.rb delete mode 100644 lib/active_model/serializer/adapter/null.rb create mode 100644 lib/active_model_serializers/adapter.rb create mode 100644 lib/active_model_serializers/adapter/attributes.rb create mode 100644 lib/active_model_serializers/adapter/base.rb create mode 100644 lib/active_model_serializers/adapter/cached_serializer.rb create mode 100644 lib/active_model_serializers/adapter/fragment_cache.rb create mode 100644 lib/active_model_serializers/adapter/json.rb create mode 100644 lib/active_model_serializers/adapter/json/fragment_cache.rb create mode 100644 lib/active_model_serializers/adapter/json_api.rb create mode 100644 lib/active_model_serializers/adapter/json_api/deserialization.rb create mode 100644 lib/active_model_serializers/adapter/json_api/fragment_cache.rb create mode 100644 lib/active_model_serializers/adapter/json_api/link.rb create mode 100644 lib/active_model_serializers/adapter/json_api/pagination_links.rb create mode 100644 lib/active_model_serializers/adapter/null.rb create mode 100644 test/active_model_serializers/adapter_for_test.rb delete mode 100644 test/serializers/adapter_for_test.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fd7e5fc9..721f42ac0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ Breaking changes: Features: Fixes: +- [#1488](https://github.com/rails-api/active_model_serializers/pull/1488) Require ActiveSupport's string inflections (@nate00) Misc: ### v0.10.0.rc4 (2016/01/27 11:00 +00:00) diff --git a/README.md b/README.md index 2106d34e7..8a48cae10 100644 --- a/README.md +++ b/README.md @@ -8,10 +8,12 @@ ## Documentation - [0.10 (master) Documentation](https://github.com/rails-api/active_model_serializers/tree/master) - - [![API Docs](http://img.shields.io/badge/yard-docs-blue.svg)](http://www.rubydoc.info/github/rails-api/active_model_serializers) + - [![API Docs](http://img.shields.io/badge/yard-docs-blue.svg)](http://www.rubydoc.info/github/rails-api/active_model_serializers/v0.10.0.rc4) - [Guides](docs) - [0.9 (0-9-stable) Documentation](https://github.com/rails-api/active_model_serializers/tree/0-9-stable) + - [![API Docs](http://img.shields.io/badge/yard-docs-blue.svg)](http://www.rubydoc.info/github/rails-api/active_model_serializers/0-9-stable) - [0.8 (0-8-stable) Documentation](https://github.com/rails-api/active_model_serializers/tree/0-8-stable) + - [![API Docs](http://img.shields.io/badge/yard-docs-blue.svg)](http://www.rubydoc.info/github/rails-api/active_model_serializers/0-8-stable) ## About diff --git a/docs/general/adapters.md b/docs/general/adapters.md index 262c4418f..fa2a15319 100644 --- a/docs/general/adapters.md +++ b/docs/general/adapters.md @@ -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) diff --git a/docs/general/instrumentation.md b/docs/general/instrumentation.md index ba6a1ffd8..560494ace 100644 --- a/docs/general/instrumentation.md +++ b/docs/general/instrumentation.md @@ -17,7 +17,7 @@ Payload (example): ```ruby { serializer: PostSerializer, - adapter: ActiveModel::Serializer::Adapter::Attributes + adapter: ActiveModelSerializers::Adapter::Attributes } ``` diff --git a/docs/rfcs/0000-namespace.md b/docs/rfcs/0000-namespace.md index 9cff50a34..9532cae5c 100644 --- a/docs/rfcs/0000-namespace.md +++ b/docs/rfcs/0000-namespace.md @@ -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 `ActiveModelSerializers::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 diff --git a/lib/active_model/serializable_resource.rb b/lib/active_model/serializable_resource.rb index c91eea170..ec83fcfc6 100644 --- a/lib/active_model/serializable_resource.rb +++ b/lib/active_model/serializable_resource.rb @@ -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 @@ def serialization_scope_name=(scope_name) 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 diff --git a/lib/active_model/serializer.rb b/lib/active_model/serializer.rb index 714ff65d4..29b765112 100644 --- a/lib/active_model/serializer.rb +++ b/lib/active_model/serializer.rb @@ -21,7 +21,8 @@ class Serializer include Caching include Links include Type - require 'active_model/serializer/adapter' + # Deprecated + require 'active_model_serializers/adapter' # @param resource [ActiveRecord::Base, ActiveModelSerializers::Model] # @return [ActiveModel::Serializer] @@ -40,9 +41,11 @@ def self.serializer_for(resource, options = {}) 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 diff --git a/lib/active_model/serializer/adapter.rb b/lib/active_model/serializer/adapter.rb deleted file mode 100644 index 61a86e1ce..000000000 --- a/lib/active_model/serializer/adapter.rb +++ /dev/null @@ -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 - def adapter_map - ADAPTER_MAP - end - - # @return [Array] 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 diff --git a/lib/active_model/serializer/adapter/attributes.rb b/lib/active_model/serializer/adapter/attributes.rb deleted file mode 100644 index 49dea8607..000000000 --- a/lib/active_model/serializer/adapter/attributes.rb +++ /dev/null @@ -1,66 +0,0 @@ -module ActiveModel - class Serializer - module Adapter - class Attributes < Base - def initialize(serializer, options = {}) - super - @include_tree = IncludeTree.from_include_args(options[:include] || '*') - end - - def serializable_hash(options = nil) - options ||= {} - - if serializer.respond_to?(:each) - serializable_hash_for_collection(options) - else - serializable_hash_for_single_resource(options) - end - end - - def fragment_cache(cached_hash, non_cached_hash) - Json::FragmentCache.new.fragment_cache(cached_hash, non_cached_hash) - end - - private - - def serializable_hash_for_collection(options) - serializer.map { |s| Attributes.new(s, instance_options).serializable_hash(options) } - end - - def serializable_hash_for_single_resource(options) - resource = resource_object_for(options) - relationships = resource_relationships(options) - resource.merge!(relationships) - end - - def resource_relationships(options) - relationships = {} - serializer.associations(@include_tree).each do |association| - relationships[association.key] = relationship_value_for(association, options) - end - - relationships - end - - def relationship_value_for(association, options) - return association.options[:virtual_value] if association.options[:virtual_value] - return unless association.serializer && association.serializer.object - - opts = instance_options.merge(include: @include_tree[association.key]) - Attributes.new(association.serializer, opts).serializable_hash(options) - end - - # no-op: Attributes adapter does not include meta data, because it does not support root. - def include_meta(json) - json - end - - def resource_object_for(options) - cache_check(serializer) do - serializer.attributes(options[:fields]) - end - end - end - end - end -end diff --git a/lib/active_model/serializer/adapter/base.rb b/lib/active_model/serializer/adapter/base.rb deleted file mode 100644 index fc06848a9..000000000 --- a/lib/active_model/serializer/adapter/base.rb +++ /dev/null @@ -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 diff --git a/lib/active_model/serializer/adapter/cached_serializer.rb b/lib/active_model/serializer/adapter/cached_serializer.rb deleted file mode 100644 index 35b101689..000000000 --- a/lib/active_model/serializer/adapter/cached_serializer.rb +++ /dev/null @@ -1,45 +0,0 @@ -module ActiveModel - class Serializer - module Adapter - class CachedSerializer - def initialize(serializer) - @cached_serializer = serializer - @klass = @cached_serializer.class - end - - def cache_check(adapter_instance) - if cached? - @klass._cache.fetch(cache_key, @klass._cache_options) do - yield - end - elsif fragment_cached? - FragmentCache.new(adapter_instance, @cached_serializer, adapter_instance.instance_options).fetch - else - yield - end - end - - def cached? - @klass._cache && !@klass._cache_only && !@klass._cache_except - end - - def fragment_cached? - @klass._cache_only && !@klass._cache_except || !@klass._cache_only && @klass._cache_except - end - - def cache_key - parts = [] - parts << object_cache_key - parts << @klass._cache_digest unless @klass._cache_options && @klass._cache_options[:skip_digest] - parts.join('/') - end - - def object_cache_key - object_time_safe = @cached_serializer.object.updated_at - object_time_safe = object_time_safe.strftime('%Y%m%d%H%M%S%9N') if object_time_safe.respond_to?(:strftime) - (@klass._cache_key) ? "#{@klass._cache_key}/#{@cached_serializer.object.id}-#{object_time_safe}" : @cached_serializer.object.cache_key - end - end - end - end -end diff --git a/lib/active_model/serializer/adapter/fragment_cache.rb b/lib/active_model/serializer/adapter/fragment_cache.rb deleted file mode 100644 index 5c97a64a4..000000000 --- a/lib/active_model/serializer/adapter/fragment_cache.rb +++ /dev/null @@ -1,111 +0,0 @@ -module ActiveModel - class Serializer - module Adapter - class FragmentCache - attr_reader :serializer - - def initialize(adapter, serializer, options) - @instance_options = options - @adapter = adapter - @serializer = serializer - end - - # TODO: Use Serializable::Resource - # TODO: call +constantize+ less - # 1. Create a CachedSerializer and NonCachedSerializer from the serializer class - # 2. Serialize the above two with the given adapter - # 3. Pass their serializations to the adapter +::fragment_cache+ - def fetch - klass = serializer.class - # It will split the serializer into two, one that will be cached and one that will not - serializers = fragment_serializer(serializer.object.class.name, klass) - - # Instantiate both serializers - cached_serializer = serializers[:cached].constantize.new(serializer.object) - non_cached_serializer = serializers[:non_cached].constantize.new(serializer.object) - - cached_adapter = adapter.class.new(cached_serializer, instance_options) - non_cached_adapter = adapter.class.new(non_cached_serializer, instance_options) - - # Get serializable hash from both - cached_hash = cached_adapter.serializable_hash - non_cached_hash = non_cached_adapter.serializable_hash - - # Merge both results - adapter.fragment_cache(cached_hash, non_cached_hash) - end - - protected - - attr_reader :instance_options, :adapter - - private - - # Given a serializer class and a hash of its cached and non-cached serializers - # 1. Determine cached attributes from serializer class options - # 2. Add cached attributes to cached Serializer - # 3. Add non-cached attributes to non-cached Serializer - def cached_attributes(klass, serializers) - attributes = serializer.class._attributes - cached_attributes = (klass._cache_only) ? klass._cache_only : attributes.reject { |attr| klass._cache_except.include?(attr) } - non_cached_attributes = attributes - cached_attributes - - cached_attributes.each do |attribute| - options = serializer.class._attributes_keys[attribute] - options ||= {} - # Add cached attributes to cached Serializer - serializers[:cached].constantize.attribute(attribute, options) - end - - non_cached_attributes.each do |attribute| - options = serializer.class._attributes_keys[attribute] - options ||= {} - # Add non-cached attributes to non-cached Serializer - serializers[:non_cached].constantize.attribute(attribute, options) - end - end - - # Given a resource name and its serializer's class - # 1. Dyanmically creates a CachedSerializer and NonCachedSerializer - # for a given class 'name' - # 2. Call - # CachedSerializer.cache(serializer._cache_options) - # CachedSerializer.fragmented(serializer) - # NontCachedSerializer.cache(serializer._cache_options) - # 3. Build a hash keyed to the +cached+ and +non_cached+ serializers - # 4. Call +cached_attributes+ on the serializer class and the above hash - # 5. Return the hash - # - # @example - # When +name+ is User::Admin - # creates the Serializer classes (if they don't exist). - # User_AdminCachedSerializer - # User_AdminNOnCachedSerializer - # - def fragment_serializer(name, klass) - cached = "#{to_valid_const_name(name)}CachedSerializer" - non_cached = "#{to_valid_const_name(name)}NonCachedSerializer" - - Object.const_set cached, Class.new(ActiveModel::Serializer) unless Object.const_defined?(cached) - Object.const_set non_cached, Class.new(ActiveModel::Serializer) unless Object.const_defined?(non_cached) - - klass._cache_options ||= {} - klass._cache_options[:key] = klass._cache_key if klass._cache_key - - cached.constantize.cache(klass._cache_options) - - cached.constantize.fragmented(serializer) - non_cached.constantize.fragmented(serializer) - - serializers = { cached: cached, non_cached: non_cached } - cached_attributes(klass, serializers) - serializers - end - - def to_valid_const_name(name) - name.gsub('::', '_') - end - end - end - end -end diff --git a/lib/active_model/serializer/adapter/json.rb b/lib/active_model/serializer/adapter/json.rb deleted file mode 100644 index ab81f5710..000000000 --- a/lib/active_model/serializer/adapter/json.rb +++ /dev/null @@ -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 diff --git a/lib/active_model/serializer/adapter/json/fragment_cache.rb b/lib/active_model/serializer/adapter/json/fragment_cache.rb deleted file mode 100644 index ff312cd93..000000000 --- a/lib/active_model/serializer/adapter/json/fragment_cache.rb +++ /dev/null @@ -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 diff --git a/lib/active_model/serializer/adapter/json_api.rb b/lib/active_model/serializer/adapter/json_api.rb deleted file mode 100644 index 744d62e47..000000000 --- a/lib/active_model/serializer/adapter/json_api.rb +++ /dev/null @@ -1,223 +0,0 @@ -module ActiveModel - class Serializer - module Adapter - class JsonApi < Base - extend ActiveSupport::Autoload - autoload :PaginationLinks - autoload :FragmentCache - autoload :Link - autoload :Deserialization - - # TODO: if we like this abstraction and other API objects to it, - # then extract to its own file and require it. - module ApiObjects - module JsonApi - ActiveModelSerializers.config.jsonapi_version = '1.0' - ActiveModelSerializers.config.jsonapi_toplevel_meta = {} - # Make JSON API top-level jsonapi member opt-in - # ref: http://jsonapi.org/format/#document-top-level - ActiveModelSerializers.config.jsonapi_include_toplevel_object = false - - module_function - - def add!(hash) - hash.merge!(object) if include_object? - end - - def include_object? - ActiveModelSerializers.config.jsonapi_include_toplevel_object - end - - # TODO: see if we can cache this - def object - object = { - jsonapi: { - version: ActiveModelSerializers.config.jsonapi_version, - meta: ActiveModelSerializers.config.jsonapi_toplevel_meta - } - } - object[:jsonapi].reject! { |_, v| v.blank? } - - object - end - end - end - - def initialize(serializer, options = {}) - super - @include_tree = IncludeTree.from_include_args(options[:include]) - @fieldset = options[:fieldset] || ActiveModel::Serializer::Fieldset.new(options.delete(:fields)) - end - - def serializable_hash(options = nil) - options ||= {} - - hash = - if serializer.respond_to?(:each) - serializable_hash_for_collection(options) - else - serializable_hash_for_single_resource - end - - ApiObjects::JsonApi.add!(hash) - - if instance_options[:links] - hash[:links] ||= {} - hash[:links].update(instance_options[:links]) - end - - hash - end - - def fragment_cache(cached_hash, non_cached_hash) - root = false if instance_options.include?(:include) - ActiveModel::Serializer::Adapter::JsonApi::FragmentCache.new.fragment_cache(root, cached_hash, non_cached_hash) - end - - protected - - attr_reader :fieldset - - private - - def serializable_hash_for_collection(options) - hash = { data: [] } - included = [] - serializer.each do |s| - result = self.class.new(s, instance_options.merge(fieldset: fieldset)).serializable_hash(options) - hash[:data] << result[:data] - next unless result[:included] - - included |= result[:included] - end - - included.delete_if { |resource| hash[:data].include?(resource) } - hash[:included] = included if included.any? - - if serializer.paginated? - hash[:links] ||= {} - hash[:links].update(pagination_links_for(serializer, options)) - end - - hash - end - - def serializable_hash_for_single_resource - primary_data = resource_object_for(serializer) - - hash = { data: primary_data } - - included = included_resources(@include_tree, [primary_data]) - hash[:included] = included if included.any? - - hash - end - - def resource_identifier_type_for(serializer) - return serializer._type if serializer._type - if ActiveModelSerializers.config.jsonapi_resource_type == :singular - serializer.object.class.model_name.singular - else - serializer.object.class.model_name.plural - end - end - - def resource_identifier_id_for(serializer) - if serializer.respond_to?(:id) - serializer.id - else - serializer.object.id - end - end - - def resource_identifier_for(serializer) - type = resource_identifier_type_for(serializer) - id = resource_identifier_id_for(serializer) - - { id: id.to_s, type: type } - end - - def attributes_for(serializer, fields) - serializer.attributes(fields).except(:id) - end - - def resource_object_for(serializer) - resource_object = cache_check(serializer) do - resource_object = resource_identifier_for(serializer) - - requested_fields = fieldset && fieldset.fields_for(resource_object[:type]) - attributes = attributes_for(serializer, requested_fields) - resource_object[:attributes] = attributes if attributes.any? - resource_object - end - - relationships = relationships_for(serializer) - resource_object[:relationships] = relationships if relationships.any? - - links = links_for(serializer) - resource_object[:links] = links if links.any? - - resource_object - end - - def relationship_value_for(serializer, options = {}) - if serializer.respond_to?(:each) - serializer.map { |s| resource_identifier_for(s) } - else - if options[:virtual_value] - options[:virtual_value] - elsif serializer && serializer.object - resource_identifier_for(serializer) - end - end - end - - def relationships_for(serializer) - resource_type = resource_identifier_type_for(serializer) - requested_associations = fieldset.fields_for(resource_type) || '*' - include_tree = IncludeTree.from_include_args(requested_associations) - serializer.associations(include_tree).each_with_object({}) do |association, hash| - hash[association.key] = { data: relationship_value_for(association.serializer, association.options) } - end - end - - def included_resources(include_tree, primary_data) - included = [] - - serializer.associations(include_tree).each do |association| - add_included_resources_for(association.serializer, include_tree[association.key], primary_data, included) - end - - included - end - - def add_included_resources_for(serializer, include_tree, primary_data, included) - if serializer.respond_to?(:each) - serializer.each { |s| add_included_resources_for(s, include_tree, primary_data, included) } - else - return unless serializer && serializer.object - - resource_object = resource_object_for(serializer) - - return if included.include?(resource_object) || primary_data.include?(resource_object) - included.push(resource_object) - - serializer.associations(include_tree).each do |association| - add_included_resources_for(association.serializer, include_tree[association.key], primary_data, included) - end - end - end - - def links_for(serializer) - serializer._links.each_with_object({}) do |(name, value), hash| - hash[name] = Link.new(serializer, value).as_json - end - end - - def pagination_links_for(serializer, options) - JsonApi::PaginationLinks.new(serializer.object, options[:serialization_context]).serializable_hash(options) - end - end - end - end -end diff --git a/lib/active_model/serializer/adapter/json_api/deserialization.rb b/lib/active_model/serializer/adapter/json_api/deserialization.rb deleted file mode 100644 index 5f35a882d..000000000 --- a/lib/active_model/serializer/adapter/json_api/deserialization.rb +++ /dev/null @@ -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 diff --git a/lib/active_model/serializer/adapter/json_api/fragment_cache.rb b/lib/active_model/serializer/adapter/json_api/fragment_cache.rb deleted file mode 100644 index 7dbc11795..000000000 --- a/lib/active_model/serializer/adapter/json_api/fragment_cache.rb +++ /dev/null @@ -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 diff --git a/lib/active_model/serializer/adapter/json_api/link.rb b/lib/active_model/serializer/adapter/json_api/link.rb deleted file mode 100644 index bed230c33..000000000 --- a/lib/active_model/serializer/adapter/json_api/link.rb +++ /dev/null @@ -1,44 +0,0 @@ -module ActiveModel - class Serializer - module Adapter - class JsonApi - class Link - def initialize(serializer, value) - @object = serializer.object - @scope = serializer.scope - - # Use the return value of the block unless it is nil. - if value.respond_to?(:call) - @value = instance_eval(&value) - else - @value = value - end - end - - def href(value) - @href = value - nil - end - - def meta(value) - @meta = value - nil - end - - def as_json - return @value if @value - - hash = { href: @href } - hash.merge!(meta: @meta) if @meta - - hash - end - - protected - - attr_reader :object, :scope - end - end - end - end -end diff --git a/lib/active_model/serializer/adapter/json_api/pagination_links.rb b/lib/active_model/serializer/adapter/json_api/pagination_links.rb deleted file mode 100644 index 9c437057b..000000000 --- a/lib/active_model/serializer/adapter/json_api/pagination_links.rb +++ /dev/null @@ -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 diff --git a/lib/active_model/serializer/adapter/null.rb b/lib/active_model/serializer/adapter/null.rb deleted file mode 100644 index f398380fd..000000000 --- a/lib/active_model/serializer/adapter/null.rb +++ /dev/null @@ -1,11 +0,0 @@ -module ActiveModel - class Serializer - module Adapter - class Null < Base - def serializable_hash(options = nil) - {} - end - end - end - end -end diff --git a/lib/active_model_serializers.rb b/lib/active_model_serializers.rb index d92823b5a..47e14208f 100644 --- a/lib/active_model_serializers.rb +++ b/lib/active_model_serializers.rb @@ -1,6 +1,7 @@ require 'active_model' require 'active_support' require 'active_support/core_ext/object/with_options' +require 'active_support/core_ext/string/inflections' module ActiveModelSerializers extend ActiveSupport::Autoload autoload :Model diff --git a/lib/active_model_serializers/adapter.rb b/lib/active_model_serializers/adapter.rb new file mode 100644 index 000000000..679b862b2 --- /dev/null +++ b/lib/active_model_serializers/adapter.rb @@ -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 + def adapter_map + ADAPTER_MAP + end + + # @return [Array] 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 diff --git a/lib/active_model_serializers/adapter/attributes.rb b/lib/active_model_serializers/adapter/attributes.rb new file mode 100644 index 000000000..34fb2583f --- /dev/null +++ b/lib/active_model_serializers/adapter/attributes.rb @@ -0,0 +1,64 @@ +module ActiveModelSerializers + module Adapter + class Attributes < Base + def initialize(serializer, options = {}) + super + @include_tree = ActiveModel::Serializer::IncludeTree.from_include_args(options[:include] || '*') + end + + def serializable_hash(options = nil) + options ||= {} + + if serializer.respond_to?(:each) + serializable_hash_for_collection(options) + else + serializable_hash_for_single_resource(options) + end + end + + def fragment_cache(cached_hash, non_cached_hash) + Json::FragmentCache.new.fragment_cache(cached_hash, non_cached_hash) + end + + private + + def serializable_hash_for_collection(options) + serializer.map { |s| Attributes.new(s, instance_options).serializable_hash(options) } + end + + def serializable_hash_for_single_resource(options) + resource = resource_object_for(options) + relationships = resource_relationships(options) + resource.merge!(relationships) + end + + def resource_relationships(options) + relationships = {} + serializer.associations(@include_tree).each do |association| + relationships[association.key] = relationship_value_for(association, options) + end + + relationships + end + + def relationship_value_for(association, options) + return association.options[:virtual_value] if association.options[:virtual_value] + return unless association.serializer && association.serializer.object + + opts = instance_options.merge(include: @include_tree[association.key]) + Attributes.new(association.serializer, opts).serializable_hash(options) + end + + # no-op: Attributes adapter does not include meta data, because it does not support root. + def include_meta(json) + json + end + + def resource_object_for(options) + cache_check(serializer) do + serializer.attributes(options[:fields]) + end + end + end + end +end diff --git a/lib/active_model_serializers/adapter/base.rb b/lib/active_model_serializers/adapter/base.rb new file mode 100644 index 000000000..9b31cffcf --- /dev/null +++ b/lib/active_model_serializers/adapter/base.rb @@ -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 diff --git a/lib/active_model_serializers/adapter/cached_serializer.rb b/lib/active_model_serializers/adapter/cached_serializer.rb new file mode 100644 index 000000000..685c5ef48 --- /dev/null +++ b/lib/active_model_serializers/adapter/cached_serializer.rb @@ -0,0 +1,43 @@ +module ActiveModelSerializers + module Adapter + class CachedSerializer + def initialize(serializer) + @cached_serializer = serializer + @klass = @cached_serializer.class + end + + def cache_check(adapter_instance) + if cached? + @klass._cache.fetch(cache_key, @klass._cache_options) do + yield + end + elsif fragment_cached? + FragmentCache.new(adapter_instance, @cached_serializer, adapter_instance.instance_options).fetch + else + yield + end + end + + def cached? + @klass._cache && !@klass._cache_only && !@klass._cache_except + end + + def fragment_cached? + @klass._cache_only && !@klass._cache_except || !@klass._cache_only && @klass._cache_except + end + + def cache_key + parts = [] + parts << object_cache_key + parts << @klass._cache_digest unless @klass._cache_options && @klass._cache_options[:skip_digest] + parts.join('/') + end + + def object_cache_key + object_time_safe = @cached_serializer.object.updated_at + object_time_safe = object_time_safe.strftime('%Y%m%d%H%M%S%9N') if object_time_safe.respond_to?(:strftime) + (@klass._cache_key) ? "#{@klass._cache_key}/#{@cached_serializer.object.id}-#{object_time_safe}" : @cached_serializer.object.cache_key + end + end + end +end diff --git a/lib/active_model_serializers/adapter/fragment_cache.rb b/lib/active_model_serializers/adapter/fragment_cache.rb new file mode 100644 index 000000000..c7a2b059d --- /dev/null +++ b/lib/active_model_serializers/adapter/fragment_cache.rb @@ -0,0 +1,109 @@ +module ActiveModelSerializers + module Adapter + class FragmentCache + attr_reader :serializer + + def initialize(adapter, serializer, options) + @instance_options = options + @adapter = adapter + @serializer = serializer + end + + # TODO: Use Serializable::Resource + # TODO: call +constantize+ less + # 1. Create a CachedSerializer and NonCachedSerializer from the serializer class + # 2. Serialize the above two with the given adapter + # 3. Pass their serializations to the adapter +::fragment_cache+ + def fetch + klass = serializer.class + # It will split the serializer into two, one that will be cached and one that will not + serializers = fragment_serializer(serializer.object.class.name, klass) + + # Instantiate both serializers + cached_serializer = serializers[:cached].constantize.new(serializer.object) + non_cached_serializer = serializers[:non_cached].constantize.new(serializer.object) + + cached_adapter = adapter.class.new(cached_serializer, instance_options) + non_cached_adapter = adapter.class.new(non_cached_serializer, instance_options) + + # Get serializable hash from both + cached_hash = cached_adapter.serializable_hash + non_cached_hash = non_cached_adapter.serializable_hash + + # Merge both results + adapter.fragment_cache(cached_hash, non_cached_hash) + end + + protected + + attr_reader :instance_options, :adapter + + private + + # Given a serializer class and a hash of its cached and non-cached serializers + # 1. Determine cached attributes from serializer class options + # 2. Add cached attributes to cached Serializer + # 3. Add non-cached attributes to non-cached Serializer + def cached_attributes(klass, serializers) + attributes = serializer.class._attributes + cached_attributes = (klass._cache_only) ? klass._cache_only : attributes.reject { |attr| klass._cache_except.include?(attr) } + non_cached_attributes = attributes - cached_attributes + + cached_attributes.each do |attribute| + options = serializer.class._attributes_keys[attribute] + options ||= {} + # Add cached attributes to cached Serializer + serializers[:cached].constantize.attribute(attribute, options) + end + + non_cached_attributes.each do |attribute| + options = serializer.class._attributes_keys[attribute] + options ||= {} + # Add non-cached attributes to non-cached Serializer + serializers[:non_cached].constantize.attribute(attribute, options) + end + end + + # Given a resource name and its serializer's class + # 1. Dyanmically creates a CachedSerializer and NonCachedSerializer + # for a given class 'name' + # 2. Call + # CachedSerializer.cache(serializer._cache_options) + # CachedSerializer.fragmented(serializer) + # NontCachedSerializer.cache(serializer._cache_options) + # 3. Build a hash keyed to the +cached+ and +non_cached+ serializers + # 4. Call +cached_attributes+ on the serializer class and the above hash + # 5. Return the hash + # + # @example + # When +name+ is User::Admin + # creates the Serializer classes (if they don't exist). + # User_AdminCachedSerializer + # User_AdminNOnCachedSerializer + # + def fragment_serializer(name, klass) + cached = "#{to_valid_const_name(name)}CachedSerializer" + non_cached = "#{to_valid_const_name(name)}NonCachedSerializer" + + Object.const_set cached, Class.new(ActiveModel::Serializer) unless Object.const_defined?(cached) + Object.const_set non_cached, Class.new(ActiveModel::Serializer) unless Object.const_defined?(non_cached) + + klass._cache_options ||= {} + klass._cache_options[:key] = klass._cache_key if klass._cache_key + + cached.constantize.cache(klass._cache_options) + + cached.constantize.fragmented(serializer) + non_cached.constantize.fragmented(serializer) + + serializers = { cached: cached, non_cached: non_cached } + cached_attributes(klass, serializers) + serializers + end + + def to_valid_const_name(name) + name.gsub('::', '_') + end + end + end +end diff --git a/lib/active_model_serializers/adapter/json.rb b/lib/active_model_serializers/adapter/json.rb new file mode 100644 index 000000000..9652a04f0 --- /dev/null +++ b/lib/active_model_serializers/adapter/json.rb @@ -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 diff --git a/lib/active_model_serializers/adapter/json/fragment_cache.rb b/lib/active_model_serializers/adapter/json/fragment_cache.rb new file mode 100644 index 000000000..d042063ad --- /dev/null +++ b/lib/active_model_serializers/adapter/json/fragment_cache.rb @@ -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 diff --git a/lib/active_model_serializers/adapter/json_api.rb b/lib/active_model_serializers/adapter/json_api.rb new file mode 100644 index 000000000..c85b65233 --- /dev/null +++ b/lib/active_model_serializers/adapter/json_api.rb @@ -0,0 +1,208 @@ +module ActiveModelSerializers + module Adapter + class JsonApi < Base + extend ActiveSupport::Autoload + autoload :PaginationLinks + autoload :FragmentCache + autoload :Link + autoload :Deserialization + + # TODO: if we like this abstraction and other API objects to it, + # then extract to its own file and require it. + module ApiObjects + module JsonApi + ActiveModelSerializers.config.jsonapi_version = '1.0' + ActiveModelSerializers.config.jsonapi_toplevel_meta = {} + # Make JSON API top-level jsonapi member opt-in + # ref: http://jsonapi.org/format/#document-top-level + ActiveModelSerializers.config.jsonapi_include_toplevel_object = false + + module_function + + def add!(hash) + hash.merge!(object) if include_object? + end + + def include_object? + ActiveModelSerializers.config.jsonapi_include_toplevel_object + end + + # TODO: see if we can cache this + def object + object = { + jsonapi: { + version: ActiveModelSerializers.config.jsonapi_version, + meta: ActiveModelSerializers.config.jsonapi_toplevel_meta + } + } + object[:jsonapi].reject! { |_, v| v.blank? } + + object + end + end + end + + def initialize(serializer, options = {}) + super + @include_tree = ActiveModel::Serializer::IncludeTree.from_include_args(options[:include]) + @fieldset = options[:fieldset] || ActiveModel::Serializer::Fieldset.new(options.delete(:fields)) + end + + def serializable_hash(options = nil) + options ||= {} + + is_collection = serializer.respond_to?(:each) + serializers = is_collection ? serializer : [serializer] + primary_data, included = resource_objects_for(serializers) + + hash = {} + hash[:data] = is_collection ? primary_data : primary_data[0] + hash[:included] = included if included.any? + + ApiObjects::JsonApi.add!(hash) + + if instance_options[:links] + hash[:links] ||= {} + hash[:links].update(instance_options[:links]) + end + + if is_collection && serializer.paginated? + hash[:links] ||= {} + hash[:links].update(pagination_links_for(serializer, options)) + end + + hash + end + + def fragment_cache(cached_hash, non_cached_hash) + root = false if instance_options.include?(:include) + ActiveModelSerializers::Adapter::JsonApi::FragmentCache.new.fragment_cache(root, cached_hash, non_cached_hash) + end + + protected + + attr_reader :fieldset + + private + + def resource_objects_for(serializers) + @primary = [] + @included = [] + @resource_identifiers = Set.new + serializers.each { |serializer| process_resource(serializer, true) } + serializers.each { |serializer| process_relationships(serializer, @include_tree) } + + [@primary, @included] + end + + def process_resource(serializer, primary) + resource_identifier = resource_identifier_for(serializer) + return false unless @resource_identifiers.add?(resource_identifier) + + resource_object = resource_object_for(serializer) + if primary + @primary << resource_object + else + @included << resource_object + end + + true + end + + def process_relationships(serializer, include_tree) + serializer.associations(include_tree).each do |association| + process_relationship(association.serializer, include_tree[association.key]) + end + end + + def process_relationship(serializer, include_tree) + if serializer.respond_to?(:each) + serializer.each { |s| process_relationship(s, include_tree) } + return + end + return unless serializer && serializer.object + return unless process_resource(serializer, false) + + process_relationships(serializer, include_tree) + end + + def resource_identifier_type_for(serializer) + return serializer._type if serializer._type + if ActiveModelSerializers.config.jsonapi_resource_type == :singular + serializer.object.class.model_name.singular + else + serializer.object.class.model_name.plural + end + end + + def resource_identifier_id_for(serializer) + if serializer.respond_to?(:id) + serializer.id + else + serializer.object.id + end + end + + def resource_identifier_for(serializer) + type = resource_identifier_type_for(serializer) + id = resource_identifier_id_for(serializer) + + { id: id.to_s, type: type } + end + + def attributes_for(serializer, fields) + serializer.attributes(fields).except(:id) + end + + def resource_object_for(serializer) + resource_object = cache_check(serializer) do + resource_object = resource_identifier_for(serializer) + + requested_fields = fieldset && fieldset.fields_for(resource_object[:type]) + attributes = attributes_for(serializer, requested_fields) + resource_object[:attributes] = attributes if attributes.any? + resource_object + end + + relationships = relationships_for(serializer) + resource_object[:relationships] = relationships if relationships.any? + + links = links_for(serializer) + resource_object[:links] = links if links.any? + + resource_object + end + + def relationship_value_for(serializer, options = {}) + if serializer.respond_to?(:each) + serializer.map { |s| resource_identifier_for(s) } + else + if options[:virtual_value] + options[:virtual_value] + elsif serializer && serializer.object + resource_identifier_for(serializer) + end + end + end + + def relationships_for(serializer) + resource_type = resource_identifier_type_for(serializer) + requested_associations = fieldset.fields_for(resource_type) || '*' + include_tree = ActiveModel::Serializer::IncludeTree.from_include_args(requested_associations) + serializer.associations(include_tree).each_with_object({}) do |association, hash| + hash[association.key] = { data: relationship_value_for(association.serializer, association.options) } + end + end + + def links_for(serializer) + serializer._links.each_with_object({}) do |(name, value), hash| + hash[name] = Link.new(serializer, value).as_json + end + end + + def pagination_links_for(serializer, options) + JsonApi::PaginationLinks.new(serializer.object, options[:serialization_context]).serializable_hash(options) + end + end + end +end diff --git a/lib/active_model_serializers/adapter/json_api/deserialization.rb b/lib/active_model_serializers/adapter/json_api/deserialization.rb new file mode 100644 index 000000000..a50aa88fe --- /dev/null +++ b/lib/active_model_serializers/adapter/json_api/deserialization.rb @@ -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 diff --git a/lib/active_model_serializers/adapter/json_api/fragment_cache.rb b/lib/active_model_serializers/adapter/json_api/fragment_cache.rb new file mode 100644 index 000000000..5ae0b08c0 --- /dev/null +++ b/lib/active_model_serializers/adapter/json_api/fragment_cache.rb @@ -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 diff --git a/lib/active_model_serializers/adapter/json_api/link.rb b/lib/active_model_serializers/adapter/json_api/link.rb new file mode 100644 index 000000000..bb490e296 --- /dev/null +++ b/lib/active_model_serializers/adapter/json_api/link.rb @@ -0,0 +1,42 @@ +module ActiveModelSerializers + module Adapter + class JsonApi + class Link + def initialize(serializer, value) + @object = serializer.object + @scope = serializer.scope + + # Use the return value of the block unless it is nil. + if value.respond_to?(:call) + @value = instance_eval(&value) + else + @value = value + end + end + + def href(value) + @href = value + nil + end + + def meta(value) + @meta = value + nil + end + + def as_json + return @value if @value + + hash = { href: @href } + hash.merge!(meta: @meta) if @meta + + hash + end + + protected + + attr_reader :object, :scope + end + end + end +end diff --git a/lib/active_model_serializers/adapter/json_api/pagination_links.rb b/lib/active_model_serializers/adapter/json_api/pagination_links.rb new file mode 100644 index 000000000..8f3252c30 --- /dev/null +++ b/lib/active_model_serializers/adapter/json_api/pagination_links.rb @@ -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 diff --git a/lib/active_model_serializers/adapter/null.rb b/lib/active_model_serializers/adapter/null.rb new file mode 100644 index 000000000..6e690b1b0 --- /dev/null +++ b/lib/active_model_serializers/adapter/null.rb @@ -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 diff --git a/lib/active_model_serializers/deserialization.rb b/lib/active_model_serializers/deserialization.rb index 15b8e8985..9eaeef44d 100644 --- a/lib/active_model_serializers/deserialization.rb +++ b/lib/active_model_serializers/deserialization.rb @@ -3,11 +3,11 @@ module Deserialization 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 diff --git a/test/active_model_serializers/adapter_for_test.rb b/test/active_model_serializers/adapter_for_test.rb new file mode 100644 index 000000000..5fe3b8578 --- /dev/null +++ b/test/active_model_serializers/adapter_for_test.rb @@ -0,0 +1,164 @@ +module ActiveModelSerializers + class AdapterForTest < ActiveSupport::TestCase + UnknownAdapterError = ::ActiveModelSerializers::Adapter::UnknownAdapterError + + def setup + @previous_adapter = ActiveModelSerializers.config.adapter + end + + def teardown + ActiveModelSerializers.config.adapter = @previous_adapter + end + + def test_returns_default_adapter + adapter = ActiveModelSerializers::Adapter.configured_adapter + assert_equal ActiveModelSerializers::Adapter::Attributes, adapter + end + + def test_overwrite_adapter_with_symbol + ActiveModelSerializers.config.adapter = :null + + adapter = ActiveModelSerializers::Adapter.configured_adapter + assert_equal ActiveModelSerializers::Adapter::Null, adapter + ensure + ActiveModelSerializers.config.adapter = @previous_adapter + end + + def test_overwrite_adapter_with_class + ActiveModelSerializers.config.adapter = ActiveModelSerializers::Adapter::Null + + adapter = ActiveModelSerializers::Adapter.configured_adapter + assert_equal ActiveModelSerializers::Adapter::Null, adapter + end + + def test_raises_exception_if_invalid_symbol_given + ActiveModelSerializers.config.adapter = :unknown + + assert_raises UnknownAdapterError do + ActiveModelSerializers::Adapter.configured_adapter + end + end + + def test_raises_exception_if_it_does_not_know_hot_to_infer_adapter + ActiveModelSerializers.config.adapter = 42 + + assert_raises UnknownAdapterError do + ActiveModelSerializers::Adapter.configured_adapter + end + end + + def test_adapter_class_for_known_adapter + klass = ActiveModelSerializers::Adapter.adapter_class(:json_api) + assert_equal ActiveModelSerializers::Adapter::JsonApi, klass + end + + def test_adapter_class_for_unknown_adapter + assert_raises UnknownAdapterError do + ActiveModelSerializers::Adapter.adapter_class(:json_simple) + end + end + + def test_adapter_map + expected_adapter_map = { + 'null'.freeze => ActiveModelSerializers::Adapter::Null, + 'json'.freeze => ActiveModelSerializers::Adapter::Json, + 'attributes'.freeze => ActiveModelSerializers::Adapter::Attributes, + 'json_api'.freeze => ActiveModelSerializers::Adapter::JsonApi + } + actual = ActiveModelSerializers::Adapter.adapter_map + assert_equal actual, expected_adapter_map + end + + def test_adapters + assert_equal ActiveModelSerializers::Adapter.adapters.sort, [ + 'attributes'.freeze, + 'json'.freeze, + 'json_api'.freeze, + 'null'.freeze + ] + end + + def test_lookup_adapter_by_string_name + assert_equal ActiveModelSerializers::Adapter.lookup('json'.freeze), ActiveModelSerializers::Adapter::Json + end + + def test_lookup_adapter_by_symbol_name + assert_equal ActiveModelSerializers::Adapter.lookup(:json), ActiveModelSerializers::Adapter::Json + end + + def test_lookup_adapter_by_class + klass = ActiveModelSerializers::Adapter::Json + assert_equal ActiveModelSerializers::Adapter.lookup(klass), klass + end + + def test_lookup_adapter_from_environment_registers_adapter + ActiveModelSerializers::Adapter.const_set(:AdapterFromEnvironment, Class.new) + klass = ::ActiveModelSerializers::Adapter::AdapterFromEnvironment + name = 'adapter_from_environment'.freeze + assert_equal ActiveModelSerializers::Adapter.lookup(name), klass + assert ActiveModelSerializers::Adapter.adapters.include?(name) + ensure + ActiveModelSerializers::Adapter.adapter_map.delete(name) + ActiveModelSerializers::Adapter.send(:remove_const, :AdapterFromEnvironment) + end + + def test_lookup_adapter_for_unknown_name + assert_raises UnknownAdapterError do + ActiveModelSerializers::Adapter.lookup(:json_simple) + end + end + + def test_adapter + assert_equal ActiveModelSerializers.config.adapter, :attributes + assert_equal ActiveModelSerializers::Adapter.configured_adapter, ActiveModelSerializers::Adapter::Attributes + end + + def test_register_adapter + new_adapter_name = :foo + new_adapter_klass = Class.new + ActiveModelSerializers::Adapter.register(new_adapter_name, new_adapter_klass) + assert ActiveModelSerializers::Adapter.adapters.include?('foo'.freeze) + assert ActiveModelSerializers::Adapter.lookup(:foo), new_adapter_klass + ensure + ActiveModelSerializers::Adapter.adapter_map.delete(new_adapter_name.to_s) + end + + def test_inherited_adapter_hooks_register_adapter + Object.const_set(:MyAdapter, Class.new) + my_adapter = MyAdapter + ActiveModelSerializers::Adapter::Base.inherited(my_adapter) + assert_equal ActiveModelSerializers::Adapter.lookup(:my_adapter), my_adapter + ensure + ActiveModelSerializers::Adapter.adapter_map.delete('my_adapter'.freeze) + Object.send(:remove_const, :MyAdapter) + end + + def test_inherited_adapter_hooks_register_namespaced_adapter + Object.const_set(:MyNamespace, Module.new) + MyNamespace.const_set(:MyAdapter, Class.new) + my_adapter = MyNamespace::MyAdapter + ActiveModelSerializers::Adapter::Base.inherited(my_adapter) + assert_equal ActiveModelSerializers::Adapter.lookup(:'my_namespace/my_adapter'), my_adapter + ensure + ActiveModelSerializers::Adapter.adapter_map.delete('my_namespace/my_adapter'.freeze) + MyNamespace.send(:remove_const, :MyAdapter) + Object.send(:remove_const, :MyNamespace) + end + + def test_inherited_adapter_hooks_register_subclass_of_registered_adapter + Object.const_set(:MyAdapter, Class.new) + my_adapter = MyAdapter + Object.const_set(:MySubclassedAdapter, Class.new(MyAdapter)) + my_subclassed_adapter = MySubclassedAdapter + ActiveModelSerializers::Adapter::Base.inherited(my_adapter) + ActiveModelSerializers::Adapter::Base.inherited(my_subclassed_adapter) + assert_equal ActiveModelSerializers::Adapter.lookup(:my_adapter), my_adapter + assert_equal ActiveModelSerializers::Adapter.lookup(:my_subclassed_adapter), my_subclassed_adapter + ensure + ActiveModelSerializers::Adapter.adapter_map.delete('my_adapter'.freeze) + ActiveModelSerializers::Adapter.adapter_map.delete('my_subclassed_adapter'.freeze) + Object.send(:remove_const, :MyAdapter) + Object.send(:remove_const, :MySubclassedAdapter) + end + end +end diff --git a/test/active_model_serializers/logging_test.rb b/test/active_model_serializers/logging_test.rb index e0f3e85ae..aa50e985f 100644 --- a/test/active_model_serializers/logging_test.rb +++ b/test/active_model_serializers/logging_test.rb @@ -65,7 +65,7 @@ def test_logs_correct_serializer 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 diff --git a/test/adapter/fragment_cache_test.rb b/test/adapter/fragment_cache_test.rb index 0ad78eb55..15c99d486 100644 --- a/test/adapter/fragment_cache_test.rb +++ b/test/adapter/fragment_cache_test.rb @@ -1,36 +1,34 @@ require 'test_helper' -module ActiveModel - class Serializer - module Adapter - class FragmentCacheTest < ActiveSupport::TestCase - def setup - super - @spam = Spam::UnrelatedLink.new(id: 'spam-id-1') - @author = Author.new(name: 'Joao M. D. Moura') - @role = Role.new(name: 'Great Author', description: nil) - @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, {}) - end +module ActiveModelSerializers + module Adapter + class FragmentCacheTest < ActiveSupport::TestCase + def setup + super + @spam = Spam::UnrelatedLink.new(id: 'spam-id-1') + @author = Author.new(name: 'Joao M. D. Moura') + @role = Role.new(name: 'Great Author', description: nil) + @role.author = [@author] + @role_serializer = RoleSerializer.new(@role) + @spam_serializer = Spam::UnrelatedLinkSerializer.new(@spam) + @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 - expected_result = { - id: @role.id, - description: @role.description, - slug: "#{@role.name}-#{@role.id}", - name: @role.name - } - assert_equal(@role_hash.fetch, expected_result) - end + def test_fragment_fetch_with_virtual_attributes + expected_result = { + id: @role.id, + description: @role.description, + slug: "#{@role.name}-#{@role.id}", + name: @role.name + } + assert_equal(@role_hash.fetch, expected_result) + end - def test_fragment_fetch_with_namespaced_object - expected_result = { - id: @spam.id - } - assert_equal(@spam_hash.fetch, expected_result) - end + def test_fragment_fetch_with_namespaced_object + expected_result = { + id: @spam.id + } + assert_equal(@spam_hash.fetch, expected_result) end end end diff --git a/test/adapter/json/belongs_to_test.rb b/test/adapter/json/belongs_to_test.rb index 940770b29..0f096f0b3 100644 --- a/test/adapter/json/belongs_to_test.rb +++ b/test/adapter/json/belongs_to_test.rb @@ -1,45 +1,43 @@ require 'test_helper' -module ActiveModel - class Serializer - module Adapter - class Json - class BelongsToTest < ActiveSupport::TestCase - def setup - @post = Post.new(id: 42, title: 'New Post', body: 'Body') - @anonymous_post = Post.new(id: 43, title: 'Hello!!', body: 'Hello, world!!') - @comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') - @post.comments = [@comment] - @anonymous_post.comments = [] - @comment.post = @post - @comment.author = nil - @anonymous_post.author = nil - @blog = Blog.new(id: 1, name: 'My Blog!!') - @post.blog = @blog - @anonymous_post.blog = nil +module ActiveModelSerializers + module Adapter + class Json + class BelongsToTest < ActiveSupport::TestCase + def setup + @post = Post.new(id: 42, title: 'New Post', body: 'Body') + @anonymous_post = Post.new(id: 43, title: 'Hello!!', body: 'Hello, world!!') + @comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') + @post.comments = [@comment] + @anonymous_post.comments = [] + @comment.post = @post + @comment.author = nil + @anonymous_post.author = nil + @blog = Blog.new(id: 1, name: 'My Blog!!') + @post.blog = @blog + @anonymous_post.blog = nil - @serializer = CommentSerializer.new(@comment) - @adapter = ActiveModel::Serializer::Adapter::Json.new(@serializer) - ActionController::Base.cache_store.clear - end + @serializer = CommentSerializer.new(@comment) + @adapter = ActiveModelSerializers::Adapter::Json.new(@serializer) + ActionController::Base.cache_store.clear + end - def test_includes_post - assert_equal({ id: 42, title: 'New Post', body: 'Body' }, @adapter.serializable_hash[:comment][:post]) - end + def test_includes_post + assert_equal({ id: 42, title: 'New Post', body: 'Body' }, @adapter.serializable_hash[:comment][:post]) + end - def test_include_nil_author - serializer = PostSerializer.new(@anonymous_post) - adapter = ActiveModel::Serializer::Adapter::Json.new(serializer) + def test_include_nil_author + serializer = PostSerializer.new(@anonymous_post) + 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 + 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) + def test_include_nil_author_with_specified_serializer + serializer = PostPreviewSerializer.new(@anonymous_post) + adapter = ActiveModelSerializers::Adapter::Json.new(serializer) - assert_equal({ post: { title: 'Hello!!', body: 'Hello, world!!', id: 43, comments: [], author: nil } }, adapter.serializable_hash) - end + assert_equal({ post: { title: 'Hello!!', body: 'Hello, world!!', id: 43, comments: [], author: nil } }, adapter.serializable_hash) end end end diff --git a/test/adapter/json/collection_test.rb b/test/adapter/json/collection_test.rb index 6be3d505a..2ff23336f 100644 --- a/test/adapter/json/collection_test.rb +++ b/test/adapter/json/collection_test.rb @@ -1,90 +1,88 @@ require 'test_helper' -module ActiveModel - class Serializer - module Adapter - class Json - class Collection < ActiveSupport::TestCase - def setup - @author = Author.new(id: 1, name: 'Steve K.') - @first_post = Post.new(id: 1, title: 'Hello!!', body: 'Hello, world!!') - @second_post = Post.new(id: 2, title: 'New Post', body: 'Body') - @first_post.comments = [] - @second_post.comments = [] - @first_post.author = @author - @second_post.author = @author - @blog = Blog.new(id: 1, name: 'My Blog!!') - @first_post.blog = @blog - @second_post.blog = nil +module ActiveModelSerializers + module Adapter + class Json + class Collection < ActiveSupport::TestCase + def setup + @author = Author.new(id: 1, name: 'Steve K.') + @first_post = Post.new(id: 1, title: 'Hello!!', body: 'Hello, world!!') + @second_post = Post.new(id: 2, title: 'New Post', body: 'Body') + @first_post.comments = [] + @second_post.comments = [] + @first_post.author = @author + @second_post.author = @author + @blog = Blog.new(id: 1, name: 'My Blog!!') + @first_post.blog = @blog + @second_post.blog = nil - ActionController::Base.cache_store.clear - end + ActionController::Base.cache_store.clear + end - 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) + def test_with_serializer_option + @blog.special_attribute = 'Special' + @blog.articles = [@first_post, @second_post] + serializer = ActiveModel::Serializer::CollectionSerializer.new([@blog], serializer: CustomBlogSerializer) + adapter = ActiveModelSerializers::Adapter::Json.new(serializer) - expected = { blogs: [{ - id: 1, - special_attribute: 'Special', - articles: [{ id: 1, title: 'Hello!!', body: 'Hello, world!!' }, { id: 2, title: 'New Post', body: 'Body' }] - }] } - assert_equal expected, adapter.serializable_hash - end + expected = { blogs: [{ + id: 1, + special_attribute: 'Special', + articles: [{ id: 1, title: 'Hello!!', body: 'Hello, world!!' }, { id: 2, title: 'New Post', body: 'Body' }] + }] } + assert_equal expected, adapter.serializable_hash + end - def test_include_multiple_posts - serializer = CollectionSerializer.new([@first_post, @second_post]) - adapter = ActiveModel::Serializer::Adapter::Json.new(serializer) + def test_include_multiple_posts + serializer = ActiveModel::Serializer::CollectionSerializer.new([@first_post, @second_post]) + adapter = ActiveModelSerializers::Adapter::Json.new(serializer) - expected = { posts: [{ - title: 'Hello!!', - body: 'Hello, world!!', + expected = { posts: [{ + title: 'Hello!!', + body: 'Hello, world!!', + id: 1, + comments: [], + author: { id: 1, - comments: [], - author: { - id: 1, - name: 'Steve K.' - }, - blog: { - id: 999, - name: 'Custom blog' - } - }, { - title: 'New Post', - body: 'Body', - id: 2, - comments: [], - author: { - id: 1, - name: 'Steve K.' - }, - blog: { - id: 999, - name: 'Custom blog' - } - }] } - assert_equal expected, adapter.serializable_hash - end + name: 'Steve K.' + }, + blog: { + id: 999, + name: 'Custom blog' + } + }, { + title: 'New Post', + body: 'Body', + id: 2, + comments: [], + author: { + id: 1, + name: 'Steve K.' + }, + blog: { + id: 999, + name: 'Custom blog' + } + }] } + assert_equal expected, adapter.serializable_hash + end - def test_root_is_underscored - virtual_value = VirtualValue.new(id: 1) - serializer = CollectionSerializer.new([virtual_value]) - adapter = ActiveModel::Serializer::Adapter::Json.new(serializer) + def test_root_is_underscored + virtual_value = VirtualValue.new(id: 1) + serializer = ActiveModel::Serializer::CollectionSerializer.new([virtual_value]) + adapter = ActiveModelSerializers::Adapter::Json.new(serializer) - assert_equal 1, adapter.serializable_hash[:virtual_values].length - end + 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: '') - actual = adapter.serializable_hash - expected = { posts: [{ id: 1, title: 'Hello!!', body: 'Hello, world!!' }, - { id: 2, title: 'New Post', body: 'Body' }] } + def test_include_option + 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' }] } - assert_equal(expected, actual) - end + assert_equal(expected, actual) end end end diff --git a/test/adapter/json/has_many_test.rb b/test/adapter/json/has_many_test.rb index 72f29e5cb..3f6fa546e 100644 --- a/test/adapter/json/has_many_test.rb +++ b/test/adapter/json/has_many_test.rb @@ -1,45 +1,43 @@ require 'test_helper' -module ActiveModel - class Serializer - module Adapter - class Json - class HasManyTestTest < ActiveSupport::TestCase - def setup - ActionController::Base.cache_store.clear - @author = Author.new(id: 1, name: 'Steve K.') - @post = Post.new(id: 42, title: 'New Post', body: 'Body') - @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') - @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT') - @post.comments = [@first_comment, @second_comment] - @post.author = @author - @first_comment.post = @post - @second_comment.post = @post - @blog = Blog.new(id: 1, name: 'My Blog!!') - @post.blog = @blog - @tag = Tag.new(id: 1, name: '#hash_tag') - @post.tags = [@tag] - end +module ActiveModelSerializers + module Adapter + class Json + class HasManyTestTest < ActiveSupport::TestCase + def setup + ActionController::Base.cache_store.clear + @author = Author.new(id: 1, name: 'Steve K.') + @post = Post.new(id: 42, title: 'New Post', body: 'Body') + @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') + @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT') + @post.comments = [@first_comment, @second_comment] + @post.author = @author + @first_comment.post = @post + @second_comment.post = @post + @blog = Blog.new(id: 1, name: 'My Blog!!') + @post.blog = @blog + @tag = Tag.new(id: 1, name: '#hash_tag') + @post.tags = [@tag] + end - def test_has_many - serializer = PostSerializer.new(@post) - adapter = ActiveModel::Serializer::Adapter::Json.new(serializer) - assert_equal([ - { id: 1, body: 'ZOMG A COMMENT' }, - { id: 2, body: 'ZOMG ANOTHER COMMENT' } - ], adapter.serializable_hash[:post][:comments]) - end + def test_has_many + serializer = PostSerializer.new(@post) + adapter = ActiveModelSerializers::Adapter::Json.new(serializer) + assert_equal([ + { id: 1, body: 'ZOMG A COMMENT' }, + { id: 2, body: 'ZOMG ANOTHER COMMENT' } + ], adapter.serializable_hash[:post][:comments]) + end - def test_has_many_with_no_serializer - serializer = PostWithTagsSerializer.new(@post) - adapter = ActiveModel::Serializer::Adapter::Json.new(serializer) - assert_equal({ - id: 42, - tags: [ - { 'id' => 1, 'name' => '#hash_tag' } - ] - }.to_json, adapter.serializable_hash[:post].to_json) - end + def test_has_many_with_no_serializer + serializer = PostWithTagsSerializer.new(@post) + adapter = ActiveModelSerializers::Adapter::Json.new(serializer) + assert_equal({ + id: 42, + tags: [ + { 'id' => 1, 'name' => '#hash_tag' } + ] + }.to_json, adapter.serializable_hash[:post].to_json) end end end diff --git a/test/adapter/json_api/belongs_to_test.rb b/test/adapter/json_api/belongs_to_test.rb index ba7253e52..c501b4d8d 100644 --- a/test/adapter/json_api/belongs_to_test.rb +++ b/test/adapter/json_api/belongs_to_test.rb @@ -1,155 +1,153 @@ require 'test_helper' -module ActiveModel - class Serializer - module Adapter - class JsonApi - class BelongsToTest < ActiveSupport::TestCase - def setup - @author = Author.new(id: 1, name: 'Steve K.') - @author.bio = nil - @author.roles = [] - @blog = Blog.new(id: 23, name: 'AMS Blog') - @post = Post.new(id: 42, title: 'New Post', body: 'Body') - @anonymous_post = Post.new(id: 43, title: 'Hello!!', body: 'Hello, world!!') - @comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') - @post.comments = [@comment] - @post.blog = @blog - @anonymous_post.comments = [] - @anonymous_post.blog = nil - @comment.post = @post - @comment.author = nil - @post.author = @author - @anonymous_post.author = nil - @blog = Blog.new(id: 1, name: 'My Blog!!') - @blog.writer = @author - @blog.articles = [@post, @anonymous_post] - @author.posts = [] +module ActiveModelSerializers + module Adapter + class JsonApi + class BelongsToTest < ActiveSupport::TestCase + def setup + @author = Author.new(id: 1, name: 'Steve K.') + @author.bio = nil + @author.roles = [] + @blog = Blog.new(id: 23, name: 'AMS Blog') + @post = Post.new(id: 42, title: 'New Post', body: 'Body') + @anonymous_post = Post.new(id: 43, title: 'Hello!!', body: 'Hello, world!!') + @comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') + @post.comments = [@comment] + @post.blog = @blog + @anonymous_post.comments = [] + @anonymous_post.blog = nil + @comment.post = @post + @comment.author = nil + @post.author = @author + @anonymous_post.author = nil + @blog = Blog.new(id: 1, name: 'My Blog!!') + @blog.writer = @author + @blog.articles = [@post, @anonymous_post] + @author.posts = [] - @serializer = CommentSerializer.new(@comment) - @adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer) - ActionController::Base.cache_store.clear - end + @serializer = CommentSerializer.new(@comment) + @adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer) + ActionController::Base.cache_store.clear + end + + def test_includes_post_id + expected = { data: { type: 'posts', id: '42' } } + + assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:post]) + end - def test_includes_post_id - expected = { data: { type: 'posts', id: '42' } } + def test_includes_linked_post + @adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer, include: [:post]) + expected = [{ + id: '42', + type: 'posts', + attributes: { + title: 'New Post', + body: 'Body', + }, + relationships: { + comments: { data: [{ type: 'comments', id: '1' }] }, + blog: { data: { type: 'blogs', id: '999' } }, + author: { data: { type: 'authors', id: '1' } } + } + }] + assert_equal expected, @adapter.serializable_hash[:included] + end - assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:post]) - end + def test_limiting_linked_post_fields + @adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer, include: [:post], fields: { post: [:title, :comments, :blog, :author] }) + expected = [{ + id: '42', + type: 'posts', + attributes: { + title: 'New Post' + }, + relationships: { + comments: { data: [{ type: 'comments', id: '1' }] }, + blog: { data: { type: 'blogs', id: '999' } }, + author: { data: { type: 'authors', id: '1' } } + } + }] + assert_equal expected, @adapter.serializable_hash[:included] + end + + def test_include_nil_author + serializer = PostSerializer.new(@anonymous_post) + adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer) + + assert_equal({ comments: { data: [] }, blog: { data: { type: 'blogs', id: '999' } }, author: { data: nil } }, adapter.serializable_hash[:data][:relationships]) + end - def test_includes_linked_post - @adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, include: [:post]) - expected = [{ + def test_include_type_for_association_when_different_than_name + serializer = BlogSerializer.new(@blog) + adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer) + relationships = adapter.serializable_hash[:data][:relationships] + expected = { + writer: { + data: { + type: 'authors', + id: '1' + } + }, + articles: { + data: [ + { + type: 'posts', + id: '42' + }, + { + type: 'posts', + id: '43' + } + ] + } + } + assert_equal expected, relationships + end + + def test_include_linked_resources_with_type_name + serializer = BlogSerializer.new(@blog) + adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, include: [:writer, :articles]) + linked = adapter.serializable_hash[:included] + expected = [ + { + id: '1', + type: 'authors', + attributes: { + name: 'Steve K.' + }, + relationships: { + posts: { data: [] }, + roles: { data: [] }, + bio: { data: nil } + } + }, { id: '42', type: 'posts', attributes: { title: 'New Post', - body: 'Body', + body: 'Body' }, relationships: { comments: { data: [{ type: 'comments', id: '1' }] }, blog: { data: { type: 'blogs', id: '999' } }, author: { data: { type: 'authors', id: '1' } } } - }] - assert_equal expected, @adapter.serializable_hash[:included] - end - - def test_limiting_linked_post_fields - @adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, include: [:post], fields: { post: [:title, :comments, :blog, :author] }) - expected = [{ - id: '42', + }, { + id: '43', type: 'posts', attributes: { - title: 'New Post' + title: 'Hello!!', + body: 'Hello, world!!' }, relationships: { - comments: { data: [{ type: 'comments', id: '1' }] }, + comments: { data: [] }, blog: { data: { type: 'blogs', id: '999' } }, - author: { data: { type: 'authors', id: '1' } } - } - }] - assert_equal expected, @adapter.serializable_hash[:included] - end - - def test_include_nil_author - serializer = PostSerializer.new(@anonymous_post) - adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer) - - assert_equal({ comments: { data: [] }, blog: { data: { type: 'blogs', id: '999' } }, author: { data: nil } }, adapter.serializable_hash[:data][:relationships]) - end - - def test_include_type_for_association_when_different_than_name - serializer = BlogSerializer.new(@blog) - adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer) - relationships = adapter.serializable_hash[:data][:relationships] - expected = { - writer: { - data: { - type: 'authors', - id: '1' - } - }, - articles: { - data: [ - { - type: 'posts', - id: '42' - }, - { - type: 'posts', - id: '43' - } - ] + author: { data: nil } } } - assert_equal expected, relationships - end - - def test_include_linked_resources_with_type_name - serializer = BlogSerializer.new(@blog) - adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer, include: [:writer, :articles]) - linked = adapter.serializable_hash[:included] - expected = [ - { - id: '1', - type: 'authors', - attributes: { - name: 'Steve K.' - }, - relationships: { - posts: { data: [] }, - roles: { data: [] }, - bio: { data: nil } - } - }, { - id: '42', - type: 'posts', - attributes: { - title: 'New Post', - body: 'Body' - }, - relationships: { - comments: { data: [{ type: 'comments', id: '1' }] }, - blog: { data: { type: 'blogs', id: '999' } }, - author: { data: { type: 'authors', id: '1' } } - } - }, { - id: '43', - type: 'posts', - attributes: { - title: 'Hello!!', - body: 'Hello, world!!' - }, - relationships: { - comments: { data: [] }, - blog: { data: { type: 'blogs', id: '999' } }, - author: { data: nil } - } - } - ] - assert_equal expected, linked - end + ] + assert_equal expected, linked end end end diff --git a/test/adapter/json_api/collection_test.rb b/test/adapter/json_api/collection_test.rb index 05d74bd16..b534108a3 100644 --- a/test/adapter/json_api/collection_test.rb +++ b/test/adapter/json_api/collection_test.rb @@ -1,95 +1,93 @@ require 'test_helper' -module ActiveModel - class Serializer - module Adapter - class JsonApi - class CollectionTest < ActiveSupport::TestCase - def setup - @author = Author.new(id: 1, name: 'Steve K.') - @author.bio = nil - @blog = Blog.new(id: 23, name: 'AMS Blog') - @first_post = Post.new(id: 1, title: 'Hello!!', body: 'Hello, world!!') - @second_post = Post.new(id: 2, title: 'New Post', body: 'Body') - @first_post.comments = [] - @second_post.comments = [] - @first_post.blog = @blog - @second_post.blog = nil - @first_post.author = @author - @second_post.author = @author - @author.posts = [@first_post, @second_post] +module ActiveModelSerializers + module Adapter + class JsonApi + class CollectionTest < ActiveSupport::TestCase + def setup + @author = Author.new(id: 1, name: 'Steve K.') + @author.bio = nil + @blog = Blog.new(id: 23, name: 'AMS Blog') + @first_post = Post.new(id: 1, title: 'Hello!!', body: 'Hello, world!!') + @second_post = Post.new(id: 2, title: 'New Post', body: 'Body') + @first_post.comments = [] + @second_post.comments = [] + @first_post.blog = @blog + @second_post.blog = nil + @first_post.author = @author + @second_post.author = @author + @author.posts = [@first_post, @second_post] - @serializer = CollectionSerializer.new([@first_post, @second_post]) - @adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer) - ActionController::Base.cache_store.clear - end + @serializer = ActiveModel::Serializer::CollectionSerializer.new([@first_post, @second_post]) + @adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer) + ActionController::Base.cache_store.clear + end - def test_include_multiple_posts - expected = [ - { - id: '1', - type: 'posts', - attributes: { - title: 'Hello!!', - body: 'Hello, world!!' - }, - relationships: { - comments: { data: [] }, - blog: { data: { type: 'blogs', id: '999' } }, - author: { data: { type: 'authors', id: '1' } } - } + def test_include_multiple_posts + expected = [ + { + id: '1', + type: 'posts', + attributes: { + title: 'Hello!!', + body: 'Hello, world!!' + }, + relationships: { + comments: { data: [] }, + blog: { data: { type: 'blogs', id: '999' } }, + author: { data: { type: 'authors', id: '1' } } + } + }, + { + id: '2', + type: 'posts', + attributes: { + title: 'New Post', + body: 'Body' }, - { - id: '2', - type: 'posts', - attributes: { - title: 'New Post', - body: 'Body' - }, - relationships: { - comments: { data: [] }, - blog: { data: { type: 'blogs', id: '999' } }, - author: { data: { type: 'authors', id: '1' } } - } + relationships: { + comments: { data: [] }, + blog: { data: { type: 'blogs', id: '999' } }, + author: { data: { type: 'authors', id: '1' } } } - ] + } + ] - assert_equal(expected, @adapter.serializable_hash[:data]) - end + assert_equal(expected, @adapter.serializable_hash[:data]) + end - def test_limiting_fields - actual = ActiveModel::SerializableResource.new( - [@first_post, @second_post], adapter: :json_api, - fields: { posts: %w(title comments blog author) }) - .serializable_hash - expected = [ - { - id: '1', - type: 'posts', - attributes: { - title: 'Hello!!' - }, - relationships: { - comments: { data: [] }, - blog: { data: { type: 'blogs', id: '999' } }, - author: { data: { type: 'authors', id: '1' } } - } + def test_limiting_fields + actual = ActiveModel::SerializableResource.new( + [@first_post, @second_post], adapter: :json_api, + fields: { posts: %w(title comments blog author) }) + .serializable_hash + expected = [ + { + id: '1', + type: 'posts', + attributes: { + title: 'Hello!!' + }, + relationships: { + comments: { data: [] }, + blog: { data: { type: 'blogs', id: '999' } }, + author: { data: { type: 'authors', id: '1' } } + } + }, + { + id: '2', + type: 'posts', + attributes: { + title: 'New Post' }, - { - id: '2', - type: 'posts', - attributes: { - title: 'New Post' - }, - relationships: { - comments: { data: [] }, - blog: { data: { type: 'blogs', id: '999' } }, - author: { data: { type: 'authors', id: '1' } } - } + relationships: { + comments: { data: [] }, + blog: { data: { type: 'blogs', id: '999' } }, + author: { data: { type: 'authors', id: '1' } } } - ] - assert_equal(expected, actual[:data]) - end + } + ] + assert_equal(expected, actual[:data]) end end end diff --git a/test/adapter/json_api/fields_test.rb b/test/adapter/json_api/fields_test.rb index b92ab590a..ad356a533 100644 --- a/test/adapter/json_api/fields_test.rb +++ b/test/adapter/json_api/fields_test.rb @@ -1,87 +1,85 @@ require 'test_helper' -module ActiveModel - class Serializer - module Adapter - class JsonApi - class FieldsTest < ActiveSupport::TestCase - Post = Class.new(::Model) - class PostSerializer < ActiveModel::Serializer - type 'posts' - attributes :title, :body - belongs_to :author - has_many :comments - end +module ActiveModelSerializers + module Adapter + class JsonApi + class FieldsTest < ActiveSupport::TestCase + Post = Class.new(::Model) + class PostSerializer < ActiveModel::Serializer + type 'posts' + attributes :title, :body + belongs_to :author + has_many :comments + end - Author = Class.new(::Model) - class AuthorSerializer < ActiveModel::Serializer - type 'authors' - attributes :name, :birthday - end + Author = Class.new(::Model) + class AuthorSerializer < ActiveModel::Serializer + type 'authors' + attributes :name, :birthday + end - Comment = Class.new(::Model) - class CommentSerializer < ActiveModel::Serializer - type 'comments' - attributes :body - belongs_to :author - end + Comment = Class.new(::Model) + class CommentSerializer < ActiveModel::Serializer + type 'comments' + attributes :body + belongs_to :author + end - def setup - @author = Author.new(id: 1, name: 'Lucas', birthday: '10.01.1990') - @comment1 = Comment.new(id: 7, body: 'cool', author: @author) - @comment2 = Comment.new(id: 12, body: 'awesome', author: @author) - @post = Post.new(id: 1337, title: 'Title 1', body: 'Body 1', - author: @author, comments: [@comment1, @comment2]) - @comment1.post = @post - @comment2.post = @post - end + def setup + @author = Author.new(id: 1, name: 'Lucas', birthday: '10.01.1990') + @comment1 = Comment.new(id: 7, body: 'cool', author: @author) + @comment2 = Comment.new(id: 12, body: 'awesome', author: @author) + @post = Post.new(id: 1337, title: 'Title 1', body: 'Body 1', + author: @author, comments: [@comment1, @comment2]) + @comment1.post = @post + @comment2.post = @post + end - def test_fields_attributes - fields = { posts: [:title] } - hash = serializable(@post, adapter: :json_api, fields: fields).serializable_hash - expected = { - title: 'Title 1' - } + def test_fields_attributes + fields = { posts: [:title] } + hash = serializable(@post, adapter: :json_api, fields: fields).serializable_hash + expected = { + title: 'Title 1' + } - assert_equal(expected, hash[:data][:attributes]) - end + assert_equal(expected, hash[:data][:attributes]) + end - def test_fields_relationships - fields = { posts: [:author] } - hash = serializable(@post, adapter: :json_api, fields: fields).serializable_hash - expected = { - author: { - data: { - type: 'authors', - id: '1' - } + def test_fields_relationships + fields = { posts: [:author] } + hash = serializable(@post, adapter: :json_api, fields: fields).serializable_hash + expected = { + author: { + data: { + type: 'authors', + id: '1' } } + } - assert_equal(expected, hash[:data][:relationships]) - end + assert_equal(expected, hash[:data][:relationships]) + end - def test_fields_included - fields = { posts: [:author], comments: [:body] } - hash = serializable(@post, adapter: :json_api, fields: fields, include: 'comments').serializable_hash - expected = [ - { - type: 'comments', - id: '7', - attributes: { - body: 'cool' - } - }, { - type: 'comments', - id: '12', - attributes: { - body: 'awesome' - } + def test_fields_included + fields = { posts: [:author], comments: [:body] } + hash = serializable(@post, adapter: :json_api, fields: fields, include: 'comments').serializable_hash + expected = [ + { + type: 'comments', + id: '7', + attributes: { + body: 'cool' } - ] + }, { + type: 'comments', + id: '12', + attributes: { + body: 'awesome' + } + } + ] - assert_equal(expected, hash[:included]) - end + assert_equal(expected, hash[:included]) end end end diff --git a/test/adapter/json_api/has_many_embed_ids_test.rb b/test/adapter/json_api/has_many_embed_ids_test.rb index b80448f70..e016de284 100644 --- a/test/adapter/json_api/has_many_embed_ids_test.rb +++ b/test/adapter/json_api/has_many_embed_ids_test.rb @@ -1,43 +1,41 @@ require 'test_helper' -module ActiveModel - class Serializer - module Adapter - class JsonApi - class HasManyEmbedIdsTest < ActiveSupport::TestCase - def setup - @author = Author.new(name: 'Steve K.') - @author.bio = nil - @author.roles = nil - @first_post = Post.new(id: 1, title: 'Hello!!', body: 'Hello, world!!') - @second_post = Post.new(id: 2, title: 'New Post', body: 'Body') - @author.posts = [@first_post, @second_post] - @first_post.author = @author - @second_post.author = @author - @first_post.comments = [] - @second_post.comments = [] - @blog = Blog.new(id: 23, name: 'AMS Blog') - @first_post.blog = @blog - @second_post.blog = nil +module ActiveModelSerializers + module Adapter + class JsonApi + class HasManyEmbedIdsTest < ActiveSupport::TestCase + def setup + @author = Author.new(name: 'Steve K.') + @author.bio = nil + @author.roles = nil + @first_post = Post.new(id: 1, title: 'Hello!!', body: 'Hello, world!!') + @second_post = Post.new(id: 2, title: 'New Post', body: 'Body') + @author.posts = [@first_post, @second_post] + @first_post.author = @author + @second_post.author = @author + @first_post.comments = [] + @second_post.comments = [] + @blog = Blog.new(id: 23, name: 'AMS Blog') + @first_post.blog = @blog + @second_post.blog = nil - @serializer = AuthorSerializer.new(@author) - @adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer) - end + @serializer = AuthorSerializer.new(@author) + @adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer) + end - def test_includes_comment_ids - expected = { - data: [ - { type: 'posts', id: '1' }, - { type: 'posts', id: '2' } - ] - } + def test_includes_comment_ids + expected = { + data: [ + { type: 'posts', id: '1' }, + { type: 'posts', id: '2' } + ] + } - assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:posts]) - end + assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:posts]) + end - def test_no_includes_linked_comments - assert_nil @adapter.serializable_hash[:linked] - end + def test_no_includes_linked_comments + assert_nil @adapter.serializable_hash[:linked] end end end diff --git a/test/adapter/json_api/has_many_explicit_serializer_test.rb b/test/adapter/json_api/has_many_explicit_serializer_test.rb index 2d2a885aa..f598bc9b0 100644 --- a/test/adapter/json_api/has_many_explicit_serializer_test.rb +++ b/test/adapter/json_api/has_many_explicit_serializer_test.rb @@ -1,96 +1,94 @@ require 'test_helper' -module ActiveModel - class Serializer - module Adapter - class JsonApi - # Test 'has_many :assocs, serializer: AssocXSerializer' - class HasManyExplicitSerializerTest < ActiveSupport::TestCase - def setup - @post = Post.new(title: 'New Post', body: 'Body') - @author = Author.new(name: 'Jane Blogger') - @author.posts = [@post] - @post.author = @author - @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') - @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT') - @post.comments = [@first_comment, @second_comment] - @first_comment.post = @post - @first_comment.author = nil - @second_comment.post = @post - @second_comment.author = nil - @blog = Blog.new(id: 23, name: 'AMS Blog') - @post.blog = @blog +module ActiveModelSerializers + module Adapter + class JsonApi + # Test 'has_many :assocs, serializer: AssocXSerializer' + class HasManyExplicitSerializerTest < ActiveSupport::TestCase + def setup + @post = Post.new(title: 'New Post', body: 'Body') + @author = Author.new(name: 'Jane Blogger') + @author.posts = [@post] + @post.author = @author + @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') + @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT') + @post.comments = [@first_comment, @second_comment] + @first_comment.post = @post + @first_comment.author = nil + @second_comment.post = @post + @second_comment.author = nil + @blog = Blog.new(id: 23, name: 'AMS Blog') + @post.blog = @blog - @serializer = PostPreviewSerializer.new(@post) - @adapter = ActiveModel::Serializer::Adapter::JsonApi.new( - @serializer, - include: [:comments, :author] - ) - end + @serializer = PostPreviewSerializer.new(@post) + @adapter = ActiveModelSerializers::Adapter::JsonApi.new( + @serializer, + include: [:comments, :author] + ) + end - def test_includes_comment_ids - expected = { - data: [ - { type: 'comments', id: '1' }, - { type: 'comments', id: '2' } - ] - } + def test_includes_comment_ids + expected = { + data: [ + { type: 'comments', id: '1' }, + { type: 'comments', id: '2' } + ] + } - assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:comments]) - end + assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:comments]) + end - def test_includes_linked_data - # If CommentPreviewSerializer is applied correctly the body text will not be present in the output - expected = [ - { - id: '1', - type: 'comments', - relationships: { - post: { data: { type: 'posts', id: @post.id.to_s } } - } - }, - { - id: '2', - type: 'comments', - relationships: { - post: { data: { type: 'posts', id: @post.id.to_s } } - } - }, - { - id: @author.id.to_s, - type: 'authors', - relationships: { - posts: { data: [{ type: 'posts', id: @post.id.to_s }] } - } + def test_includes_linked_data + # If CommentPreviewSerializer is applied correctly the body text will not be present in the output + expected = [ + { + id: '1', + type: 'comments', + relationships: { + post: { data: { type: 'posts', id: @post.id.to_s } } } - ] + }, + { + id: '2', + type: 'comments', + relationships: { + post: { data: { type: 'posts', id: @post.id.to_s } } + } + }, + { + id: @author.id.to_s, + type: 'authors', + relationships: { + posts: { data: [{ type: 'posts', id: @post.id.to_s }] } + } + } + ] - assert_equal(expected, @adapter.serializable_hash[:included]) - end + assert_equal(expected, @adapter.serializable_hash[:included]) + end - def test_includes_author_id - expected = { - data: { type: 'authors', id: @author.id.to_s } - } + def test_includes_author_id + expected = { + data: { type: 'authors', id: @author.id.to_s } + } - assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:author]) - end + assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:author]) + end - def test_explicit_serializer_with_null_resource - @post.author = nil + def test_explicit_serializer_with_null_resource + @post.author = nil - expected = { data: nil } + expected = { data: nil } - assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:author]) - end + assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:author]) + end - def test_explicit_serializer_with_null_collection - @post.comments = [] + def test_explicit_serializer_with_null_collection + @post.comments = [] - expected = { data: [] } + expected = { data: [] } - assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:comments]) - end + assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:comments]) end end end diff --git a/test/adapter/json_api/has_many_test.rb b/test/adapter/json_api/has_many_test.rb index a5753c6ed..d590b8dfd 100644 --- a/test/adapter/json_api/has_many_test.rb +++ b/test/adapter/json_api/has_many_test.rb @@ -1,143 +1,141 @@ require 'test_helper' -module ActiveModel - class Serializer - module Adapter - class JsonApi - class HasManyTest < ActiveSupport::TestCase - def setup - ActionController::Base.cache_store.clear - @author = Author.new(id: 1, name: 'Steve K.') - @author.posts = [] - @author.bio = nil - @post = Post.new(id: 1, title: 'New Post', body: 'Body') - @post_without_comments = Post.new(id: 2, title: 'Second Post', body: 'Second') - @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') - @first_comment.author = nil - @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT') - @second_comment.author = nil - @post.comments = [@first_comment, @second_comment] - @post_without_comments.comments = [] - @first_comment.post = @post - @second_comment.post = @post - @post.author = @author - @post_without_comments.author = nil - @blog = Blog.new(id: 1, name: 'My Blog!!') - @blog.writer = @author - @blog.articles = [@post] - @post.blog = @blog - @post_without_comments.blog = nil - @tag = Tag.new(id: 1, name: '#hash_tag') - @post.tags = [@tag] - @serializer = PostSerializer.new(@post) - @adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer) +module ActiveModelSerializers + module Adapter + class JsonApi + class HasManyTest < ActiveSupport::TestCase + def setup + ActionController::Base.cache_store.clear + @author = Author.new(id: 1, name: 'Steve K.') + @author.posts = [] + @author.bio = nil + @post = Post.new(id: 1, title: 'New Post', body: 'Body') + @post_without_comments = Post.new(id: 2, title: 'Second Post', body: 'Second') + @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') + @first_comment.author = nil + @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT') + @second_comment.author = nil + @post.comments = [@first_comment, @second_comment] + @post_without_comments.comments = [] + @first_comment.post = @post + @second_comment.post = @post + @post.author = @author + @post_without_comments.author = nil + @blog = Blog.new(id: 1, name: 'My Blog!!') + @blog.writer = @author + @blog.articles = [@post] + @post.blog = @blog + @post_without_comments.blog = nil + @tag = Tag.new(id: 1, name: '#hash_tag') + @post.tags = [@tag] + @serializer = PostSerializer.new(@post) + @adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer) - @virtual_value = VirtualValue.new(id: 1) - end + @virtual_value = VirtualValue.new(id: 1) + end - def test_includes_comment_ids - expected = { data: [{ type: 'comments', id: '1' }, { type: 'comments', id: '2' }] } + def test_includes_comment_ids + expected = { data: [{ type: 'comments', id: '1' }, { type: 'comments', id: '2' }] } - assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:comments]) - end + assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:comments]) + end - def test_includes_linked_comments - @adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, include: [:comments]) - expected = [{ - id: '1', - type: 'comments', - attributes: { - body: 'ZOMG A COMMENT' - }, - relationships: { - post: { data: { type: 'posts', id: '1' } }, - author: { data: nil } - } - }, { - id: '2', - type: 'comments', - attributes: { - body: 'ZOMG ANOTHER COMMENT' - }, - relationships: { - post: { data: { type: 'posts', id: '1' } }, - author: { data: nil } - } - }] - assert_equal expected, @adapter.serializable_hash[:included] - end + def test_includes_linked_comments + @adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer, include: [:comments]) + expected = [{ + id: '1', + type: 'comments', + attributes: { + body: 'ZOMG A COMMENT' + }, + relationships: { + post: { data: { type: 'posts', id: '1' } }, + author: { data: nil } + } + }, { + id: '2', + type: 'comments', + attributes: { + body: 'ZOMG ANOTHER COMMENT' + }, + relationships: { + post: { data: { type: 'posts', id: '1' } }, + author: { data: nil } + } + }] + assert_equal expected, @adapter.serializable_hash[:included] + end - def test_limit_fields_of_linked_comments - @adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, include: [:comments], fields: { comment: [:id, :post, :author] }) - expected = [{ - id: '1', - type: 'comments', - relationships: { - post: { data: { type: 'posts', id: '1' } }, - author: { data: nil } - } - }, { - id: '2', - type: 'comments', - relationships: { - post: { data: { type: 'posts', id: '1' } }, - author: { data: nil } - } - }] - assert_equal expected, @adapter.serializable_hash[:included] - end + def test_limit_fields_of_linked_comments + @adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer, include: [:comments], fields: { comment: [:id, :post, :author] }) + expected = [{ + id: '1', + type: 'comments', + relationships: { + post: { data: { type: 'posts', id: '1' } }, + author: { data: nil } + } + }, { + id: '2', + type: 'comments', + relationships: { + post: { data: { type: 'posts', id: '1' } }, + author: { data: nil } + } + }] + assert_equal expected, @adapter.serializable_hash[:included] + end - def test_no_include_linked_if_comments_is_empty - serializer = PostSerializer.new(@post_without_comments) - adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer) + def test_no_include_linked_if_comments_is_empty + serializer = PostSerializer.new(@post_without_comments) + adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer) - assert_nil adapter.serializable_hash[:linked] - end + assert_nil adapter.serializable_hash[:linked] + end - def test_include_type_for_association_when_different_than_name - serializer = BlogSerializer.new(@blog) - adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer) - actual = adapter.serializable_hash[:data][:relationships][:articles] + def test_include_type_for_association_when_different_than_name + serializer = BlogSerializer.new(@blog) + adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer) + actual = adapter.serializable_hash[:data][:relationships][:articles] - expected = { - data: [{ - type: 'posts', - id: '1' - }] - } - assert_equal expected, actual - end + expected = { + data: [{ + type: 'posts', + id: '1' + }] + } + assert_equal expected, actual + end - def test_has_many_with_no_serializer - serializer = PostWithTagsSerializer.new(@post) - adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer) + def test_has_many_with_no_serializer + serializer = PostWithTagsSerializer.new(@post) + adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer) - assert_equal({ - data: { - id: '1', - type: 'posts', - relationships: { - tags: { data: [@tag.as_json] } - } + assert_equal({ + data: { + id: '1', + type: 'posts', + relationships: { + tags: { data: [@tag.as_json] } } - }, adapter.serializable_hash) - end + } + }, adapter.serializable_hash) + end - def test_has_many_with_virtual_value - serializer = VirtualValueSerializer.new(@virtual_value) - adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer) + def test_has_many_with_virtual_value + serializer = VirtualValueSerializer.new(@virtual_value) + adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer) - assert_equal({ - data: { - id: '1', - type: 'virtual_values', - relationships: { - maker: { data: { id: 1 } }, - reviews: { data: [{ id: 1 }, { id: 2 }] } - } + assert_equal({ + data: { + id: '1', + type: 'virtual_values', + relationships: { + maker: { data: { id: 1 } }, + reviews: { data: [{ id: 1 }, { id: 2 }] } } - }, adapter.serializable_hash) - end + } + }, adapter.serializable_hash) end end end diff --git a/test/adapter/json_api/has_one_test.rb b/test/adapter/json_api/has_one_test.rb index 7bc25ff6f..b346dcd08 100644 --- a/test/adapter/json_api/has_one_test.rb +++ b/test/adapter/json_api/has_one_test.rb @@ -1,79 +1,77 @@ require 'test_helper' -module ActiveModel - class Serializer - module Adapter - class JsonApi - class HasOneTest < ActiveSupport::TestCase - def setup - @author = Author.new(id: 1, name: 'Steve K.') - @bio = Bio.new(id: 43, content: 'AMS Contributor') - @author.bio = @bio - @bio.author = @author - @post = Post.new(id: 42, title: 'New Post', body: 'Body') - @anonymous_post = Post.new(id: 43, title: 'Hello!!', body: 'Hello, world!!') - @comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') - @post.comments = [@comment] - @anonymous_post.comments = [] - @comment.post = @post - @comment.author = nil - @post.author = @author - @anonymous_post.author = nil - @blog = Blog.new(id: 1, name: 'My Blog!!') - @blog.writer = @author - @blog.articles = [@post, @anonymous_post] - @author.posts = [] - @author.roles = [] +module ActiveModelSerializers + module Adapter + class JsonApi + class HasOneTest < ActiveSupport::TestCase + def setup + @author = Author.new(id: 1, name: 'Steve K.') + @bio = Bio.new(id: 43, content: 'AMS Contributor') + @author.bio = @bio + @bio.author = @author + @post = Post.new(id: 42, title: 'New Post', body: 'Body') + @anonymous_post = Post.new(id: 43, title: 'Hello!!', body: 'Hello, world!!') + @comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') + @post.comments = [@comment] + @anonymous_post.comments = [] + @comment.post = @post + @comment.author = nil + @post.author = @author + @anonymous_post.author = nil + @blog = Blog.new(id: 1, name: 'My Blog!!') + @blog.writer = @author + @blog.articles = [@post, @anonymous_post] + @author.posts = [] + @author.roles = [] - @virtual_value = VirtualValue.new(id: 1) + @virtual_value = VirtualValue.new(id: 1) - @serializer = AuthorSerializer.new(@author) - @adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, include: [:bio, :posts]) - end + @serializer = AuthorSerializer.new(@author) + @adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer, include: [:bio, :posts]) + end - def test_includes_bio_id - expected = { data: { type: 'bios', id: '43' } } + def test_includes_bio_id + expected = { data: { type: 'bios', id: '43' } } - assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:bio]) - end + assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:bio]) + end - def test_includes_linked_bio - @adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, include: [:bio]) + def test_includes_linked_bio + @adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer, include: [:bio]) - expected = [ - { - id: '43', - type: 'bios', - attributes: { - content: 'AMS Contributor', - rating: nil - }, - relationships: { - author: { data: { type: 'authors', id: '1' } } - } + expected = [ + { + id: '43', + type: 'bios', + attributes: { + content: 'AMS Contributor', + rating: nil + }, + relationships: { + author: { data: { type: 'authors', id: '1' } } } - ] + } + ] - assert_equal(expected, @adapter.serializable_hash[:included]) - end + assert_equal(expected, @adapter.serializable_hash[:included]) + end - def test_has_one_with_virtual_value - serializer = VirtualValueSerializer.new(@virtual_value) - adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer) + def test_has_one_with_virtual_value + serializer = VirtualValueSerializer.new(@virtual_value) + adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer) - expected = { - data: { - id: '1', - type: 'virtual_values', - relationships: { - maker: { data: { id: 1 } }, - reviews: { data: [{ id: 1 }, { id: 2 }] } - } + expected = { + data: { + id: '1', + type: 'virtual_values', + relationships: { + maker: { data: { id: 1 } }, + reviews: { data: [{ id: 1 }, { id: 2 }] } } } + } - assert_equal(expected, adapter.serializable_hash) - end + assert_equal(expected, adapter.serializable_hash) end end end diff --git a/test/adapter/json_api/json_api_test.rb b/test/adapter/json_api/json_api_test.rb index b205a4ec1..1b9e89ad8 100644 --- a/test/adapter/json_api/json_api_test.rb +++ b/test/adapter/json_api/json_api_test.rb @@ -1,37 +1,35 @@ require 'test_helper' -module ActiveModel - class Serializer - module Adapter - class JsonApiTest < ActiveSupport::TestCase - def setup - ActionController::Base.cache_store.clear - @author = Author.new(id: 1, name: 'Steve K.') - @post = Post.new(id: 1, title: 'New Post', body: 'Body') - @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') - @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT') - @post.comments = [@first_comment, @second_comment] - @first_comment.post = @post - @second_comment.post = @post - @post.author = @author - @blog = Blog.new(id: 1, name: 'My Blog!!') - @post.blog = @blog - end +module ActiveModelSerializers + module Adapter + class JsonApiTest < ActiveSupport::TestCase + def setup + ActionController::Base.cache_store.clear + @author = Author.new(id: 1, name: 'Steve K.') + @post = Post.new(id: 1, title: 'New Post', body: 'Body') + @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') + @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT') + @post.comments = [@first_comment, @second_comment] + @first_comment.post = @post + @second_comment.post = @post + @post.author = @author + @blog = Blog.new(id: 1, name: 'My Blog!!') + @post.blog = @blog + end - def test_custom_keys - serializer = PostWithCustomKeysSerializer.new(@post) - adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer) + def test_custom_keys + serializer = PostWithCustomKeysSerializer.new(@post) + adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer) - assert_equal({ - reviews: { data: [ - { type: 'comments', id: '1' }, - { type: 'comments', id: '2' } - ] }, - writer: { data: { type: 'authors', id: '1' } }, - site: { data: { type: 'blogs', id: '1' } } - }, adapter.serializable_hash[:data][:relationships]) - end + assert_equal({ + reviews: { data: [ + { type: 'comments', id: '1' }, + { type: 'comments', id: '2' } + ] }, + writer: { data: { type: 'authors', id: '1' } }, + site: { data: { type: 'blogs', id: '1' } } + }, adapter.serializable_hash[:data][:relationships]) end end end -end \ No newline at end of file +end diff --git a/test/adapter/json_api/linked_test.rb b/test/adapter/json_api/linked_test.rb index c16147d66..bcf181232 100644 --- a/test/adapter/json_api/linked_test.rb +++ b/test/adapter/json_api/linked_test.rb @@ -5,168 +5,106 @@ class NestedPostSerializer < ActiveModel::Serializer has_many :nested_posts end -module ActiveModel - class Serializer - module Adapter - class JsonApi - class LinkedTest < ActiveSupport::TestCase - def setup - @author1 = Author.new(id: 1, name: 'Steve K.') - @author2 = Author.new(id: 2, name: 'Tenderlove') - @bio1 = Bio.new(id: 1, content: 'AMS Contributor') - @bio2 = Bio.new(id: 2, content: 'Rails Contributor') - @first_post = Post.new(id: 10, title: 'Hello!!', body: 'Hello, world!!') - @second_post = Post.new(id: 20, title: 'New Post', body: 'Body') - @third_post = Post.new(id: 30, title: 'Yet Another Post', body: 'Body') - @blog = Blog.new({ name: 'AMS Blog' }) - @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') - @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT') - @first_post.blog = @blog - @second_post.blog = @blog - @third_post.blog = nil - @first_post.comments = [@first_comment, @second_comment] - @second_post.comments = [] - @third_post.comments = [] - @first_post.author = @author1 - @second_post.author = @author2 - @third_post.author = @author1 - @first_comment.post = @first_post - @first_comment.author = nil - @second_comment.post = @first_post - @second_comment.author = nil - @author1.posts = [@first_post, @third_post] - @author1.bio = @bio1 - @author1.roles = [] - @author2.posts = [@second_post] - @author2.bio = @bio2 - @author2.roles = [] - @bio1.author = @author1 - @bio2.author = @author2 - end +module ActiveModelSerializers + module Adapter + class JsonApi + class LinkedTest < ActiveSupport::TestCase + def setup + @author1 = Author.new(id: 1, name: 'Steve K.') + @author2 = Author.new(id: 2, name: 'Tenderlove') + @bio1 = Bio.new(id: 1, content: 'AMS Contributor') + @bio2 = Bio.new(id: 2, content: 'Rails Contributor') + @first_post = Post.new(id: 10, title: 'Hello!!', body: 'Hello, world!!') + @second_post = Post.new(id: 20, title: 'New Post', body: 'Body') + @third_post = Post.new(id: 30, title: 'Yet Another Post', body: 'Body') + @blog = Blog.new({ name: 'AMS Blog' }) + @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') + @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT') + @first_post.blog = @blog + @second_post.blog = @blog + @third_post.blog = nil + @first_post.comments = [@first_comment, @second_comment] + @second_post.comments = [] + @third_post.comments = [] + @first_post.author = @author1 + @second_post.author = @author2 + @third_post.author = @author1 + @first_comment.post = @first_post + @first_comment.author = nil + @second_comment.post = @first_post + @second_comment.author = nil + @author1.posts = [@first_post, @third_post] + @author1.bio = @bio1 + @author1.roles = [] + @author2.posts = [@second_post] + @author2.bio = @bio2 + @author2.roles = [] + @bio1.author = @author1 + @bio2.author = @author2 + end - def test_include_multiple_posts_and_linked_array - serializer = CollectionSerializer.new([@first_post, @second_post]) - adapter = ActiveModel::Serializer::Adapter::JsonApi.new( - serializer, - include: [:comments, author: [:bio]] - ) - alt_adapter = ActiveModel::Serializer::Adapter::JsonApi.new( - serializer, - include: [:comments, author: [:bio]] - ) + def test_include_multiple_posts_and_linked_array + serializer = ActiveModel::Serializer::CollectionSerializer.new([@first_post, @second_post]) + adapter = ActiveModelSerializers::Adapter::JsonApi.new( + serializer, + include: [:comments, author: [:bio]] + ) + alt_adapter = ActiveModelSerializers::Adapter::JsonApi.new( + serializer, + include: [:comments, author: [:bio]] + ) - expected = { - data: [ - { - id: '10', - type: 'posts', - attributes: { - title: 'Hello!!', - body: 'Hello, world!!' - }, - relationships: { - comments: { data: [{ type: 'comments', id: '1' }, { type: 'comments', id: '2' }] }, - blog: { data: { type: 'blogs', id: '999' } }, - author: { data: { type: 'authors', id: '1' } } - } + expected = { + data: [ + { + id: '10', + type: 'posts', + attributes: { + title: 'Hello!!', + body: 'Hello, world!!' }, - { - id: '20', - type: 'posts', - attributes: { - title: 'New Post', - body: 'Body' - }, - relationships: { - comments: { data: [] }, - blog: { data: { type: 'blogs', id: '999' } }, - author: { data: { type: 'authors', id: '2' } } - } + relationships: { + comments: { data: [{ type: 'comments', id: '1' }, { type: 'comments', id: '2' }] }, + blog: { data: { type: 'blogs', id: '999' } }, + author: { data: { type: 'authors', id: '1' } } } - ], - included: [ - { - id: '1', - type: 'comments', - attributes: { - body: 'ZOMG A COMMENT' - }, - relationships: { - post: { data: { type: 'posts', id: '10' } }, - author: { data: nil } - } - }, { - id: '2', - type: 'comments', - attributes: { - body: 'ZOMG ANOTHER COMMENT', - }, - relationships: { - post: { data: { type: 'posts', id: '10' } }, - author: { data: nil } - } - }, { - id: '1', - type: 'authors', - attributes: { - name: 'Steve K.' - }, - relationships: { - posts: { data: [{ type: 'posts', id: '10' }, { type: 'posts', id: '30' }] }, - roles: { data: [] }, - bio: { data: { type: 'bios', id: '1' } } - } - }, { - id: '1', - type: 'bios', - attributes: { - content: 'AMS Contributor', - rating: nil - }, - relationships: { - author: { data: { type: 'authors', id: '1' } } - } - }, { - id: '2', - type: 'authors', - attributes: { - name: 'Tenderlove' - }, - relationships: { - posts: { data: [{ type: 'posts', id: '20' }] }, - roles: { data: [] }, - bio: { data: { type: 'bios', id: '2' } } - } - }, { - id: '2', - type: 'bios', - attributes: { - rating: nil, - content: 'Rails Contributor', - }, - relationships: { - author: { data: { type: 'authors', id: '2' } } - } + }, + { + id: '20', + type: 'posts', + attributes: { + title: 'New Post', + body: 'Body' + }, + relationships: { + comments: { data: [] }, + blog: { data: { type: 'blogs', id: '999' } }, + author: { data: { type: 'authors', id: '2' } } } - ] - } - assert_equal expected, adapter.serializable_hash - assert_equal expected, alt_adapter.serializable_hash - end - - def test_include_multiple_posts_and_linked - serializer = BioSerializer.new @bio1 - adapter = ActiveModel::Serializer::Adapter::JsonApi.new( - serializer, - include: [author: [:posts]] - ) - alt_adapter = ActiveModel::Serializer::Adapter::JsonApi.new( - serializer, - include: [author: [:posts]] - ) - - expected = [ + } + ], + included: [ { + id: '1', + type: 'comments', + attributes: { + body: 'ZOMG A COMMENT' + }, + relationships: { + post: { data: { type: 'posts', id: '10' } }, + author: { data: nil } + } + }, { + id: '2', + type: 'comments', + attributes: { + body: 'ZOMG ANOTHER COMMENT', + }, + relationships: { + post: { data: { type: 'posts', id: '10' } }, + author: { data: nil } + } + }, { id: '1', type: 'authors', attributes: { @@ -178,215 +116,275 @@ def test_include_multiple_posts_and_linked bio: { data: { type: 'bios', id: '1' } } } }, { - id: '10', - type: 'posts', + id: '1', + type: 'bios', attributes: { - title: 'Hello!!', - body: 'Hello, world!!' + content: 'AMS Contributor', + rating: nil }, relationships: { - comments: { data: [{ type: 'comments', id: '1' }, { type: 'comments', id: '2' }] }, - blog: { data: { type: 'blogs', id: '999' } }, author: { data: { type: 'authors', id: '1' } } } }, { - id: '30', - type: 'posts', + id: '2', + type: 'authors', attributes: { - title: 'Yet Another Post', - body: 'Body' + name: 'Tenderlove' }, relationships: { - comments: { data: [] }, - blog: { data: { type: 'blogs', id: '999' } }, - author: { data: { type: 'authors', id: '1' } } + posts: { data: [{ type: 'posts', id: '20' }] }, + roles: { data: [] }, + bio: { data: { type: 'bios', id: '2' } } + } + }, { + id: '2', + type: 'bios', + attributes: { + rating: nil, + content: 'Rails Contributor', + }, + relationships: { + author: { data: { type: 'authors', id: '2' } } } } ] + } + assert_equal expected, adapter.serializable_hash + assert_equal expected, alt_adapter.serializable_hash + end - assert_equal expected, adapter.serializable_hash[:included] - assert_equal expected, alt_adapter.serializable_hash[:included] - end + def test_include_multiple_posts_and_linked + serializer = BioSerializer.new @bio1 + adapter = ActiveModelSerializers::Adapter::JsonApi.new( + serializer, + include: [author: [:posts]] + ) + alt_adapter = ActiveModelSerializers::Adapter::JsonApi.new( + serializer, + include: [author: [:posts]] + ) - def test_underscore_model_namespace_for_linked_resource_type - spammy_post = Post.new(id: 123) - spammy_post.related = [Spam::UnrelatedLink.new(id: 456)] - serializer = SpammyPostSerializer.new(spammy_post) - adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer) - relationships = adapter.serializable_hash[:data][:relationships] - expected = { - related: { - data: [{ - type: 'spam_unrelated_links', - id: '456' - }] + expected = [ + { + id: '1', + type: 'authors', + attributes: { + name: 'Steve K.' + }, + relationships: { + posts: { data: [{ type: 'posts', id: '10' }, { type: 'posts', id: '30' }] }, + roles: { data: [] }, + bio: { data: { type: 'bios', id: '1' } } + } + }, { + id: '10', + type: 'posts', + attributes: { + title: 'Hello!!', + body: 'Hello, world!!' + }, + relationships: { + comments: { data: [{ type: 'comments', id: '1' }, { type: 'comments', id: '2' }] }, + blog: { data: { type: 'blogs', id: '999' } }, + author: { data: { type: 'authors', id: '1' } } + } + }, { + id: '30', + type: 'posts', + attributes: { + title: 'Yet Another Post', + body: 'Body' + }, + relationships: { + comments: { data: [] }, + blog: { data: { type: 'blogs', id: '999' } }, + author: { data: { type: 'authors', id: '1' } } } } - assert_equal expected, relationships - end + ] - def test_multiple_references_to_same_resource - serializer = CollectionSerializer.new([@first_comment, @second_comment]) - adapter = ActiveModel::Serializer::Adapter::JsonApi.new( - serializer, - include: [:post] - ) + assert_equal expected, adapter.serializable_hash[:included] + assert_equal expected, alt_adapter.serializable_hash[:included] + end - expected = [ - { - id: '10', - type: 'posts', - attributes: { - title: 'Hello!!', - body: 'Hello, world!!' + def test_underscore_model_namespace_for_linked_resource_type + spammy_post = Post.new(id: 123) + spammy_post.related = [Spam::UnrelatedLink.new(id: 456)] + serializer = SpammyPostSerializer.new(spammy_post) + adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer) + relationships = adapter.serializable_hash[:data][:relationships] + expected = { + related: { + data: [{ + type: 'spam_unrelated_links', + id: '456' + }] + } + } + assert_equal expected, relationships + end + + def test_multiple_references_to_same_resource + serializer = ActiveModel::Serializer::CollectionSerializer.new([@first_comment, @second_comment]) + adapter = ActiveModelSerializers::Adapter::JsonApi.new( + serializer, + include: [:post] + ) + + expected = [ + { + id: '10', + type: 'posts', + attributes: { + title: 'Hello!!', + body: 'Hello, world!!' + }, + relationships: { + comments: { + data: [{ type: 'comments', id: '1' }, { type: 'comments', id: '2' }] }, - relationships: { - comments: { - data: [{ type: 'comments', id: '1' }, { type: 'comments', id: '2' }] - }, - blog: { - data: { type: 'blogs', id: '999' } - }, - author: { - data: { type: 'authors', id: '1' } - } + blog: { + data: { type: 'blogs', id: '999' } + }, + author: { + data: { type: 'authors', id: '1' } } } - ] + } + ] - assert_equal expected, adapter.serializable_hash[:included] - end + assert_equal expected, adapter.serializable_hash[:included] + end - def test_nil_link_with_specified_serializer - @first_post.author = nil - serializer = PostPreviewSerializer.new(@first_post) - adapter = ActiveModel::Serializer::Adapter::JsonApi.new( - serializer, - include: [:author] - ) + def test_nil_link_with_specified_serializer + @first_post.author = nil + serializer = PostPreviewSerializer.new(@first_post) + adapter = ActiveModelSerializers::Adapter::JsonApi.new( + serializer, + include: [:author] + ) - expected = { - data: { - id: '10', - type: 'posts', - attributes: { - title: 'Hello!!', - body: 'Hello, world!!' - }, - relationships: { - comments: { data: [{ type: 'comments', id: '1' }, { type: 'comments', id: '2' }] }, - author: { data: nil } - } + expected = { + data: { + id: '10', + type: 'posts', + attributes: { + title: 'Hello!!', + body: 'Hello, world!!' + }, + relationships: { + comments: { data: [{ type: 'comments', id: '1' }, { type: 'comments', id: '2' }] }, + author: { data: nil } } } - assert_equal expected, adapter.serializable_hash - end + } + assert_equal expected, adapter.serializable_hash end + end - class NoDuplicatesTest < ActiveSupport::TestCase - Post = Class.new(::Model) - Author = Class.new(::Model) + class NoDuplicatesTest < ActiveSupport::TestCase + Post = Class.new(::Model) + Author = Class.new(::Model) - class PostSerializer < ActiveModel::Serializer - type 'posts' - belongs_to :author - end + class PostSerializer < ActiveModel::Serializer + type 'posts' + belongs_to :author + end - class AuthorSerializer < ActiveModel::Serializer - type 'authors' - has_many :posts - end + class AuthorSerializer < ActiveModel::Serializer + type 'authors' + has_many :posts + end - def setup - @author = Author.new(id: 1, posts: [], roles: [], bio: nil) - @post1 = Post.new(id: 1, author: @author) - @post2 = Post.new(id: 2, author: @author) - @author.posts << @post1 - @author.posts << @post2 + def setup + @author = Author.new(id: 1, posts: [], roles: [], bio: nil) + @post1 = Post.new(id: 1, author: @author) + @post2 = Post.new(id: 2, author: @author) + @author.posts << @post1 + @author.posts << @post2 - @nestedpost1 = ::NestedPost.new(id: 1, nested_posts: []) - @nestedpost2 = ::NestedPost.new(id: 2, nested_posts: []) - @nestedpost1.nested_posts << @nestedpost1 - @nestedpost1.nested_posts << @nestedpost2 - @nestedpost2.nested_posts << @nestedpost1 - @nestedpost2.nested_posts << @nestedpost2 - end + @nestedpost1 = ::NestedPost.new(id: 1, nested_posts: []) + @nestedpost2 = ::NestedPost.new(id: 2, nested_posts: []) + @nestedpost1.nested_posts << @nestedpost1 + @nestedpost1.nested_posts << @nestedpost2 + @nestedpost2.nested_posts << @nestedpost1 + @nestedpost2.nested_posts << @nestedpost2 + end - def test_no_duplicates - hash = ActiveModel::SerializableResource.new(@post1, adapter: :json_api, - include: '*.*') - .serializable_hash - expected = [ - { - type: 'authors', id: '1', - relationships: { - posts: { - data: [ - { type: 'posts', id: '1' }, - { type: 'posts', id: '2' } - ] - } - } - }, - { - type: 'posts', id: '2', - relationships: { - author: { - data: { type: 'authors', id: '1' } - } + def test_no_duplicates + hash = ActiveModel::SerializableResource.new(@post1, adapter: :json_api, + include: '*.*') + .serializable_hash + expected = [ + { + type: 'authors', id: '1', + relationships: { + posts: { + data: [ + { type: 'posts', id: '1' }, + { type: 'posts', id: '2' } + ] } } - ] - assert_equal(expected, hash[:included]) - end - - def test_no_duplicates_collection - hash = ActiveModel::SerializableResource.new( - [@post1, @post2], adapter: :json_api, - include: '*.*') - .serializable_hash - expected = [ - { - type: 'authors', id: '1', - relationships: { - posts: { - data: [ - { type: 'posts', id: '1' }, - { type: 'posts', id: '2' } - ] - } + }, + { + type: 'posts', id: '2', + relationships: { + author: { + data: { type: 'authors', id: '1' } } } - ] - assert_equal(expected, hash[:included]) - end + } + ] + assert_equal(expected, hash[:included]) + end - def test_no_duplicates_global - hash = ActiveModel::SerializableResource.new( - @nestedpost1, - adapter: :json_api, - include: '*').serializable_hash - expected = [ - type: 'nested_posts', id: '2', + def test_no_duplicates_collection + hash = ActiveModel::SerializableResource.new( + [@post1, @post2], adapter: :json_api, + include: '*.*') + .serializable_hash + expected = [ + { + type: 'authors', id: '1', relationships: { - nested_posts: { + posts: { data: [ - { type: 'nested_posts', id: '1' }, - { type: 'nested_posts', id: '2' } + { type: 'posts', id: '1' }, + { type: 'posts', id: '2' } ] } } - ] - assert_equal(expected, hash[:included]) - end + } + ] + assert_equal(expected, hash[:included]) + end + + def test_no_duplicates_global + hash = ActiveModel::SerializableResource.new( + @nestedpost1, + adapter: :json_api, + include: '*').serializable_hash + expected = [ + type: 'nested_posts', id: '2', + relationships: { + nested_posts: { + data: [ + { type: 'nested_posts', id: '1' }, + { type: 'nested_posts', id: '2' } + ] + } + } + ] + assert_equal(expected, hash[:included]) + end - def test_no_duplicates_collection_global - hash = ActiveModel::SerializableResource.new( - [@nestedpost1, @nestedpost2], - adapter: :json_api, - include: '*').serializable_hash - assert_nil(hash[:included]) - end + def test_no_duplicates_collection_global + hash = ActiveModel::SerializableResource.new( + [@nestedpost1, @nestedpost2], + adapter: :json_api, + include: '*').serializable_hash + assert_nil(hash[:included]) end end end diff --git a/test/adapter/json_api/links_test.rb b/test/adapter/json_api/links_test.rb index dbda88ea0..4e97caa90 100644 --- a/test/adapter/json_api/links_test.rb +++ b/test/adapter/json_api/links_test.rb @@ -1,66 +1,64 @@ require 'test_helper' -module ActiveModel - class Serializer - module Adapter - class JsonApi - class LinksTest < ActiveSupport::TestCase - LinkAuthor = Class.new(::Model) - class LinkAuthorSerializer < ActiveModel::Serializer - link :self do - href "//example.com/link_author/#{object.id}" - meta stuff: 'value' - end +module ActiveModelSerializers + module Adapter + class JsonApi + class LinksTest < ActiveSupport::TestCase + LinkAuthor = Class.new(::Model) + class LinkAuthorSerializer < ActiveModel::Serializer + link :self do + href "//example.com/link_author/#{object.id}" + meta stuff: 'value' + end - link :other, '//example.com/resource' + link :other, '//example.com/resource' - link :yet_another do - "//example.com/resource/#{object.id}" - end + link :yet_another do + "//example.com/resource/#{object.id}" end + end - def setup - @post = Post.new(id: 1337, comments: [], author: nil) - @author = LinkAuthor.new(id: 1337) - end + def setup + @post = Post.new(id: 1337, comments: [], author: nil) + @author = LinkAuthor.new(id: 1337) + end - def test_toplevel_links - hash = ActiveModel::SerializableResource.new( - @post, - adapter: :json_api, - links: { - self: { - href: '//example.com/posts', - meta: { - stuff: 'value' - } - } - }).serializable_hash - expected = { + def test_toplevel_links + hash = ActiveModel::SerializableResource.new( + @post, + adapter: :json_api, + links: { self: { href: '//example.com/posts', meta: { stuff: 'value' } } + }).serializable_hash + expected = { + self: { + href: '//example.com/posts', + meta: { + stuff: 'value' + } } - assert_equal(expected, hash[:links]) - end + } + assert_equal(expected, hash[:links]) + end - def test_resource_links - hash = serializable(@author, adapter: :json_api).serializable_hash - expected = { - self: { - href: '//example.com/link_author/1337', - meta: { - stuff: 'value' - } - }, - other: '//example.com/resource', - yet_another: '//example.com/resource/1337' - } - assert_equal(expected, hash[:data][:links]) - end + def test_resource_links + hash = serializable(@author, adapter: :json_api).serializable_hash + expected = { + self: { + href: '//example.com/link_author/1337', + meta: { + stuff: 'value' + } + }, + other: '//example.com/resource', + yet_another: '//example.com/resource/1337' + } + assert_equal(expected, hash[:data][:links]) end end end diff --git a/test/adapter/json_api/pagination_links_test.rb b/test/adapter/json_api/pagination_links_test.rb index 805e5eb30..80046e102 100644 --- a/test/adapter/json_api/pagination_links_test.rb +++ b/test/adapter/json_api/pagination_links_test.rb @@ -4,110 +4,108 @@ require 'kaminari/hooks' ::Kaminari::Hooks.init -module ActiveModel - class Serializer - module Adapter - class JsonApi - class PaginationLinksTest < ActiveSupport::TestCase - URI = 'http://example.com' - - def setup - ActionController::Base.cache_store.clear - @array = [ - Profile.new({ id: 1, name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }), - Profile.new({ id: 2, name: 'Name 2', description: 'Description 2', comments: 'Comments 2' }), - Profile.new({ id: 3, name: 'Name 3', description: 'Description 3', comments: 'Comments 3' }) - ] - end +module ActiveModelSerializers + module Adapter + class JsonApi + class PaginationLinksTest < ActiveSupport::TestCase + URI = 'http://example.com' + + def setup + ActionController::Base.cache_store.clear + @array = [ + Profile.new({ id: 1, name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }), + Profile.new({ id: 2, name: 'Name 2', description: 'Description 2', comments: 'Comments 2' }), + Profile.new({ id: 3, name: 'Name 3', description: 'Description 3', comments: 'Comments 3' }) + ] + end - def mock_request(query_parameters = {}, original_url = URI) - context = Minitest::Mock.new - context.expect(:request_url, original_url) - context.expect(:query_parameters, query_parameters) - @options = {} - @options[:serialization_context] = context - end + def mock_request(query_parameters = {}, original_url = URI) + context = Minitest::Mock.new + context.expect(:request_url, original_url) + context.expect(:query_parameters, query_parameters) + @options = {} + @options[:serialization_context] = context + end - def load_adapter(paginated_collection, options = {}) - options = options.merge(adapter: :json_api) - ActiveModel::SerializableResource.new(paginated_collection, options) - end + def load_adapter(paginated_collection, options = {}) + options = options.merge(adapter: :json_api) + ActiveModel::SerializableResource.new(paginated_collection, options) + end - def using_kaminari - Kaminari.paginate_array(@array).page(2).per(1) - end + def using_kaminari + Kaminari.paginate_array(@array).page(2).per(1) + end - def using_will_paginate - @array.paginate(page: 2, per_page: 1) - end + def using_will_paginate + @array.paginate(page: 2, per_page: 1) + end - def data - { data: [ - { id: '1', type: 'profiles', attributes: { name: 'Name 1', description: 'Description 1' } }, - { id: '2', type: 'profiles', attributes: { name: 'Name 2', description: 'Description 2' } }, - { id: '3', type: 'profiles', attributes: { name: 'Name 3', description: 'Description 3' } } - ] - } - end + def data + { data: [ + { id: '1', type: 'profiles', attributes: { name: 'Name 1', description: 'Description 1' } }, + { id: '2', type: 'profiles', attributes: { name: 'Name 2', description: 'Description 2' } }, + { id: '3', type: 'profiles', attributes: { name: 'Name 3', description: 'Description 3' } } + ] + } + end - def links - { - links: { - self: "#{URI}?page%5Bnumber%5D=2&page%5Bsize%5D=1", - first: "#{URI}?page%5Bnumber%5D=1&page%5Bsize%5D=1", - prev: "#{URI}?page%5Bnumber%5D=1&page%5Bsize%5D=1", - next: "#{URI}?page%5Bnumber%5D=3&page%5Bsize%5D=1", - last: "#{URI}?page%5Bnumber%5D=3&page%5Bsize%5D=1" - } + def links + { + links: { + self: "#{URI}?page%5Bnumber%5D=2&page%5Bsize%5D=1", + first: "#{URI}?page%5Bnumber%5D=1&page%5Bsize%5D=1", + prev: "#{URI}?page%5Bnumber%5D=1&page%5Bsize%5D=1", + next: "#{URI}?page%5Bnumber%5D=3&page%5Bsize%5D=1", + last: "#{URI}?page%5Bnumber%5D=3&page%5Bsize%5D=1" } - end + } + end - def expected_response_without_pagination_links - data - end + def expected_response_without_pagination_links + data + end - def expected_response_with_pagination_links - {}.tap do |hash| - hash[:data] = [data.values.flatten.second] - hash.merge! links - end + def expected_response_with_pagination_links + {}.tap do |hash| + hash[:data] = [data.values.flatten.second] + hash.merge! links end + end - def expected_response_with_pagination_links_and_additional_params - new_links = links[:links].each_with_object({}) { |(key, value), hash| hash[key] = "#{value}&test=test" } - {}.tap do |hash| - hash[:data] = [data.values.flatten.second] - hash.merge! links: new_links - end + def expected_response_with_pagination_links_and_additional_params + new_links = links[:links].each_with_object({}) { |(key, value), hash| hash[key] = "#{value}&test=test" } + {}.tap do |hash| + hash[:data] = [data.values.flatten.second] + hash.merge! links: new_links end + end - def test_pagination_links_using_kaminari - adapter = load_adapter(using_kaminari) + def test_pagination_links_using_kaminari + adapter = load_adapter(using_kaminari) - mock_request - assert_equal expected_response_with_pagination_links, adapter.serializable_hash(@options) - end + mock_request + assert_equal expected_response_with_pagination_links, adapter.serializable_hash(@options) + end - def test_pagination_links_using_will_paginate - adapter = load_adapter(using_will_paginate) + def test_pagination_links_using_will_paginate + adapter = load_adapter(using_will_paginate) - mock_request - assert_equal expected_response_with_pagination_links, adapter.serializable_hash(@options) - end + mock_request + assert_equal expected_response_with_pagination_links, adapter.serializable_hash(@options) + end - def test_pagination_links_with_additional_params - adapter = load_adapter(using_will_paginate) + def test_pagination_links_with_additional_params + adapter = load_adapter(using_will_paginate) - mock_request({ test: 'test' }) - assert_equal expected_response_with_pagination_links_and_additional_params, - adapter.serializable_hash(@options) - end + mock_request({ test: 'test' }) + assert_equal expected_response_with_pagination_links_and_additional_params, + adapter.serializable_hash(@options) + end - def test_not_showing_pagination_links - adapter = load_adapter(@array) + def test_not_showing_pagination_links + adapter = load_adapter(@array) - assert_equal expected_response_without_pagination_links, adapter.serializable_hash - end + assert_equal expected_response_without_pagination_links, adapter.serializable_hash end end end diff --git a/test/adapter/json_api/parse_test.rb b/test/adapter/json_api/parse_test.rb index c80988168..bee79c8c1 100644 --- a/test/adapter/json_api/parse_test.rb +++ b/test/adapter/json_api/parse_test.rb @@ -1,136 +1,134 @@ require 'test_helper' -module ActiveModel - class Serializer - module Adapter - class JsonApi - module Deserialization - class ParseTest < Minitest::Test - def setup - @hash = { - 'data' => { - 'type' => 'photos', - 'id' => 'zorglub', - 'attributes' => { - 'title' => 'Ember Hamster', - 'src' => 'http://example.com/images/productivity.png' +module ActiveModelSerializers + module Adapter + class JsonApi + module Deserialization + class ParseTest < Minitest::Test + def setup + @hash = { + 'data' => { + 'type' => 'photos', + 'id' => 'zorglub', + 'attributes' => { + 'title' => 'Ember Hamster', + 'src' => 'http://example.com/images/productivity.png' + }, + 'relationships' => { + 'author' => { + 'data' => nil }, - 'relationships' => { - 'author' => { - 'data' => nil - }, - 'photographer' => { - 'data' => { 'type' => 'people', 'id' => '9' } - }, - 'comments' => { - 'data' => [ - { 'type' => 'comments', 'id' => '1' }, - { 'type' => 'comments', 'id' => '2' } - ] - } + 'photographer' => { + 'data' => { 'type' => 'people', 'id' => '9' } + }, + 'comments' => { + 'data' => [ + { 'type' => 'comments', 'id' => '1' }, + { 'type' => 'comments', 'id' => '2' } + ] } } } - @params = ActionController::Parameters.new(@hash) - @expected = { - id: 'zorglub', - title: 'Ember Hamster', - src: 'http://example.com/images/productivity.png', - author_id: nil, - photographer_id: '9', - comment_ids: %w(1 2) - } + } + @params = ActionController::Parameters.new(@hash) + @expected = { + id: 'zorglub', + title: 'Ember Hamster', + src: 'http://example.com/images/productivity.png', + author_id: nil, + photographer_id: '9', + comment_ids: %w(1 2) + } - @illformed_payloads = [nil, - {}, - { - 'data' => nil - }, { - 'data' => { 'attributes' => [] } - }, { - 'data' => { 'relationships' => [] } - }, { - 'data' => { - 'relationships' => { 'rel' => nil } - } - }, { - 'data' => { - 'relationships' => { 'rel' => {} } - } - }] - end + @illformed_payloads = [nil, + {}, + { + 'data' => nil + }, { + 'data' => { 'attributes' => [] } + }, { + 'data' => { 'relationships' => [] } + }, { + 'data' => { + 'relationships' => { 'rel' => nil } + } + }, { + 'data' => { + 'relationships' => { 'rel' => {} } + } + }] + end - def test_hash - parsed_hash = ActiveModel::Serializer::Adapter::JsonApi::Deserialization.parse!(@hash) - assert_equal(@expected, parsed_hash) - end + def test_hash + parsed_hash = ActiveModelSerializers::Adapter::JsonApi::Deserialization.parse!(@hash) + assert_equal(@expected, parsed_hash) + end - def test_actioncontroller_parameters - assert_equal(false, @params.permitted?) - parsed_hash = ActiveModel::Serializer::Adapter::JsonApi::Deserialization.parse!(@params) - assert_equal(@expected, parsed_hash) - end + def test_actioncontroller_parameters + assert_equal(false, @params.permitted?) + parsed_hash = ActiveModelSerializers::Adapter::JsonApi::Deserialization.parse!(@params) + assert_equal(@expected, parsed_hash) + end - def test_illformed_payloads_safe - @illformed_payloads.each do |p| - parsed_hash = ActiveModel::Serializer::Adapter::JsonApi::Deserialization.parse(p) - assert_equal({}, parsed_hash) - end + def test_illformed_payloads_safe + @illformed_payloads.each do |p| + parsed_hash = ActiveModelSerializers::Adapter::JsonApi::Deserialization.parse(p) + assert_equal({}, parsed_hash) end + end - def test_illformed_payloads_unsafe - @illformed_payloads.each do |p| - assert_raises(InvalidDocument) do - ActiveModel::Serializer::Adapter::JsonApi::Deserialization.parse!(p) - end + def test_illformed_payloads_unsafe + @illformed_payloads.each do |p| + assert_raises(InvalidDocument) do + 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]) - expected = { - id: 'zorglub', - title: 'Ember Hamster', - author_id: nil - } - assert_equal(expected, parsed_hash) - end + def test_filter_fields_only + parsed_hash = ActiveModelSerializers::Adapter::JsonApi::Deserialization.parse!(@hash, only: [:id, :title, :author]) + expected = { + id: 'zorglub', + title: 'Ember Hamster', + author_id: nil + } + assert_equal(expected, parsed_hash) + end - def test_filter_fields_except - parsed_hash = ActiveModel::Serializer::Adapter::JsonApi::Deserialization.parse!(@hash, except: [:id, :title, :author]) - expected = { - src: 'http://example.com/images/productivity.png', - photographer_id: '9', - comment_ids: %w(1 2) - } - assert_equal(expected, parsed_hash) - end + def test_filter_fields_except + parsed_hash = ActiveModelSerializers::Adapter::JsonApi::Deserialization.parse!(@hash, except: [:id, :title, :author]) + expected = { + src: 'http://example.com/images/productivity.png', + photographer_id: '9', + comment_ids: %w(1 2) + } + assert_equal(expected, parsed_hash) + end - def test_keys - parsed_hash = ActiveModel::Serializer::Adapter::JsonApi::Deserialization.parse!(@hash, keys: { author: :user, title: :post_title }) - expected = { - id: 'zorglub', - post_title: 'Ember Hamster', - src: 'http://example.com/images/productivity.png', - user_id: nil, - photographer_id: '9', - comment_ids: %w(1 2) - } - assert_equal(expected, parsed_hash) - end + def test_keys + parsed_hash = ActiveModelSerializers::Adapter::JsonApi::Deserialization.parse!(@hash, keys: { author: :user, title: :post_title }) + expected = { + id: 'zorglub', + post_title: 'Ember Hamster', + src: 'http://example.com/images/productivity.png', + user_id: nil, + photographer_id: '9', + comment_ids: %w(1 2) + } + assert_equal(expected, parsed_hash) + end - def test_polymorphic - parsed_hash = ActiveModel::Serializer::Adapter::JsonApi::Deserialization.parse!(@hash, polymorphic: [:photographer]) - expected = { - id: 'zorglub', - title: 'Ember Hamster', - src: 'http://example.com/images/productivity.png', - author_id: nil, - photographer_id: '9', - photographer_type: 'people', - comment_ids: %w(1 2) - } - assert_equal(expected, parsed_hash) - end + def test_polymorphic + parsed_hash = ActiveModelSerializers::Adapter::JsonApi::Deserialization.parse!(@hash, polymorphic: [:photographer]) + expected = { + id: 'zorglub', + title: 'Ember Hamster', + src: 'http://example.com/images/productivity.png', + author_id: nil, + photographer_id: '9', + photographer_type: 'people', + comment_ids: %w(1 2) + } + assert_equal(expected, parsed_hash) end end end diff --git a/test/adapter/json_api/resource_type_config_test.rb b/test/adapter/json_api/resource_type_config_test.rb index d4301c756..571552d92 100644 --- a/test/adapter/json_api/resource_type_config_test.rb +++ b/test/adapter/json_api/resource_type_config_test.rb @@ -1,69 +1,67 @@ require 'test_helper' -module ActiveModel - class Serializer - module Adapter - class JsonApi - class ResourceTypeConfigTest < ActiveSupport::TestCase - class ProfileTypeSerializer < ActiveModel::Serializer - attributes :name - type 'profile' - end +module ActiveModelSerializers + module Adapter + class JsonApi + class ResourceTypeConfigTest < ActiveSupport::TestCase + class ProfileTypeSerializer < ActiveModel::Serializer + attributes :name + type 'profile' + end - def setup - @author = Author.new(id: 1, name: 'Steve K.') - @author.bio = nil - @author.roles = [] - @blog = Blog.new(id: 23, name: 'AMS Blog') - @post = Post.new(id: 42, title: 'New Post', body: 'Body') - @anonymous_post = Post.new(id: 43, title: 'Hello!!', body: 'Hello, world!!') - @comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') - @post.comments = [@comment] - @post.blog = @blog - @anonymous_post.comments = [] - @anonymous_post.blog = nil - @comment.post = @post - @comment.author = nil - @post.author = @author - @anonymous_post.author = nil - @blog = Blog.new(id: 1, name: 'My Blog!!') - @blog.writer = @author - @blog.articles = [@post, @anonymous_post] - @author.posts = [] - end + def setup + @author = Author.new(id: 1, name: 'Steve K.') + @author.bio = nil + @author.roles = [] + @blog = Blog.new(id: 23, name: 'AMS Blog') + @post = Post.new(id: 42, title: 'New Post', body: 'Body') + @anonymous_post = Post.new(id: 43, title: 'Hello!!', body: 'Hello, world!!') + @comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') + @post.comments = [@comment] + @post.blog = @blog + @anonymous_post.comments = [] + @anonymous_post.blog = nil + @comment.post = @post + @comment.author = nil + @post.author = @author + @anonymous_post.author = nil + @blog = Blog.new(id: 1, name: 'My Blog!!') + @blog.writer = @author + @blog.articles = [@post, @anonymous_post] + @author.posts = [] + end - def with_jsonapi_resource_type type - old_type = ActiveModelSerializers.config.jsonapi_resource_type - ActiveModelSerializers.config.jsonapi_resource_type = type - yield - ensure - ActiveModelSerializers.config.jsonapi_resource_type = old_type - end + def with_jsonapi_resource_type type + old_type = ActiveModelSerializers.config.jsonapi_resource_type + ActiveModelSerializers.config.jsonapi_resource_type = type + yield + ensure + ActiveModelSerializers.config.jsonapi_resource_type = old_type + end - def test_config_plural - with_jsonapi_resource_type :plural do - hash = serializable(@comment, adapter: :json_api).serializable_hash - assert_equal('comments', hash[:data][:type]) - end + def test_config_plural + with_jsonapi_resource_type :plural do + hash = serializable(@comment, adapter: :json_api).serializable_hash + assert_equal('comments', hash[:data][:type]) end + end - def test_config_singular - with_jsonapi_resource_type :singular do - hash = serializable(@comment, adapter: :json_api).serializable_hash - assert_equal('comment', hash[:data][:type]) - end + def test_config_singular + with_jsonapi_resource_type :singular do + hash = serializable(@comment, adapter: :json_api).serializable_hash + assert_equal('comment', hash[:data][:type]) end + end - def test_explicit_type_value - hash = serializable(@author, serializer: ProfileTypeSerializer, adapter: :json_api).serializable_hash - assert_equal('profile', hash.fetch(:data).fetch(:type)) - end + def test_explicit_type_value + hash = serializable(@author, serializer: ProfileTypeSerializer, adapter: :json_api).serializable_hash + assert_equal('profile', hash.fetch(:data).fetch(:type)) + end - private + private - def serializable(resource, options = {}) - ActiveModel::SerializableResource.new(resource, options) - end + def serializable(resource, options = {}) + ActiveModel::SerializableResource.new(resource, options) end end end diff --git a/test/adapter/json_api/toplevel_jsonapi_test.rb b/test/adapter/json_api/toplevel_jsonapi_test.rb index e0c410ac3..7b0357e52 100644 --- a/test/adapter/json_api/toplevel_jsonapi_test.rb +++ b/test/adapter/json_api/toplevel_jsonapi_test.rb @@ -1,82 +1,80 @@ require 'test_helper' -module ActiveModel - class Serializer - module Adapter - class JsonApi - class TopLevelJsonApiTest < ActiveSupport::TestCase - def setup - @author = Author.new(id: 1, name: 'Steve K.') - @author.bio = nil - @author.roles = [] - @blog = Blog.new(id: 23, name: 'AMS Blog') - @post = Post.new(id: 42, title: 'New Post', body: 'Body') - @anonymous_post = Post.new(id: 43, title: 'Hello!!', body: 'Hello, world!!') - @comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') - @post.comments = [@comment] - @post.blog = @blog - @anonymous_post.comments = [] - @anonymous_post.blog = nil - @comment.post = @post - @comment.author = nil - @post.author = @author - @anonymous_post.author = nil - @blog = Blog.new(id: 1, name: 'My Blog!!') - @blog.writer = @author - @blog.articles = [@post, @anonymous_post] - @author.posts = [] - end +module ActiveModelSerializers + module Adapter + class JsonApi + class TopLevelJsonApiTest < ActiveSupport::TestCase + def setup + @author = Author.new(id: 1, name: 'Steve K.') + @author.bio = nil + @author.roles = [] + @blog = Blog.new(id: 23, name: 'AMS Blog') + @post = Post.new(id: 42, title: 'New Post', body: 'Body') + @anonymous_post = Post.new(id: 43, title: 'Hello!!', body: 'Hello, world!!') + @comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') + @post.comments = [@comment] + @post.blog = @blog + @anonymous_post.comments = [] + @anonymous_post.blog = nil + @comment.post = @post + @comment.author = nil + @post.author = @author + @anonymous_post.author = nil + @blog = Blog.new(id: 1, name: 'My Blog!!') + @blog.writer = @author + @blog.articles = [@post, @anonymous_post] + @author.posts = [] + end - def test_toplevel_jsonapi_defaults_to_false - assert_equal config.fetch(:jsonapi_include_toplevel_object), false - end + def test_toplevel_jsonapi_defaults_to_false + assert_equal config.fetch(:jsonapi_include_toplevel_object), false + end - def test_disable_toplevel_jsonapi - with_config(jsonapi_include_toplevel_object: false) do - hash = serialize(@post) - assert_nil(hash[:jsonapi]) - end + def test_disable_toplevel_jsonapi + with_config(jsonapi_include_toplevel_object: false) do + hash = serialize(@post) + assert_nil(hash[:jsonapi]) end + end - def test_enable_toplevel_jsonapi - with_config(jsonapi_include_toplevel_object: true) do - hash = serialize(@post) - refute_nil(hash[:jsonapi]) - end + def test_enable_toplevel_jsonapi + with_config(jsonapi_include_toplevel_object: true) do + hash = serialize(@post) + refute_nil(hash[:jsonapi]) end + end - def test_default_toplevel_jsonapi_version - with_config(jsonapi_include_toplevel_object: true) do - hash = serialize(@post) - assert_equal('1.0', hash[:jsonapi][:version]) - end + def test_default_toplevel_jsonapi_version + with_config(jsonapi_include_toplevel_object: true) do + hash = serialize(@post) + assert_equal('1.0', hash[:jsonapi][:version]) end + end - def test_toplevel_jsonapi_no_meta - with_config(jsonapi_include_toplevel_object: true) do - hash = serialize(@post) - assert_nil(hash[:jsonapi][:meta]) - end + def test_toplevel_jsonapi_no_meta + with_config(jsonapi_include_toplevel_object: true) do + hash = serialize(@post) + assert_nil(hash[:jsonapi][:meta]) end + end - def test_toplevel_jsonapi_meta - new_config = { - jsonapi_include_toplevel_object: true, - jsonapi_toplevel_meta: { - 'copyright' => 'Copyright 2015 Example Corp.' - } + def test_toplevel_jsonapi_meta + new_config = { + jsonapi_include_toplevel_object: true, + jsonapi_toplevel_meta: { + 'copyright' => 'Copyright 2015 Example Corp.' } - with_config(new_config) do - hash = serialize(@post) - assert_equal(new_config[:jsonapi_toplevel_meta], hash.fetch(:jsonapi).fetch(:meta)) - end + } + with_config(new_config) do + hash = serialize(@post) + assert_equal(new_config[:jsonapi_toplevel_meta], hash.fetch(:jsonapi).fetch(:meta)) end + end - private + private - def serialize(resource, options = {}) - serializable(resource, { adapter: :json_api }.merge!(options)).serializable_hash - end + def serialize(resource, options = {}) + serializable(resource, { adapter: :json_api }.merge!(options)).serializable_hash end end end diff --git a/test/adapter/json_test.rb b/test/adapter/json_test.rb index ab4e756aa..a7d3bc834 100644 --- a/test/adapter/json_test.rb +++ b/test/adapter/json_test.rb @@ -1,46 +1,44 @@ require 'test_helper' -module ActiveModel - class Serializer - module Adapter - class JsonTest < ActiveSupport::TestCase - def setup - ActionController::Base.cache_store.clear - @author = Author.new(id: 1, name: 'Steve K.') - @post = Post.new(id: 1, title: 'New Post', body: 'Body') - @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') - @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT') - @post.comments = [@first_comment, @second_comment] - @first_comment.post = @post - @second_comment.post = @post - @post.author = @author - @blog = Blog.new(id: 1, name: 'My Blog!!') - @post.blog = @blog +module ActiveModelSerializers + module Adapter + class JsonTest < ActiveSupport::TestCase + def setup + ActionController::Base.cache_store.clear + @author = Author.new(id: 1, name: 'Steve K.') + @post = Post.new(id: 1, title: 'New Post', body: 'Body') + @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') + @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT') + @post.comments = [@first_comment, @second_comment] + @first_comment.post = @post + @second_comment.post = @post + @post.author = @author + @blog = Blog.new(id: 1, name: 'My Blog!!') + @post.blog = @blog - @serializer = PostSerializer.new(@post) - @adapter = ActiveModel::Serializer::Adapter::Json.new(@serializer) - end + @serializer = PostSerializer.new(@post) + @adapter = ActiveModelSerializers::Adapter::Json.new(@serializer) + end - def test_has_many - assert_equal([ - { id: 1, body: 'ZOMG A COMMENT' }, - { id: 2, body: 'ZOMG ANOTHER COMMENT' } - ], @adapter.serializable_hash[:post][:comments]) - end + def test_has_many + assert_equal([ + { id: 1, body: 'ZOMG A COMMENT' }, + { id: 2, body: 'ZOMG ANOTHER COMMENT' } + ], @adapter.serializable_hash[:post][:comments]) + end - def test_custom_keys - serializer = PostWithCustomKeysSerializer.new(@post) - adapter = ActiveModel::Serializer::Adapter::Json.new(serializer) + def test_custom_keys + serializer = PostWithCustomKeysSerializer.new(@post) + adapter = ActiveModelSerializers::Adapter::Json.new(serializer) - assert_equal({ - id: 1, - reviews: [{ id: 1, body: 'ZOMG A COMMENT' }, - { id: 2, body: 'ZOMG ANOTHER COMMENT' } - ], - writer: { id: 1, name: 'Steve K.' }, - site: { id: 1, name: 'My Blog!!' } - }, adapter.serializable_hash[:post]) - end + assert_equal({ + id: 1, + reviews: [{ id: 1, body: 'ZOMG A COMMENT' }, + { id: 2, body: 'ZOMG ANOTHER COMMENT' } + ], + writer: { id: 1, name: 'Steve K.' }, + site: { id: 1, name: 'My Blog!!' } + }, adapter.serializable_hash[:post]) end end end diff --git a/test/adapter/null_test.rb b/test/adapter/null_test.rb index 9d9896035..3dd666b07 100644 --- a/test/adapter/null_test.rb +++ b/test/adapter/null_test.rb @@ -1,23 +1,21 @@ require 'test_helper' -module ActiveModel - class Serializer - module Adapter - class NullTest < ActiveSupport::TestCase - def setup - profile = Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }) - serializer = ProfileSerializer.new(profile) +module ActiveModelSerializers + module Adapter + class NullTest < ActiveSupport::TestCase + def setup + profile = Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }) + serializer = ProfileSerializer.new(profile) - @adapter = Null.new(serializer) - end + @adapter = Null.new(serializer) + end - def test_serializable_hash - assert_equal({}, @adapter.serializable_hash) - end + def test_serializable_hash + assert_equal({}, @adapter.serializable_hash) + end - def test_it_returns_empty_json - assert_equal('{}', @adapter.to_json) - end + def test_it_returns_empty_json + assert_equal('{}', @adapter.to_json) end end end diff --git a/test/adapter_test.rb b/test/adapter_test.rb index 1253882af..a3700c154 100644 --- a/test/adapter_test.rb +++ b/test/adapter_test.rb @@ -1,42 +1,40 @@ require 'test_helper' -module ActiveModel - class Serializer - class AdapterTest < ActiveSupport::TestCase - def setup - profile = Profile.new - @serializer = ProfileSerializer.new(profile) - @adapter = ActiveModel::Serializer::Adapter::Base.new(@serializer) - end +module ActiveModelSerializers + class AdapterTest < ActiveSupport::TestCase + def setup + profile = Profile.new + @serializer = ProfileSerializer.new(profile) + @adapter = ActiveModelSerializers::Adapter::Base.new(@serializer) + end - def test_serializable_hash_is_abstract_method - assert_raises(NotImplementedError) do - @adapter.serializable_hash(only: [:name]) - end + def test_serializable_hash_is_abstract_method + assert_raises(NotImplementedError) do + @adapter.serializable_hash(only: [:name]) end + end - def test_serializer - assert_equal @serializer, @adapter.serializer - end + def test_serializer + assert_equal @serializer, @adapter.serializer + end - def test_create_adapter - adapter = ActiveModel::Serializer::Adapter.create(@serializer) - assert_equal ActiveModel::Serializer::Adapter::Attributes, adapter.class - end + def test_create_adapter + 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 - end + def test_create_adapter_with_override + 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) + def test_inflected_adapter_class_for_known_adapter + ActiveSupport::Inflector.inflections(:en) { |inflect| inflect.acronym 'API' } + klass = ActiveModelSerializers::Adapter.adapter_class(:json_api) - ActiveSupport::Inflector.inflections.acronyms.clear + ActiveSupport::Inflector.inflections.acronyms.clear - assert_equal ActiveModel::Serializer::Adapter::JsonApi, klass - end + assert_equal ActiveModelSerializers::Adapter::JsonApi, klass end end end diff --git a/test/serializable_resource_test.rb b/test/serializable_resource_test.rb index 62bc5d91c..d32e9fcff 100644 --- a/test/serializable_resource_test.rb +++ b/test/serializable_resource_test.rb @@ -5,7 +5,7 @@ class SerializableResourceTest < ActiveSupport::TestCase 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 diff --git a/test/serializers/adapter_for_test.rb b/test/serializers/adapter_for_test.rb deleted file mode 100644 index 1775620d2..000000000 --- a/test/serializers/adapter_for_test.rb +++ /dev/null @@ -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 diff --git a/test/serializers/attribute_test.rb b/test/serializers/attribute_test.rb index c675e0aca..198be84cd 100644 --- a/test/serializers/attribute_test.rb +++ b/test/serializers/attribute_test.rb @@ -14,14 +14,14 @@ def test_attributes_definition 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 @@ def test_id_attribute_override 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 @@ def test_object_attribute_override 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 @@ def test_type_attribute 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 From e35390623d190958d865050ca8d0a71ba9d4a85a Mon Sep 17 00:00:00 2001 From: Benjamin Fleischer Date: Thu, 25 Feb 2016 23:08:20 -0600 Subject: [PATCH 2/3] Improve adapter test coverage per groyoh --- docs/rfcs/0000-namespace.md | 2 +- lib/active_model/serializer.rb | 2 - lib/active_model_serializers.rb | 1 + .../adapter_for_test.rb | 39 +++++++++++++++++++ 4 files changed, 41 insertions(+), 3 deletions(-) diff --git a/docs/rfcs/0000-namespace.md b/docs/rfcs/0000-namespace.md index 9532cae5c..da07c4c18 100644 --- a/docs/rfcs/0000-namespace.md +++ b/docs/rfcs/0000-namespace.md @@ -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::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 diff --git a/lib/active_model/serializer.rb b/lib/active_model/serializer.rb index b3d6a1fa6..64bff0eca 100644 --- a/lib/active_model/serializer.rb +++ b/lib/active_model/serializer.rb @@ -23,8 +23,6 @@ class Serializer include Links include Meta include Type - # Deprecated - require 'active_model_serializers/adapter' # @param resource [ActiveRecord::Base, ActiveModelSerializers::Model] # @return [ActiveModel::Serializer] diff --git a/lib/active_model_serializers.rb b/lib/active_model_serializers.rb index 47e14208f..e75e30a0d 100644 --- a/lib/active_model_serializers.rb +++ b/lib/active_model_serializers.rb @@ -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)) diff --git a/test/active_model_serializers/adapter_for_test.rb b/test/active_model_serializers/adapter_for_test.rb index 5fe3b8578..8dfbc9f3f 100644 --- a/test/active_model_serializers/adapter_for_test.rb +++ b/test/active_model_serializers/adapter_for_test.rb @@ -10,6 +10,12 @@ 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 @@ -24,11 +30,40 @@ def test_overwrite_adapter_with_symbol 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 @@ -37,6 +72,8 @@ def test_raises_exception_if_invalid_symbol_given 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 @@ -45,6 +82,8 @@ def test_raises_exception_if_it_does_not_know_hot_to_infer_adapter assert_raises UnknownAdapterError do ActiveModelSerializers::Adapter.configured_adapter end + ensure + ActiveModelSerializers.config.adapter = @previous_adapter end def test_adapter_class_for_known_adapter From fcdb58f67d924042f7e46a5170cc4bd94628fa57 Mon Sep 17 00:00:00 2001 From: Benjamin Fleischer Date: Thu, 25 Feb 2016 23:21:44 -0600 Subject: [PATCH 3/3] Remove AS::Testing::Stream in favor of Minitest assert_output --- .rubocop_todo.yml | 1 - test/action_controller/serialization_test.rb | 9 ++-- test/array_serializer_test.rb | 11 ++--- test/serializers/cache_test.rb | 6 +-- test/support/stream_capture.rb | 50 -------------------- test/test_helper.rb | 2 - 6 files changed, 11 insertions(+), 68 deletions(-) delete mode 100644 test/support/stream_capture.rb diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index c9c8e604e..43b34bb91 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -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. diff --git a/test/action_controller/serialization_test.rb b/test/action_controller/serialization_test.rb index 6c5663001..e8e99dd5e 100644 --- a/test/action_controller/serialization_test.rb +++ b/test/action_controller/serialization_test.rb @@ -3,7 +3,6 @@ module ActionController module Serialization class ImplicitSerializerTest < ActionController::TestCase - include ActiveSupport::Testing::Stream class ImplicitSerializationTestController < ActionController::Base include SerializationTesting def render_using_implicit_serializer @@ -438,9 +437,9 @@ def use_adapter? 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 @@ def use_adapter? 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 diff --git a/test/array_serializer_test.rb b/test/array_serializer_test.rb index 350e44473..50028371a 100644 --- a/test/array_serializer_test.rb +++ b/test/array_serializer_test.rb @@ -6,11 +6,11 @@ class Serializer # 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 @@ def collection_serializer 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 diff --git a/test/serializers/cache_test.rb b/test/serializers/cache_test.rb index 4167a77fb..7c76d2704 100644 --- a/test/serializers/cache_test.rb +++ b/test/serializers/cache_test.rb @@ -3,8 +3,6 @@ require 'tempfile' 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') @@ -222,10 +220,10 @@ def test_digest_caller_file 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 diff --git a/test/support/stream_capture.rb b/test/support/stream_capture.rb deleted file mode 100644 index da6acba3b..000000000 --- a/test/support/stream_capture.rb +++ /dev/null @@ -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 - diff --git a/test/test_helper.rb b/test/test_helper.rb index 59c0d3f41..4a6950d35 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -43,8 +43,6 @@ require 'minitest/reporters' Minitest::Reporters.use! -require 'support/stream_capture' - require 'support/rails_app' require 'support/test_case'