Merge pull request #810 from joaomdmoura/fragment-cache

Adding Fragment Cache to AMS
This commit is contained in:
Alexandre de Oliveira
2015-04-21 17:57:55 -03:00
19 changed files with 553 additions and 101 deletions

View File

@@ -111,6 +111,8 @@ module ActionController
"id" => "1",
"type" => "roles",
"name" => "admin",
"description" => nil,
"slug" => "admin-1",
"links" => {
"author" => { "linkage" => { "type" =>"authors", "id" => "1" } }
}
@@ -118,6 +120,8 @@ module ActionController
"id" => "2",
"type" => "roles",
"name" => "colab",
"description" => nil,
"slug" => "colab-2",
"links" => {
"author" => { "linkage" => { "type" =>"authors", "id" => "1" } }
}

View File

@@ -48,36 +48,79 @@ module ActionController
end
def render_object_with_cache_enabled
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', blog:nil, body: 'Body', comments: [comment], author: author })
@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 })
generate_cached_serializer(post)
generate_cached_serializer(@post)
post.title = 'ZOMG a New Post'
render json: post
@post.title = 'ZOMG a New 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
def render_object_expired_with_cache_enabled
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', 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)
post.title = 'ZOMG a New Post'
sleep 0.05
sleep 0.1
render json: post
end
def render_changed_object_with_cache_enabled
comment = Comment.new({ id: 1, body: 'ZOMG A COMMENT' })
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
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
def generate_cached_serializer(obj)
serializer_class = ActiveModel::Serializer.serializer_for(obj)
@@ -249,6 +292,74 @@ module ActionController
assert_equal 'application/json', @response.content_type
assert_equal expected.to_json, @response.body
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

View 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

View File

@@ -6,6 +6,7 @@ module ActiveModel
class Json
class HasManyTestTest < Minitest::Test
def setup
ActionController::Base.cache_store.clear
@author = Author.new(id: 1, name: 'Steve K.')
@post = Post.new(title: 'New Post', body: 'Body')
@first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')

View File

@@ -41,6 +41,7 @@ module ActiveModel
expected = [
{
id: "43",
rating: nil,
type: "bios",
content:"AMS Contributor",
links: {

View File

@@ -1,5 +1,4 @@
require 'test_helper'
module ActiveModel
class Serializer
class Adapter
@@ -104,8 +103,9 @@ module ActiveModel
}
}, {
id: "1",
content: "AMS Contributor",
rating: nil,
type: "bios",
content: "AMS Contributor",
links: {
author: { linkage: { type: "authors", id: "1" } }
}
@@ -120,8 +120,9 @@ module ActiveModel
}
}, {
id: "2",
content: "Rails Contributor",
rating: nil,
type: "bios",
content: "Rails Contributor",
links: {
author: { linkage: { type: "authors", id: "2" } }
}

View File

@@ -5,6 +5,7 @@ module ActiveModel
class Adapter
class JsonTest < Minitest::Test
def setup
ActionController::Base.cache_store.clear
@author = Author.new(id: 1, name: 'Steve K.')
@post = Post.new(title: 'New Post', body: 'Body')
@first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')

51
test/fixtures/poro.rb vendored
View File

@@ -57,18 +57,22 @@ class ProfilePreviewSerializer < ActiveModel::Serializer
urls :posts, :comments
end
Post = Class.new(Model)
Comment = Class.new(Model)
Author = Class.new(Model)
Bio = Class.new(Model)
Blog = Class.new(Model)
Role = Class.new(Model)
Post = Class.new(Model)
Like = Class.new(Model)
Comment = Class.new(Model)
Author = Class.new(Model)
Bio = Class.new(Model)
Blog = Class.new(Model)
Role = Class.new(Model)
User = Class.new(Model)
Location = Class.new(Model)
Place = Class.new(Model)
module Spam; end
Spam::UnrelatedLink = Class.new(Model)
PostSerializer = Class.new(ActiveModel::Serializer) do
cache key:'post', expires_in: 0.05
cache key:'post', expires_in: 0.1
attributes :id, :title, :body
has_many :comments
@@ -116,13 +120,42 @@ AuthorSerializer = Class.new(ActiveModel::Serializer) do
end
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
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
attributes :id, :content
cache except: [:content]
attributes :id, :content, :rating
belongs_to :author
end

View File

@@ -3,21 +3,33 @@ module ActiveModel
class Serializer
class CacheTest < Minitest::Test
def setup
@post = Post.new({ title: 'New Post', body: 'Body' })
@comment = Comment.new({ id: 1, body: 'ZOMG A COMMENT' })
@author = Author.new(name: 'Joao M. D. Moura')
@role = Role.new(name: 'Great Author')
@author.posts = [@post]
@author.roles = [@role]
@author.bio = nil
@post.comments = [@comment]
@post.author = @author
@comment.post = @post
@comment.author = @author
ActionController::Base.cache_store.clear
@comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')
@blog = Blog.new(id: 999, name: "Custom blog")
@post = Post.new(title: 'New Post', body: 'Body')
@bio = Bio.new(id: 1, content: 'AMS Contributor')
@author = Author.new(name: 'Joao M. D. Moura')
@role = Role.new(name: 'Great Author')
@location = Location.new(lat: '-23.550520', lng: '-46.633309')
@place = Place.new(name: 'Amazing Place')
@author.posts = [@post]
@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)
@author_serializer = AuthorSerializer.new(@author)
@comment_serializer = CommentSerializer.new(@comment)
@location_serializer = LocationSerializer.new(@location)
@bio_serializer = BioSerializer.new(@bio)
@role_serializer = RoleSerializer.new(@role)
@post_serializer = PostSerializer.new(@post)
@author_serializer = AuthorSerializer.new(@author)
@comment_serializer = CommentSerializer.new(@comment)
end
def test_cache_definition
@@ -33,28 +45,82 @@ module ActiveModel
end
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(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
def test_default_cache_key_fallback
comment = render_object_with_cache_without_cache_key(@comment)
assert_equal(comment, ActionController::Base.cache_store.fetch(@comment.cache_key).to_json)
comment = render_object_with_cache(@comment)
assert_equal(@comment_serializer.attributes.to_json, ActionController::Base.cache_store.fetch(@comment.cache_key).to_json)
end
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({expires_in: 1.day}, @comment_serializer.class._cache_options)
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
def render_object_with_cache_without_cache_key(obj)
def render_object_with_cache(obj)
serializer_class = ActiveModel::Serializer.serializer_for(obj)
serializer = serializer_class.new(obj)
adapter = ActiveModel::Serializer.adapter.new(serializer)
adapter.to_json
adapter.serializable_hash
end
end
end

View File

@@ -4,6 +4,7 @@ module ActiveModel
class Serializer
class MetaTest < Minitest::Test
def setup
ActionController::Base.cache_store.clear
@blog = Blog.new(id: 1,
name: 'AMS Hints',
writer: Author.new(id: 2, name: "Steve"),