Skip to content

Commit

Permalink
Follow up to rails-api#1535
Browse files Browse the repository at this point in the history
- The removed classes and modules were added back with deprecation
  warning and deprecation test were added for them.
- One test was renamed because it contained `__`.
- Some tests were refactored.
- The ActiveModelSerializers::Deserialization module is now called
  Adapter instead of ActiveModelSerializers::Adapter.
- The changelog was added for rails-api#1535
  • Loading branch information
Yohan Robert committed Feb 28, 2016
1 parent 8a04005 commit e6fdeac
Show file tree
Hide file tree
Showing 19 changed files with 1,181 additions and 51 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ Fixes:
- [#1488](https://github.com/rails-api/active_model_serializers/pull/1488) Require ActiveSupport's string inflections (@nate00)

Misc:
- [#1543](https://github.com/rails-api/active_model_serializers/pull/1543) Add the changes missing from #1535
which were not already in an open PR. (@groyoh)
- [#1535](https://github.com/rails-api/active_model_serializers/pull/1535) Move the adapter and adapter folder to
active_model_serializers folder and changes the module namespace. (@domitian @bf4)
- [#1497](https://github.com/rails-api/active_model_serializers/pull/1497) Add JRuby-9000 to appveyor.yml(@corainchicago)

### v0.10.0.rc4 (2016/01/27 11:00 +00:00)
Expand Down
3 changes: 2 additions & 1 deletion lib/active_model/serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class Serializer
include Links
include Meta
include Type
require 'active_model/serializer/adapter'

# @param resource [ActiveRecord::Base, ActiveModelSerializers::Model]
# @return [ActiveModel::Serializer]
Expand All @@ -44,7 +45,7 @@ def self.serializer_for(resource, options = {})
# @see ActiveModelSerializers::Adapter.lookup
# Deprecated
def self.adapter
warn 'Calling adapter method in Serializer, please use the ActiveModelSerializers::configured_adapter'
warn 'Calling adapter method in Serializer, please use the ActiveModelSerializers::Adapter.configured_adapter'
ActiveModelSerializers::Adapter.lookup(config.adapter)
end

Expand Down
99 changes: 99 additions & 0 deletions lib/active_model/serializer/adapter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
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 = {})
warn "Calling deprecated ActiveModel::Serializer::Adater in #{caller[0..2].join(', ')}. Please use ActiveModelSerializers::Adapter"
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)
warn "Calling deprecated ActiveModel::Serializer::Adater in #{caller[0..2].join(', ')}. Please use ActiveModelSerializers::Adapter"
ActiveModel::Serializer::Adapter.lookup(adapter)
end

# @return Hash<adapter_name, adapter_class>
def adapter_map
warn "Calling deprecated ActiveModel::Serializer::Adater in #{caller[0..2].join(', ')}. Please use ActiveModelSerializers::Adapter"
ADAPTER_MAP
end

# @return [Array<Symbol>] list of adapter names
def adapters
warn "Calling deprecated ActiveModel::Serializer::Adater in #{caller[0..2].join(', ')}. Please use ActiveModelSerializers::Adapter"
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).underscore
unless %w(json_api json attributes null).include?(name)
warn "Calling deprecated ActiveModel::Serializer::Adater in #{caller[0..2].join(', ')}. Please use ActiveModelSerializers::Adapter"
end
ADAPTER_MAP.update(name => 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)
warn "Calling deprecated ActiveModel::Serializer::Adater in #{caller[0..2].join(', ')}. Please use ActiveModelSerializers::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
101 changes: 101 additions & 0 deletions lib/active_model/serializer/adapter/attributes.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
module ActiveModel
class Serializer
module Adapter
class Attributes < Base
def initialize(serializer, options = {})
warn 'Using ActiveModel::Serializer::Adater::Attributes is deprecated, please use the ActiveModelSerializers::Adapter::Attributes'
super
@include_tree = IncludeTree.from_include_args(options[:include] || '*')
@cached_attributes = options[:cache_attributes] || {}
end

def serializable_hash(options = nil)
options ||= {}

if serializer.respond_to?(:each)
serializable_hash_for_collection(options)
else
serializable_hash_for_single_resource(options)
end
end

def fragment_cache(cached_hash, non_cached_hash)
Json::FragmentCache.new.fragment_cache(cached_hash, non_cached_hash)
end

private

def serializable_hash_for_collection(options)
cache_attributes

serializer.map { |s| Attributes.new(s, instance_options).serializable_hash(options) }
end

# Read cache from cache_store
# @return [Hash]
def cache_read_multi
return {} if ActiveModelSerializers.config.cache_store.blank?

keys = CachedSerializer.object_cache_keys(serializer, @include_tree)

return {} if keys.blank?

ActiveModelSerializers.config.cache_store.read_multi(*keys)
end

# Set @cached_attributes
def cache_attributes
return if @cached_attributes.present?

@cached_attributes = cache_read_multi
end

# Get attributes from @cached_attributes
# @return [Hash] cached attributes
def cached_attributes(cached_serializer)
return yield unless cached_serializer.cached?

@cached_attributes.fetch(cached_serializer.cache_key) { yield }
end

def serializable_hash_for_single_resource(options)
resource = resource_object_for(options)
relationships = resource_relationships(options)
resource.merge!(relationships)
end

def resource_relationships(options)
relationships = {}
serializer.associations(@include_tree).each do |association|
relationships[association.key] = relationship_value_for(association, options)
end

relationships
end

def relationship_value_for(association, options)
return association.options[:virtual_value] if association.options[:virtual_value]
return unless association.serializer && association.serializer.object

opts = instance_options.merge(include: @include_tree[association.key])
Attributes.new(association.serializer, opts).serializable_hash(options)
end

# no-op: Attributes adapter does not include meta data, because it does not support root.
def include_meta(json)
json
end

def resource_object_for(options)
cached_serializer = CachedSerializer.new(serializer)

cached_attributes(cached_serializer) do
cached_serializer.cache_check(self) do
serializer.attributes(options[:fields])
end
end
end
end
end
end
end
67 changes: 67 additions & 0 deletions lib/active_model/serializer/adapter/base.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
module ActiveModel
class Serializer
module Adapter
class Base
DEPRECATED_ADAPTERS = [
'ActiveModel::Serializer::Adapter::JsonApi',
'ActiveModel::Serializer::Adapter::Attributes',
'ActiveModel::Serializer::Adapter::Json',
'ActiveModel::Serializer::Adapter::Null'
]
# Automatically register adapters when subclassing
def self.inherited(subclass)
unless DEPRECATED_ADAPTERS.include?(subclass.to_s)
warn "Inheriting deprecated ActiveModel::Serializer::Adater::Base in #{caller[0..2].join(', ')}. Please use ActiveModelSerializers::Adapter"
end
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
79 changes: 79 additions & 0 deletions lib/active_model/serializer/adapter/cached_serializer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
module ActiveModel
class Serializer
module Adapter
class CachedSerializer
def initialize(serializer)
@cached_serializer = serializer
@klass = @cached_serializer.class
end

def cache_check(adapter_instance)
if cached?
@klass._cache.fetch(cache_key, @klass._cache_options) do
yield
end
elsif fragment_cached?
FragmentCache.new(adapter_instance, @cached_serializer, adapter_instance.instance_options).fetch
else
yield
end
end

def cached?
@klass._cache && !@klass._cache_only && !@klass._cache_except
end

def fragment_cached?
@klass._cache && (@klass._cache_only && !@klass._cache_except || !@klass._cache_only && @klass._cache_except)
end

def cache_key
return @cache_key if defined?(@cache_key)

parts = []
parts << object_cache_key
parts << @klass._cache_digest unless @klass._cache_options && @klass._cache_options[:skip_digest]
@cache_key = parts.join('/')
end

def object_cache_key
object_time_safe = @cached_serializer.object.updated_at
object_time_safe = object_time_safe.strftime('%Y%m%d%H%M%S%9N') if object_time_safe.respond_to?(:strftime)
(@klass._cache_key) ? "#{@klass._cache_key}/#{@cached_serializer.object.id}-#{object_time_safe}" : @cached_serializer.object.cache_key
end

# find all cache_key for the collection_serializer
# @param collection_serializer
# @param include_tree
# @return [Array] all cache_key of collection_serializer
def self.object_cache_keys(serializers, include_tree)
cache_keys = []

serializers.each do |serializer|
cache_keys << object_cache_key(serializer)

serializer.associations(include_tree).each do |association|
if association.serializer.respond_to?(:each)
association.serializer.each do |sub_serializer|
cache_keys << object_cache_key(sub_serializer)
end
else
cache_keys << object_cache_key(association.serializer)
end
end
end

cache_keys.compact.uniq
end

# @return [String, nil] the cache_key of the serializer or nil
def self.object_cache_key(serializer)
return unless serializer.present? && serializer.object.present?

cached_serializer = new(serializer)
cached_serializer.cached? ? cached_serializer.cache_key : nil
end
end
end
end
end
Loading

0 comments on commit e6fdeac

Please sign in to comment.