diff --git a/lib/rabl-fast-json/compiler.rb b/lib/rabl-fast-json/compiler.rb index 5469bbd..4653712 100644 --- a/lib/rabl-fast-json/compiler.rb +++ b/lib/rabl-fast-json/compiler.rb @@ -1,7 +1,7 @@ module RablFastJson class Compiler - def initialize(context = nil, assigns = nil) + def initialize(context = nil) @context = context @glue_count = 0 end @@ -50,7 +50,7 @@ module RablFastJson end def extends(path) - t = Library.instance.get(path) + t = Library.instance.get(path, @context) @template.merge!(t.source) end @@ -77,7 +77,7 @@ module RablFastJson end def _compile_sub_template(name, data, &block) - compiler = Compiler.new(@context, @assigns) + compiler = Compiler.new(@context) template = compiler.compile_block(&block) @template[name] = template.merge!(:_data => data) end diff --git a/lib/rabl-fast-json/library.rb b/lib/rabl-fast-json/library.rb index ba7e012..aa67ab9 100644 --- a/lib/rabl-fast-json/library.rb +++ b/lib/rabl-fast-json/library.rb @@ -11,33 +11,22 @@ module RablFastJson def get_rendered_template(source, context) path = context.instance_variable_get(:@virtual_path) @view_renderer = context.instance_variable_get(:@view_renderer) - start = Time.now + compiled_template = get_compiled_template(path, source, context) - compiled_time = Time.now 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" - 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 + ActiveSupport::JSON.encode(compiled_template.render) end def get_compiled_template(path, source, context) - #@cached_templates[path] ||= - Compiler.new(context).compile_source(source) + @cached_templates[path] ||= Compiler.new(context).compile_source(source) end - def get(path) + def get(path, context) template = @cached_templates[path] return template if !template.nil? 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 \ No newline at end of file diff --git a/lib/rabl-fast-json/template.rb b/lib/rabl-fast-json/template.rb index 06e3957..f1de078 100644 --- a/lib/rabl-fast-json/template.rb +++ b/lib/rabl-fast-json/template.rb @@ -24,21 +24,24 @@ module RablFastJson source.inject({}) { |output, current| key, value = current + out = case value when Symbol data.send(value) # attributes when Proc value.call(data) # node when Hash - data_symbol = value.delete(:_data) - object = data_symbol.to_s.start_with?('@') ? @context.instance_variable_get(data_symbol) : @object.send(data_symbol) + current_value = value.dup + 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 - value.each_pair { |k, v| + current_value.each_pair { |k, v| output[k] = object.send(v) } next output 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 output[key] = out diff --git a/test/compiler_test.rb b/test/compiler_test.rb index 17f80d7..7cb0926 100644 --- a/test/compiler_test.rb +++ b/test/compiler_test.rb @@ -87,9 +87,9 @@ class CompilerTest < ActiveSupport::TestCase assert_equal :users, t.root_name 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 }) - 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' }) assert_equal({ :id => :id }, t.source) end diff --git a/test/deep_nesting_test.rb b/test/deep_nesting_test.rb new file mode 100644 index 0000000..607ba98 --- /dev/null +++ b/test/deep_nesting_test.rb @@ -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 + + + diff --git a/test/test_helper.rb b/test/test_helper.rb index 5e757bf..4d1ad71 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -5,6 +5,21 @@ $:.unshift File.expand_path('../../lib', __FILE__) require 'rspec/mocks' require 'minitest/autorun' require 'active_support/test_case' + +require 'singleton' +class <