Permalink
Browse files

caching support for collections that define cache_key (only supports …

…1 level of caching)
  • Loading branch information...
1 parent 9ce1fcc commit 88d54136648490db2ab63afa3710185d13400942 @nc committed Mar 27, 2012
Showing with 112 additions and 8 deletions.
  1. +3 −1 Gemfile
  2. +7 −3 Gemfile.lock
  3. +39 −3 lib/jbuilder.rb
  4. +3 −1 lib/jbuilder_template.rb
  5. +60 −0 test/jbuilder_test.rb
View
@@ -1,3 +1,5 @@
source "http://rubygems.org"
-gemspec
+gemspec
+
+gem "yajl-ruby", :require => 'yajl'
View
@@ -1,20 +1,24 @@
PATH
remote: .
specs:
- jbuilder (0.3)
+ jbuilder (0.4.0)
activesupport (>= 3.0.0)
blankslate (>= 2.1.2.4)
GEM
remote: http://rubygems.org/
specs:
- activesupport (3.1.3)
+ activesupport (3.2.1)
+ i18n (~> 0.6)
multi_json (~> 1.0)
blankslate (2.1.2.4)
- multi_json (1.0.4)
+ i18n (0.6.0)
+ multi_json (1.1.0)
+ yajl-ruby (1.1.0)
PLATFORMS
ruby
DEPENDENCIES
jbuilder!
+ yajl-ruby
View
@@ -3,13 +3,33 @@
require 'active_support/core_ext/array/access'
require 'active_support/core_ext/enumerable'
require 'active_support/json'
+require "yajl"
+
+class JsonWrapper
+ def initialize(json_string)
+ @json_string = json_string
+ end
+
+ # yajl-ruby will check if this method exists and call it if so
+ # then append the return value directly onto the output buffer as-is
+ # this means that this method is assumed to be returning valid JSON
+ def to_json
+ @json_string
+ end
+end
class Jbuilder < BlankSlate
# Yields a builder and automatically turns the result into a JSON string
def self.encode
new._tap { |jbuilder| yield jbuilder }.target!
end
+ def self.encode_with_cache(cache_key)
+ Rails.cache.fetch(cache_key) do
+ new._tap { |jbuilder| yield jbuilder }.target!
+ end
+ end
+
define_method(:__class__, find_hidden_method(:class))
define_method(:_tap, find_hidden_method(:tap))
@@ -43,6 +63,11 @@ def child!
@attributes << _new_instance._tap { |jbuilder| yield jbuilder }.attributes!
end
+ def child_json!(json)
+ @attributes = [] unless @attributes.is_a? Array
+ @attributes << JsonWrapper.new(json)
+ end
+
# Turns the current element into an array and iterates over the passed collection, adding each iteration as
# an element of the resulting array.
#
@@ -71,9 +96,20 @@ def array!(collection)
@attributes = [] and return if collection.empty?
collection.each do |element|
- child! do |child|
- yield child, element
+ if element.respond_to?(:cache_key)
+ key = "#{element.cache_key}.json"
+
+ cached_json = Rails.cache.fetch(key) do
+ _new_instance._tap { |jbuilder| yield jbuilder, element }.target!
+ end
+ child_json! cached_json
+
+ else
+ child! do |child|
+ yield child, element
+ end
end
+
end
end
@@ -112,7 +148,7 @@ def attributes!
# Encodes the current builder as JSON.
def target!
- ActiveSupport::JSON.encode @attributes
+ Yajl::Encoder.encode @attributes
end
View
@@ -9,7 +9,7 @@ def initialize(context)
end
def partial!(partial_name, options = {})
- @context.render(partial_name, options.merge(:json => self))
+ @context.render(partial_name, options.merge(:json => new(@context)))
end
private
@@ -23,6 +23,8 @@ class JbuilderHandler
self.default_format = Mime::JSON
def self.call(template)
+
+
%{
if defined?(json)
#{template.source}
View
@@ -3,6 +3,30 @@
require 'jbuilder'
+class Cache
+ def initialize
+ @values = {}
+ end
+
+ def fetch(key)
+ @values[key] || (@values[key] = yield)
+ end
+
+ def read(key)
+ @values[key]
+ end
+
+ def write(key, value)
+ @values[key] = value
+ end
+end
+
+class Rails
+ def self.cache
+ @cache ||= Cache.new
+ end
+end
+
class JbuilderTest < ActiveSupport::TestCase
test "single key" do
json = Jbuilder.encode do |json|
@@ -12,6 +36,14 @@ class JbuilderTest < ActiveSupport::TestCase
assert_equal "hello", JSON.parse(json)["content"]
end
+ test "cache_key" do
+ json = Jbuilder.encode_with_cache("12345") do |json|
+ json.content "hello"
+ end
+
+ assert_equal "hello", JSON.parse(json)["content"]
+ end
+
test "single key with false value" do
json = Jbuilder.encode do |json|
json.content false
@@ -222,6 +254,34 @@ def initialize(name, age)
assert_equal "world", parsed.second["content"]
end
end
+
+ class Comment
+ attr_accessor :content, :id
+
+ def cache_key
+ "cache_key_#{id}"
+ end
+
+ def initialize(content, id)
+ @content, @id = content, id
+ end
+
+ end
+
+ test "top-level array (cached)" do
+ comments = [ Comment.new("hello", 1), Comment.new("world", 2) ]
+
+ json = Jbuilder.encode do |json|
+ json.array!(comments) do |json, comment|
+ json.content comment.content
+ end
+ end
+
+ JSON.parse(json).tap do |parsed|
+ assert_equal "hello", parsed.first["content"]
+ assert_equal "world", parsed.second["content"]
+ end
+ end
test "empty top-level array" do
comments = []

0 comments on commit 88d5413

Please sign in to comment.