mirror of
https://github.com/ditkrg/active_model_serializers.git
synced 2026-01-23 06:16:50 +00:00
Merge pull request #810 from joaomdmoura/fragment-cache
Adding Fragment Cache to AMS
This commit is contained in:
commit
168c36fc7d
@ -4,3 +4,5 @@
|
|||||||
* adds method to override association [adcb99e, @kurko]
|
* adds method to override association [adcb99e, @kurko]
|
||||||
* adds `has_one` attribute for backwards compatibility [@ggordon]
|
* adds `has_one` attribute for backwards compatibility [@ggordon]
|
||||||
* updates JSON API support to RC3 [@mateomurphy]
|
* updates JSON API support to RC3 [@mateomurphy]
|
||||||
|
* adds fragment cache support [@joaomdmoura]
|
||||||
|
* adds cache support to attributes and associations [@joaomdmoura]
|
||||||
24
README.md
24
README.md
@ -271,7 +271,10 @@ The options are the same options of ```ActiveSupport::Cache::Store```, plus
|
|||||||
a ```key``` option that will be the prefix of the object cache
|
a ```key``` option that will be the prefix of the object cache
|
||||||
on a pattern ```"#{key}/#{object.id}-#{object.updated_at}"```.
|
on a pattern ```"#{key}/#{object.id}-#{object.updated_at}"```.
|
||||||
|
|
||||||
|
The cache support is optimized to use the cached object in multiple request. An object cached on an ```show``` request will be reused at the ```index```. If there is a relationship with another cached serializer it will also be created and reused automatically.
|
||||||
|
|
||||||
**[NOTE] Every object is individually cached.**
|
**[NOTE] Every object is individually cached.**
|
||||||
|
|
||||||
**[NOTE] The cache is automatically expired after update an object but it's not deleted.**
|
**[NOTE] The cache is automatically expired after update an object but it's not deleted.**
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
@ -295,6 +298,27 @@ On this example every ```Post``` object will be cached with
|
|||||||
the key ```"post/#{post.id}-#{post.updated_at}"```. You can use this key to expire it as you want,
|
the key ```"post/#{post.id}-#{post.updated_at}"```. You can use this key to expire it as you want,
|
||||||
but in this case it will be automatically expired after 3 hours.
|
but in this case it will be automatically expired after 3 hours.
|
||||||
|
|
||||||
|
### Fragmenting Caching
|
||||||
|
|
||||||
|
If there is some API endpoint that shouldn't be fully cached, you can still optmise it, using Fragment Cache on the attributes and relationships that you want to cache.
|
||||||
|
|
||||||
|
You can define the attribute by using ```only``` or ```except``` option on cache method.
|
||||||
|
|
||||||
|
**[NOTE] Cache serializers will be used at their relationships**
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
class PostSerializer < ActiveModel::Serializer
|
||||||
|
cache key: 'post', expires_in: 3.hours, only: [:title]
|
||||||
|
attributes :title, :body
|
||||||
|
|
||||||
|
has_many :comments
|
||||||
|
|
||||||
|
url :post
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
## Getting Help
|
## Getting Help
|
||||||
|
|
||||||
If you find a bug, please report an [Issue](https://github.com/rails-api/active_model_serializers/issues/new).
|
If you find a bug, please report an [Issue](https://github.com/rails-api/active_model_serializers/issues/new).
|
||||||
|
|||||||
@ -10,41 +10,54 @@ module ActiveModel
|
|||||||
|
|
||||||
class << self
|
class << self
|
||||||
attr_accessor :_attributes
|
attr_accessor :_attributes
|
||||||
|
attr_accessor :_attributes_keys
|
||||||
attr_accessor :_associations
|
attr_accessor :_associations
|
||||||
attr_accessor :_urls
|
attr_accessor :_urls
|
||||||
attr_accessor :_cache
|
attr_accessor :_cache
|
||||||
|
attr_accessor :_fragmented
|
||||||
attr_accessor :_cache_key
|
attr_accessor :_cache_key
|
||||||
|
attr_accessor :_cache_only
|
||||||
|
attr_accessor :_cache_except
|
||||||
attr_accessor :_cache_options
|
attr_accessor :_cache_options
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.inherited(base)
|
def self.inherited(base)
|
||||||
base._attributes = []
|
base._attributes = []
|
||||||
|
base._attributes_keys = {}
|
||||||
base._associations = {}
|
base._associations = {}
|
||||||
base._urls = []
|
base._urls = []
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.attributes(*attrs)
|
def self.attributes(*attrs)
|
||||||
|
attrs = attrs.first if attrs.first.class == Array
|
||||||
@_attributes.concat attrs
|
@_attributes.concat attrs
|
||||||
|
|
||||||
attrs.each do |attr|
|
attrs.each do |attr|
|
||||||
define_method attr do
|
define_method attr do
|
||||||
object && object.read_attribute_for_serialization(attr)
|
object && object.read_attribute_for_serialization(attr)
|
||||||
end unless method_defined?(attr)
|
end unless method_defined?(attr) || _fragmented.respond_to?(attr)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.attribute(attr, options = {})
|
def self.attribute(attr, options = {})
|
||||||
key = options.fetch(:key, attr)
|
key = options.fetch(:key, attr)
|
||||||
|
@_attributes_keys[attr] = {key: key} if key != attr
|
||||||
@_attributes.concat [key]
|
@_attributes.concat [key]
|
||||||
define_method key do
|
define_method key do
|
||||||
object.read_attribute_for_serialization(attr)
|
object.read_attribute_for_serialization(attr)
|
||||||
end unless method_defined?(key)
|
end unless method_defined?(key) || _fragmented.respond_to?(attr)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.fragmented(serializer)
|
||||||
|
@_fragmented = serializer
|
||||||
end
|
end
|
||||||
|
|
||||||
# Enables a serializer to be automatically cached
|
# Enables a serializer to be automatically cached
|
||||||
def self.cache(options = {})
|
def self.cache(options = {})
|
||||||
@_cache = ActionController::Base.cache_store if Rails.configuration.action_controller.perform_caching
|
@_cache = ActionController::Base.cache_store if Rails.configuration.action_controller.perform_caching
|
||||||
@_cache_key = options.delete(:key)
|
@_cache_key = options.delete(:key)
|
||||||
|
@_cache_only = options.delete(:only)
|
||||||
|
@_cache_except = options.delete(:except)
|
||||||
@_cache_options = (options.empty?) ? nil : options
|
@_cache_options = (options.empty?) ? nil : options
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -141,12 +154,12 @@ module ActiveModel
|
|||||||
attr_accessor :object, :root, :meta, :meta_key, :scope
|
attr_accessor :object, :root, :meta, :meta_key, :scope
|
||||||
|
|
||||||
def initialize(object, options = {})
|
def initialize(object, options = {})
|
||||||
@object = object
|
@object = object
|
||||||
@options = options
|
@options = options
|
||||||
@root = options[:root] || (self.class._root ? self.class.root_name : false)
|
@root = options[:root] || (self.class._root ? self.class.root_name : false)
|
||||||
@meta = options[:meta]
|
@meta = options[:meta]
|
||||||
@meta_key = options[:meta_key]
|
@meta_key = options[:meta_key]
|
||||||
@scope = options[:scope]
|
@scope = options[:scope]
|
||||||
|
|
||||||
scope_name = options[:scope_name]
|
scope_name = options[:scope_name]
|
||||||
if scope_name && !respond_to?(scope_name)
|
if scope_name && !respond_to?(scope_name)
|
||||||
@ -183,22 +196,29 @@ module ActiveModel
|
|||||||
attributes += options[:required_fields] if options[:required_fields]
|
attributes += options[:required_fields] if options[:required_fields]
|
||||||
|
|
||||||
attributes.each_with_object({}) do |name, hash|
|
attributes.each_with_object({}) do |name, hash|
|
||||||
hash[name] = send(name)
|
unless self.class._fragmented
|
||||||
|
hash[name] = send(name)
|
||||||
|
else
|
||||||
|
hash[name] = self.class._fragmented.public_send(name)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def each_association(&block)
|
def each_association(&block)
|
||||||
self.class._associations.dup.each do |name, association_options|
|
self.class._associations.dup.each do |name, association_options|
|
||||||
next unless object
|
next unless object
|
||||||
|
|
||||||
association_value = send(name)
|
association_value = send(name)
|
||||||
|
|
||||||
serializer_class = ActiveModel::Serializer.serializer_for(association_value, association_options)
|
serializer_class = ActiveModel::Serializer.serializer_for(association_value, association_options)
|
||||||
|
|
||||||
serializer = serializer_class.new(
|
if serializer_class
|
||||||
association_value,
|
serializer = serializer_class.new(
|
||||||
options.merge(serializer_from_options(association_options))
|
association_value,
|
||||||
) if serializer_class
|
options.merge(serializer_from_options(association_options))
|
||||||
|
)
|
||||||
|
elsif !association_value.nil? && !association_value.instance_of?(Object)
|
||||||
|
association_options[:association_options][:virtual_value] = association_value
|
||||||
|
end
|
||||||
|
|
||||||
if block_given?
|
if block_given?
|
||||||
block.call(name, serializer, association_options[:association_options])
|
block.call(name, serializer, association_options[:association_options])
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
require 'active_model/serializer/adapter/fragment_cache'
|
||||||
|
|
||||||
module ActiveModel
|
module ActiveModel
|
||||||
class Serializer
|
class Serializer
|
||||||
class Adapter
|
class Adapter
|
||||||
@ -32,8 +34,38 @@ module ActiveModel
|
|||||||
"ActiveModel::Serializer::Adapter::#{adapter.to_s.classify}".safe_constantize
|
"ActiveModel::Serializer::Adapter::#{adapter.to_s.classify}".safe_constantize
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def fragment_cache(*args)
|
||||||
|
raise NotImplementedError, 'This is an abstract method. Should be implemented at the concrete adapter.'
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def cache_check(serializer)
|
||||||
|
@cached_serializer = serializer
|
||||||
|
@klass = @cached_serializer.class
|
||||||
|
if is_cached?
|
||||||
|
@klass._cache.fetch(cache_key, @klass._cache_options) do
|
||||||
|
yield
|
||||||
|
end
|
||||||
|
elsif is_fragment_cached?
|
||||||
|
FragmentCache.new(self, @cached_serializer, @options, @root).fetch
|
||||||
|
else
|
||||||
|
yield
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def is_cached?
|
||||||
|
@klass._cache && !@klass._cache_only && !@klass._cache_except
|
||||||
|
end
|
||||||
|
|
||||||
|
def is_fragment_cached?
|
||||||
|
@klass._cache_only && !@klass._cache_except || !@klass._cache_only && @klass._cache_except
|
||||||
|
end
|
||||||
|
|
||||||
|
def cache_key
|
||||||
|
(@klass._cache_key) ? "#{@klass._cache_key}/#{@cached_serializer.object.id}-#{@cached_serializer.object.updated_at}" : @cached_serializer.object.cache_key
|
||||||
|
end
|
||||||
|
|
||||||
def meta
|
def meta
|
||||||
serializer.meta if serializer.respond_to?(:meta)
|
serializer.meta if serializer.respond_to?(:meta)
|
||||||
end
|
end
|
||||||
@ -50,20 +82,6 @@ module ActiveModel
|
|||||||
json[meta_key] = meta if meta && root
|
json[meta_key] = meta if meta && root
|
||||||
json
|
json
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def cached_object
|
|
||||||
klass = serializer.class
|
|
||||||
if klass._cache
|
|
||||||
_cache_key = (klass._cache_key) ? "#{klass._cache_key}/#{serializer.object.id}-#{serializer.object.updated_at}" : serializer.object.cache_key
|
|
||||||
klass._cache.fetch(_cache_key, klass._cache_options) do
|
|
||||||
yield
|
|
||||||
end
|
|
||||||
else
|
|
||||||
yield
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
78
lib/active_model/serializer/adapter/fragment_cache.rb
Normal file
78
lib/active_model/serializer/adapter/fragment_cache.rb
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
module ActiveModel
|
||||||
|
class Serializer
|
||||||
|
class Adapter
|
||||||
|
class FragmentCache
|
||||||
|
|
||||||
|
attr_reader :serializer
|
||||||
|
|
||||||
|
def initialize(adapter, serializer, options, root)
|
||||||
|
@root = root
|
||||||
|
@options = options
|
||||||
|
@adapter = adapter
|
||||||
|
@serializer = serializer
|
||||||
|
end
|
||||||
|
|
||||||
|
def fetch
|
||||||
|
klass = serializer.class
|
||||||
|
# It will split the serializer into two, one that will be cached and other wont
|
||||||
|
serializers = fragment_serializer(serializer.object.class.name, klass)
|
||||||
|
|
||||||
|
# Instanciate 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, @options)
|
||||||
|
non_cached_adapter = @adapter.class.new(non_cached_serializer, @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
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def cached_attributes(klass, serializers)
|
||||||
|
cached_attributes = (klass._cache_only) ? klass._cache_only : serializer.attributes.keys.delete_if {|attr| klass._cache_except.include?(attr) }
|
||||||
|
non_cached_attributes = serializer.attributes.keys.delete_if {|attr| cached_attributes.include?(attr) }
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
def fragment_serializer(name, klass)
|
||||||
|
cached = "#{name.capitalize}CachedSerializer"
|
||||||
|
non_cached = "#{name.capitalize}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
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -1,3 +1,5 @@
|
|||||||
|
require 'active_model/serializer/adapter/json/fragment_cache'
|
||||||
|
|
||||||
module ActiveModel
|
module ActiveModel
|
||||||
class Serializer
|
class Serializer
|
||||||
class Adapter
|
class Adapter
|
||||||
@ -6,31 +8,45 @@ module ActiveModel
|
|||||||
if serializer.respond_to?(:each)
|
if serializer.respond_to?(:each)
|
||||||
@result = serializer.map{|s| self.class.new(s).serializable_hash }
|
@result = serializer.map{|s| self.class.new(s).serializable_hash }
|
||||||
else
|
else
|
||||||
@result = cached_object do
|
@hash = {}
|
||||||
@hash = serializer.attributes(options)
|
|
||||||
serializer.each_association do |name, association, opts|
|
@core = cache_check(serializer) do
|
||||||
if association.respond_to?(:each)
|
serializer.attributes(options)
|
||||||
array_serializer = association
|
end
|
||||||
@hash[name] = array_serializer.map { |item| item.attributes(opts) }
|
|
||||||
else
|
serializer.each_association do |name, association, opts|
|
||||||
if association
|
if association.respond_to?(:each)
|
||||||
@hash[name] = association.attributes(options)
|
array_serializer = association
|
||||||
else
|
@hash[name] = array_serializer.map do |item|
|
||||||
@hash[name] = nil
|
cache_check(item) do
|
||||||
|
item.attributes(opts)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
if association
|
||||||
|
@hash[name] = cache_check(association) do
|
||||||
|
association.attributes(options)
|
||||||
|
end
|
||||||
|
elsif opts[:virtual_value]
|
||||||
|
@hash[name] = opts[:virtual_value]
|
||||||
|
else
|
||||||
|
@hash[name] = nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
@hash
|
|
||||||
end
|
end
|
||||||
|
@result = @core.merge @hash
|
||||||
end
|
end
|
||||||
|
|
||||||
if root = options.fetch(:root, serializer.json_key)
|
if root = options.fetch(:root, serializer.json_key)
|
||||||
@result = { root => @result }
|
@result = { root => @result }
|
||||||
end
|
end
|
||||||
|
|
||||||
@result
|
@result
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def fragment_cache(cached_hash, non_cached_hash)
|
||||||
|
Json::FragmentCache.new().fragment_cache(cached_hash, non_cached_hash)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
15
lib/active_model/serializer/adapter/json/fragment_cache.rb
Normal file
15
lib/active_model/serializer/adapter/json/fragment_cache.rb
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
module ActiveModel
|
||||||
|
class Serializer
|
||||||
|
class Adapter
|
||||||
|
class Json < Adapter
|
||||||
|
class FragmentCache
|
||||||
|
|
||||||
|
def fragment_cache(cached_hash, non_cached_hash)
|
||||||
|
non_cached_hash.merge cached_hash
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -1,3 +1,5 @@
|
|||||||
|
require 'active_model/serializer/adapter/json_api/fragment_cache'
|
||||||
|
|
||||||
module ActiveModel
|
module ActiveModel
|
||||||
class Serializer
|
class Serializer
|
||||||
class Adapter
|
class Adapter
|
||||||
@ -26,15 +28,17 @@ module ActiveModel
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
@hash = cached_object do
|
@hash[:data] = attributes_for_serializer(serializer, @options)
|
||||||
@hash[:data] = attributes_for_serializer(serializer, @options)
|
add_resource_links(@hash[:data], serializer)
|
||||||
add_resource_links(@hash[:data], serializer)
|
|
||||||
@hash
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
@hash
|
@hash
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def fragment_cache(cached_hash, non_cached_hash)
|
||||||
|
root = false if @options.include?(:include)
|
||||||
|
JsonApi::FragmentCache.new().fragment_cache(root, cached_hash, non_cached_hash)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def add_links(resource, name, serializers)
|
def add_links(resource, name, serializers)
|
||||||
@ -43,7 +47,7 @@ module ActiveModel
|
|||||||
resource[:links][name][:linkage] += serializers.map { |serializer| { type: serializer.type, id: serializer.id.to_s } }
|
resource[:links][name][:linkage] += serializers.map { |serializer| { type: serializer.type, id: serializer.id.to_s } }
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_link(resource, name, serializer)
|
def add_link(resource, name, serializer, val=nil)
|
||||||
resource[:links] ||= {}
|
resource[:links] ||= {}
|
||||||
resource[:links][name] = { linkage: nil }
|
resource[:links][name] = { linkage: nil }
|
||||||
|
|
||||||
@ -77,24 +81,27 @@ module ActiveModel
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def attributes_for_serializer(serializer, options)
|
def attributes_for_serializer(serializer, options)
|
||||||
if serializer.respond_to?(:each)
|
if serializer.respond_to?(:each)
|
||||||
result = []
|
result = []
|
||||||
serializer.each do |object|
|
serializer.each do |object|
|
||||||
options[:fields] = @fieldset && @fieldset.fields_for(serializer)
|
options[:fields] = @fieldset && @fieldset.fields_for(serializer)
|
||||||
options[:required_fields] = [:id, :type]
|
result << cache_check(object) do
|
||||||
attributes = object.attributes(options)
|
options[:required_fields] = [:id, :type]
|
||||||
attributes[:id] = attributes[:id].to_s
|
attributes = object.attributes(options)
|
||||||
result << attributes
|
attributes[:id] = attributes[:id].to_s
|
||||||
|
result << attributes
|
||||||
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
options[:fields] = @fieldset && @fieldset.fields_for(serializer)
|
options[:fields] = @fieldset && @fieldset.fields_for(serializer)
|
||||||
options[:required_fields] = [:id, :type]
|
options[:required_fields] = [:id, :type]
|
||||||
result = serializer.attributes(options)
|
result = cache_check(serializer) do
|
||||||
result[:id] = result[:id].to_s
|
result = serializer.attributes(options)
|
||||||
|
result[:id] = result[:id].to_s
|
||||||
|
result
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
result
|
result
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -125,7 +132,11 @@ module ActiveModel
|
|||||||
if association.respond_to?(:each)
|
if association.respond_to?(:each)
|
||||||
add_links(attrs, name, association)
|
add_links(attrs, name, association)
|
||||||
else
|
else
|
||||||
add_link(attrs, name, association)
|
if opts[:virtual_value]
|
||||||
|
add_link(attrs, name, nil, opts[:virtual_value])
|
||||||
|
else
|
||||||
|
add_link(attrs, name, association)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if options[:add_included]
|
if options[:add_included]
|
||||||
|
|||||||
@ -0,0 +1,22 @@
|
|||||||
|
module ActiveModel
|
||||||
|
class Serializer
|
||||||
|
class Adapter
|
||||||
|
class JsonApi < Adapter
|
||||||
|
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].merge(core_non_cached[1]) : core_non_cached[1]
|
||||||
|
hash = (root) ? { root => cached_resource } : cached_resource
|
||||||
|
hash.merge no_root_non_cache.merge no_root_cache
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -111,6 +111,8 @@ module ActionController
|
|||||||
"id" => "1",
|
"id" => "1",
|
||||||
"type" => "roles",
|
"type" => "roles",
|
||||||
"name" => "admin",
|
"name" => "admin",
|
||||||
|
"description" => nil,
|
||||||
|
"slug" => "admin-1",
|
||||||
"links" => {
|
"links" => {
|
||||||
"author" => { "linkage" => { "type" =>"authors", "id" => "1" } }
|
"author" => { "linkage" => { "type" =>"authors", "id" => "1" } }
|
||||||
}
|
}
|
||||||
@ -118,6 +120,8 @@ module ActionController
|
|||||||
"id" => "2",
|
"id" => "2",
|
||||||
"type" => "roles",
|
"type" => "roles",
|
||||||
"name" => "colab",
|
"name" => "colab",
|
||||||
|
"description" => nil,
|
||||||
|
"slug" => "colab-2",
|
||||||
"links" => {
|
"links" => {
|
||||||
"author" => { "linkage" => { "type" =>"authors", "id" => "1" } }
|
"author" => { "linkage" => { "type" =>"authors", "id" => "1" } }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -48,36 +48,79 @@ module ActionController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def render_object_with_cache_enabled
|
def render_object_with_cache_enabled
|
||||||
comment = Comment.new({ id: 1, body: 'ZOMG A COMMENT' })
|
@comment = Comment.new({ id: 1, body: 'ZOMG A COMMENT' })
|
||||||
author = Author.new(id: 1, name: 'Joao Moura.')
|
@author = Author.new(id: 1, name: 'Joao Moura.')
|
||||||
post = Post.new({ id: 1, title: 'New Post', blog:nil, body: 'Body', comments: [comment], author: author })
|
@post = Post.new({ id: 1, title: 'New Post', body: 'Body', comments: [@comment], author: @author })
|
||||||
|
|
||||||
generate_cached_serializer(post)
|
generate_cached_serializer(@post)
|
||||||
|
|
||||||
post.title = 'ZOMG a New Post'
|
@post.title = 'ZOMG a New Post'
|
||||||
render json: post
|
render json: @post
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_and_render_object_with_cache_enabled
|
||||||
|
@post.updated_at = DateTime.now
|
||||||
|
|
||||||
|
generate_cached_serializer(@post)
|
||||||
|
render json: @post
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_object_expired_with_cache_enabled
|
def render_object_expired_with_cache_enabled
|
||||||
comment = Comment.new({ id: 1, body: 'ZOMG A COMMENT' })
|
comment = Comment.new({ id: 1, body: 'ZOMG A COMMENT' })
|
||||||
author = Author.new(id: 1, name: 'Joao Moura.')
|
author = Author.new(id: 1, name: 'Joao Moura.')
|
||||||
post = Post.new({ id: 1, title: 'New Post', blog:nil, body: 'Body', comments: [comment], author: author })
|
post = Post.new({ id: 1, title: 'New Post', body: 'Body', comments: [comment], author: author })
|
||||||
|
|
||||||
generate_cached_serializer(post)
|
generate_cached_serializer(post)
|
||||||
|
|
||||||
post.title = 'ZOMG a New Post'
|
post.title = 'ZOMG a New Post'
|
||||||
sleep 0.05
|
sleep 0.1
|
||||||
render json: post
|
render json: post
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_changed_object_with_cache_enabled
|
def render_changed_object_with_cache_enabled
|
||||||
comment = Comment.new({ id: 1, body: 'ZOMG A COMMENT' })
|
comment = Comment.new({ id: 1, body: 'ZOMG A COMMENT' })
|
||||||
author = Author.new(id: 1, name: 'Joao Moura.')
|
author = Author.new(id: 1, name: 'Joao Moura.')
|
||||||
post = Post.new({ id: 1, title: 'ZOMG a New Post', blog:nil, body: 'Body', comments: [comment], author: author })
|
post = Post.new({ id: 1, title: 'ZOMG a New Post', body: 'Body', comments: [comment], author: author })
|
||||||
|
|
||||||
render json: post
|
render json: post
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def render_fragment_changed_object_with_only_cache_enabled
|
||||||
|
author = Author.new(id: 1, name: 'Joao Moura.')
|
||||||
|
role = Role.new({ id: 42, name: 'ZOMG A ROLE', description: 'DESCRIPTION HERE', author: author })
|
||||||
|
|
||||||
|
generate_cached_serializer(role)
|
||||||
|
role.name = 'lol'
|
||||||
|
role.description = 'HUEHUEBRBR'
|
||||||
|
|
||||||
|
render json: role
|
||||||
|
end
|
||||||
|
|
||||||
|
def render_fragment_changed_object_with_except_cache_enabled
|
||||||
|
author = Author.new(id: 1, name: 'Joao Moura.')
|
||||||
|
bio = Bio.new({ id: 42, content: 'ZOMG A ROLE', rating: 5, author: author })
|
||||||
|
|
||||||
|
generate_cached_serializer(bio)
|
||||||
|
bio.content = 'lol'
|
||||||
|
bio.rating = 0
|
||||||
|
|
||||||
|
render json: bio
|
||||||
|
end
|
||||||
|
|
||||||
|
def render_fragment_changed_object_with_relationship
|
||||||
|
comment = Comment.new({ id: 1, body: 'ZOMG A COMMENT' })
|
||||||
|
author = Author.new(id: 1, name: 'Joao Moura.')
|
||||||
|
post = Post.new({ id: 1, title: 'New Post', body: 'Body', comments: [comment], author: author })
|
||||||
|
post2 = Post.new({ id: 1, title: 'New Post2', body: 'Body2', comments: [comment], author: author })
|
||||||
|
like = Like.new({ id: 1, post: post, time: 3.days.ago })
|
||||||
|
|
||||||
|
generate_cached_serializer(like)
|
||||||
|
like.post = post2
|
||||||
|
like.time = DateTime.now.to_s
|
||||||
|
|
||||||
|
render json: like
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def generate_cached_serializer(obj)
|
def generate_cached_serializer(obj)
|
||||||
serializer_class = ActiveModel::Serializer.serializer_for(obj)
|
serializer_class = ActiveModel::Serializer.serializer_for(obj)
|
||||||
@ -249,6 +292,74 @@ module ActionController
|
|||||||
assert_equal 'application/json', @response.content_type
|
assert_equal 'application/json', @response.content_type
|
||||||
assert_equal expected.to_json, @response.body
|
assert_equal expected.to_json, @response.body
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_render_with_fragment_only_cache_enable
|
||||||
|
ActionController::Base.cache_store.clear
|
||||||
|
get :render_fragment_changed_object_with_only_cache_enabled
|
||||||
|
response = JSON.parse(@response.body)
|
||||||
|
|
||||||
|
assert_equal 'application/json', @response.content_type
|
||||||
|
assert_equal 'ZOMG A ROLE', response["name"]
|
||||||
|
assert_equal 'HUEHUEBRBR', response["description"]
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_render_with_fragment_except_cache_enable
|
||||||
|
ActionController::Base.cache_store.clear
|
||||||
|
get :render_fragment_changed_object_with_except_cache_enabled
|
||||||
|
response = JSON.parse(@response.body)
|
||||||
|
|
||||||
|
assert_equal 'application/json', @response.content_type
|
||||||
|
assert_equal 5, response["rating"]
|
||||||
|
assert_equal 'lol', response["content"]
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_render_fragment_changed_object_with_relationship
|
||||||
|
ActionController::Base.cache_store.clear
|
||||||
|
get :render_fragment_changed_object_with_relationship
|
||||||
|
response = JSON.parse(@response.body)
|
||||||
|
|
||||||
|
expected_return = {
|
||||||
|
"post" => {
|
||||||
|
"id"=>1,
|
||||||
|
"title"=>"New Post",
|
||||||
|
"body"=>"Body"
|
||||||
|
},
|
||||||
|
"id"=>1,
|
||||||
|
"time"=>DateTime.now.to_s
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_equal 'application/json', @response.content_type
|
||||||
|
assert_equal expected_return, response
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_cache_expiration_on_update
|
||||||
|
ActionController::Base.cache_store.clear
|
||||||
|
get :render_object_with_cache_enabled
|
||||||
|
|
||||||
|
expected = {
|
||||||
|
id: 1,
|
||||||
|
title: 'ZOMG a New Post',
|
||||||
|
body: 'Body',
|
||||||
|
comments: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
body: 'ZOMG A COMMENT' }
|
||||||
|
],
|
||||||
|
blog: {
|
||||||
|
id:999,
|
||||||
|
name: "Custom blog"
|
||||||
|
},
|
||||||
|
author: {
|
||||||
|
id: 1,
|
||||||
|
name: 'Joao Moura.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get :update_and_render_object_with_cache_enabled
|
||||||
|
|
||||||
|
assert_equal 'application/json', @response.content_type
|
||||||
|
assert_equal expected.to_json, @response.body
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
27
test/adapter/fragment_cache_test.rb
Normal file
27
test/adapter/fragment_cache_test.rb
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
require 'test_helper'
|
||||||
|
module ActiveModel
|
||||||
|
class Serializer
|
||||||
|
class Adapter
|
||||||
|
class FragmentCacheTest < Minitest::Test
|
||||||
|
def setup
|
||||||
|
@author = Author.new(name: 'Joao M. D. Moura')
|
||||||
|
@role = Role.new(name: 'Great Author', description:nil)
|
||||||
|
@role.author = [@author]
|
||||||
|
@role_serializer = RoleSerializer.new(@role)
|
||||||
|
@role_hash = FragmentCache.new(RoleSerializer.adapter.new(@role_serializer), @role_serializer, {}, nil)
|
||||||
|
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
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
@ -6,6 +6,7 @@ module ActiveModel
|
|||||||
class Json
|
class Json
|
||||||
class HasManyTestTest < Minitest::Test
|
class HasManyTestTest < Minitest::Test
|
||||||
def setup
|
def setup
|
||||||
|
ActionController::Base.cache_store.clear
|
||||||
@author = Author.new(id: 1, name: 'Steve K.')
|
@author = Author.new(id: 1, name: 'Steve K.')
|
||||||
@post = Post.new(title: 'New Post', body: 'Body')
|
@post = Post.new(title: 'New Post', body: 'Body')
|
||||||
@first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')
|
@first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')
|
||||||
|
|||||||
@ -41,6 +41,7 @@ module ActiveModel
|
|||||||
expected = [
|
expected = [
|
||||||
{
|
{
|
||||||
id: "43",
|
id: "43",
|
||||||
|
rating: nil,
|
||||||
type: "bios",
|
type: "bios",
|
||||||
content:"AMS Contributor",
|
content:"AMS Contributor",
|
||||||
links: {
|
links: {
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
require 'test_helper'
|
require 'test_helper'
|
||||||
|
|
||||||
module ActiveModel
|
module ActiveModel
|
||||||
class Serializer
|
class Serializer
|
||||||
class Adapter
|
class Adapter
|
||||||
@ -104,8 +103,9 @@ module ActiveModel
|
|||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
id: "1",
|
id: "1",
|
||||||
content: "AMS Contributor",
|
rating: nil,
|
||||||
type: "bios",
|
type: "bios",
|
||||||
|
content: "AMS Contributor",
|
||||||
links: {
|
links: {
|
||||||
author: { linkage: { type: "authors", id: "1" } }
|
author: { linkage: { type: "authors", id: "1" } }
|
||||||
}
|
}
|
||||||
@ -120,8 +120,9 @@ module ActiveModel
|
|||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
id: "2",
|
id: "2",
|
||||||
content: "Rails Contributor",
|
rating: nil,
|
||||||
type: "bios",
|
type: "bios",
|
||||||
|
content: "Rails Contributor",
|
||||||
links: {
|
links: {
|
||||||
author: { linkage: { type: "authors", id: "2" } }
|
author: { linkage: { type: "authors", id: "2" } }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ module ActiveModel
|
|||||||
class Adapter
|
class Adapter
|
||||||
class JsonTest < Minitest::Test
|
class JsonTest < Minitest::Test
|
||||||
def setup
|
def setup
|
||||||
|
ActionController::Base.cache_store.clear
|
||||||
@author = Author.new(id: 1, name: 'Steve K.')
|
@author = Author.new(id: 1, name: 'Steve K.')
|
||||||
@post = Post.new(title: 'New Post', body: 'Body')
|
@post = Post.new(title: 'New Post', body: 'Body')
|
||||||
@first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')
|
@first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')
|
||||||
|
|||||||
51
test/fixtures/poro.rb
vendored
51
test/fixtures/poro.rb
vendored
@ -57,18 +57,22 @@ class ProfilePreviewSerializer < ActiveModel::Serializer
|
|||||||
urls :posts, :comments
|
urls :posts, :comments
|
||||||
end
|
end
|
||||||
|
|
||||||
Post = Class.new(Model)
|
Post = Class.new(Model)
|
||||||
Comment = Class.new(Model)
|
Like = Class.new(Model)
|
||||||
Author = Class.new(Model)
|
Comment = Class.new(Model)
|
||||||
Bio = Class.new(Model)
|
Author = Class.new(Model)
|
||||||
Blog = Class.new(Model)
|
Bio = Class.new(Model)
|
||||||
Role = Class.new(Model)
|
Blog = Class.new(Model)
|
||||||
|
Role = Class.new(Model)
|
||||||
User = Class.new(Model)
|
User = Class.new(Model)
|
||||||
|
Location = Class.new(Model)
|
||||||
|
Place = Class.new(Model)
|
||||||
|
|
||||||
module Spam; end
|
module Spam; end
|
||||||
Spam::UnrelatedLink = Class.new(Model)
|
Spam::UnrelatedLink = Class.new(Model)
|
||||||
|
|
||||||
PostSerializer = Class.new(ActiveModel::Serializer) do
|
PostSerializer = Class.new(ActiveModel::Serializer) do
|
||||||
cache key:'post', expires_in: 0.05
|
cache key:'post', expires_in: 0.1
|
||||||
attributes :id, :title, :body
|
attributes :id, :title, :body
|
||||||
|
|
||||||
has_many :comments
|
has_many :comments
|
||||||
@ -116,13 +120,42 @@ AuthorSerializer = Class.new(ActiveModel::Serializer) do
|
|||||||
end
|
end
|
||||||
|
|
||||||
RoleSerializer = Class.new(ActiveModel::Serializer) do
|
RoleSerializer = Class.new(ActiveModel::Serializer) do
|
||||||
attributes :id, :name
|
cache only: [:name]
|
||||||
|
attributes :id, :name, :description, :slug
|
||||||
|
|
||||||
|
def slug
|
||||||
|
"#{name}-#{id}"
|
||||||
|
end
|
||||||
|
|
||||||
belongs_to :author
|
belongs_to :author
|
||||||
end
|
end
|
||||||
|
|
||||||
|
LikeSerializer = Class.new(ActiveModel::Serializer) do
|
||||||
|
attributes :id, :time
|
||||||
|
|
||||||
|
belongs_to :post
|
||||||
|
end
|
||||||
|
|
||||||
|
LocationSerializer = Class.new(ActiveModel::Serializer) do
|
||||||
|
cache only: [:place]
|
||||||
|
attributes :id, :lat, :lng
|
||||||
|
|
||||||
|
belongs_to :place
|
||||||
|
|
||||||
|
def place
|
||||||
|
'Nowhere'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
PlaceSerializer = Class.new(ActiveModel::Serializer) do
|
||||||
|
attributes :id, :name
|
||||||
|
|
||||||
|
has_many :locations
|
||||||
|
end
|
||||||
|
|
||||||
BioSerializer = Class.new(ActiveModel::Serializer) do
|
BioSerializer = Class.new(ActiveModel::Serializer) do
|
||||||
attributes :id, :content
|
cache except: [:content]
|
||||||
|
attributes :id, :content, :rating
|
||||||
|
|
||||||
belongs_to :author
|
belongs_to :author
|
||||||
end
|
end
|
||||||
|
|||||||
@ -3,21 +3,33 @@ module ActiveModel
|
|||||||
class Serializer
|
class Serializer
|
||||||
class CacheTest < Minitest::Test
|
class CacheTest < Minitest::Test
|
||||||
def setup
|
def setup
|
||||||
@post = Post.new({ title: 'New Post', body: 'Body' })
|
ActionController::Base.cache_store.clear
|
||||||
@comment = Comment.new({ id: 1, body: 'ZOMG A COMMENT' })
|
@comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')
|
||||||
@author = Author.new(name: 'Joao M. D. Moura')
|
@blog = Blog.new(id: 999, name: "Custom blog")
|
||||||
@role = Role.new(name: 'Great Author')
|
@post = Post.new(title: 'New Post', body: 'Body')
|
||||||
@author.posts = [@post]
|
@bio = Bio.new(id: 1, content: 'AMS Contributor')
|
||||||
@author.roles = [@role]
|
@author = Author.new(name: 'Joao M. D. Moura')
|
||||||
@author.bio = nil
|
@role = Role.new(name: 'Great Author')
|
||||||
@post.comments = [@comment]
|
@location = Location.new(lat: '-23.550520', lng: '-46.633309')
|
||||||
@post.author = @author
|
@place = Place.new(name: 'Amazing Place')
|
||||||
@comment.post = @post
|
@author.posts = [@post]
|
||||||
@comment.author = @author
|
@author.roles = [@role]
|
||||||
|
@role.author = @author
|
||||||
|
@author.bio = @bio
|
||||||
|
@bio.author = @author
|
||||||
|
@post.comments = [@comment]
|
||||||
|
@post.author = @author
|
||||||
|
@comment.post = @post
|
||||||
|
@comment.author = @author
|
||||||
|
@post.blog = @blog
|
||||||
|
@location.place = @place
|
||||||
|
|
||||||
@post_serializer = PostSerializer.new(@post)
|
@location_serializer = LocationSerializer.new(@location)
|
||||||
@author_serializer = AuthorSerializer.new(@author)
|
@bio_serializer = BioSerializer.new(@bio)
|
||||||
@comment_serializer = CommentSerializer.new(@comment)
|
@role_serializer = RoleSerializer.new(@role)
|
||||||
|
@post_serializer = PostSerializer.new(@post)
|
||||||
|
@author_serializer = AuthorSerializer.new(@author)
|
||||||
|
@comment_serializer = CommentSerializer.new(@comment)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_cache_definition
|
def test_cache_definition
|
||||||
@ -33,28 +45,82 @@ module ActiveModel
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_cache_key_interpolation_with_updated_at
|
def test_cache_key_interpolation_with_updated_at
|
||||||
author = render_object_with_cache_without_cache_key(@author)
|
author = render_object_with_cache(@author)
|
||||||
assert_equal(nil, ActionController::Base.cache_store.fetch(@author.cache_key))
|
assert_equal(nil, ActionController::Base.cache_store.fetch(@author.cache_key))
|
||||||
assert_equal(author, ActionController::Base.cache_store.fetch("#{@author_serializer.class._cache_key}/#{@author_serializer.object.id}-#{@author_serializer.object.updated_at}").to_json)
|
assert_equal(@author_serializer.attributes.to_json, ActionController::Base.cache_store.fetch("#{@author_serializer.class._cache_key}/#{@author_serializer.object.id}-#{@author_serializer.object.updated_at}").to_json)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_default_cache_key_fallback
|
def test_default_cache_key_fallback
|
||||||
comment = render_object_with_cache_without_cache_key(@comment)
|
comment = render_object_with_cache(@comment)
|
||||||
assert_equal(comment, ActionController::Base.cache_store.fetch(@comment.cache_key).to_json)
|
assert_equal(@comment_serializer.attributes.to_json, ActionController::Base.cache_store.fetch(@comment.cache_key).to_json)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_cache_options_definition
|
def test_cache_options_definition
|
||||||
assert_equal({expires_in: 0.05}, @post_serializer.class._cache_options)
|
assert_equal({expires_in: 0.1}, @post_serializer.class._cache_options)
|
||||||
assert_equal(nil, @author_serializer.class._cache_options)
|
assert_equal(nil, @author_serializer.class._cache_options)
|
||||||
assert_equal({expires_in: 1.day}, @comment_serializer.class._cache_options)
|
assert_equal({expires_in: 1.day}, @comment_serializer.class._cache_options)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_fragment_cache_definition
|
||||||
|
assert_equal([:name], @role_serializer.class._cache_only)
|
||||||
|
assert_equal([:content], @bio_serializer.class._cache_except)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_associations_separately_cache
|
||||||
|
ActionController::Base.cache_store.clear
|
||||||
|
assert_equal(nil, ActionController::Base.cache_store.fetch(@post.cache_key))
|
||||||
|
assert_equal(nil, ActionController::Base.cache_store.fetch(@comment.cache_key))
|
||||||
|
|
||||||
|
post = render_object_with_cache(@post)
|
||||||
|
|
||||||
|
assert_equal(@post_serializer.attributes, ActionController::Base.cache_store.fetch(@post.cache_key))
|
||||||
|
assert_equal(@comment_serializer.attributes, ActionController::Base.cache_store.fetch(@comment.cache_key))
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_associations_cache_when_updated
|
||||||
|
# Clean the Cache
|
||||||
|
ActionController::Base.cache_store.clear
|
||||||
|
|
||||||
|
# Generate a new Cache of Post object and each objects related to it.
|
||||||
|
render_object_with_cache(@post)
|
||||||
|
|
||||||
|
# Check if if cache the objects separately
|
||||||
|
assert_equal(@post_serializer.attributes, ActionController::Base.cache_store.fetch(@post.cache_key))
|
||||||
|
assert_equal(@comment_serializer.attributes, ActionController::Base.cache_store.fetch(@comment.cache_key))
|
||||||
|
|
||||||
|
# Simulating update on comments relationship with Post
|
||||||
|
new_comment = Comment.new(id: 2, body: 'ZOMG A NEW COMMENT')
|
||||||
|
new_comment_serializer = CommentSerializer.new(new_comment)
|
||||||
|
@post.comments = [new_comment]
|
||||||
|
|
||||||
|
# Ask for the serialized object
|
||||||
|
render_object_with_cache(@post)
|
||||||
|
|
||||||
|
# Check if the the new comment was cached
|
||||||
|
assert_equal(new_comment_serializer.attributes, ActionController::Base.cache_store.fetch(new_comment.cache_key))
|
||||||
|
assert_equal(@post_serializer.attributes, ActionController::Base.cache_store.fetch(@post.cache_key))
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_fragment_fetch_with_virtual_associations
|
||||||
|
expected_result = {
|
||||||
|
id: @location.id,
|
||||||
|
lat: @location.lat,
|
||||||
|
lng: @location.lng,
|
||||||
|
place: 'Nowhere'
|
||||||
|
}
|
||||||
|
|
||||||
|
hash = render_object_with_cache(@location)
|
||||||
|
|
||||||
|
assert_equal(hash, expected_result)
|
||||||
|
assert_equal({place: 'Nowhere'}, ActionController::Base.cache_store.fetch(@location.cache_key))
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def render_object_with_cache_without_cache_key(obj)
|
def render_object_with_cache(obj)
|
||||||
serializer_class = ActiveModel::Serializer.serializer_for(obj)
|
serializer_class = ActiveModel::Serializer.serializer_for(obj)
|
||||||
serializer = serializer_class.new(obj)
|
serializer = serializer_class.new(obj)
|
||||||
adapter = ActiveModel::Serializer.adapter.new(serializer)
|
adapter = ActiveModel::Serializer.adapter.new(serializer)
|
||||||
adapter.to_json
|
adapter.serializable_hash
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -4,6 +4,7 @@ module ActiveModel
|
|||||||
class Serializer
|
class Serializer
|
||||||
class MetaTest < Minitest::Test
|
class MetaTest < Minitest::Test
|
||||||
def setup
|
def setup
|
||||||
|
ActionController::Base.cache_store.clear
|
||||||
@blog = Blog.new(id: 1,
|
@blog = Blog.new(id: 1,
|
||||||
name: 'AMS Hints',
|
name: 'AMS Hints',
|
||||||
writer: Author.new(id: 2, name: "Steve"),
|
writer: Author.new(id: 2, name: "Steve"),
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user