Fix deep nesting case

This commit is contained in:
ccocchi 2012-03-02 11:32:11 +01:00
parent 844b2703e2
commit 1a033a60c2
6 changed files with 93 additions and 25 deletions

View File

@ -1,7 +1,7 @@
module RablFastJson module RablFastJson
class Compiler class Compiler
def initialize(context = nil, assigns = nil) def initialize(context = nil)
@context = context @context = context
@glue_count = 0 @glue_count = 0
end end
@ -50,7 +50,7 @@ module RablFastJson
end end
def extends(path) def extends(path)
t = Library.instance.get(path) t = Library.instance.get(path, @context)
@template.merge!(t.source) @template.merge!(t.source)
end end
@ -77,7 +77,7 @@ module RablFastJson
end end
def _compile_sub_template(name, data, &block) def _compile_sub_template(name, data, &block)
compiler = Compiler.new(@context, @assigns) compiler = Compiler.new(@context)
template = compiler.compile_block(&block) template = compiler.compile_block(&block)
@template[name] = template.merge!(:_data => data) @template[name] = template.merge!(:_data => data)
end end

View File

@ -11,33 +11,22 @@ module RablFastJson
def get_rendered_template(source, context) def get_rendered_template(source, context)
path = context.instance_variable_get(:@virtual_path) path = context.instance_variable_get(:@virtual_path)
@view_renderer = context.instance_variable_get(:@view_renderer) @view_renderer = context.instance_variable_get(:@view_renderer)
start = Time.now
compiled_template = get_compiled_template(path, source, context) compiled_template = get_compiled_template(path, source, context)
compiled_time = Time.now
compiled_template.context = context compiled_template.context = context
r = compiled_template.render
render_time = Time.now
res = ActiveSupport::JSON.encode(r)
final_time = Time.now
Rails.logger.warn "[BENCHMARK] Compilation:\t#{(compiled_time - start) * 1000.0}ms" ActiveSupport::JSON.encode(compiled_template.render)
Rails.logger.warn "[BENCHMARK] Rendering:\t\t#{(render_time - compiled_time) * 1000.0}ms"
Rails.logger.warn "[BENCHMARK] JSON encoding:\t#{(final_time - render_time) * 1000.0}ms"
Rails.logger.warn "[BENCHMARK] Total:\t\t#{(final_time - start) * 1000.0}ms"
res
end end
def get_compiled_template(path, source, context) def get_compiled_template(path, source, context)
#@cached_templates[path] ||= @cached_templates[path] ||= Compiler.new(context).compile_source(source)
Compiler.new(context).compile_source(source)
end end
def get(path) def get(path, context)
template = @cached_templates[path] template = @cached_templates[path]
return template if !template.nil? return template if !template.nil?
t = @view_renderer.lookup_context.find_template(path, [], false) t = @view_renderer.lookup_context.find_template(path, [], false)
get_compiled_template(path, t.source, nil) get_compiled_template(path, t.source, context)
end end
end end
end end

View File

@ -24,21 +24,24 @@ module RablFastJson
source.inject({}) { |output, current| source.inject({}) { |output, current|
key, value = current key, value = current
out = case value out = case value
when Symbol when Symbol
data.send(value) # attributes data.send(value) # attributes
when Proc when Proc
value.call(data) # node value.call(data) # node
when Hash when Hash
data_symbol = value.delete(:_data) current_value = value.dup
object = data_symbol.to_s.start_with?('@') ? @context.instance_variable_get(data_symbol) : @object.send(data_symbol) data_symbol = current_value.delete(:_data)
object = data_symbol.nil? ? data : data_symbol.to_s.start_with?('@') ? @context.instance_variable_get(data_symbol) : data.send(data_symbol)
if key.to_s.start_with?('_') # glue if key.to_s.start_with?('_') # glue
value.each_pair { |k, v| current_value.each_pair { |k, v|
output[k] = object.send(v) output[k] = object.send(v)
} }
next output next output
else # child else # child
object.respond_to?(:each) ? render_collection(object, value) : render_resource(object, value) object.respond_to?(:each) ? render_collection(object, current_value) : render_resource(object, current_value)
end end
end end
output[key] = out output[key] = out

View File

@ -87,9 +87,9 @@ class CompilerTest < ActiveSupport::TestCase
assert_equal :users, t.root_name assert_equal :users, t.root_name
end end
test "extends use other template one's active" do test "extends use other template source as itself" do
template = mock('template', :source => { :id => :id }) template = mock('template', :source => { :id => :id })
RablFastJson::Library.stub_chain(:instance, :get).with('users/base').and_return(template) RablFastJson::Library.instance.stub(:get).with('users/base', @context).and_return(template)
t = @compiler.compile_source(%{ extends 'users/base' }) t = @compiler.compile_source(%{ extends 'users/base' })
assert_equal({ :id => :id }, t.source) assert_equal({ :id => :id }, t.source)
end end

61
test/deep_nesting_test.rb Normal file
View File

@ -0,0 +1,61 @@
require 'test_helper'
class DeepNestingTest < ActiveSupport::TestCase
class Post
attr_accessor :id, :title
def initialize(id, title)
@id, @title = id, title
end
def comments
[Struct.new(:id, :content).new(1, 'first'), Struct.new(:id, :content).new(2, 'second')]
end
end
setup do
RablFastJson::Library.reset_instance
@post = Post.new(42, 'I rock !')
@user = User.new(1, 'foobar', 'male')
@user.stub(:posts).and_return([@post])
@user.stub(:respond_to?).with(:each).and_return(false)
@view_renderer = mock()
@view_renderer.stub_chain(:lookup_context, :find_template).with('comments/show', [], false).and_return(
mock(:source => %{ object :@comment\n attribute :content }))
@context = Context.new
@context.stub(:instance_variable_get).with(:@user).and_return(@user)
@context.stub(:instance_variable_get).with(:@view_renderer).and_return(@view_renderer)
@context.stub(:instance_variable_get).with(:@virtual_path).and_return('users/show')
end
test "compile and render deep nesting template" do
source = %{
object :@user
attributes :id, :name
child :posts do
attribute :title
child :comments do
extends 'comments/show'
end
end
}
assert_equal(ActiveSupport::JSON.encode({
:id => 1,
:name => 'foobar',
:posts => [{
:title => 'I rock !',
:comments => [
{ :content => 'first' },
{ :content => 'second' }
]
}]
}), RablFastJson::Library.instance.get_rendered_template(source, @context))
end
end

View File

@ -5,6 +5,21 @@ $:.unshift File.expand_path('../../lib', __FILE__)
require 'rspec/mocks' require 'rspec/mocks'
require 'minitest/autorun' require 'minitest/autorun'
require 'active_support/test_case' require 'active_support/test_case'
require 'singleton'
class <<Singleton
def included_with_reset(klass)
included_without_reset(klass)
class <<klass
def reset_instance
Singleton.send :__init__, self
self
end
end
end
alias_method_chain :included, :reset
end
require 'rabl-fast-json' require 'rabl-fast-json'
module ActiveSupport module ActiveSupport