From 49f7fe24ae0f28e034c64841fe75d9c389df24d6 Mon Sep 17 00:00:00 2001 From: ccocchi Date: Tue, 3 Jul 2012 17:45:23 +0200 Subject: [PATCH 01/27] Add custom Rabl reponder --- lib/rabl-rails.rb | 16 +++++++++++----- lib/rabl-rails/responder.rb | 22 ++++++++++++++++++++++ 2 files changed, 33 insertions(+), 5 deletions(-) create mode 100644 lib/rabl-rails/responder.rb diff --git a/lib/rabl-rails.rb b/lib/rabl-rails.rb index 77c6c4c..89b56aa 100644 --- a/lib/rabl-rails.rb +++ b/lib/rabl-rails.rb @@ -14,19 +14,25 @@ require 'rabl-rails/library' require 'rabl-rails/handler' require 'rabl-rails/railtie' - - module RablRails - extend self + autoload :Responder, 'rabl-rails/responder' mattr_accessor :cache_templates @@cache_templates = true - def configure + mattr_accessor :use_custom_responder + @@use_custom_responder = false + + mattr_accessor :responder_default_template + @@responder_default_template = 'show' + + def self.configure yield self + + ActionController::Base.responder = Responder if self.use_custom_responder end - def cache_templates? + def self.cache_templates? ActionController::Base.perform_caching && @@cache_templates end end diff --git a/lib/rabl-rails/responder.rb b/lib/rabl-rails/responder.rb new file mode 100644 index 0000000..fb094bc --- /dev/null +++ b/lib/rabl-rails/responder.rb @@ -0,0 +1,22 @@ +module RablRails + # + # Override default responder's api behavior to not + # user to_format methods on a resource as a default + # representation but instead use a rabl template + # + class Responder < ActionController::Responder + protected + + def api_behavior(error) + rabl_options = options.merge(template: RablRails.responder_default_template) + + if get? + controller.default_render rabl_options + elsif post? + controller.default_render rabl_options.merge!(status: :created, location: api_location) + else + head :no_content + end + end + end +end \ No newline at end of file From 9364803048fd38c4cf865fc633c80994c7b47660 Mon Sep 17 00:00:00 2001 From: ccocchi Date: Sun, 15 Jul 2012 18:23:21 +0200 Subject: [PATCH 02/27] Add XML renderer --- lib/rabl-rails/renderers/xml.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 lib/rabl-rails/renderers/xml.rb diff --git a/lib/rabl-rails/renderers/xml.rb b/lib/rabl-rails/renderers/xml.rb new file mode 100644 index 0000000..289dae5 --- /dev/null +++ b/lib/rabl-rails/renderers/xml.rb @@ -0,0 +1,13 @@ +module RablRails + module Renderers + class XML < Base + DEFAULT_OPTIONS = { dasherize: true, skip_types: false } + + def format_output(hash) + # hash = hash[options[:root_name]] if options[:root_name] + xml_options = { root: options[:root_name] }.merge!(DEFAULT_OPTIONS) + hash.to_xml(xml_options) + end + end + end +end \ No newline at end of file From 5c1490c24600f302c8c7d9e3c2ac76711cdc9d75 Mon Sep 17 00:00:00 2001 From: ccocchi Date: Tue, 11 Sep 2012 11:49:07 +0200 Subject: [PATCH 03/27] Update CHANGELOG --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e43728..c443a3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # CHANGELOG +## 0.1.3 (unreleased) + * Add custom Responder + ## 0.1.2 * Add RablRails#render method (see README or source code) * Fix fail when JSON engine is not found. Now fallback to MultiJson.default_adapter From dc66b43638107b586b9dfc694bc094ebece051d0 Mon Sep 17 00:00:00 2001 From: ccocchi Date: Wed, 12 Sep 2012 01:33:04 +0200 Subject: [PATCH 04/27] Replace Struct by Class to avoid stubbing respond_to? Add some tests with local methods --- test/deep_nesting_test.rb | 1 - test/renderers/json_renderer_test.rb | 25 ++++++++++++++++++------- test/test_helper.rb | 13 ++++++++++--- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/test/deep_nesting_test.rb b/test/deep_nesting_test.rb index e689233..4292877 100644 --- a/test/deep_nesting_test.rb +++ b/test/deep_nesting_test.rb @@ -19,7 +19,6 @@ class DeepNestingTest < ActiveSupport::TestCase @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) @context = Context.new @context.assigns['user'] = @user diff --git a/test/renderers/json_renderer_test.rb b/test/renderers/json_renderer_test.rb index c04052d..b2ab458 100644 --- a/test/renderers/json_renderer_test.rb +++ b/test/renderers/json_renderer_test.rb @@ -4,7 +4,6 @@ class TestJsonRenderer < ActiveSupport::TestCase setup do @data = User.new(1, 'foobar', 'male') - @data.stub(:respond_to?).with(:each).and_return(false) @context = Context.new @context.assigns['data'] = @data @@ -28,6 +27,13 @@ class TestJsonRenderer < ActiveSupport::TestCase assert_equal %q([{}]), render_json_output end + test "render object with local methods (used by decent_exposure)" do + @context.stub(:user).and_return(@data) + @template.data = :user + @template.source = { :id => :id } + assert_equal %q({"id":1}), render_json_output + end + test "render single object attributes" do @template.source = { :id => :id, :name => :name } assert_equal %q({"id":1,"name":"foobar"}), render_json_output @@ -44,6 +50,12 @@ class TestJsonRenderer < ActiveSupport::TestCase assert_equal %q({"author":{"name":"foobar"}}), render_json_output end + test "render child with local methods (used by decent_exposure)" do + @context.stub(:user).and_return(@data) + @template.source = { :author => { :_data => :user, :name => :name } } + 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 %q({"name":"foobar"}), render_json_output @@ -75,7 +87,7 @@ 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') @@ -86,7 +98,6 @@ class TestJsonRenderer < ActiveSupport::TestCase test "partial should be evaluated at rendering time" do # Set assigns - @data.stub(:respond_to?).with(:empty?).and_return(false) @context.assigns['user'] = @data # Stub Library#get @@ -114,17 +125,17 @@ class TestJsonRenderer < ActiveSupport::TestCase assert_equal %q({"users":[]}), render_json_output end - + test "render object with root node" do @template.root_name = :author @template.source = { :id => :id, :name => :name } - assert_equal %q({"author":{"id":1,"name":"foobar"}}), render_json_output + assert_equal %q({"author":{"id":1,"name":"foobar"}}), render_json_output end - + test "render object with root options set to false" do RablRails.include_json_root = false @template.root_name = :author @template.source = { :id => :id, :name => :name } - assert_equal %q({"id":1,"name":"foobar"}), render_json_output + assert_equal %q({"id":1,"name":"foobar"}), render_json_output end end \ No newline at end of file diff --git a/test/test_helper.rb b/test/test_helper.rb index 26b144e..7468f73 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -32,12 +32,12 @@ end class Context attr_writer :virtual_path - + def initialize @_assigns = {} @virtual_path = nil end - + def assigns @_assigns end @@ -47,4 +47,11 @@ class Context end end -User = Struct.new(:id, :name, :sex) \ No newline at end of file +class User + attr_accessor :id, :name, :sex + def initialize(id=nil, name=nil, sex=nil) + @id = id + @name = name + @sex = sex + end +end \ No newline at end of file From 8376c0d974cb21bd608bebc0a9dde52aae7aa5c2 Mon Sep 17 00:00:00 2001 From: ccocchi Date: Wed, 12 Sep 2012 01:34:54 +0200 Subject: [PATCH 05/27] RablRails now accepts local methods for child nodes and root objects. Local methods are used by decent_exposure or focused_controller --- lib/rabl-rails/compiler.rb | 2 +- lib/rabl-rails/renderers/base.rb | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/rabl-rails/compiler.rb b/lib/rabl-rails/compiler.rb index 7e78ea2..eb2f9a5 100644 --- a/lib/rabl-rails/compiler.rb +++ b/lib/rabl-rails/compiler.rb @@ -133,7 +133,7 @@ module RablRails name_or_data end end - + def sub_compile(data) return {} unless block_given? old_template, @template = @template, {} diff --git a/lib/rabl-rails/renderers/base.rb b/lib/rabl-rails/renderers/base.rb index cb9bcac..2f09f30 100644 --- a/lib/rabl-rails/renderers/base.rb +++ b/lib/rabl-rails/renderers/base.rb @@ -18,7 +18,9 @@ module RablRails # method defined by the renderer. # def render(template) - collection_or_resource = instance_variable_get(template.data) if template.data + collection_or_resource = if template.data + template.data.to_s.start_with?('@') ? instance_variable_get(template.data) : @_context.send(template.data) + end collection_or_resource = @_context.target_object unless collection_or_resource || template.data == false || !@_context.respond_to?(:target_object) output_hash = collection_or_resource.respond_to?(:each) ? render_collection(collection_or_resource, template.source) : render_resource(collection_or_resource, template.source) @@ -55,7 +57,13 @@ module RablRails when Hash current_value = value.dup data_symbol = current_value.delete(:_data) - object = data_symbol.nil? ? data : data_symbol.to_s.start_with?('@') ? instance_variable_get(data_symbol) : data.send(data_symbol) + object = if data_symbol == nil + data + else + data_symbol.to_s.start_with?('@') ? instance_variable_get(data_symbol) + : data.respond_to?(data_symbol) ? data.send(data_symbol) + : send(data_symbol) + end if key.to_s.start_with?('_') # glue current_value.each_pair { |k, v| From ed3592566dd8b9bebf89b721f184f98511626155 Mon Sep 17 00:00:00 2001 From: Christopher Cocchi-Perrier Date: Wed, 12 Sep 2012 02:47:20 +0300 Subject: [PATCH 06/27] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c443a3c..39cd874 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # CHANGELOG ## 0.1.3 (unreleased) + * Render correcly when variables are not passed via the assigns ivar but as helper methods + (decent_exposure, focused_controller) * Add custom Responder ## 0.1.2 From 7e6da1a619a823ab5e372ab47bcf0c2e75b759f8 Mon Sep 17 00:00:00 2001 From: ccocchi Date: Wed, 12 Sep 2012 10:19:29 +0200 Subject: [PATCH 07/27] Bump to 0.1.3 --- CHANGELOG.md | 2 +- lib/rabl-rails/version.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 39cd874..e839416 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # CHANGELOG -## 0.1.3 (unreleased) +## 0.1.3 * Render correcly when variables are not passed via the assigns ivar but as helper methods (decent_exposure, focused_controller) * Add custom Responder diff --git a/lib/rabl-rails/version.rb b/lib/rabl-rails/version.rb index 50534f0..e19060c 100644 --- a/lib/rabl-rails/version.rb +++ b/lib/rabl-rails/version.rb @@ -1,3 +1,3 @@ module RablRails - VERSION = '0.1.2' + VERSION = '0.1.3' end From 28f3dbedd0ee03fb7e1495fbfbe41b4b8cf7413f Mon Sep 17 00:00:00 2001 From: Alex Smelik Date: Wed, 12 Sep 2012 10:45:45 -0700 Subject: [PATCH 08/27] Let MultiJson select default JSON adapter (engine), support for latest MultiJson syntax --- lib/rabl-rails.rb | 5 ++--- lib/rabl-rails/renderers/json.rb | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/rabl-rails.rb b/lib/rabl-rails.rb index d6b645b..830c797 100644 --- a/lib/rabl-rails.rb +++ b/lib/rabl-rails.rb @@ -27,7 +27,6 @@ module RablRails @@include_json_root = true mattr_reader :json_engine - @@json_engine = :yajl mattr_accessor :use_custom_responder @@use_custom_responder = false @@ -42,7 +41,7 @@ module RablRails end def self.json_engine=(name) - MultiJson.engine = name + MultiJson.respond_to?(:use) ? MultiJson.use(name) : MultiJson.engine = name @@json_engine = name rescue LoadError Rails.logger.warn %Q(WARNING: rabl-rails could not load "#{self.json_engine}" as JSON engine, fallback to default) @@ -53,6 +52,6 @@ module RablRails end def self.load_default_engines! - self.json_engine = :yajl + self.json_engine = MultiJson.respond_to?(default_adapter) ? MultiJson.default_adapter : MultiJson.default_engine end end diff --git a/lib/rabl-rails/renderers/json.rb b/lib/rabl-rails/renderers/json.rb index 632aaec..5ab487a 100644 --- a/lib/rabl-rails/renderers/json.rb +++ b/lib/rabl-rails/renderers/json.rb @@ -3,7 +3,7 @@ module RablRails class JSON < Base def format_output(hash) hash = { _options[:root_name] => hash } if _options[:root_name] && RablRails.include_json_root - MultiJson.encode(hash) + MultiJson.respond_to?(:dump) ? MultiJson.dump(hash) : MultiJson.encode(hash) end end end From 4b61edad644e66d9bfa0b83f0e40e3c0c3c6c717 Mon Sep 17 00:00:00 2001 From: ccocchi Date: Thu, 13 Sep 2012 15:57:05 +0200 Subject: [PATCH 09/27] Locals are now passed to the renderer object. Allow to skip object or collection definition when using `respond_to` block --- lib/rabl-rails/handler.rb | 2 +- lib/rabl-rails/library.rb | 4 ++-- lib/rabl-rails/renderer.rb | 14 ++++++-------- lib/rabl-rails/renderers/base.rb | 11 ++++++++--- test/render_test.rb | 9 ++++----- test/renderers/json_renderer_test.rb | 1 + 6 files changed, 22 insertions(+), 19 deletions(-) diff --git a/lib/rabl-rails/handler.rb b/lib/rabl-rails/handler.rb index 8cc028e..6b1d9ab 100644 --- a/lib/rabl-rails/handler.rb +++ b/lib/rabl-rails/handler.rb @@ -7,7 +7,7 @@ module RablRails def self.call(template) %{ RablRails::Library.instance. - get_rendered_template(#{template.source.inspect}, self) + get_rendered_template(#{template.source.inspect}, self, local_assigns) } end end diff --git a/lib/rabl-rails/library.rb b/lib/rabl-rails/library.rb index 0324369..e53bc4a 100644 --- a/lib/rabl-rails/library.rb +++ b/lib/rabl-rails/library.rb @@ -8,14 +8,14 @@ module RablRails @cached_templates = {} end - def get_rendered_template(source, context) + def get_rendered_template(source, context, locals = nil) path = context.instance_variable_get(:@virtual_path) @lookup_context = context.lookup_context compiled_template = compile_template_from_source(source, path) format = context.params[:format] || 'json' - Renderers.const_get(format.upcase!).new(context).render(compiled_template) + Renderers.const_get(format.upcase!).new(context, locals).render(compiled_template) end def compile_template_from_source(source, path = nil) diff --git a/lib/rabl-rails/renderer.rb b/lib/rabl-rails/renderer.rb index 168b409..b74d3d0 100644 --- a/lib/rabl-rails/renderer.rb +++ b/lib/rabl-rails/renderer.rb @@ -4,7 +4,7 @@ require 'rabl-rails/renderers/json' module RablRails module Renderer class TemplateNotFound < StandardError; end - + mattr_reader :view_path @@view_path = 'app/views' @@ -33,7 +33,6 @@ module RablRails # class Context attr_reader :format - attr_accessor :target_object def initialize(path, options) @virtual_path = path @@ -56,17 +55,17 @@ module RablRails @lookup_context ||= LookupContext.new(@options[:view_path], format) end end - + # # Renders object with the given rabl template. - # + # # Object can also be passed as an option : # { locals: { object: obj_to_render } } # # Default render format is JSON, but can be changed via # an option: { format: 'xml' } # - # If template includes uses of instance variables (usually + # If template includes uses of instance variables (usually # defined in the controller), you can passed them as locals # options. # For example, if you have this template: @@ -80,12 +79,11 @@ module RablRails object = options[:locals].delete(:object) if !object && options[:locals] c = Context.new(template, options) - c.target_object = object - t = c.lookup_context.find_template(template, [], false) + raise TemplateNotFound unless t - Library.instance.get_rendered_template(t.source, c) + Library.instance.get_rendered_template(t.source, c, resource: object) end end end \ No newline at end of file diff --git a/lib/rabl-rails/renderers/base.rb b/lib/rabl-rails/renderers/base.rb index 2f09f30..c336de3 100644 --- a/lib/rabl-rails/renderers/base.rb +++ b/lib/rabl-rails/renderers/base.rb @@ -5,9 +5,10 @@ module RablRails class Base attr_accessor :_options - def initialize(context) # :nodoc: + def initialize(context, locals = nil) # :nodoc: @_context = context @_options = {} + @_resource = locals[:resource] if locals setup_render_context end @@ -19,9 +20,13 @@ module RablRails # def render(template) collection_or_resource = if template.data - template.data.to_s.start_with?('@') ? instance_variable_get(template.data) : @_context.send(template.data) + if @_context.respond_to?(template.data) + @_context.send(template.data) + else + instance_variable_get(template.data) + end end - collection_or_resource = @_context.target_object unless collection_or_resource || template.data == false || !@_context.respond_to?(:target_object) + collection_or_resource ||= @_resource output_hash = collection_or_resource.respond_to?(:each) ? render_collection(collection_or_resource, template.source) : render_resource(collection_or_resource, template.source) _options[:root_name] = template.root_name diff --git a/test/render_test.rb b/test/render_test.rb index 8108f7c..9b4b26d 100644 --- a/test/render_test.rb +++ b/test/render_test.rb @@ -6,7 +6,6 @@ class RenderTest < ActiveSupport::TestCase setup do @user = User.new(1, 'Marty') - @user.stub(:respond_to?).with(:each).and_return(false) @tmp_path = Pathname.new(Dir.mktmpdir) end @@ -29,16 +28,16 @@ class RenderTest < ActiveSupport::TestCase end assert_equal %q({"user":{"id":1,"name":"Marty"}}), RablRails.render(@user, 'show', view_path: @tmp_path) end - + test "raise error if template is not found" do assert_raises(RablRails::Renderer::TemplateNotFound) { RablRails.render(@user, 'not_found') } end - + test "instance variables can be passed via options[:locals]" do File.open(@tmp_path + "instance.json.rabl", "w") do |f| f.puts %q{ object false - node(:username) { |_| @user.name } + node(:username) { |_| @user.name } } end assert_equal %q({"username":"Marty"}), RablRails.render(nil, 'instance', view_path: @tmp_path, locals: { user: @user }) @@ -51,7 +50,7 @@ class RenderTest < ActiveSupport::TestCase extends 'base' } end - + File.open(@tmp_path + "base.json.rabl", "w") do |f| f.puts %q{ attribute :name, as: :extended_name diff --git a/test/renderers/json_renderer_test.rb b/test/renderers/json_renderer_test.rb index b2ab458..93d3ab0 100644 --- a/test/renderers/json_renderer_test.rb +++ b/test/renderers/json_renderer_test.rb @@ -89,6 +89,7 @@ class TestJsonRenderer < ActiveSupport::TestCase end test "node with context method call" do + @context.stub(:respond_to?).with(:@data).and_return(false) @context.stub(:respond_to?).with(:context_method).and_return(true) @context.stub(:context_method).and_return('marty') proc = lambda { |object| context_method } From 395a3d743969cddde7b31ff905793f31570174d6 Mon Sep 17 00:00:00 2001 From: ccocchi Date: Thu, 13 Sep 2012 16:04:43 +0200 Subject: [PATCH 10/27] Use locals in responder. Responder now works out of the box with Devise --- lib/rabl-rails/responder.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/rabl-rails/responder.rb b/lib/rabl-rails/responder.rb index fb094bc..0a03c3c 100644 --- a/lib/rabl-rails/responder.rb +++ b/lib/rabl-rails/responder.rb @@ -6,14 +6,14 @@ module RablRails # class Responder < ActionController::Responder protected - + def api_behavior(error) rabl_options = options.merge(template: RablRails.responder_default_template) - + if get? controller.default_render rabl_options elsif post? - controller.default_render rabl_options.merge!(status: :created, location: api_location) + controller.default_render rabl_options.merge!(status: :created, location: api_location, locals: { resource: resource }) else head :no_content end From e9f0c69f155f40a4a4127e876e27305dfc6e3f53 Mon Sep 17 00:00:00 2001 From: ccocchi Date: Thu, 13 Sep 2012 16:05:29 +0200 Subject: [PATCH 11/27] Default template to render can be defined per controller --- lib/rabl-rails/responder.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/rabl-rails/responder.rb b/lib/rabl-rails/responder.rb index 0a03c3c..997bf80 100644 --- a/lib/rabl-rails/responder.rb +++ b/lib/rabl-rails/responder.rb @@ -5,10 +5,15 @@ module RablRails # representation but instead use a rabl template # class Responder < ActionController::Responder + def initialize(controller, resources, options = {}) + super + @api_template = @controller.respond_to?(:responder_default_template, true) ? controller.send(:responder_default_template) : nil + end + protected def api_behavior(error) - rabl_options = options.merge(template: RablRails.responder_default_template) + rabl_options = options.merge(template: @api_template || RablRails.responder_default_template) if get? controller.default_render rabl_options From 04defc9a1906b5cb172489b1dc2a33e584fd0bc8 Mon Sep 17 00:00:00 2001 From: ccocchi Date: Thu, 13 Sep 2012 16:08:42 +0200 Subject: [PATCH 12/27] Add CHANGELOG entries --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e839416..7668f0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # CHANGELOG +## 0.2.0 (unrelased) + * Default template to render with responder can be set per controller + * Reponder works out of the box with devise + * object or collection can be skipped if use with `respond_to` blocks + ## 0.1.3 * Render correcly when variables are not passed via the assigns ivar but as helper methods (decent_exposure, focused_controller) From 360d70b8381efc0f4565769f388588f8f5219fa8 Mon Sep 17 00:00:00 2001 From: Alex Smelik Date: Thu, 13 Sep 2012 08:05:52 -0700 Subject: [PATCH 13/27] Revert to old MultiJson syntax. --- CHANGELOG.md | 7 +++++-- lib/rabl-rails.rb | 13 +++++++------ lib/rabl-rails/renderers/json.rb | 2 +- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e839416..2b3873e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,10 @@ # CHANGELOG +## 0.1.4 (Unreleased) + * Use MultiJson's preferred JSON engine as default + ## 0.1.3 - * Render correcly when variables are not passed via the assigns ivar but as helper methods + * Render correctly when variables are not passed via the assigns ivar but as helper methods (decent_exposure, focused_controller) * Add custom Responder @@ -13,7 +16,7 @@ ## 0.1.1 * Add CHANGELOG - * Remove unnused test in loop + * Remove unused test in loop * Speed up rendering by not double copying variable from context * Rename private variable to avoid name conflict * Remove sqlite3 development dependency \ No newline at end of file diff --git a/lib/rabl-rails.rb b/lib/rabl-rails.rb index 830c797..d57e57d 100644 --- a/lib/rabl-rails.rb +++ b/lib/rabl-rails.rb @@ -26,8 +26,6 @@ module RablRails mattr_accessor :include_json_root @@include_json_root = true - mattr_reader :json_engine - mattr_accessor :use_custom_responder @@use_custom_responder = false @@ -41,10 +39,13 @@ module RablRails end def self.json_engine=(name) - MultiJson.respond_to?(:use) ? MultiJson.use(name) : MultiJson.engine = name - @@json_engine = name + MultiJson.engine = name rescue LoadError - Rails.logger.warn %Q(WARNING: rabl-rails could not load "#{self.json_engine}" as JSON engine, fallback to default) + Rails.logger.warn %Q(WARNING: rabl-rails could not load "#{name}" as JSON engine, fallback to default) + end + + def self.json_engine + MultiJson.engine end def self.cache_templates? @@ -52,6 +53,6 @@ module RablRails end def self.load_default_engines! - self.json_engine = MultiJson.respond_to?(default_adapter) ? MultiJson.default_adapter : MultiJson.default_engine + self.json_engine = MultiJson.default_engine end end diff --git a/lib/rabl-rails/renderers/json.rb b/lib/rabl-rails/renderers/json.rb index 5ab487a..632aaec 100644 --- a/lib/rabl-rails/renderers/json.rb +++ b/lib/rabl-rails/renderers/json.rb @@ -3,7 +3,7 @@ module RablRails class JSON < Base def format_output(hash) hash = { _options[:root_name] => hash } if _options[:root_name] && RablRails.include_json_root - MultiJson.respond_to?(:dump) ? MultiJson.dump(hash) : MultiJson.encode(hash) + MultiJson.encode(hash) end end end From 283b957666a066ee616eb751f8253174596ea3d0 Mon Sep 17 00:00:00 2001 From: ccocchi Date: Mon, 17 Sep 2012 15:08:28 +0200 Subject: [PATCH 14/27] Add tests for XML renderers --- lib/rabl-rails/renderer.rb | 1 + lib/rabl-rails/renderers/xml.rb | 7 +- test/renderers/xml_renderer_test.rb | 131 ++++++++++++++++++++++++++++ 3 files changed, 136 insertions(+), 3 deletions(-) create mode 100644 test/renderers/xml_renderer_test.rb diff --git a/lib/rabl-rails/renderer.rb b/lib/rabl-rails/renderer.rb index b74d3d0..ad53d88 100644 --- a/lib/rabl-rails/renderer.rb +++ b/lib/rabl-rails/renderer.rb @@ -1,5 +1,6 @@ require 'rabl-rails/renderers/base' require 'rabl-rails/renderers/json' +require 'rabl-rails/renderers/xml' module RablRails module Renderer diff --git a/lib/rabl-rails/renderers/xml.rb b/lib/rabl-rails/renderers/xml.rb index 289dae5..225a732 100644 --- a/lib/rabl-rails/renderers/xml.rb +++ b/lib/rabl-rails/renderers/xml.rb @@ -1,11 +1,12 @@ +require 'active_support/core_ext/hash/conversions' + module RablRails module Renderers class XML < Base - DEFAULT_OPTIONS = { dasherize: true, skip_types: false } + DEFAULT_OPTIONS = { dasherize: true, skip_types: false } def format_output(hash) - # hash = hash[options[:root_name]] if options[:root_name] - xml_options = { root: options[:root_name] }.merge!(DEFAULT_OPTIONS) + xml_options = { root: _options[:root_name] }.merge!(DEFAULT_OPTIONS) hash.to_xml(xml_options) end end diff --git a/test/renderers/xml_renderer_test.rb b/test/renderers/xml_renderer_test.rb new file mode 100644 index 0000000..e46a8e7 --- /dev/null +++ b/test/renderers/xml_renderer_test.rb @@ -0,0 +1,131 @@ +require 'test_helper' + +class TestXmlRenderer < ActiveSupport::TestCase + INDENT_REGEXP = /\n(\s)*/ + HEADER_REGEXP = /<[^>]+>/ + + setup do + @data = User.new(1, 'foobar', 'male') + + @context = Context.new + @context.assigns['data'] = @data + + @template = RablRails::CompiledTemplate.new + @template.data = :@data + @template.root_name = :user + end + + def render_xml_output + RablRails::Renderers::XML.new(@context).render(@template).to_s.gsub!(INDENT_REGEXP, '').sub!(HEADER_REGEXP, '') + end + + test "render object simple object" do + @template.source = {} + assert_equal %q(), render_xml_output + end + + test "render collection with empty template" do + @context.assigns['data'] = [@data] + @template.source = {} + @template.root_name = :users + assert_equal %q(), render_xml_output + end + + test "render object with local methods (used by decent_exposure)" do + @context.stub(:user).and_return(@data) + @template.source = { :id => :id } + assert_equal %q(1), render_xml_output + end + + test "render single object attributes" do + @template.source = { :name => :name } + assert_equal %q(foobar), render_xml_output + end + + test "render child with arbitrary data source" do + @template.source = { :author => { :_data => :@data, :name => :name } } + @template.root_name = :post + assert_equal %q(foobar), render_xml_output + end + + test "render child with local methods (used by decent_exposure)" do + @context.stub(:user).and_return(@data) + @template.source = { :author => { :_data => :user, :name => :name } } + @template.root_name = :post + assert_equal %q(foobar), render_xml_output + end + + test "render glued attributes from single object" do + @template.source = { :_glue0 => { :_data => :@data, :name => :name } } + assert_equal %q(foobar), render_xml_output + end + + test "render collection with attributes" do + @data = [User.new(1, 'foo', 'male'), User.new(2, 'bar', 'female')] + @context.assigns['data'] = @data + @template.root_name = :users + @template.source = { :uid => :id, :name => :name } + assert_equal %q(1foo2bar), render_xml_output + end + + test "render node property" do + proc = lambda { |object| object.name } + @template.source = { :name => proc } + assert_equal %q(foobar), render_xml_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 %q(foobar), render_xml_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 %q(), render_xml_output + end + + test "node with context method call" do + @context.stub(:respond_to?).with(:@data).and_return(false) + @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(marty), render_xml_output + end + + test "partial should be evaluated at rendering time" do + # Set assigns + @context.assigns['user'] = @data + + # Stub Library#get + t = RablRails::CompiledTemplate.new + t.source = { :name => :name } + RablRails::Library.reset_instance + RablRails::Library.instance.should_receive(:compile_template_from_path).with('users/base').and_return(t) + + @template.data = false + @template.root_name = :post + @template.source = { :user => ->(s) { partial('users/base', :object => @user) } } + + assert_equal %q(foobar), render_xml_output + end + + test "partial with no values should raise an error" do + @template.data = false + @template.source = { :user => ->(s) { partial('users/base') } } + + assert_raises(RablRails::Renderers::PartialError) { render_xml_output } + end + + test "partial with empty values should not raise an error" do + @template.data = false + @template.root_name = :list + @template.source = { :users => ->(s) { partial('users/base', :object => []) } } + + assert_equal %q(), render_xml_output + end +end \ No newline at end of file From c566c0d78854a1e3aedb85058afa07d6ab00f38e Mon Sep 17 00:00:00 2001 From: ccocchi Date: Mon, 17 Sep 2012 15:08:54 +0200 Subject: [PATCH 15/27] Add libxml to faster XML output generation --- Gemfile | 1 + test/test_helper.rb | 2 ++ 2 files changed, 3 insertions(+) diff --git a/Gemfile b/Gemfile index 46ed377..62a2914 100644 --- a/Gemfile +++ b/Gemfile @@ -3,6 +3,7 @@ source "http://rubygems.org" gemspec gem 'yajl-ruby' +gem 'libxml-ruby' group :test do gem 'rspec-mocks' diff --git a/test/test_helper.rb b/test/test_helper.rb index 7468f73..9106808 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -23,6 +23,8 @@ end require 'rabl-rails' +ActiveSupport::XmlMini.backend = 'LibXML' + module ActiveSupport class TestCase RSpec::Mocks::setup(self) From 84cd69bdfd83f0413fc34b92f5ee9cae914cee71 Mon Sep 17 00:00:00 2001 From: ccocchi Date: Mon, 17 Sep 2012 15:24:39 +0200 Subject: [PATCH 16/27] Add XML engine configuration --- lib/rabl-rails.rb | 11 +++++++++++ test/test_helper.rb | 2 -- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/rabl-rails.rb b/lib/rabl-rails.rb index d57e57d..2ce32c2 100644 --- a/lib/rabl-rails.rb +++ b/lib/rabl-rails.rb @@ -48,11 +48,22 @@ module RablRails MultiJson.engine end + def self.xml_engine=(name) + ActiveSupport::XmlMini.backend = name + rescue LoadError, NameError + Rails.logger.warn %Q(WARNING: rabl-rails could not load "#{name}" as XML engine, fallback to default) + end + + def self.xml_engine + ActiveSupport::XmlMini.backend + end + def self.cache_templates? ActionController::Base.perform_caching && @@cache_templates end def self.load_default_engines! self.json_engine = MultiJson.default_engine + self.xml_engine = 'LibXML' end end diff --git a/test/test_helper.rb b/test/test_helper.rb index 9106808..7468f73 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -23,8 +23,6 @@ end require 'rabl-rails' -ActiveSupport::XmlMini.backend = 'LibXML' - module ActiveSupport class TestCase RSpec::Mocks::setup(self) From d81c3bebfe5a3ba825c217757435ad10de3e7e8f Mon Sep 17 00:00:00 2001 From: ccocchi Date: Mon, 17 Sep 2012 15:26:36 +0200 Subject: [PATCH 17/27] Update README --- README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 1df7dda..417473c 100644 --- a/README.md +++ b/README.md @@ -71,9 +71,9 @@ The only places where you can actually used instance variables are into Proc (o ```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 +# will be evaluated when rendering the object node(:read) { |post| post.read_by?(@user) } ``` @@ -92,6 +92,7 @@ RablRails works out of the box, with default options and fastest engine availabl # config.cache_templates = true # config.include_json_root = true # config.json_engine = :yajl + # config.xml_engine = 'LibXML' end ``` @@ -139,7 +140,7 @@ You can aliases these attributes in your response ```ruby attributes title: :foo, to_s: :bar -# => { "foo" : , "bar" : <to_s value> } +# => { "foo" : <title value>, "bar" : <to_s value> } ``` ### Child nodes @@ -175,7 +176,7 @@ node(:full_name) { |u| u.first_name + " " + u.last_name } You can add the node only if a condition is true ```ruby -node(:email, if: -> { |u| u.valid_email? }) do |u| +node(:email, if: -> { |u| u.valid_email? }) do |u| u.email end ``` @@ -246,7 +247,7 @@ There are cases when you want to render object outside Rails view context. For i Rabl.render(object, template, :view_path => 'app/views', :format => :json) #=> "{...}" ``` -You can find more informations about how to use this method in the [wiki](http://github.com/ccocchi/rabl-rails/wiki/Render-object-directly) +You can find more informations about how to use this method in the [wiki](http://github.com/ccocchi/rabl-rails/wiki/Render-object-directly) ## Performance From 62cabb5f37eb38d9c8a10a03152fe4b8507d2f82 Mon Sep 17 00:00:00 2001 From: ccocchi <cocchi.c@gmail.com> Date: Mon, 17 Sep 2012 15:28:14 +0200 Subject: [PATCH 18/27] Add entry to CHANGELOG --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 53a0739..bb479a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,8 @@ # CHANGELOG ## 0.2.0 (unrelased) - * Use MultiJson's preferred JSON engine as default + * Add XML renderer + * Use MultiJson's preferred JSON engine as default (shmeltex) * Default template to render with responder can be set per controller * Reponder works out of the box with devise * object or collection can be skipped if use with `respond_to` blocks From a97a8cd24f3d31f6854a8c293e5a1e3918e2c2a9 Mon Sep 17 00:00:00 2001 From: ccocchi <cocchi.c@gmail.com> Date: Wed, 19 Sep 2012 17:36:57 +0200 Subject: [PATCH 19/27] Add root in DSL --- CHANGELOG.md | 1 + lib/rabl-rails/compiler.rb | 4 ++++ test/compiler_test.rb | 14 ++++++++++++-- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb479a8..3620461 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # CHANGELOG ## 0.2.0 (unrelased) + * Add `root` in DSL to set root without changing the source * Add XML renderer * Use MultiJson's preferred JSON engine as default (shmeltex) * Default template to render with responder can be set per controller diff --git a/lib/rabl-rails/compiler.rb b/lib/rabl-rails/compiler.rb index eb2f9a5..357e2c2 100644 --- a/lib/rabl-rails/compiler.rb +++ b/lib/rabl-rails/compiler.rb @@ -30,6 +30,10 @@ module RablRails end alias_method :collection, :object + def root(name) + @template.root_name = name + end + # # Includes the attribute or method in the output # Example: diff --git a/test/compiler_test.rb b/test/compiler_test.rb index b9f8fcc..d2d8c8f 100644 --- a/test/compiler_test.rb +++ b/test/compiler_test.rb @@ -24,6 +24,16 @@ class CompilerTest < ActiveSupport::TestCase assert_equal({}, t.source) end + test "root can be defined via keyword" do + t = @compiler.compile_source(%{ root :author }) + assert_equal :author, t.root_name + end + + test "root keyword override object root" do + t = @compiler.compile_source(%{ object :@user ; root :author }) + assert_equal :author, t.root_name + end + test "collection set the data for the template" do t = @compiler.compile_source(%{ collection :@user }) assert_equal :@user, t.data @@ -42,7 +52,7 @@ class CompilerTest < ActiveSupport::TestCase assert_equal :@user, t.data assert_equal :users, t.root_name end - + test "root can be set to false via options" do t = @compiler.compile_source(%( object :@user, root: false)) assert_equal false, t.root_name @@ -154,7 +164,7 @@ class CompilerTest < ActiveSupport::TestCase assert_equal({ :user => { :_data => :@user, :id => :id } }, t.source) assert_equal false, t.data end - + test "name extraction from argument" do assert_equal [:@users, 'users'], @compiler.send(:extract_data_and_name, :@users) assert_equal [:users, :users], @compiler.send(:extract_data_and_name, :users) From 85eb4ebbf779f7778c417e5a02dd5a6f04b31f91 Mon Sep 17 00:00:00 2001 From: ccocchi <cocchi.c@gmail.com> Date: Wed, 19 Sep 2012 18:03:40 +0200 Subject: [PATCH 20/27] Pass resource to all requests (not only POST) to use it instead of ivar in RABL views with respond_to --- lib/rabl-rails/responder.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rabl-rails/responder.rb b/lib/rabl-rails/responder.rb index 997bf80..f6e8a03 100644 --- a/lib/rabl-rails/responder.rb +++ b/lib/rabl-rails/responder.rb @@ -13,12 +13,12 @@ module RablRails protected def api_behavior(error) - rabl_options = options.merge(template: @api_template || RablRails.responder_default_template) + rabl_options = options.merge(template: @api_template || RablRails.responder_default_template, locals: { resource: resource }) if get? controller.default_render rabl_options elsif post? - controller.default_render rabl_options.merge!(status: :created, location: api_location, locals: { resource: resource }) + controller.default_render rabl_options.merge!(status: :created, location: api_location) else head :no_content end From 137234e7052382385894f8a654995c3d2b3da541 Mon Sep 17 00:00:00 2001 From: ccocchi <cocchi.c@gmail.com> Date: Thu, 20 Sep 2012 13:07:18 +0200 Subject: [PATCH 21/27] Correctly pass resource for simple render. Only fetch template name when needed --- lib/rabl-rails/responder.rb | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/rabl-rails/responder.rb b/lib/rabl-rails/responder.rb index f6e8a03..dc137ce 100644 --- a/lib/rabl-rails/responder.rb +++ b/lib/rabl-rails/responder.rb @@ -7,13 +7,19 @@ module RablRails class Responder < ActionController::Responder def initialize(controller, resources, options = {}) super - @api_template = @controller.respond_to?(:responder_default_template, true) ? controller.send(:responder_default_template) : nil + if options[:locals] + options[:locals][:resource] = resource + else + options[:locals] = { resource: resource } + end end protected def api_behavior(error) - rabl_options = options.merge(template: @api_template || RablRails.responder_default_template, locals: { resource: resource }) + template = @controller.respond_to?(:responder_default_template, true) ? controller.send(:responder_default_template) + : RablRails.responder_default_template + rabl_options = options.merge(template: template) if get? controller.default_render rabl_options From a347ebcf5b0b9a5735bdc35f3dad27ffac639ad5 Mon Sep 17 00:00:00 2001 From: ccocchi <cocchi.c@gmail.com> Date: Thu, 20 Sep 2012 18:33:35 +0200 Subject: [PATCH 22/27] Only use LibXML if present to avoid generating warning --- lib/rabl-rails.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rabl-rails.rb b/lib/rabl-rails.rb index 2ce32c2..2b67406 100644 --- a/lib/rabl-rails.rb +++ b/lib/rabl-rails.rb @@ -64,6 +64,6 @@ module RablRails def self.load_default_engines! self.json_engine = MultiJson.default_engine - self.xml_engine = 'LibXML' + self.xml_engine = 'LibXML' if defined?(LibXML) end end From 9891d093223b32dea1fec593246600a6e4a27a0f Mon Sep 17 00:00:00 2001 From: ccocchi <cocchi.c@gmail.com> Date: Thu, 20 Sep 2012 18:45:46 +0200 Subject: [PATCH 23/27] Replace yajl by oj --- Gemfile | 2 +- README.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index 62a2914..c9d2029 100644 --- a/Gemfile +++ b/Gemfile @@ -2,7 +2,7 @@ source "http://rubygems.org" gemspec -gem 'yajl-ruby' +gem 'oj' gem 'libxml-ruby' group :test do diff --git a/README.md b/README.md index 417473c..bf38312 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ After the template is compiled into a hash, Rabl-rails will use a renderer to do ## Configuration -RablRails works out of the box, with default options and fastest engine available (yajl, libxml). But depending on your needs, you might want to change that or how your output looks like. You can set global configuration in your application: +RablRails works out of the box, with default options and fastest engine available (oj, libxml). But depending on your needs, you might want to change that or how your output looks like. You can set global configuration in your application: ```ruby # config/initializers/rabl_rails.rb @@ -91,7 +91,7 @@ RablRails works out of the box, with default options and fastest engine availabl # These are the default # config.cache_templates = true # config.include_json_root = true - # config.json_engine = :yajl + # config.json_engine = :oj # config.xml_engine = 'LibXML' end ``` From de8a0da9bec1bd939d6c601d1387a4bf51ab9795 Mon Sep 17 00:00:00 2001 From: Christopher Cocchi-Perrier <cocchi.c@gmail.com> Date: Mon, 24 Sep 2012 16:58:51 +0300 Subject: [PATCH 24/27] Update README.md --- README.md | 56 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index bf38312..842e5b7 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ 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 ! +rabl-rails is **faster** and uses **less memory** than the standard rabl gem while letting you access the same features. 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. -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. +rabl-rails only target **Rails 3+ application**. ## Installation @@ -24,7 +24,7 @@ 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, +Once you have installed rabl-rails, you can directly used RABL-rails 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 @@ -32,7 +32,7 @@ class PostController < ApplicationController respond_to :html, :json, :xml def index - @posts = Post.order('created_at DESC') + @posts = Post.order('created_at DESC') respond_with(@posts) end end @@ -86,14 +86,16 @@ After the template is compiled into a hash, Rabl-rails will use a renderer to do RablRails works out of the box, with default options and fastest engine available (oj, libxml). But depending on your needs, you might want to change that or how your output looks like. You can set global configuration in your application: ```ruby - # config/initializers/rabl_rails.rb - RablRails.configure do |config| - # These are the default - # config.cache_templates = true - # config.include_json_root = true - # config.json_engine = :oj - # config.xml_engine = 'LibXML' - end +# config/initializers/rabl_rails.rb +RablRails.configure do |config| + # These are the default + # config.cache_templates = true + # config.include_json_root = true + # config.json_engine = :oj + # config.xml_engine = 'LibXML' + # config.use_custom_responder = false + # config.default_responder_template = 'show' +end ``` ## Usage @@ -128,6 +130,24 @@ node(:some_count) { |_| @user.posts.count } child(:@user) { attribute :name } ``` +If you use gem like *decent_exposure* or *focused_controller*, you can use your variable directly without the leading `@` + +```ruby +object :object_exposed +``` + +You can even skip data declaration at all. If you used `respond_with`, rabl-rails will render the data you passed to it. +As there is no name, you can set a root via the `root` macro. This allow you to use your template without caring about variables passed to it. + +```ruby +# in controller +respond_with(@post) + +# in rabl-rails template +root :article +attribute :title +``` + ### Attributes / Methods Basic usage is to declared attributes to include in the response. These can be database attributes or any instance method. @@ -233,12 +253,6 @@ child :posts do end ``` -### Caching - -Caching is not a part of Rabl-rails. It is already in Rails itself, because caching all view output is the same as action caching (Rails caching is even better because it will also not run your queries). - -Moreover caching each object in a collection can be really not effective with big collections or simple objects. This is also a nightmare with cache expiration. - ### Render object directly There are cases when you want to render object outside Rails view context. For instance to render objects in the console or to create message queue payloads. For these situations, you can use `RablRails.render` as show below: @@ -249,11 +263,15 @@ Rabl.render(object, template, :view_path => 'app/views', :format => :json) #=> " You can find more informations about how to use this method in the [wiki](http://github.com/ccocchi/rabl-rails/wiki/Render-object-directly) +### Other features + +You can find more informations about other features (caching, custom_responder, ...) in the [WIKI](https://github.com/ccocchi/rabl-rails/wiki) + ## Performance Benchmarks have been made using this [application](http://github.com/ccocchi/rabl-benchmark), with rabl 0.6.14 and rabl-rails 0.1.0 -Overall, Rabl-rails is **20% faster and use 10% less memory**. +Overall, Rabl-rails is **20% faster and use 10% less memory**, even **twice faster** when rendering collections with extends. You can see full tests on test application repository. From 0cf1efea14e6c3cdda3fc919d00299f4409ba2ce Mon Sep 17 00:00:00 2001 From: ccocchi <cocchi.c@gmail.com> Date: Mon, 24 Sep 2012 16:02:18 +0200 Subject: [PATCH 25/27] Bump to 0.2.0 --- CHANGELOG.md | 4 ++-- lib/rabl-rails/version.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3620461..99aca41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ # CHANGELOG -## 0.2.0 (unrelased) - * Add `root` in DSL to set root without changing the source +## 0.2.0 + * Add `root` in DSL to set root without changing the data source * Add XML renderer * Use MultiJson's preferred JSON engine as default (shmeltex) * Default template to render with responder can be set per controller diff --git a/lib/rabl-rails/version.rb b/lib/rabl-rails/version.rb index e19060c..8c35cae 100644 --- a/lib/rabl-rails/version.rb +++ b/lib/rabl-rails/version.rb @@ -1,3 +1,3 @@ module RablRails - VERSION = '0.1.3' + VERSION = '0.2.0' end From 35d21b38e69be888d15656d885922bdfce31de2f Mon Sep 17 00:00:00 2001 From: ccocchi <cocchi.c@gmail.com> Date: Thu, 27 Sep 2012 18:19:35 +0200 Subject: [PATCH 26/27] Override Responder#to_format to avoid first missing render on POST request. Fallback to default Rails responder behavior if template is not found --- lib/rabl-rails/responder.rb | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/lib/rabl-rails/responder.rb b/lib/rabl-rails/responder.rb index dc137ce..47220c4 100644 --- a/lib/rabl-rails/responder.rb +++ b/lib/rabl-rails/responder.rb @@ -14,20 +14,33 @@ module RablRails end end + def to_format + if get? || response_overridden? + default_render + elsif has_errors? + display_errors + else + api_behavior(nil) + end + end + protected def api_behavior(error) - template = @controller.respond_to?(:responder_default_template, true) ? controller.send(:responder_default_template) - : RablRails.responder_default_template - rabl_options = options.merge(template: template) + if post? + template = if @controller.respond_to?(:responder_default_template, true) + controller.send(:responder_default_template) + else + RablRails.responder_default_template + end + options[:template] ||= "#{@controller.controller_name}/#{template}" - if get? - controller.default_render rabl_options - elsif post? - controller.default_render rabl_options.merge!(status: :created, location: api_location) + controller.default_render options.merge(status: :created, location: api_location) else head :no_content end + rescue ActionView::MissingTemplate => e + super(e) end end end \ No newline at end of file From 3ce9be556fdbf43098e604c22d133396d9c2026b Mon Sep 17 00:00:00 2001 From: ccocchi <cocchi.c@gmail.com> Date: Thu, 27 Sep 2012 18:23:05 +0200 Subject: [PATCH 27/27] Bump to 0.2.1 --- CHANGELOG.md | 4 ++++ lib/rabl-rails/version.rb | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99aca41..8262c53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +## 0.2.1 + * Avoid useless render on POST request with custom responder + * Custom responder now fallback to Rails default in case the template is not found + ## 0.2.0 * Add `root` in DSL to set root without changing the data source * Add XML renderer diff --git a/lib/rabl-rails/version.rb b/lib/rabl-rails/version.rb index 8c35cae..c4daa75 100644 --- a/lib/rabl-rails/version.rb +++ b/lib/rabl-rails/version.rb @@ -1,3 +1,3 @@ module RablRails - VERSION = '0.2.0' + VERSION = '0.2.1' end