active_model_serializers/lib/active_model/serializer.rb
Joao Moura 8a432ad2b3 Adding cache support to version 0.10.0
It's a new implementation of cache based on ActiveSupport::Cache.
The implementation abstracts the cache in Adapter class on a
private method called cached_object, this method is intended
to be used on Adapters inside serializable_hash method in order
to cache each instance of the object that will be returned by
the serializer.

Some of its features are:
- A different syntax. (no longer need the cache_key method).
- An options argument that have the same arguments of ActiveSupport::Cache::Store, plus a key option that will be the prefix of the object cache on a pattern "#{key}-#{object.id}".
- It cache the objects individually and not the whole Serializer return, re-using it in different requests (as a show and a index method for example.)
2015-02-02 14:53:34 -02:00

199 lines
5.2 KiB
Ruby

module ActiveModel
class Serializer
extend ActiveSupport::Autoload
autoload :Configuration
autoload :ArraySerializer
autoload :Adapter
include Configuration
class << self
attr_accessor :_attributes
attr_accessor :_associations
attr_accessor :_urls
attr_accessor :_cache
attr_accessor :_cache_key
attr_accessor :_cache_options
end
def self.inherited(base)
base._attributes = []
base._associations = {}
base._urls = []
end
def self.attributes(*attrs)
@_attributes.concat attrs
attrs.each do |attr|
define_method attr do
object && object.read_attribute_for_serialization(attr)
end unless method_defined?(attr)
end
end
def self.attribute(attr, options = {})
key = options.fetch(:key, attr)
@_attributes.concat [key]
define_method key do
object.read_attribute_for_serialization(attr)
end unless method_defined?(key)
end
# Enables a serializer to be automatically cached
def self.cache(options = {})
@_cache = ActionController::Base.cache_store if Rails.configuration.action_controller.perform_caching
@_cache_key = options.delete(:key)
@_cache_options = (options.empty?) ? nil : options
end
# Defines an association in the object should be rendered.
#
# The serializer object should implement the association name
# as a method which should return an array when invoked. If a method
# with the association name does not exist, the association name is
# dispatched to the serialized object.
def self.has_many(*attrs)
associate(:has_many, attrs)
end
# Defines an association in the object that should be rendered.
#
# The serializer object should implement the association name
# as a method which should return an object when invoked. If a method
# with the association name does not exist, the association name is
# dispatched to the serialized object.
def self.belongs_to(*attrs)
associate(:belongs_to, attrs)
end
def self.associate(type, attrs) #:nodoc:
options = attrs.extract_options!
self._associations = _associations.dup
attrs.each do |attr|
unless method_defined?(attr)
define_method attr do
object.send attr
end
end
self._associations[attr] = {type: type, association_options: options}
end
end
def self.url(attr)
@_urls.push attr
end
def self.urls(*attrs)
@_urls.concat attrs
end
def self.serializer_for(resource, options = {})
if resource.respond_to?(:to_ary)
config.array_serializer
else
options
.fetch(:association_options, {})
.fetch(:serializer, get_serializer_for(resource.class))
end
end
def self.adapter
adapter_class = case config.adapter
when Symbol
ActiveModel::Serializer::Adapter.adapter_class(config.adapter)
when Class
config.adapter
end
unless adapter_class
valid_adapters = Adapter.constants.map { |klass| ":#{klass.to_s.downcase}" }
raise ArgumentError, "Unknown adapter: #{config.adapter}. Valid adapters are: #{valid_adapters}"
end
adapter_class
end
def self._root
@@root ||= false
end
def self._root=(root)
@@root = root
end
def self.root_name
name.demodulize.underscore.sub(/_serializer$/, '') if name
end
attr_accessor :object, :root, :meta, :meta_key
def initialize(object, options = {})
@object = object
@root = options[:root] || (self.class._root ? self.class.root_name : false)
@meta = options[:meta]
@meta_key = options[:meta_key]
end
def json_key
if root == true || root.nil?
self.class.root_name
else
root
end
end
def attributes(options = {})
attributes =
if options[:fields]
self.class._attributes & options[:fields]
else
self.class._attributes.dup
end
attributes.each_with_object({}) do |name, hash|
hash[name] = send(name)
end
end
def each_association(&block)
self.class._associations.dup.each do |name, options|
next unless object
association = object.send(name)
association_value = send(name)
serializer_class = ActiveModel::Serializer.serializer_for(association, options)
serializer = serializer_class.new(
association_value,
serializer_from_options(options)
) if serializer_class
if block_given?
block.call(name, serializer, options[:association_options])
end
end
end
def serializer_from_options(options)
opts = {}
serializer = options.fetch(:options, {}).fetch(:serializer, nil)
opts[:serializer] = serializer if serializer
opts
end
private
def self.get_serializer_for(klass)
serializer_class_name = "#{klass.name}Serializer"
serializer_class = serializer_class_name.safe_constantize
if serializer_class
serializer_class
elsif klass.superclass
get_serializer_for(klass.superclass)
end
end
end
end