mirror of
https://github.com/ditkrg/active_model_serializers.git
synced 2026-01-23 06:16:50 +00:00
Idea per remear (Ben Mills) in the slack: https://amserializers.slack.com/archives/general/p1455140474000171 remear: just so i understand, the adapter in `render json: resource, status: 422, adapter: 'json_api/error', serializer: ActiveModel::Serializer::ErrorSerializer` is a different one than, say what i’ve specified in a base serializer with `ActiveModel::Serializer.config.adapter = :json_api`. correct? and a followup question of, why not same adapter but different serializer? me: With the way the code is written now, it might be possible to not require a special jsonapi adapter. However, the behavior is pretty different from the jsonapi adapter. this first draft of the PR had it automatically set the adapter if there were errors. since that requires more discussion, I took a step back and made it explicit for this PR If I were to re-use the json api adapter and remove the error one, it think the serializable hash method would look like ``` def serializable_hash(options = nil) return { errors: JsonApi::Error.collection_errors } if serializer.is_a?(ErrorsSerializer) return { errors: JsonApi::Error.resource_errors(serializer) } if serializer.is_a?(ErrorSerializer) options ||= {} ``` I suppose it could be something more duckish like ``` def serializable_hash(options = nil) if serializer.errors? # object.errors.any? || object.any? {|o| o.errors.any? } JsonApi::Error.new(serializer).serializable_hash else # etc ```
145 lines
4.6 KiB
Ruby
145 lines
4.6 KiB
Ruby
require 'thread_safe'
|
|
require 'active_model/serializer/collection_serializer'
|
|
require 'active_model/serializer/array_serializer'
|
|
require 'active_model/serializer/error_serializer'
|
|
require 'active_model/serializer/errors_serializer'
|
|
require 'active_model/serializer/include_tree'
|
|
require 'active_model/serializer/associations'
|
|
require 'active_model/serializer/attributes'
|
|
require 'active_model/serializer/caching'
|
|
require 'active_model/serializer/configuration'
|
|
require 'active_model/serializer/fieldset'
|
|
require 'active_model/serializer/lint'
|
|
require 'active_model/serializer/links'
|
|
require 'active_model/serializer/meta'
|
|
require 'active_model/serializer/type'
|
|
|
|
# ActiveModel::Serializer is an abstract class that is
|
|
# reified when subclassed to decorate a resource.
|
|
module ActiveModel
|
|
class Serializer
|
|
include Configuration
|
|
include Associations
|
|
include Attributes
|
|
include Caching
|
|
include Links
|
|
include Meta
|
|
include Type
|
|
|
|
# @param resource [ActiveRecord::Base, ActiveModelSerializers::Model]
|
|
# @return [ActiveModel::Serializer]
|
|
# Preferentially returns
|
|
# 1. resource.serializer
|
|
# 2. ArraySerializer when resource is a collection
|
|
# 3. options[:serializer]
|
|
# 4. lookup serializer when resource is a Class
|
|
def self.serializer_for(resource, options = {})
|
|
if resource.respond_to?(:serializer_class)
|
|
resource.serializer_class
|
|
elsif resource.respond_to?(:to_ary)
|
|
config.collection_serializer
|
|
else
|
|
options.fetch(:serializer) { get_serializer_for(resource.class) }
|
|
end
|
|
end
|
|
|
|
# @see ActiveModelSerializers::Adapter.lookup
|
|
# Deprecated
|
|
def self.adapter
|
|
warn 'Calling adapter method in Serializer, please use the ActiveModelSerializers::configured_adapter'
|
|
ActiveModelSerializers::Adapter.lookup(config.adapter)
|
|
end
|
|
|
|
# @api private
|
|
def self.serializer_lookup_chain_for(klass)
|
|
chain = []
|
|
|
|
resource_class_name = klass.name.demodulize
|
|
resource_namespace = klass.name.deconstantize
|
|
serializer_class_name = "#{resource_class_name}Serializer"
|
|
|
|
chain.push("#{name}::#{serializer_class_name}") if self != ActiveModel::Serializer
|
|
chain.push("#{resource_namespace}::#{serializer_class_name}")
|
|
|
|
chain
|
|
end
|
|
|
|
# Used to cache serializer name => serializer class
|
|
# when looked up by Serializer.get_serializer_for.
|
|
def self.serializers_cache
|
|
@serializers_cache ||= ThreadSafe::Cache.new
|
|
end
|
|
|
|
# @api private
|
|
# Find a serializer from a class and caches the lookup.
|
|
# Preferentially returns:
|
|
# 1. class name appended with "Serializer"
|
|
# 2. try again with superclass, if present
|
|
# 3. nil
|
|
def self.get_serializer_for(klass)
|
|
return nil unless config.serializer_lookup_enabled
|
|
serializers_cache.fetch_or_store(klass) do
|
|
# NOTE(beauby): When we drop 1.9.3 support we can lazify the map for perfs.
|
|
serializer_class = serializer_lookup_chain_for(klass).map(&:safe_constantize).find { |x| x && x < ActiveModel::Serializer }
|
|
|
|
if serializer_class
|
|
serializer_class
|
|
elsif klass.superclass
|
|
get_serializer_for(klass.superclass)
|
|
end
|
|
end
|
|
end
|
|
|
|
def self._serializer_instance_method_defined?(name)
|
|
_serializer_instance_methods.include?(name)
|
|
end
|
|
|
|
def self._serializer_instance_methods
|
|
@_serializer_instance_methods ||= (public_instance_methods - Object.public_instance_methods).to_set
|
|
end
|
|
private_class_method :_serializer_instance_methods
|
|
|
|
attr_accessor :object, :root, :scope
|
|
|
|
# `scope_name` is set as :current_user by default in the controller.
|
|
# If the instance does not have a method named `scope_name`, it
|
|
# defines the method so that it calls the +scope+.
|
|
def initialize(object, options = {})
|
|
self.object = object
|
|
self.instance_options = options
|
|
self.root = instance_options[:root]
|
|
self.scope = instance_options[:scope]
|
|
|
|
scope_name = instance_options[:scope_name]
|
|
if scope_name && !respond_to?(scope_name)
|
|
self.class.class_eval do
|
|
define_method scope_name, lambda { scope }
|
|
end
|
|
end
|
|
end
|
|
|
|
def success?
|
|
true
|
|
end
|
|
|
|
# Used by adapter as resource root.
|
|
def json_key
|
|
root || object.class.model_name.to_s.underscore
|
|
end
|
|
|
|
def read_attribute_for_serialization(attr)
|
|
if self.class._serializer_instance_method_defined?(attr)
|
|
send(attr)
|
|
elsif self.class._fragmented
|
|
self.class._fragmented.read_attribute_for_serialization(attr)
|
|
else
|
|
object.read_attribute_for_serialization(attr)
|
|
end
|
|
end
|
|
|
|
protected
|
|
|
|
attr_accessor :instance_options
|
|
end
|
|
end
|