From 52941e623217311fa2a683a70669acb3ed0433ee Mon Sep 17 00:00:00 2001 From: ccocchi Date: Tue, 10 Apr 2012 01:03:07 +0200 Subject: [PATCH 1/8] Extract rendering from template into renderers --- lib/rabl-fast-json.rb | 9 ++- lib/rabl-fast-json/handler.rb | 2 +- lib/rabl-fast-json/library.rb | 10 +-- lib/rabl-fast-json/renderer.rb | 2 + lib/rabl-fast-json/renderers/base.rb | 108 +++++++++++++++++++++++++++ lib/rabl-fast-json/renderers/json.rb | 9 +++ lib/rabl-fast-json/template.rb | 77 +------------------ test/cache_templates_test.rb | 26 +++---- test/compiled_template_test.rb | 4 + 9 files changed, 150 insertions(+), 97 deletions(-) create mode 100644 lib/rabl-fast-json/renderer.rb create mode 100644 lib/rabl-fast-json/renderers/base.rb create mode 100644 lib/rabl-fast-json/renderers/json.rb diff --git a/lib/rabl-fast-json.rb b/lib/rabl-fast-json.rb index f3c43fd..0f29c9c 100644 --- a/lib/rabl-fast-json.rb +++ b/lib/rabl-fast-json.rb @@ -8,20 +8,25 @@ require 'rabl-fast-json/version' require 'rabl-fast-json/helpers' require 'rabl-fast-json/template' require 'rabl-fast-json/compiler' + +require 'rabl-fast-json/renderer' + require 'rabl-fast-json/library' require 'rabl-fast-json/handler' require 'rabl-fast-json/railtie' + + module RablFastJson extend self - + mattr_accessor :cache_templates @@cache_templates = true def configure yield self end - + def cache_templates? ActionController::Base.perform_caching && @@cache_templates end diff --git a/lib/rabl-fast-json/handler.rb b/lib/rabl-fast-json/handler.rb index b95f5e2..c86635c 100644 --- a/lib/rabl-fast-json/handler.rb +++ b/lib/rabl-fast-json/handler.rb @@ -4,7 +4,7 @@ module RablFastJson cattr_accessor :default_format self.default_format = 'application/json' - def self.call(template) + def self.call(template) %{ RablFastJson::Library.instance. get_rendered_template(#{template.source.inspect}, self) diff --git a/lib/rabl-fast-json/library.rb b/lib/rabl-fast-json/library.rb index 32ccaad..10565e0 100644 --- a/lib/rabl-fast-json/library.rb +++ b/lib/rabl-fast-json/library.rb @@ -12,12 +12,12 @@ module RablFastJson def get_rendered_template(source, context) path = context.instance_variable_get(:@virtual_path) - @view_renderer = context.instance_variable_get(:@view_renderer) + @lookup_context = context.lookup_context compiled_template = get_compiled_template(path, source) - compiled_template.context = context - body = compiled_template.render - ActiveSupport::JSON.encode(compiled_template.root_name ? { compiled_template.root_name => body } : body) + + format = context.params[:format] || 'json' + Renderers.const_get(format.upcase!).new(context).render(compiled_template) end def get_compiled_template(path, source) @@ -32,7 +32,7 @@ module RablFastJson def get(path) template = @cached_templates[path] return template unless template.nil? - t = @view_renderer.lookup_context.find_template(path, [], false) + t = @lookup_context.find_template(path, [], false) get_compiled_template(path, t.source) end end diff --git a/lib/rabl-fast-json/renderer.rb b/lib/rabl-fast-json/renderer.rb new file mode 100644 index 0000000..3065875 --- /dev/null +++ b/lib/rabl-fast-json/renderer.rb @@ -0,0 +1,2 @@ +require 'rabl-fast-json/renderers/base' +require 'rabl-fast-json/renderers/json' \ No newline at end of file diff --git a/lib/rabl-fast-json/renderers/base.rb b/lib/rabl-fast-json/renderers/base.rb new file mode 100644 index 0000000..5709580 --- /dev/null +++ b/lib/rabl-fast-json/renderers/base.rb @@ -0,0 +1,108 @@ +module RablFastJson + module Renderers + class Base + + def initialize(context) # :nodoc: + @_context = context + setup_render_context + end + + # + # Render a template. + # Uses the compiled template source to get a hash with the actual + # data and then format the result according to the `format_result` + # method defined by the renderer. + # + def render(template) + collection_or_resource = @_context.instance_variable_get(template.data) if template.data + output_hash = collection_or_resource.respond_to?(:each) ? render_collection(collection_or_resource, template.source) : + render_resource(collection_or_resource, template.source) + output_hash = { template.root_name => output_hash } if template.root_name + format_output(output_hash) + end + + # + # Format a hash into the desired output. + # Renderer subclasses must implement this method + # + def format_output(hash) + raise "Muse be implemented by renderer" + end + + protected + + # + # Render a single resource as a hash, according to the compiled + # template source passed. + # + def render_resource(data, source) + source.inject({}) { |output, current| + key, value = current + + out = case value + when Symbol + data.send(value) # attributes + when Proc + instance_exec data, &value # node + when Array # node with condition + next output if !instance_exec data, &(value.first) + instance_exec data, &(value.last) + when Hash + 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 + current_value.each_pair { |k, v| + output[k] = object.send(v) + } + next output + else # child + object.respond_to?(:each) ? render_collection(object, current_value) : render_resource(object, current_value) + end + end + output[key] = out + output + } + end + + # + # Call the render_resource mtehod on each object of the collection + # and return an array of the returned values. + # + def render_collection(collection, source) + collection.map { |o| render_resource(o, source) } + end + + def partial(template_path, options = {}) + raise "No object was given to partial" unless options[:object] + object = options[:object] + + return [] if object.respond_to?(:empty?) && object.empty? + + template = Library.instance.get(template_path) + object.respond_to?(:each) ? render_collection(object, template.source) : render_resource(object, template.source) + end + + # + # If a method is called inside a 'node' property or a 'if' lambda + # it will be passed to context if it exists or treated as a standard + # missing method. + # + def method_missing(name, *args, &block) + @context.respond_to?(name) ? @context.send(name, *args, &block) : super + end + + # + # Copy assigns from controller's context into this + # renderer context to include instances variables when + # evaluating 'node' properties. + # + def setup_render_context + @_context.instance_variable_get(:@_assigns).each_pair { |k, v| + instance_variable_set("@#{k}", v) unless k.start_with?('_') || k == @data + } + end + end + end +end \ No newline at end of file diff --git a/lib/rabl-fast-json/renderers/json.rb b/lib/rabl-fast-json/renderers/json.rb new file mode 100644 index 0000000..ed36cef --- /dev/null +++ b/lib/rabl-fast-json/renderers/json.rb @@ -0,0 +1,9 @@ +module RablFastJson + module Renderers + class JSON < Base + def format_output(hash) + ActiveSupport::JSON.encode(hash) + end + 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 0a755ca..8445de1 100644 --- a/lib/rabl-fast-json/template.rb +++ b/lib/rabl-fast-json/template.rb @@ -1,86 +1,11 @@ module RablFastJson class CompiledTemplate - attr_accessor :source, :data, :root_name, :context + attr_accessor :source, :data, :root_name delegate :[], :[]=, :merge!, :to => :source def initialize @source = {} end - - def has_root_name? - !@root_name.nil? - end - - def render - get_object_from_context - get_assigns_from_context - @object.respond_to?(:each) ? render_collection : render_resource - end - - def render_resource(data = nil, source = nil) - data ||= @object - source ||= @source - - source.inject({}) { |output, current| - key, value = current - - out = case value - when Symbol - data.send(value) # attributes - when Proc - instance_exec data, &value # node - when Array # node with condition - next output if !instance_exec data, &(value.first) #value.first.call(data) - instance_exec data, &(value.last) - when Hash - 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 - current_value.each_pair { |k, v| - output[k] = object.send(v) - } - next output - else # child - object.respond_to?(:each) ? render_collection(object, current_value) : render_resource(object, current_value) - end - end - output[key] = out - output - } - end - - def render_collection(collection = nil, source = nil) - collection ||= @object - collection.inject([]) { |output, o| output << render_resource(o, source) } - end - - def method_missing(name, *args, &block) - @context.respond_to?(name) ? @context.send(name, *args, &block) : super - end - - def partial(template_path, options = {}) - raise "No object was given to partial" if options[:object].nil? - object = options[:object] - - return [] if object.respond_to?(:empty?) && object.empty? - - template = Library.instance.get(template_path) - object.respond_to?(:each) ? template.render_collection(object) : template.render_resource(object) - end - - protected - - def get_object_from_context - @object = @context.instance_variable_get(@data) if @data - end - - def get_assigns_from_context - @context.instance_variable_get(:@_assigns).each_pair { |k, v| - instance_variable_set("@#{k}", v) unless k.start_with?('_') || k == @data - } - end end end \ No newline at end of file diff --git a/test/cache_templates_test.rb b/test/cache_templates_test.rb index 5de4e56..288c668 100644 --- a/test/cache_templates_test.rb +++ b/test/cache_templates_test.rb @@ -4,31 +4,31 @@ class CacheTemplatesTest < ActiveSupport::TestCase setup do RablFastJson::Library.reset_instance - @library = RablFastJson::Library.instance - end - - test "cache templates if perform_caching is active and cache_templates is enabled" do + @library = RablFastJson::Library.instance RablFastJson.cache_templates = true - ActionController::Base.stub(:perform_caching).and_return(true) + end + + test "cache templates if perform_caching is active and cache_templates is enabled" do + ActionController::Base.stub(:perform_caching).and_return(true) @library.get_compiled_template('some/path', "") t = @library.get_compiled_template('some/path', "attribute :id") - + assert_equal({}, t.source) end - + test "cached templates should not be modifiable in place" do - RablFastJson.cache_templates = true ActionController::Base.stub(:perform_caching).and_return(true) t = @library.get_compiled_template('some/path', "") assert_nil t.context t.context = "foobar" assert_nil @library.get_compiled_template('some/path', "").context end - + test "don't cache templates cache_templates is enabled but perform_caching is not active" do - RablFastJson.cache_templates = true - ActionController::Base.stub(:perform_caching).and_return(false) - - refute_equal @library.get_compiled_template('some/path', ""), @library.get_compiled_template('some/path', "") + ActionController::Base.stub(:perform_caching).and_return(false) + @library.get_compiled_template('some/path', "") + t = @library.get_compiled_template('some/path', "attribute :id") + + assert_equal({ :id => :id }, t.source) end end \ No newline at end of file diff --git a/test/compiled_template_test.rb b/test/compiled_template_test.rb index 7971bbe..3ad7ec1 100644 --- a/test/compiled_template_test.rb +++ b/test/compiled_template_test.rb @@ -13,6 +13,10 @@ class TestCompiledTemplate < ActiveSupport::TestCase @template.data = :@data end + def render_json_output + RablFastJson::Renderers::JSON.new(@context).render(@template) + end + test "render object wth empty template" do @template.source = {} assert_equal({}, @template.render) From 4e6142602f5c74f9565a38f94aead060924f0e3c Mon Sep 17 00:00:00 2001 From: ccocchi Date: Sun, 15 Apr 2012 18:17:49 +0200 Subject: [PATCH 2/8] Test suite refactor --- lib/rabl-fast-json.rb | 1 - lib/rabl-fast-json/compiler.rb | 8 +- lib/rabl-fast-json/helpers.rb | 11 --- lib/rabl-fast-json/library.rb | 4 +- lib/rabl-fast-json/renderers/base.rb | 4 + test/cache_templates_test.rb | 8 +- test/compiler_test.rb | 73 ++++++++++--------- test/deep_nesting_test.rb | 6 +- test/non_restful_response_test.rb | 12 +-- .../json_renderer_test.rb} | 52 +++++++------ test/test_helper.rb | 11 +-- 11 files changed, 87 insertions(+), 103 deletions(-) delete mode 100644 lib/rabl-fast-json/helpers.rb rename test/{compiled_template_test.rb => renderers/json_renderer_test.rb} (66%) diff --git a/lib/rabl-fast-json.rb b/lib/rabl-fast-json.rb index 0f29c9c..0fcadee 100644 --- a/lib/rabl-fast-json.rb +++ b/lib/rabl-fast-json.rb @@ -5,7 +5,6 @@ require 'active_support/json' require 'active_support/core_ext/class/attribute_accessors' require 'rabl-fast-json/version' -require 'rabl-fast-json/helpers' require 'rabl-fast-json/template' require 'rabl-fast-json/compiler' diff --git a/lib/rabl-fast-json/compiler.rb b/lib/rabl-fast-json/compiler.rb index 5e3c4fe..a8422d1 100644 --- a/lib/rabl-fast-json/compiler.rb +++ b/lib/rabl-fast-json/compiler.rb @@ -4,8 +4,6 @@ module RablFastJson # representing data structure # class Compiler - include Helpers - def initialize @glue_count = 0 end @@ -49,7 +47,7 @@ module RablFastJson # def collection(data, options = {}) object(data) - @template.root_name = options[:root] if root_given?(options) + @template.root_name = options[:root] if options[:root] end # @@ -82,8 +80,8 @@ module RablFastJson # def child(name_or_data, options = {}, &block) data, name = extract_data_and_name(name_or_data) - name = options[:root] if root_given?(options) - if partial_given?(options) + name = options[:root] if options[:root] + if options[:partial] template = Library.instance.get(options[:partial]) @template[name] = template.merge!(:_data => data) else diff --git a/lib/rabl-fast-json/helpers.rb b/lib/rabl-fast-json/helpers.rb deleted file mode 100644 index e105b67..0000000 --- a/lib/rabl-fast-json/helpers.rb +++ /dev/null @@ -1,11 +0,0 @@ -module RablFastJson - module Helpers - def root_given?(options) #:nodoc: - options[:root].present? - end - - def partial_given?(options) #:nodoc: - options[:partial].present? - end - end -end \ No newline at end of file diff --git a/lib/rabl-fast-json/library.rb b/lib/rabl-fast-json/library.rb index 10565e0..62b511e 100644 --- a/lib/rabl-fast-json/library.rb +++ b/lib/rabl-fast-json/library.rb @@ -4,8 +4,6 @@ module RablFastJson class Library include Singleton - attr_accessor :view_renderer - def initialize @cached_templates = {} end @@ -31,7 +29,7 @@ module RablFastJson def get(path) template = @cached_templates[path] - return template unless template.nil? + return template if template t = @lookup_context.find_template(path, [], false) get_compiled_template(path, t.source) end diff --git a/lib/rabl-fast-json/renderers/base.rb b/lib/rabl-fast-json/renderers/base.rb index 5709580..aa7235a 100644 --- a/lib/rabl-fast-json/renderers/base.rb +++ b/lib/rabl-fast-json/renderers/base.rb @@ -74,6 +74,10 @@ module RablFastJson collection.map { |o| render_resource(o, source) } end + # + # Allow to use partial inside of node blocks (they are evaluated at) + # rendering time. + # def partial(template_path, options = {}) raise "No object was given to partial" unless options[:object] object = options[:object] diff --git a/test/cache_templates_test.rb b/test/cache_templates_test.rb index 288c668..3d792df 100644 --- a/test/cache_templates_test.rb +++ b/test/cache_templates_test.rb @@ -18,10 +18,10 @@ class CacheTemplatesTest < ActiveSupport::TestCase test "cached templates should not be modifiable in place" do ActionController::Base.stub(:perform_caching).and_return(true) - t = @library.get_compiled_template('some/path', "") - assert_nil t.context - t.context = "foobar" - assert_nil @library.get_compiled_template('some/path', "").context + @library.get_compiled_template('some/path', "") + t = @library.get_compiled_template('some/path', "attribute :id") + + assert_equal({}, t.source) end test "don't cache templates cache_templates is enabled but perform_caching is not active" do diff --git a/test/compiler_test.rb b/test/compiler_test.rb index 862d3d4..c9b550f 100644 --- a/test/compiler_test.rb +++ b/test/compiler_test.rb @@ -11,6 +11,40 @@ class CompilerTest < ActiveSupport::TestCase assert_instance_of RablFastJson::CompiledTemplate, @compiler.compile_source("") end + test "object set data for the template" do + t = @compiler.compile_source(%{ object :@user }) + assert_equal :@user, t.data + assert_equal({}, t.source) + end + + test "object property can define root name" do + t = @compiler.compile_source(%{ object :@user => :author }) + assert_equal :@user, t.data + assert_equal :author, t.root_name + assert_equal({}, t.source) + end + + test "collection set the data for the template" do + t = @compiler.compile_source(%{ collection :@user }) + assert_equal :@user, t.data + assert_equal({}, t.source) + end + + test "collection property can define root name" do + t = @compiler.compile_source(%{ collection :@user => :users }) + assert_equal :@user, t.data + assert_equal :users, t.root_name + assert_equal({}, t.source) + end + + test "collection property can define root name via options" do + t = @compiler.compile_source(%{ collection :@user, :root => :users }) + assert_equal :@user, t.data + assert_equal :users, t.root_name + end + + # Compilation + test "simple attributes are compiled to hash" do t = @compiler.compile_source(%{ attributes :id, :name }) assert_equal({ :id => :id, :name => :name}, t.source) @@ -57,11 +91,10 @@ class CompilerTest < ActiveSupport::TestCase end test "child with succint partial notation" do - @view_renderer = mock() - @view_renderer.stub_chain(:lookup_context, :find_template).with('users/base', [], false).and_return( - mock(:source => %{ attribute :id })) + mock_template = RablFastJson::CompiledTemplate.new + mock_template.source = { :id => :id } RablFastJson::Library.reset_instance - RablFastJson::Library.instance.view_renderer = @view_renderer + RablFastJson::Library.instance.stub(:get).with('users/base').and_return(mock_template) t = @compiler.compile_source(%{child(:user, :partial => 'users/base') }) assert_equal( {:user => { :_data => :user, :id => :id } }, t.source) @@ -84,38 +117,6 @@ class CompilerTest < ActiveSupport::TestCase }, t.source) end - test "object set data for the template" do - t = @compiler.compile_source(%{ object :@user }) - assert_equal :@user, t.data - assert_equal({}, t.source) - end - - test "object property can define root name" do - t = @compiler.compile_source(%{ object :@user => :author }) - assert_equal :@user, t.data - assert_equal :author, t.root_name - assert_equal({}, t.source) - end - - test "collection set the data for the template" do - t = @compiler.compile_source(%{ collection :@user }) - assert_equal :@user, t.data - assert_equal({}, t.source) - end - - test "collection property can define root name" do - t = @compiler.compile_source(%{ collection :@user => :users }) - assert_equal :@user, t.data - assert_equal :users, t.root_name - assert_equal({}, t.source) - end - - test "collection property can define root name via options" do - t = @compiler.compile_source(%{ collection :@user, :root => :users }) - assert_equal :@user, t.data - assert_equal :users, t.root_name - end - test "extends use other template source as itself" do template = mock('template', :source => { :id => :id }) RablFastJson::Library.reset_instance diff --git a/test/deep_nesting_test.rb b/test/deep_nesting_test.rb index 3c3e87c..2dca67a 100644 --- a/test/deep_nesting_test.rb +++ b/test/deep_nesting_test.rb @@ -21,15 +21,11 @@ class DeepNestingTest < ActiveSupport::TestCase @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') @context.stub(:instance_variable_get).with(:@_assigns).and_return({}) + @context.stub(:lookup_context).and_return(mock(:find_template => mock(:source => %{ object :@comment\n attribute :content }))) end test "compile and render deep nesting template" do diff --git a/test/non_restful_response_test.rb b/test/non_restful_response_test.rb index 8eadfcd..d0a7e83 100644 --- a/test/non_restful_response_test.rb +++ b/test/non_restful_response_test.rb @@ -3,18 +3,18 @@ require 'test_helper' class NonRestfulResponseTest < ActiveSupport::TestCase setup do RablFastJson::Library.reset_instance - + @user = User.new(1, 'foo', 'male') @user.stub_chain(:posts, :count).and_return(10) @user.stub(:respond_to?).with(:each).and_return(false) - + @context = Context.new @context.stub(:instance_variable_get).with(:@user).and_return(@user) - @context.stub(:instance_variable_get).with(:@view_renderer).and_return(mock()) @context.stub(:instance_variable_get).with(:@virtual_path).and_return('user/show') @context.stub(:instance_variable_get).with(:@_assigns).and_return({'user' => @user}) + @context.stub(:lookup_context) end - + test "compile and render non restful resource" do source = %{ object false @@ -23,13 +23,13 @@ class NonRestfulResponseTest < ActiveSupport::TestCase attributes :id, :name end } - + assert_equal(ActiveSupport::JSON.encode({ :post_count => 10, :user => { :id => 1, :name => 'foo' } - }), RablFastJson::Library.instance.get_rendered_template(source, @context)) + }), RablFastJson::Library.instance.get_rendered_template(source, @context)) end end \ No newline at end of file diff --git a/test/compiled_template_test.rb b/test/renderers/json_renderer_test.rb similarity index 66% rename from test/compiled_template_test.rb rename to test/renderers/json_renderer_test.rb index 3ad7ec1..4c25e8a 100644 --- a/test/compiled_template_test.rb +++ b/test/renderers/json_renderer_test.rb @@ -1,76 +1,80 @@ require 'test_helper' -class TestCompiledTemplate < ActiveSupport::TestCase +class TestJsonRenderer < ActiveSupport::TestCase setup do - @context = Context.new @data = User.new(1, 'foobar', 'male') @data.stub(:respond_to?).with(:each).and_return(false) + + @context = Context.new @context.stub(:instance_variable_get).with(:@data).and_return(@data) @context.stub(:instance_variable_get).with(:@_assigns).and_return({}) + @template = RablFastJson::CompiledTemplate.new - @template.context = @context @template.data = :@data end def render_json_output - RablFastJson::Renderers::JSON.new(@context).render(@template) + RablFastJson::Renderers::JSON.new(@context).render(@template).to_s end test "render object wth empty template" do @template.source = {} - assert_equal({}, @template.render) + assert_equal %q({}), render_json_output end test "render collection with empty template" do @context.stub(:instance_variable_get).with(:@data).and_return([@data]) @template.source = {} - assert_equal([{}], @template.render) + assert_equal %q([{}]), render_json_output end test "render single object attributes" do @template.source = { :id => :id, :name => :name } - assert_equal({ :id => 1, :name => 'foobar'}, @template.render) + assert_equal %q({"id":1,"name":"foobar"}), render_json_output end - test "render object as a child" do + test "render child with object association" do + @data.stub(:address).and_return(mock(:city => 'Paris')) + @template.source = { :address => { :_data => :address, :city => :city } } + assert_equal %q({"address":{"city":"Paris"}}), render_json_output + end + + test "render child with arbitrary data source" do @template.source = { :author => { :_data => :@data, :name => :name } } - assert_equal({ :author => { :name => 'foobar' } }, @template.render) + assert_equal %q({"author":{"name":"foobar"}}), render_json_output end test "render glued attributes from single object" do @template.source = { :_glue0 => { :_data => :@data, :name => :name } } - assert_equal({ :name => 'foobar' }, @template.render) + assert_equal %q({"name":"foobar"}), render_json_output end test "render collection with attributes" do @data = [User.new(1, 'foo', 'male'), User.new(2, 'bar', 'female')] @context.stub(:instance_variable_get).with(:@data).and_return(@data) @template.source = { :uid => :id, :name => :name, :gender => :sex } - assert_equal([ - { :uid => 1, :name => 'foo', :gender => 'male'}, - { :uid => 2, :name => 'bar', :gender => 'female'} - ], @template.render) + assert_equal %q([{"uid":1,"name":"foo","gender":"male"},{"uid":2,"name":"bar","gender":"female"}]), render_json_output end test "render node property" do - proc = lambda { |object| object.sex } - @template.source = { :sex => proc } - assert_equal({ :sex => 'male' }, @template.render) + proc = lambda { |object| object.name } + @template.source = { :name => proc } + assert_equal %q({"name":"foobar"}), render_json_output end test "render node property with true condition" do condition = lambda { |u| true } proc = lambda { |object| object.name } @template.source = { :name => [condition, proc] } - assert_equal({ :name => 'foobar' }, @template.render) + assert_equal %q({"name":"foobar"}), render_json_output end test "render node property with false condition" do condition = lambda { |u| false } proc = lambda { |object| object.name } @template.source = { :name => [condition, proc] } - assert_equal({}, @template.render) + assert_equal %q({}), render_json_output end test "partial should be evaluated at rendering time" do @@ -80,27 +84,27 @@ class TestCompiledTemplate < ActiveSupport::TestCase # Stub Library#get t = RablFastJson::CompiledTemplate.new - t.source, t.context = { :name => :name }, @context + t.source = { :name => :name } RablFastJson::Library.reset_instance RablFastJson::Library.instance.should_receive(:get).with('users/base').and_return(t) @template.data = false @template.source = { :user => ->(s) { partial('users/base', :object => @user) } } - assert_equal({ :user => { :name => 'foobar' } }, @template.render) + assert_equal %q({"user":{"name":"foobar"}}), render_json_output end - test "partial with nil values should raise an error" do + test "partial with no values should raise an error" do @template.data = false @template.source = { :user => ->(s) { partial('users/base') } } - assert_raises(RuntimeError) { @template.render } + assert_raises(RuntimeError) { render_json_output } end test "partial with empty values should not raise an error" do @template.data = false @template.source = { :users => ->(s) { partial('users/base', :object => []) } } - assert_equal({ :users => [] }, @template.render) + assert_equal %q({"users":[]}), render_json_output end end \ No newline at end of file diff --git a/test/test_helper.rb b/test/test_helper.rb index f315ce5..c9f4c64 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -35,16 +35,11 @@ class Context def initialize @_assigns = {} - @virtual_path = '/users' end - def set_assign(key, value) - @_assigns[key] = value - end - - def get_assign(key) - @_assigns[key] + def params + {} end end - + User = Struct.new(:id, :name, :sex) \ No newline at end of file From e1e5ec6f23abde211377ebdb5258762f92fe1164 Mon Sep 17 00:00:00 2001 From: ccocchi Date: Sun, 15 Apr 2012 23:32:26 +0200 Subject: [PATCH 3/8] Use explicit error --- lib/rabl-fast-json/renderers/base.rb | 4 +++- test/renderers/json_renderer_test.rb | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/rabl-fast-json/renderers/base.rb b/lib/rabl-fast-json/renderers/base.rb index aa7235a..bb8bd9e 100644 --- a/lib/rabl-fast-json/renderers/base.rb +++ b/lib/rabl-fast-json/renderers/base.rb @@ -1,5 +1,7 @@ module RablFastJson module Renderers + class PartialError < StandardError; end + class Base def initialize(context) # :nodoc: @@ -79,7 +81,7 @@ module RablFastJson # rendering time. # def partial(template_path, options = {}) - raise "No object was given to partial" unless options[:object] + raise PartialError.new("No object was given to partial") unless options[:object] object = options[:object] return [] if object.respond_to?(:empty?) && object.empty? diff --git a/test/renderers/json_renderer_test.rb b/test/renderers/json_renderer_test.rb index 4c25e8a..b4fd0ec 100644 --- a/test/renderers/json_renderer_test.rb +++ b/test/renderers/json_renderer_test.rb @@ -98,7 +98,7 @@ class TestJsonRenderer < ActiveSupport::TestCase @template.data = false @template.source = { :user => ->(s) { partial('users/base') } } - assert_raises(RuntimeError) { render_json_output } + assert_raises(RablFastJson::Renderers::PartialError) { render_json_output } end test "partial with empty values should not raise an error" do From f9e2f0766b3c2089fedebf9622b25dce79c3eaee Mon Sep 17 00:00:00 2001 From: ccocchi Date: Tue, 17 Apr 2012 16:23:08 +0200 Subject: [PATCH 4/8] Remove useless &block and use yield instead --- lib/rabl-fast-json/compiler.rb | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/lib/rabl-fast-json/compiler.rb b/lib/rabl-fast-json/compiler.rb index a8422d1..8619b26 100644 --- a/lib/rabl-fast-json/compiler.rb +++ b/lib/rabl-fast-json/compiler.rb @@ -18,15 +18,6 @@ module RablFastJson @template end - # - # Same as compile_source but from a block - # - def compile_block(&block) - @template = {} - instance_eval(&block) - @template - end - # # Sets the object to be used as the data for the template # Example: @@ -78,14 +69,14 @@ module RablFastJson # child(:@posts, :root => :posts) { attribute :id } # child(:posts, :partial => 'posts/base') # - def child(name_or_data, options = {}, &block) + def child(name_or_data, options = {}) data, name = extract_data_and_name(name_or_data) name = options[:root] if options[:root] if options[:partial] template = Library.instance.get(options[:partial]) @template[name] = template.merge!(:_data => data) - else - _compile_sub_template(name, data, &block) + elsif block_given? + @template[name] = sub_compile(data) { yield } end end @@ -94,11 +85,11 @@ module RablFastJson # Example: # glue(:@user) { attribute :name } # - def glue(data, &block) + def glue(data) return unless block_given? name = :"_glue#{@glue_count}" @glue_count += 1 - _compile_sub_template(name, data, &block) + @template[name] = sub_compile(data) { yield } end # @@ -156,11 +147,14 @@ module RablFastJson name_or_data end end - - def _compile_sub_template(name, data, &block) #:nodoc: - compiler = Compiler.new - template = compiler.compile_block(&block) - @template[name] = template.merge!(:_data => data) + + def sub_compile(data) + return {} unless block_given? + old_template, @template = @template, {} + yield + @template.merge!(:_data => data) + ensure + @template = old_template end end end \ No newline at end of file From c12e14700fa7655da67415d61b7d36d7113c6065 Mon Sep 17 00:00:00 2001 From: ccocchi Date: Fri, 20 Apr 2012 16:28:34 +0200 Subject: [PATCH 5/8] rabl-fast-json => rabl-rails --- Gemfile | 2 +- Gemfile.lock | 4 ++-- README.md | 2 +- Rakefile | 2 +- lib/rabl-fast-json/renderer.rb | 2 -- lib/{rabl-fast-json.rb => rabl-rails.rb} | 16 ++++++++-------- lib/{rabl-fast-json => rabl-rails}/compiler.rb | 2 +- lib/{rabl-fast-json => rabl-rails}/handler.rb | 4 ++-- lib/{rabl-fast-json => rabl-rails}/library.rb | 4 ++-- lib/{rabl-fast-json => rabl-rails}/railtie.rb | 4 ++-- lib/rabl-rails/renderer.rb | 2 ++ .../renderers/base.rb | 2 +- .../renderers/json.rb | 2 +- lib/{rabl-fast-json => rabl-rails}/template.rb | 2 +- lib/{rabl-fast-json => rabl-rails}/version.rb | 2 +- ...rabl-fast-json_tasks.rake => rabl-rails.rake} | 2 +- rabl-fast-json.gemspec => rabl-rails.gemspec | 10 +++++----- test/cache_templates_test.rb | 6 +++--- test/compiler_test.rb | 14 +++++++------- test/deep_nesting_test.rb | 4 ++-- test/non_restful_response_test.rb | 4 ++-- test/renderers/json_renderer_test.rb | 12 ++++++------ test/test_helper.rb | 2 +- 23 files changed, 53 insertions(+), 53 deletions(-) mode change 100644 => 100755 Rakefile delete mode 100644 lib/rabl-fast-json/renderer.rb rename lib/{rabl-fast-json.rb => rabl-rails.rb} (58%) rename lib/{rabl-fast-json => rabl-rails}/compiler.rb (99%) rename lib/{rabl-fast-json => rabl-rails}/handler.rb (80%) rename lib/{rabl-fast-json => rabl-rails}/library.rb (93%) rename lib/{rabl-fast-json => rabl-rails}/railtie.rb (81%) create mode 100644 lib/rabl-rails/renderer.rb rename lib/{rabl-fast-json => rabl-rails}/renderers/base.rb (99%) rename lib/{rabl-fast-json => rabl-rails}/renderers/json.rb (87%) rename lib/{rabl-fast-json => rabl-rails}/template.rb (89%) rename lib/{rabl-fast-json => rabl-rails}/version.rb (54%) rename lib/tasks/{rabl-fast-json_tasks.rake => rabl-rails.rake} (71%) rename rabl-fast-json.gemspec => rabl-rails.gemspec (73%) diff --git a/Gemfile b/Gemfile index 516b68f..577d5f7 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,6 @@ source "http://rubygems.org" -# Declare your gem's dependencies in rabl-fast-json.gemspec. +# Declare your gem's dependencies in rabl-rails.gemspec. # Bundler will treat runtime dependencies like base dependencies, and # development dependencies will be added by default to the :development group. gemspec diff --git a/Gemfile.lock b/Gemfile.lock index c2c5f09..a07074c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - rabl-fast-json (0.1.0) + rabl-rails (0.1.0) activesupport (~> 3.2.1) GEM @@ -61,7 +61,7 @@ PLATFORMS DEPENDENCIES actionpack (~> 3.2.1) - rabl-fast-json! + rabl-rails! railties (~> 3.2.1) rspec-mocks sqlite3 diff --git a/README.md b/README.md index 24bb8db..7162f32 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ -= RablFastJson += RablRails This project rocks and uses MIT-LICENSE. \ No newline at end of file diff --git a/Rakefile b/Rakefile old mode 100644 new mode 100755 index 065f9e5..e54349b --- a/Rakefile +++ b/Rakefile @@ -14,7 +14,7 @@ # # RDoc::Task.new(:rdoc) do |rdoc| # rdoc.rdoc_dir = 'rdoc' -# rdoc.title = 'RablFastJson' +# rdoc.title = 'RablRails' # rdoc.options << '--line-numbers' # rdoc.rdoc_files.include('README.rdoc') # rdoc.rdoc_files.include('lib/**/*.rb') diff --git a/lib/rabl-fast-json/renderer.rb b/lib/rabl-fast-json/renderer.rb deleted file mode 100644 index 3065875..0000000 --- a/lib/rabl-fast-json/renderer.rb +++ /dev/null @@ -1,2 +0,0 @@ -require 'rabl-fast-json/renderers/base' -require 'rabl-fast-json/renderers/json' \ No newline at end of file diff --git a/lib/rabl-fast-json.rb b/lib/rabl-rails.rb similarity index 58% rename from lib/rabl-fast-json.rb rename to lib/rabl-rails.rb index 0fcadee..77c6c4c 100644 --- a/lib/rabl-fast-json.rb +++ b/lib/rabl-rails.rb @@ -4,19 +4,19 @@ require 'active_support' require 'active_support/json' require 'active_support/core_ext/class/attribute_accessors' -require 'rabl-fast-json/version' -require 'rabl-fast-json/template' -require 'rabl-fast-json/compiler' +require 'rabl-rails/version' +require 'rabl-rails/template' +require 'rabl-rails/compiler' -require 'rabl-fast-json/renderer' +require 'rabl-rails/renderer' -require 'rabl-fast-json/library' -require 'rabl-fast-json/handler' -require 'rabl-fast-json/railtie' +require 'rabl-rails/library' +require 'rabl-rails/handler' +require 'rabl-rails/railtie' -module RablFastJson +module RablRails extend self mattr_accessor :cache_templates diff --git a/lib/rabl-fast-json/compiler.rb b/lib/rabl-rails/compiler.rb similarity index 99% rename from lib/rabl-fast-json/compiler.rb rename to lib/rabl-rails/compiler.rb index 8619b26..43976b9 100644 --- a/lib/rabl-fast-json/compiler.rb +++ b/lib/rabl-rails/compiler.rb @@ -1,4 +1,4 @@ -module RablFastJson +module RablRails # # Class that will compile RABL source code into a hash # representing data structure diff --git a/lib/rabl-fast-json/handler.rb b/lib/rabl-rails/handler.rb similarity index 80% rename from lib/rabl-fast-json/handler.rb rename to lib/rabl-rails/handler.rb index c86635c..8cc028e 100644 --- a/lib/rabl-fast-json/handler.rb +++ b/lib/rabl-rails/handler.rb @@ -1,4 +1,4 @@ -module RablFastJson +module RablRails module Handlers class Rabl cattr_accessor :default_format @@ -6,7 +6,7 @@ module RablFastJson def self.call(template) %{ - RablFastJson::Library.instance. + RablRails::Library.instance. get_rendered_template(#{template.source.inspect}, self) } end diff --git a/lib/rabl-fast-json/library.rb b/lib/rabl-rails/library.rb similarity index 93% rename from lib/rabl-fast-json/library.rb rename to lib/rabl-rails/library.rb index 62b511e..a727e7f 100644 --- a/lib/rabl-fast-json/library.rb +++ b/lib/rabl-rails/library.rb @@ -1,6 +1,6 @@ require 'singleton' -module RablFastJson +module RablRails class Library include Singleton @@ -19,7 +19,7 @@ module RablFastJson end def get_compiled_template(path, source) - if path && RablFastJson.cache_templates? + if path && RablRails.cache_templates? @cached_templates[path] ||= Compiler.new.compile_source(source) @cached_templates[path].dup else diff --git a/lib/rabl-fast-json/railtie.rb b/lib/rabl-rails/railtie.rb similarity index 81% rename from lib/rabl-fast-json/railtie.rb rename to lib/rabl-rails/railtie.rb index 0bc78c3..207bd61 100644 --- a/lib/rabl-fast-json/railtie.rb +++ b/lib/rabl-rails/railtie.rb @@ -1,8 +1,8 @@ -module RablFastJson +module RablRails class Railtie < Rails::Railtie initializer "rabl.initialize" do |app| ActiveSupport.on_load(:action_view) do - ActionView::Template.register_template_handler :rabl, RablFastJson::Handlers::Rabl + ActionView::Template.register_template_handler :rabl, RablRails::Handlers::Rabl end end end diff --git a/lib/rabl-rails/renderer.rb b/lib/rabl-rails/renderer.rb new file mode 100644 index 0000000..0ee0acf --- /dev/null +++ b/lib/rabl-rails/renderer.rb @@ -0,0 +1,2 @@ +require 'rabl-rails/renderers/base' +require 'rabl-rails/renderers/json' \ No newline at end of file diff --git a/lib/rabl-fast-json/renderers/base.rb b/lib/rabl-rails/renderers/base.rb similarity index 99% rename from lib/rabl-fast-json/renderers/base.rb rename to lib/rabl-rails/renderers/base.rb index bb8bd9e..7f7e087 100644 --- a/lib/rabl-fast-json/renderers/base.rb +++ b/lib/rabl-rails/renderers/base.rb @@ -1,4 +1,4 @@ -module RablFastJson +module RablRails module Renderers class PartialError < StandardError; end diff --git a/lib/rabl-fast-json/renderers/json.rb b/lib/rabl-rails/renderers/json.rb similarity index 87% rename from lib/rabl-fast-json/renderers/json.rb rename to lib/rabl-rails/renderers/json.rb index ed36cef..f903476 100644 --- a/lib/rabl-fast-json/renderers/json.rb +++ b/lib/rabl-rails/renderers/json.rb @@ -1,4 +1,4 @@ -module RablFastJson +module RablRails module Renderers class JSON < Base def format_output(hash) diff --git a/lib/rabl-fast-json/template.rb b/lib/rabl-rails/template.rb similarity index 89% rename from lib/rabl-fast-json/template.rb rename to lib/rabl-rails/template.rb index 8445de1..403b701 100644 --- a/lib/rabl-fast-json/template.rb +++ b/lib/rabl-rails/template.rb @@ -1,4 +1,4 @@ -module RablFastJson +module RablRails class CompiledTemplate attr_accessor :source, :data, :root_name diff --git a/lib/rabl-fast-json/version.rb b/lib/rabl-rails/version.rb similarity index 54% rename from lib/rabl-fast-json/version.rb rename to lib/rabl-rails/version.rb index 37c0dc8..f73f1e5 100644 --- a/lib/rabl-fast-json/version.rb +++ b/lib/rabl-rails/version.rb @@ -1,3 +1,3 @@ -module RablFastJson +module RablRails VERSION = '0.1.0' end diff --git a/lib/tasks/rabl-fast-json_tasks.rake b/lib/tasks/rabl-rails.rake similarity index 71% rename from lib/tasks/rabl-fast-json_tasks.rake rename to lib/tasks/rabl-rails.rake index 8762ae3..0a0b7a3 100644 --- a/lib/tasks/rabl-fast-json_tasks.rake +++ b/lib/tasks/rabl-rails.rake @@ -1,4 +1,4 @@ # desc "Explaining what the task does" -# task :rabl-fast-json do +# task :rabl-rails do # # Task goes here # end diff --git a/rabl-fast-json.gemspec b/rabl-rails.gemspec similarity index 73% rename from rabl-fast-json.gemspec rename to rabl-rails.gemspec index de27583..0748115 100644 --- a/rabl-fast-json.gemspec +++ b/rabl-rails.gemspec @@ -1,17 +1,17 @@ $:.push File.expand_path("../lib", __FILE__) # Maintain your gem's version: -require "rabl-fast-json/version" +require "rabl-rails/version" # Describe your gem and declare its dependencies: Gem::Specification.new do |s| - s.name = "rabl-fast-json" - s.version = RablFastJson::VERSION + s.name = "rabl-rails" + s.version = RablRails::VERSION s.authors = ["TODO: Your name"] s.email = ["TODO: Your email"] s.homepage = "TODO" - s.summary = "TODO: Summary of RablFastJson." - s.description = "TODO: Description of RablFastJson." + s.summary = "TODO: Summary of RablRails." + s.description = "TODO: Description of RablRails." s.files = Dir["{app,config,db,lib}/**/*"] + ["MIT-LICENSE", "Rakefile", "README.md"] s.test_files = Dir["test/**/*"] diff --git a/test/cache_templates_test.rb b/test/cache_templates_test.rb index 3d792df..7a7797c 100644 --- a/test/cache_templates_test.rb +++ b/test/cache_templates_test.rb @@ -3,9 +3,9 @@ require 'test_helper' class CacheTemplatesTest < ActiveSupport::TestCase setup do - RablFastJson::Library.reset_instance - @library = RablFastJson::Library.instance - RablFastJson.cache_templates = true + RablRails::Library.reset_instance + @library = RablRails::Library.instance + RablRails.cache_templates = true end test "cache templates if perform_caching is active and cache_templates is enabled" do diff --git a/test/compiler_test.rb b/test/compiler_test.rb index c9b550f..944fc56 100644 --- a/test/compiler_test.rb +++ b/test/compiler_test.rb @@ -4,11 +4,11 @@ class CompilerTest < ActiveSupport::TestCase setup do @user = User.new - @compiler = RablFastJson::Compiler.new + @compiler = RablRails::Compiler.new end test "compiler return a compiled template" do - assert_instance_of RablFastJson::CompiledTemplate, @compiler.compile_source("") + assert_instance_of RablRails::CompiledTemplate, @compiler.compile_source("") end test "object set data for the template" do @@ -91,10 +91,10 @@ class CompilerTest < ActiveSupport::TestCase end test "child with succint partial notation" do - mock_template = RablFastJson::CompiledTemplate.new + mock_template = RablRails::CompiledTemplate.new mock_template.source = { :id => :id } - RablFastJson::Library.reset_instance - RablFastJson::Library.instance.stub(:get).with('users/base').and_return(mock_template) + RablRails::Library.reset_instance + RablRails::Library.instance.stub(:get).with('users/base').and_return(mock_template) t = @compiler.compile_source(%{child(:user, :partial => 'users/base') }) assert_equal( {:user => { :_data => :user, :id => :id } }, t.source) @@ -119,8 +119,8 @@ class CompilerTest < ActiveSupport::TestCase test "extends use other template source as itself" do template = mock('template', :source => { :id => :id }) - RablFastJson::Library.reset_instance - RablFastJson::Library.instance.stub(:get).with('users/base').and_return(template) + RablRails::Library.reset_instance + RablRails::Library.instance.stub(:get).with('users/base').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 index 2dca67a..7fc8bed 100644 --- a/test/deep_nesting_test.rb +++ b/test/deep_nesting_test.rb @@ -15,7 +15,7 @@ class DeepNestingTest < ActiveSupport::TestCase end setup do - RablFastJson::Library.reset_instance + RablRails::Library.reset_instance @post = Post.new(42, 'I rock !') @user = User.new(1, 'foobar', 'male') @user.stub(:posts).and_return([@post]) @@ -50,7 +50,7 @@ class DeepNestingTest < ActiveSupport::TestCase { :content => 'second' } ] }] - }), RablFastJson::Library.instance.get_rendered_template(source, @context)) + }), RablRails::Library.instance.get_rendered_template(source, @context)) end end diff --git a/test/non_restful_response_test.rb b/test/non_restful_response_test.rb index d0a7e83..44c3109 100644 --- a/test/non_restful_response_test.rb +++ b/test/non_restful_response_test.rb @@ -2,7 +2,7 @@ require 'test_helper' class NonRestfulResponseTest < ActiveSupport::TestCase setup do - RablFastJson::Library.reset_instance + RablRails::Library.reset_instance @user = User.new(1, 'foo', 'male') @user.stub_chain(:posts, :count).and_return(10) @@ -30,6 +30,6 @@ class NonRestfulResponseTest < ActiveSupport::TestCase :id => 1, :name => 'foo' } - }), RablFastJson::Library.instance.get_rendered_template(source, @context)) + }), RablRails::Library.instance.get_rendered_template(source, @context)) end end \ No newline at end of file diff --git a/test/renderers/json_renderer_test.rb b/test/renderers/json_renderer_test.rb index b4fd0ec..a871a81 100644 --- a/test/renderers/json_renderer_test.rb +++ b/test/renderers/json_renderer_test.rb @@ -10,12 +10,12 @@ class TestJsonRenderer < ActiveSupport::TestCase @context.stub(:instance_variable_get).with(:@data).and_return(@data) @context.stub(:instance_variable_get).with(:@_assigns).and_return({}) - @template = RablFastJson::CompiledTemplate.new + @template = RablRails::CompiledTemplate.new @template.data = :@data end def render_json_output - RablFastJson::Renderers::JSON.new(@context).render(@template).to_s + RablRails::Renderers::JSON.new(@context).render(@template).to_s end test "render object wth empty template" do @@ -83,10 +83,10 @@ class TestJsonRenderer < ActiveSupport::TestCase @data.stub(:respond_to?).with(:empty?).and_return(false) # Stub Library#get - t = RablFastJson::CompiledTemplate.new + t = RablRails::CompiledTemplate.new t.source = { :name => :name } - RablFastJson::Library.reset_instance - RablFastJson::Library.instance.should_receive(:get).with('users/base').and_return(t) + RablRails::Library.reset_instance + RablRails::Library.instance.should_receive(:get).with('users/base').and_return(t) @template.data = false @template.source = { :user => ->(s) { partial('users/base', :object => @user) } } @@ -98,7 +98,7 @@ class TestJsonRenderer < ActiveSupport::TestCase @template.data = false @template.source = { :user => ->(s) { partial('users/base') } } - assert_raises(RablFastJson::Renderers::PartialError) { render_json_output } + assert_raises(RablRails::Renderers::PartialError) { render_json_output } end test "partial with empty values should not raise an error" do diff --git a/test/test_helper.rb b/test/test_helper.rb index c9f4c64..1a054dc 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -21,7 +21,7 @@ class < Date: Fri, 20 Apr 2012 18:02:33 +0200 Subject: [PATCH 6/8] README first part --- README.md | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 83 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7162f32..594c214 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,84 @@ -= RablRails +# RABL for Rails # -This project rocks and uses MIT-LICENSE. \ No newline at end of file +RABL (Ruby API Builder Language) is a ruby templating system for rendering resources in different format (JSON, XML, BSON, ...). You can find documentation [here](http://github.com/nesquena/rabl). + +RABL-rails only target Rails 3+ application because Rails 2 applications are becoming less and less present and will be obsolete with Rails 4. So let's look to the future ! + +So now you ask why used `rabl-rails` if `rabl` already exists and supports Rails. Rabl-rails is *faster* and uses * less memory* than standard rabl gem while letting you access same features. Of course, there are some slight changes to do on your templates to get this gem to work but it should't take you more than 5 minutes. + +## Installation + +Install as a gem : + +``` +gem install rabl-rails +``` + +or add directly to your `Gemfile` + +``` +gem 'rabl' +``` + +And that's it ! + +## Overview + +Once you have installed RABL, you can directly used RABL templates to render your resources without changing anything to you controller. As example, +assuming you have a `Post` model filled with blog posts, and a `PostController` that look like this : + +```ruby +class PostController < ApplicationController + respond_to :html, :json, :xml + + def index + @posts = Post.order('created_at DESC') + respond_with(@posts) + end +end +``` + +You can create the following RABL-rails template to express the API output of `@posts` + +```ruby +# app/views/post/index.rabl +collection :@posts +attributes :id, :title, :subject +child(:user) { attributes :full_name } +node(:read) { |post| post.read_by?(@user) } +``` + +This would output the following JSON when visiting `http://localhost:3000/posts.json` + +```js +[{ + "id" : 5, title: "...", subject: "...", + "user" : { full_name : "..." }, + "read" : true +}] +``` + +That's a basic overview but there is a lot more to see such as partials, inheritance or fragment caching. + +## How it works + +As opposed to standard RABL gem, this gem separate compiling (a.k.a transforming a RABL-rails template into a Ruby hash) and the actual rendering of the object or collection. This allow to only compile the template once and only Ruby hashes. + +The fact of compiling the template outside of any rendering context prevent us to use any instances variables (with the exception of node) in the template because they are rendering objects. So instead, you'll have to use symbols of these variables. For example, to render the collection `@posts` inside your `PostController`, you need to use `:@posts` inside of the template. + +The only places where you can actually used instance variables are into Proc (or lambda) or into custom node (because they are treated as Proc). + +```ruby + # We reference the @posts varibles that will be used at rendering time + collection :@posts + + # Here you can use directly the instance variable because it + # will be evaluated when rendering the object + node(:read) { |post| post.read_by?(@user) } +``` + +The same rule applies for view helpers such as `current_user` + +After the template is compiled into a hash, Rabl-rails will use a renderer to do the actual output. Actually, only JSON and XML formats are supported. + +## Usage \ No newline at end of file From 961c6ac5335dc19b4feafd2ce0f9921ca597048f Mon Sep 17 00:00:00 2001 From: ccocchi Date: Wed, 2 May 2012 17:53:13 +0200 Subject: [PATCH 7/8] Fix typo with instance variable --- lib/rabl-rails/renderers/base.rb | 2 +- test/renderers/json_renderer_test.rb | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/rabl-rails/renderers/base.rb b/lib/rabl-rails/renderers/base.rb index 7f7e087..7027620 100644 --- a/lib/rabl-rails/renderers/base.rb +++ b/lib/rabl-rails/renderers/base.rb @@ -96,7 +96,7 @@ module RablRails # missing method. # def method_missing(name, *args, &block) - @context.respond_to?(name) ? @context.send(name, *args, &block) : super + @_context.respond_to?(name) ? @_context.send(name, *args, &block) : super end # diff --git a/test/renderers/json_renderer_test.rb b/test/renderers/json_renderer_test.rb index a871a81..84a508f 100644 --- a/test/renderers/json_renderer_test.rb +++ b/test/renderers/json_renderer_test.rb @@ -76,6 +76,14 @@ class TestJsonRenderer < ActiveSupport::TestCase @template.source = { :name => [condition, proc] } assert_equal %q({}), render_json_output end + + test "node with context method call" do + @context.stub(:respond_to?).with(:context_method).and_return(true) + @context.stub(:context_method).and_return('marty') + proc = lambda { |object| context_method } + @template.source = { :name => proc } + assert_equal %q({"name":"marty"}), render_json_output + end test "partial should be evaluated at rendering time" do # Set assigns From 4747cd95c1d465fdca7dc6c583f708373f6ee327 Mon Sep 17 00:00:00 2001 From: ccocchi Date: Fri, 4 May 2012 15:21:06 +0200 Subject: [PATCH 8/8] Better error explanation --- lib/rabl-rails/renderers/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rabl-rails/renderers/base.rb b/lib/rabl-rails/renderers/base.rb index 7027620..6eebe49 100644 --- a/lib/rabl-rails/renderers/base.rb +++ b/lib/rabl-rails/renderers/base.rb @@ -81,7 +81,7 @@ module RablRails # rendering time. # def partial(template_path, options = {}) - raise PartialError.new("No object was given to partial") unless options[:object] + raise PartialError.new("No object was given to partial #{template_path}") unless options[:object] object = options[:object] return [] if object.respond_to?(:empty?) && object.empty?