rabl-rails-0.3.2/0000755000175000017500000000000012151754461013130 5ustar boutilboutilrabl-rails-0.3.2/CHANGELOG.md0000644000175000017500000000346112151754461014745 0ustar boutilboutil# CHANGELOG ## 0.3.2 * Using child with a nil value will be correctly formatted as nil * Allow controller's assigns to have symbol keys * Does not modify in place format extracted from context * Add JSONP support ## 0.3.1 * Add `merge` keywork * Format can be passed as a string or a symbol * Avoid to unexpectedly change cached templates (johnbintz) * Add full template stack support to `glue` (fnordfish) * Allow format to be a symbol (lloydmeta) ## 0.3.0 * Travis integration * Add test for keywords used as variable names * Add PList renderer * Remove location header from post responses in responder * Fix bug with incomplete template prefixing ## 0.2.2 * Add condition blocks ## 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 * 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 ## 0.1.3 * Render correctly when variables are not passed via the assigns ivar but as helper methods (decent_exposure, focused_controller) * 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 * Warning message printed on logger when JSON engine fail to load ## 0.1.1 * Add CHANGELOG * 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 rabl-rails-0.3.2/Gemfile0000644000175000017500000000032312151754461014421 0ustar boutilboutilsource "http://rubygems.org" gemspec gem 'plist' platforms :ruby do gem 'oj' end platforms :mri do gem 'libxml-ruby' end platforms :jruby do gem 'nokogiri' end group :test do gem 'rspec-mocks' end rabl-rails-0.3.2/metadata.yml0000644000175000017500000000737112151754461015443 0ustar boutilboutil--- !ruby/object:Gem::Specification name: rabl-rails version: !ruby/object:Gem::Version version: 0.3.2 prerelease: platform: ruby authors: - Christopher Cocchi-Perrier autorequire: bindir: bin cert_chain: [] date: 2013-05-28 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: activesupport requirement: !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version version: '3.0' type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version version: '3.0' - !ruby/object:Gem::Dependency name: railties requirement: !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version version: '3.0' type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version version: '3.0' - !ruby/object:Gem::Dependency name: actionpack requirement: !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version version: '3.0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version version: '3.0' - !ruby/object:Gem::Dependency name: rspec-mocks requirement: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' description: Fast Rails 3+ templating system with JSON, XML and PList support email: - cocchi.c@gmail.com executables: [] extensions: [] extra_rdoc_files: [] files: - .gitignore - .travis.yml - CHANGELOG.md - Gemfile - MIT-LICENSE - README.md - Rakefile - lib/rabl-rails.rb - lib/rabl-rails/compiler.rb - lib/rabl-rails/condition.rb - lib/rabl-rails/handler.rb - lib/rabl-rails/library.rb - lib/rabl-rails/railtie.rb - lib/rabl-rails/renderer.rb - lib/rabl-rails/renderers/base.rb - lib/rabl-rails/renderers/json.rb - lib/rabl-rails/renderers/plist.rb - lib/rabl-rails/renderers/xml.rb - lib/rabl-rails/responder.rb - lib/rabl-rails/template.rb - lib/rabl-rails/version.rb - lib/tasks/rabl-rails.rake - rabl-rails.gemspec - test/base_renderer_test.rb - test/cache_templates_test.rb - test/compiler_test.rb - test/deep_nesting_test.rb - test/keyword_test.rb - test/non_restful_response_test.rb - test/render_test.rb - test/renderers/json_renderer_test.rb - test/renderers/plist_renderer_test.rb - test/renderers/xml_renderer_test.rb - test/test_helper.rb homepage: https://github.com/ccocchi/rabl-rails licenses: [] post_install_message: rdoc_options: [] require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' required_rubygems_version: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' requirements: [] rubyforge_project: rubygems_version: 1.8.21 signing_key: specification_version: 3 summary: Fast Rails 3+ templating system with JSON, XML and PList support test_files: - test/base_renderer_test.rb - test/cache_templates_test.rb - test/compiler_test.rb - test/deep_nesting_test.rb - test/keyword_test.rb - test/non_restful_response_test.rb - test/render_test.rb - test/renderers/json_renderer_test.rb - test/renderers/plist_renderer_test.rb - test/renderers/xml_renderer_test.rb - test/test_helper.rb rabl-rails-0.3.2/rabl-rails.gemspec0000644000175000017500000000152412151754461016527 0ustar boutilboutil$:.push File.expand_path("../lib", __FILE__) require "rabl-rails/version" Gem::Specification.new do |s| s.name = "rabl-rails" s.version = RablRails::VERSION s.platform = Gem::Platform::RUBY s.authors = ["Christopher Cocchi-Perrier"] s.email = ["cocchi.c@gmail.com"] s.homepage = "https://github.com/ccocchi/rabl-rails" s.summary = "Fast Rails 3+ templating system with JSON, XML and PList support" s.description = "Fast Rails 3+ templating system with JSON, XML and PList support" s.files = `git ls-files`.split("\n") s.test_files = `git ls-files -- test/*`.split("\n") s.require_paths = ["lib"] s.add_dependency "activesupport", "~> 3.0" s.add_dependency "railties", "~> 3.0" s.add_development_dependency "actionpack", "~> 3.0" s.add_development_dependency "rspec-mocks" end rabl-rails-0.3.2/test/0000755000175000017500000000000012151754461014107 5ustar boutilboutilrabl-rails-0.3.2/test/base_renderer_test.rb0000644000175000017500000000144612151754461020300 0ustar boutilboutilrequire 'test_helper' class TestBaseRenderer < ActiveSupport::TestCase RablRails::Renderers::Base.class_eval do def format_output(hash) hash end end setup do @data = User.new(1, 'foobar', 'male') @context = Context.new @context.assigns['data'] = @data @template = RablRails::CompiledTemplate.new @template.data = :@data end def render_hash RablRails::Renderers::Base.new(@context).render(@template) end test "child with nil data should render nil" do @template.source = { :author => { :_data => :@nil, :name => :name } } assert_equal({ :author => nil }, render_hash) end test "properly handle assigns with symbol keys" do @context.assigns[:foo] = 'bar' @template.source = {} assert_nothing_raised { render_hash } end endrabl-rails-0.3.2/test/cache_templates_test.rb0000644000175000017500000000226012151754461020614 0ustar boutilboutilrequire 'test_helper' class CacheTemplatesTest < ActiveSupport::TestCase setup do 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 ActionController::Base.stub(:perform_caching).and_return(true) @library.compile_template_from_source('', 'some/path') t = @library.compile_template_from_source("attribute :id", 'some/path') assert_equal({}, t.source) end test "cached templates should not be modifiable in place" do ActionController::Base.stub(:perform_caching).and_return(true) t = @library.compile_template_from_source('', 'some/path') t.merge!(:_data => :foo) assert_equal({}, @library.compile_template_from_path('some/path').source) end test "don't cache templates cache_templates is enabled but perform_caching is not active" do ActionController::Base.stub(:perform_caching).and_return(false) @library.compile_template_from_source('', 'some/path') t = @library.compile_template_from_source("attribute :id", 'some/path') assert_equal({ :id => :id }, t.source) end endrabl-rails-0.3.2/test/renderers/0000755000175000017500000000000012151754461016100 5ustar boutilboutilrabl-rails-0.3.2/test/renderers/plist_renderer_test.rb0000644000175000017500000001177312151754461022516 0ustar boutilboutilrequire 'test_helper' class TestPlistRenderer < 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_plist_output output = RablRails::Renderers::PLIST.new(@context).render(@template).to_s.gsub!(INDENT_REGEXP, '') output.sub!(HEADER_REGEXP, '').gsub!(%r(]*>), '').sub!(%r(), '').sub(%r(), '') end test "plist engine should responsd to #dump" do assert_raises(RuntimeError) { RablRails.plist_engine = Object.new } end test "render object wth empty template" do @template.source = {} assert_equal %q(), render_plist_output end test "render collection with empty template" do @context.assigns['data'] = [@data] @template.source = {} assert_equal %q(), render_plist_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(id1), render_plist_output end test "render single object attributes" do @template.source = { :id => :id, :name => :name } assert_equal %q(id1namefoobar), render_plist_output end 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(addresscityParis), render_plist_output end test "render child with arbitrary data source" do @template.source = { :author => { :_data => :@data, :name => :name } } assert_equal %q(authornamefoobar), render_plist_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(authornamefoobar), render_plist_output end test "render node property" do proc = lambda { |object| object.name } @template.source = { :name => proc } assert_equal %q(namefoobar), render_plist_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(namefoobar), render_plist_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_plist_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(namemarty), render_plist_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_plist_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 %q(users), render_plist_output end test "condition blocks are transparent if the condition passed" do c = RablRails::Condition.new(->(u) { true }, { :name => :name }) @template.source = { :_if0 => c } assert_equal %q(namefoobar), render_plist_output end test "condition blocks are ignored if the condition is not met" do c = RablRails::Condition.new(->(u) { false }, { :name => :name }) @template.source = { :_if0 => c } assert_equal %q(), render_plist_output end test "render object with root node" do RablRails.include_plist_root = true @template.root_name = :author @template.source = { :id => :id, :name => :name } assert_equal %q(authorid1namefoobar), render_plist_output end test "render object with root options set to false" do RablRails.include_plist_root = false @template.root_name = :author @template.source = { :id => :id, :name => :name } assert_equal %q(id1namefoobar), render_plist_output end endrabl-rails-0.3.2/test/renderers/json_renderer_test.rb0000644000175000017500000001421612151754461022327 0ustar boutilboutilrequire 'test_helper' class TestJsonRenderer < ActiveSupport::TestCase setup do @data = User.new(1, 'foobar', 'male') @context = Context.new @context.assigns['data'] = @data @template = RablRails::CompiledTemplate.new @template.data = :@data end def render_json_output RablRails::Renderers::JSON.new(@context).render(@template) end test "render object wth empty template" do @template.source = {} assert_equal %q({}), render_json_output end test "render collection with empty template" do @context.assigns['data'] = [@data] @template.source = {} 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 end 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 %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 end test "render glued node" do @template.source = { :_glue0 => { :_data => :@data, :foo => lambda { |u| u.name } } } assert_equal(%q({"foo":"foobar"}), render_json_output) end test "render collection with attributes" do @data = [User.new(1, 'foo', 'male'), User.new(2, 'bar', 'female')] @context.assigns['data'] = @data @template.source = { :uid => :id, :name => :name, :gender => :sex } 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.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 %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 %q({}), render_json_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({"name":"marty"}), render_json_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.source = { :user => ->(s) { partial('users/base', :object => @user) } } assert_equal %q({"user":{"name":"foobar"}}), render_json_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_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 %q({"users":[]}), render_json_output end test "condition blocks are transparent if the condition passed" do c = RablRails::Condition.new(->(u) { true }, { :name => :name }) @template.source = { :_if0 => c } assert_equal %q({"name":"foobar"}), render_json_output end test "condition blocks are ignored if the condition is not met" do c = RablRails::Condition.new(->(u) { false }, { :name => :name }) @template.source = { :_if0 => c } assert_equal %q({}), render_json_output end test "render object with root node" do RablRails.include_json_root = true @template.root_name = :author @template.source = { :id => :id, :name => :name } 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 end test "merge should raise is return from given block is not a hash" do @template.source = { :_merge0 => ->(c) { 'foo' } } assert_raises(RablRails::Renderers::PartialError) { render_json_output } end test "result from merge is merge inside current response" do @template.source = { :_merge0 => ->(c) { { :custom => c.name } } } assert_equal %q({"custom":"foobar"}), render_json_output end test "render with jsonp callback" do RablRails.enable_jsonp_callbacks = true @context.stub(:params).and_return({ callback: 'some_callback' }) @template.source = { :name => :name } assert_equal %q[some_callback({"name":"foobar"})], render_json_output end endrabl-rails-0.3.2/test/renderers/xml_renderer_test.rb0000644000175000017500000001116412151754461022155 0ustar boutilboutilrequire '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 endrabl-rails-0.3.2/test/non_restful_response_test.rb0000644000175000017500000000177612151754461021762 0ustar boutilboutilrequire 'test_helper' class NonRestfulResponseTest < ActiveSupport::TestCase setup do RablRails::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(:@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 node(:post_count) { @user.posts.count } child(:@user => :user) do attributes :id, :name end } assert_equal(MultiJson.encode({ :post_count => 10, :user => { :id => 1, :name => 'foo' } }), RablRails::Library.instance.get_rendered_template(source, @context)) end endrabl-rails-0.3.2/test/render_test.rb0000644000175000017500000000442312151754461016755 0ustar boutilboutilrequire 'test_helper' require 'pathname' require 'tmpdir' class RenderTest < ActiveSupport::TestCase setup do @user = User.new(1, 'Marty') @tmp_path = Pathname.new(Dir.mktmpdir) end test "allow object to be passed as an option" do File.open(@tmp_path + "nil.json.rabl", "w") do |f| f.puts %q{ object :@user attributes :name } end assert_equal %q({"user":{"name":"Marty"}}), RablRails.render(nil, 'nil', locals: { object: @user }, view_path: @tmp_path) end test "load source from file" do File.open(@tmp_path + "show.json.rabl", "w") do |f| f.puts %q{ object :@user attributes :id, :name } 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 } } end assert_equal %q({"username":"Marty"}), RablRails.render(nil, 'instance', view_path: @tmp_path, locals: { user: @user }) end test "handle path for extends" do File.open(@tmp_path + "extend.json.rabl", "w") do |f| f.puts %q{ object :@user extends 'base' } end File.open(@tmp_path + "base.json.rabl", "w") do |f| f.puts %q{ attribute :name, as: :extended_name } end assert_equal %q({"user":{"extended_name":"Marty"}}), RablRails.render(@user, 'extend', view_path: @tmp_path) end test "format can be passed as symbol or a string" do File.open(@tmp_path + "show.json.rabl", "w") do |f| f.puts %q{ object :@user attributes :id, :name } end assert_equal %q({"user":{"id":1,"name":"Marty"}}), RablRails.render(@user, 'show', view_path: @tmp_path, format: :json) assert_equal %q({"user":{"id":1,"name":"Marty"}}), RablRails.render(@user, 'show', view_path: @tmp_path, format: 'json') assert_equal %q({"user":{"id":1,"name":"Marty"}}), RablRails.render(@user, 'show', view_path: @tmp_path, format: 'JSON') end endrabl-rails-0.3.2/test/test_helper.rb0000644000175000017500000000210112151754461016744 0ustar boutilboutilENV["RAILS_ENV"] = "test" $:.unshift File.expand_path('../../lib', __FILE__) require 'rspec/mocks' require 'minitest/unit' MiniTest::Unit.autorun require 'active_support/test_case' require 'action_controller' require 'singleton' class < mock(:source => %{ object :@comment\n attribute :content }))) end test "compile and render deep nesting template" do source = %{ object :@user attributes :id, :name child :posts do attribute :title child :comments do extends 'comments/show' end end } assert_equal(MultiJson.encode(:user => { :id => 1, :name => 'foobar', :posts => [{ :title => 'I rock !', :comments => [ { :content => 'first' }, { :content => 'second' } ] }] }), RablRails::Library.instance.get_rendered_template(source, @context)) end end rabl-rails-0.3.2/test/compiler_test.rb0000644000175000017500000001546112151754461017314 0ustar boutilboutilrequire 'test_helper' class CompilerTest < ActiveSupport::TestCase setup do @user = User.new @compiler = RablRails::Compiler.new end test "compiler return a compiled template" do assert_instance_of RablRails::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 "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 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 "root can be set to false via options" do t = @compiler.compile_source(%( object :@user, root: false)) assert_equal false, 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) end test "attributes appeared only once even if called mutiple times" do t = @compiler.compile_source(%{ attribute :id ; attribute :id }) assert_equal({ :id => :id }, t.source) end test "attribute can be aliased through :as option" do t = @compiler.compile_source(%{ attribute :foo, :as => :bar }) assert_equal({ :bar => :foo}, t.source) end test "attribute can be aliased through hash" do t = @compiler.compile_source(%{ attribute :foo => :bar }) assert_equal({ :bar => :foo }, t.source) end test "multiple attributes can be aliased" do t = @compiler.compile_source(%{ attributes :foo => :bar, :id => :uid }) assert_equal({ :bar => :foo, :uid => :id }, t.source) end test "child with association use association name as data" do t = @compiler.compile_source(%{ child :address do attributes :foo end}) assert_equal({ :address => { :_data => :address, :foo => :foo } }, t.source) end test "child with association can be aliased" do t = @compiler.compile_source(%{ child :address => :bar do attributes :foo end}) assert_equal({ :bar => { :_data => :address, :foo => :foo } }, t.source) end test "child with root name defined as option" do t = @compiler.compile_source(%{ child(:user, :root => :author) do attributes :foo end }) assert_equal({ :author => { :_data => :user, :foo => :foo } }, t.source) end test "child with arbitrary source store the data with the template" do t = @compiler.compile_source(%{ child :@user => :author do attribute :name end }) assert_equal({ :author => { :_data => :@user, :name => :name } }, t.source) end test "child with succint partial notation" do mock_template = RablRails::CompiledTemplate.new mock_template.source = { :id => :id } RablRails::Library.reset_instance RablRails::Library.instance.stub(:compile_template_from_path).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) end test "glue is compiled as a child but with anonymous name" do t = @compiler.compile_source(%{ glue(:@user) do attribute :name end }) assert_equal({ :_glue0 => { :_data => :@user, :name => :name } }, t.source) end test "multiple glue don't come with name collisions" do t = @compiler.compile_source(%{ glue :@user do attribute :name end glue :@user do attribute :foo end }) assert_equal({ :_glue0 => { :_data => :@user, :name => :name}, :_glue1 => { :_data => :@user, :foo => :foo} }, t.source) end test "glue accepts all dsl in its body" do t = @compiler.compile_source(%{ glue :@user do node(:foo) { |u| u.name } end }) assert_not_nil(t.source[:_glue0]) s = t.source[:_glue0] assert_equal(:@user, s[:_data]) assert_instance_of(Proc, s[:foo]) end test "extends use other template source as itself" do template = mock('template', :source => { :id => :id }) RablRails::Library.reset_instance RablRails::Library.instance.stub(:compile_template_from_path).with('users/base').and_return(template) t = @compiler.compile_source(%{ extends 'users/base' }) assert_equal({ :id => :id }, t.source) end test "node are compiled without evaluating the block" do t = @compiler.compile_source(%{ node(:foo) { bar } }) assert_not_nil t.source[:foo] assert_instance_of Proc, t.source[:foo] end test "node with condition are compiled as an array of procs" do t = @compiler.compile_source(%{ node(:foo, :if => lambda { |m| m.foo.present? }) do |m| m.foo end }) assert_not_nil t.source[:foo] assert_instance_of Array, t.source[:foo] assert_equal 2, t.source[:foo].size end test "node can take no arguments and behave like a merge" do t = @compiler.compile_source(%{ node do |m| m.foo end }) assert_instance_of Proc, t.source[:_merge0] end test "merge compile like a node but with a reserved keyword as name" do t = @compiler.compile_source(%{ merge do |m| m.foo end }) assert_instance_of Proc, t.source[:_merge0] end test "conditionnal block compile nicely" do t = @compiler.compile_source(%{ condition(->(u) {}) do attributes :secret end }) assert_instance_of RablRails::Condition, t.source[:_if0] assert_equal({ :secret => :secret }, t.source[:_if0].source) end test "compile with no object" do t = @compiler.compile_source(%{ object false child(:@user => :user) do attribute :id end }) 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) assert_equal [:@users, :authors], @compiler.send(:extract_data_and_name, :@users => :authors) end endrabl-rails-0.3.2/test/keyword_test.rb0000644000175000017500000000221112151754461017153 0ustar boutilboutilrequire 'test_helper' class KeywordTest < ActiveSupport::TestCase class Collection attr_accessor :id, :name def initialize(id, name) @id = id @name = name end def cover(size) "foo_#{size}" end end setup do RablRails::Library.reset_instance @context = Context.new @user = User.new(1, 'Marty') @collections = [Collection.new(1, 'first'), Collection.new(2, 'last')] @context.assigns['user'] = @user @context.assigns['collections'] = @collections @context.virtual_path = 'user/show' @context.stub(lookup_context: nil) end test "collections model should not collide with rabl-rails reserved keyword" do source = %{ object :@user child(:@collections => :collections) do attributes :id, :name node(:cover_url) { |c| c.cover(:medium) } end } assert_equal(MultiJson.encode( user: { collections: [{ id: 1, name: 'first', cover_url: "foo_medium" }, { id: 2, name: 'last', cover_url: "foo_medium" }] } ), RablRails::Library.instance.get_rendered_template(source, @context)) end endrabl-rails-0.3.2/.travis.yml0000644000175000017500000000007512151754461015243 0ustar boutilboutillanguage: ruby rvm: - 1.9.3 - jruby-19mode - rbx-19moderabl-rails-0.3.2/README.md0000644000175000017500000002332012151754461014407 0ustar boutilboutil# RABL for Rails # 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 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. rabl-rails only targets **Rails 3+ application** and is compatible with mri 1.9.3, jRuby and rubinius. ## Installation Install as a gem : ``` gem install rabl-rails ``` or add directly to your `Gemfile` ``` gem 'rabl-rails' ``` And that's it ! ## Overview 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 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. ## Configuration 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' # config.use_custom_responder = false # config.default_responder_template = 'show' # config.enable_jsonp_callbacks = false end ``` ## Usage ### Data declaration To declare data to use in the template, you can use either `object` or `collection` with the symbol name or your data. ```ruby # app/views/users/show.json.rabl object :@user # app/views/users/index.json.rabl collection :@users ``` You can specify root label for the collection using hash or `:root` option ```ruby collection :@posts, root: :articles #is equivalent to collection :@posts => :articles # => { "articles" : [{...}, {...}] } ``` There are rares cases when the template doesn't map directly to any object. In these cases, you can set data to false or skip data declaration altogether. ```ruby object false 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. ```ruby attributes :id, :title, :to_s ``` You can aliases these attributes in your response ```ruby attributes title: :foo, to_s: :bar # => { "foo" : , "bar" : <to_s value> } ``` ### Child nodes You can include informations from data associated with the parent model or arbitrary data. These informations can be grouped under a node or directly merged into current node. For example if you have a `Post` model that belongs to a `User` ```ruby object :@post child(user: :author) do attributes :name end # => { "post" : { "author" : { "name" : "John D." } } } ``` You can also use arbitrary data source with child nodes ```ruby child(:@users) do attributes :id, :name end ``` If you want to merge directly into current node, you can use the `glue` keywork ```ruby attribute :title glue(:user) do attributes :name => :author_name end # => { "post" : { "title" : "Foo", "author_name" : "John D." } } ``` ### Custom nodes You can create custom node in your response, based on the result of the given block. ```ruby object :@user node(:full_name) { |u| u.first_name + " " + u.last_name } # => { "user" : { "full_name" : "John Doe" } } ``` You can add condition on your custom nodes (if the condition is evaluated to false, the node will not be included). ```ruby node(:email, if: ->(u) { u.valid_email? }) do |u| u.email end ``` Nodes are evaluated at the rendering time, so you can use any instance variables or view helpers inside them ```ruby node(:url) { |post| post_url(post) } ``` If you want to include directly the result into the current node, use the `merge` keyword (result returned from the block should be a hash) ```ruby object :@user merge { |u| { name: u.first_name + " " + u.last_name } } # => { "user" : { "name" : "John Doe" } } ``` Custom nodes are really usefull to create flexible representations of your resources. ### Extends & Partials Often objects have a basic representation that is shared accross different views and enriched according to it. To avoid code redundancy you can extend your template from any other RABL template. ```ruby # app/views/users/base.json.rabl attributes :id, :name # app/views/users/private.json.rabl extends 'users/base' attributes :super_secret_attribute ``` You can also extends template in child nodes using `partial` option (this is the same as using `extends` in the child block) ```ruby collection @posts attribute :title child(:user, partial: 'users/base') ``` Partials can also be used inside custom nodes. When using partial this way, you MUST declare the object associated to the partial ```ruby node(:location) do |user| { city: user.city, address: partial('users/address', object: m.address) } end ``` ### Nesting Rabl allow you to define easily your templates, even with hierarchy of 2 or 3 levels. Let's suppose your have a `thread` model that has many `posts` and that each post has many `comments`. We can display a full thread in a few lines ```ruby object :@thread attribute :caption child :posts do attribute :title child :comments do extends 'comments/base' end end ``` ### 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: ```ruby 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.7.6 and rabl-rails 0.3.0 Overall, Rabl-rails is **20% faster and use 10% less memory**, even **twice faster** when using extends. You can see full tests on test application repository. ## Authors and contributors * [Christopher Cocchi-Perrier](http://github.com/ccocchi) - Creator of the project Want to add another format to Rabl-rails ? Checkout [JSON renderer](http://github.com/ccocchi/rabl-rails/blob/master/lib/rabl-rails/renderers/json.rb) for reference Want to make another change ? Just fork and contribute, any help is very much appreciated. If you found a bug, you can report it via the Github issues. ## Original idea * [RABL](http://github.com/nesquena/rabl) Standart RABL gem. I used it a lot but I needed to improve my API response time, and since most of the time was spent in view rendering, I decided to implement a faster rabl gem. ## Copyright Copyright © 2011-2012 Christopher Cocchi-Perrier. See [MIT-LICENSE](http://github.com/ccocchi/rabl-rails/blob/master/MIT-LICENSE) for details. ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������rabl-rails-0.3.2/Rakefile���������������������������������������������������������������������������0000755�0001750�0001750�00000001364�12151754461�014604� 0����������������������������������������������������������������������������������������������������ustar �boutil��������������������������boutil�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env rake # begin # require 'bundler/setup' # rescue LoadError # puts 'You must `gem install bundler` and `bundle install` to run rake tasks' # end # begin # require 'rdoc/task' # rescue LoadError # require 'rdoc/rdoc' # require 'rake/rdoctask' # RDoc::Task = Rake::RDocTask # end # # RDoc::Task.new(:rdoc) do |rdoc| # rdoc.rdoc_dir = 'rdoc' # rdoc.title = 'RablRails' # rdoc.options << '--line-numbers' # rdoc.rdoc_files.include('README.rdoc') # rdoc.rdoc_files.include('lib/**/*.rb') # end require 'bundler' Bundler::GemHelper.install_tasks require 'rake/testtask' Rake::TestTask.new(:test) do |t| t.libs << 'lib' t.libs << 'test' t.pattern = 'test/**/*_test.rb' t.verbose = true end task :default => :test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������rabl-rails-0.3.2/lib/�������������������������������������������������������������������������������0000755�0001750�0001750�00000000000�12151754461�013676� 5����������������������������������������������������������������������������������������������������ustar �boutil��������������������������boutil�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������rabl-rails-0.3.2/lib/tasks/�������������������������������������������������������������������������0000755�0001750�0001750�00000000000�12151754461�015023� 5����������������������������������������������������������������������������������������������������ustar �boutil��������������������������boutil�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������rabl-rails-0.3.2/lib/tasks/rabl-rails.rake����������������������������������������������������������0000644�0001750�0001750�00000000130�12151754461�017711� 0����������������������������������������������������������������������������������������������������ustar �boutil��������������������������boutil�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# desc "Explaining what the task does" # task :rabl-rails do # # Task goes here # end ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������rabl-rails-0.3.2/lib/rabl-rails.rb������������������������������������������������������������������0000644�0001750�0001750�00000004075�12151754461�016261� 0����������������������������������������������������������������������������������������������������ustar �boutil��������������������������boutil�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'rails/railtie' require 'active_support' require 'active_support/core_ext/class/attribute_accessors' require 'rabl-rails/version' require 'rabl-rails/template' require 'rabl-rails/condition' require 'rabl-rails/compiler' require 'rabl-rails/renderer' require 'rabl-rails/library' require 'rabl-rails/handler' require 'rabl-rails/railtie' require 'multi_json' module RablRails extend Renderer autoload :Responder, 'rabl-rails/responder' mattr_accessor :cache_templates @@cache_templates = true mattr_accessor :include_json_root @@include_json_root = true mattr_accessor :use_custom_responder @@use_custom_responder = false mattr_accessor :responder_default_template @@responder_default_template = 'show' mattr_reader :plist_engine @@plist_engine = nil mattr_accessor :include_plist_root @@include_plist_root = nil mattr_accessor :enable_jsonp_callbacks @@enable_jsonp_callbacks = false def self.configure yield self ActionController::Base.responder = Responder if self.use_custom_responder end def self.json_engine=(name) MultiJson.engine = name rescue LoadError 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.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.plist_engine=(name) raise "Your plist engine does not respond to #dump" unless name.respond_to?(:dump) @@plist_engine = name end def self.cache_templates? ActionController::Base.perform_caching && @@cache_templates end def self.load_default_engines! self.json_engine = MultiJson.default_engine self.plist_engine = Plist::Emit if defined?(Plist) if defined?(LibXML) self.xml_engine = 'LibXML' elsif defined?(Nokogiri) self.xml_engine = 'Nokogiri' end end end �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������rabl-rails-0.3.2/lib/rabl-rails/��������������������������������������������������������������������0000755�0001750�0001750�00000000000�12151754461�015726� 5����������������������������������������������������������������������������������������������������ustar �boutil��������������������������boutil�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������rabl-rails-0.3.2/lib/rabl-rails/library.rb����������������������������������������������������������0000644�0001750�0001750�00000002062�12151754461�017717� 0����������������������������������������������������������������������������������������������������ustar �boutil��������������������������boutil�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'singleton' module RablRails class Library include Singleton def initialize @cached_templates = {} end 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] ? context.params[:format].to_s.upcase : :JSON Renderers.const_get(format).new(context, locals).render(compiled_template) end def compile_template_from_source(source, path = nil) if path && RablRails.cache_templates? @cached_templates[path] ||= Compiler.new.compile_source(source) @cached_templates[path].dup else Compiler.new.compile_source(source) end end def compile_template_from_path(path) return @cached_templates[path].dup if @cached_templates.has_key?(path) t = @lookup_context.find_template(path, [], false) compile_template_from_source(t.source, path) end end end������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������rabl-rails-0.3.2/lib/rabl-rails/handler.rb����������������������������������������������������������0000644�0001750�0001750�00000000517�12151754461�017673� 0����������������������������������������������������������������������������������������������������ustar �boutil��������������������������boutil�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������module RablRails module Handlers class Rabl cattr_accessor :default_format self.default_format = 'application/json' def self.call(template) %{ RablRails::Library.instance. get_rendered_template(#{template.source.inspect}, self, local_assigns) } end end end end���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������rabl-rails-0.3.2/lib/rabl-rails/template.rb���������������������������������������������������������0000644�0001750�0001750�00000000430�12151754461�020063� 0����������������������������������������������������������������������������������������������������ustar �boutil��������������������������boutil�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������module RablRails class CompiledTemplate attr_accessor :source, :data, :root_name delegate :[], :[]=, :merge!, :to => :source def initialize @source = {} end def initialize_dup(other) super self.source = other.source.dup end end end����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������rabl-rails-0.3.2/lib/rabl-rails/renderers/����������������������������������������������������������0000755�0001750�0001750�00000000000�12151754461�017717� 5����������������������������������������������������������������������������������������������������ustar �boutil��������������������������boutil�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������rabl-rails-0.3.2/lib/rabl-rails/renderers/base.rb���������������������������������������������������0000644�0001750�0001750�00000011457�12151754461�021166� 0����������������������������������������������������������������������������������������������������ustar �boutil��������������������������boutil�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������module RablRails module Renderers class PartialError < StandardError; end class Base attr_accessor :_options def initialize(context, locals = nil) # :nodoc: @_context = context @_options = {} @_resource = locals[:resource] if locals 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 = if template.data if @_context.respond_to?(template.data) @_context.send(template.data) else instance_variable_get(template.data) end end 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 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, (key, value)| out = case value when Symbol data.send(value) # attributes when Proc result = instance_exec data, &value if key.to_s.start_with?('_') # merge raise PartialError, '`merge` block should return a hash' unless result.is_a?(Hash) output.merge!(result) next output else # node result end 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 = 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 output.merge!(render_resource(object, current_value)) next output else # child if object object.respond_to?(:each) ? render_collection(object, current_value) : render_resource(object, current_value) else nil end end when Condition if instance_exec data, &(value.proc) output.merge!(render_resource(data, value.source)) end next output end output[key] = out output } end def params @_context.params 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 # # Allow to use partial inside of node blocks (they are evaluated at) # rendering time. # def partial(template_path, options = {}) 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? template = Library.instance.compile_template_from_path(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.to_s.start_with?('_') } end end end end�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������rabl-rails-0.3.2/lib/rabl-rails/renderers/plist.rb��������������������������������������������������0000644�0001750�0001750�00000000407�12151754461�021400� 0����������������������������������������������������������������������������������������������������ustar �boutil��������������������������boutil�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������module RablRails module Renderers class PLIST < Base def format_output(hash) hash = { _options[:root_name] => hash } if _options[:root_name] && RablRails.include_plist_root RablRails.plist_engine.dump(hash) end end end end���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������rabl-rails-0.3.2/lib/rabl-rails/renderers/json.rb���������������������������������������������������0000644�0001750�0001750�00000000561�12151754461�021217� 0����������������������������������������������������������������������������������������������������ustar �boutil��������������������������boutil�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������module RablRails module Renderers class JSON < Base def format_output(hash) hash = { _options[:root_name] => hash } if _options[:root_name] && RablRails.include_json_root json = MultiJson.encode(hash) RablRails.enable_jsonp_callbacks && params.has_key?(:callback) ? "#{params[:callback]}(#{json})" : json end end end end�����������������������������������������������������������������������������������������������������������������������������������������������rabl-rails-0.3.2/lib/rabl-rails/renderers/xml.rb����������������������������������������������������0000644�0001750�0001750�00000000517�12151754461�021047� 0����������������������������������������������������������������������������������������������������ustar �boutil��������������������������boutil�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'active_support/core_ext/hash/conversions' module RablRails module Renderers class XML < Base DEFAULT_OPTIONS = { dasherize: true, skip_types: false } def format_output(hash) xml_options = { root: _options[:root_name] }.merge!(DEFAULT_OPTIONS) hash.to_xml(xml_options) end end end end���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������rabl-rails-0.3.2/lib/rabl-rails/condition.rb��������������������������������������������������������0000644�0001750�0001750�00000000237�12151754461�020243� 0����������������������������������������������������������������������������������������������������ustar �boutil��������������������������boutil�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������module RablRails class Condition attr_reader :proc, :source def initialize(proc, source) @proc = proc @source = source end end end�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������rabl-rails-0.3.2/lib/rabl-rails/railtie.rb����������������������������������������������������������0000644�0001750�0001750�00000000452�12151754461�017705� 0����������������������������������������������������������������������������������������������������ustar �boutil��������������������������boutil�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������module RablRails class Railtie < Rails::Railtie initializer "rabl.initialize" do |app| RablRails.load_default_engines! ActiveSupport.on_load(:action_view) do ActionView::Template.register_template_handler :rabl, RablRails::Handlers::Rabl end end end end����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������rabl-rails-0.3.2/lib/rabl-rails/compiler.rb���������������������������������������������������������0000644�0001750�0001750�00000011131�12151754461�020062� 0����������������������������������������������������������������������������������������������������ustar �boutil��������������������������boutil�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������module RablRails # # Class that will compile RABL source code into a hash # representing data structure # class Compiler def initialize @i = -1 end # # Compile from source code and return the CompiledTemplate # created. # def compile_source(source) @template = CompiledTemplate.new instance_eval(source) @template end # # Sets the object to be used as the data for the template # Example: # object :@user # object :@user, :root => :author # def object(data, options = {}) @template.data, @template.root_name = extract_data_and_name(data) @template.root_name = options[:root] if options.has_key? :root end alias_method :collection, :object def root(name) @template.root_name = name end # # Includes the attribute or method in the output # Example: # attributes :id, :name # attribute :email => :super_secret # def attribute(*args) if args.first.is_a?(Hash) args.first.each_pair { |k, v| @template[v] = k } else options = args.extract_options! args.each { |name| key = options[:as] || name @template[key] = name } end end alias_method :attributes, :attribute # # Creates a child node to be included in the output. # name_or data can be an object or collection or a method to call on the data. It # accepts :root and :partial options. # Notes that partial and blocks are not compatible # Example: # child(:@posts, :root => :posts) { attribute :id } # child(:posts, :partial => 'posts/base') # def child(name_or_data, options = {}) data, name = extract_data_and_name(name_or_data) name = options[:root] if options.has_key? :root if options[:partial] template = Library.instance.compile_template_from_path(options[:partial]) @template[name] = template.merge!(:_data => data) elsif block_given? @template[name] = sub_compile(data) { yield } end end # # Glues data from a child node to the output # Example: # glue(:@user) { attribute :name } # def glue(data) return unless block_given? @template[sequence('glue')] = sub_compile(data) { yield } end # # Creates an arbitrary node in the json output. # It accepts :if option to create conditionnal nodes. The current data will # be passed to the block so it is advised to use it instead of ivars. # Example: # node(:name) { |user| user.first_name + user.last_name } # node(:role, if: ->(u) { !u.admin? }) { |u| u.role } # def node(name = nil, options = {}, &block) name ||= sequence('merge') condition = options[:if] if condition if condition.is_a?(Proc) @template[name] = [condition, block] else @template[name] = block if condition end else @template[name] = block end end alias_method :code, :node # # Merge arbitrary data into json output. Given block should # return a hash. # Example: # merge { |item| partial("specific/#{item.to_s}", object: item) } # def merge(&block) return unless block_given? node(sequence('merge'), &block) end # # Extends an existing rabl template # Example: # extends 'users/base' # def extends(path) t = Library.instance.compile_template_from_path(path) @template.merge!(t.source) end # # Provide a conditionnal block # # condition(->(u) { u.is_a?(Admin) }) do # attributes :secret # end # def condition(proc) return unless block_given? @template[sequence('if')] = Condition.new(proc, sub_compile(nil) { yield }) end protected # # Return unique symbol starting with given name # def sequence(name) @i += 1 :"_#{name}#{@i}" end # # Extract data root_name and root name # Example: # :@users -> [:@users, nil] # :@users => :authors -> [:@users, :authors] # def extract_data_and_name(name_or_data) case name_or_data when Symbol str = name_or_data.to_s str.start_with?('@') ? [name_or_data, str[1..-1]] : [name_or_data, name_or_data] when Hash name_or_data.first else name_or_data end end def sub_compile(data) return {} unless block_given? old_template, @template = @template, {} yield data ? @template.merge!(:_data => data) : @template ensure @template = old_template end end end���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������rabl-rails-0.3.2/lib/rabl-rails/renderer.rb���������������������������������������������������������0000644�0001750�0001750�00000004653�12151754461�020071� 0����������������������������������������������������������������������������������������������������ustar �boutil��������������������������boutil�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'rabl-rails/renderers/base' require 'rabl-rails/renderers/json' require 'rabl-rails/renderers/xml' require 'rabl-rails/renderers/plist' module RablRails module Renderer class TemplateNotFound < StandardError; end mattr_reader :view_path @@view_path = 'app/views' class LookupContext T = Struct.new(:source) def initialize(view_path, format) @view_path = view_path || RablRails::Renderer.view_path @format = format.to_s.downcase end # # Manually find given rabl template file with given format. # View path can be set via options, otherwise default Rails # path is used # def find_template(name, opt, partial = false) path = File.join(@view_path, "#{name}.#{@format}.rabl") File.exists?(path) ? T.new(File.read(path)) : nil end end # # Context class to emulate normal Rails view # context # class Context attr_reader :format def initialize(path, options) @virtual_path = path @format = options.delete(:format) || 'json' @_assigns = {} @options = options options[:locals].each { |k, v| @_assigns[k.to_s] = v } if options[:locals] end def assigns @_assigns end def params { format: format } end def lookup_context @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 # defined in the controller), you can passed them as locals # options. # For example, if you have this template: # object :@user # node(:read) { |u| u.has_read?(@post) } # # Your method call should look like this: # RablRails.render(user, 'users/show', locals: { post: Post.new }) # def render(object, template, options = {}) object = options[:locals].delete(:object) if !object && options[:locals] c = Context.new(template, options) t = c.lookup_context.find_template(template, [], false) raise TemplateNotFound unless t Library.instance.get_rendered_template(t.source, c, resource: object) end end end�������������������������������������������������������������������������������������rabl-rails-0.3.2/lib/rabl-rails/responder.rb��������������������������������������������������������0000644�0001750�0001750�00000002232�12151754461�020253� 0����������������������������������������������������������������������������������������������������ustar �boutil��������������������������boutil�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������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 def initialize(controller, resources, options = {}) super if options[:locals] options[:locals][:resource] = resource else options[:locals] = { resource: resource } 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) if post? template = if controller.respond_to?(:responder_default_template, true) controller.send(:responder_default_template) else RablRails.responder_default_template end options[:prefixes] = controller._prefixes options[:template] ||= template controller.default_render options.merge(status: :created) else head :no_content end rescue ActionView::MissingTemplate => e super(e) end end end����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������rabl-rails-0.3.2/lib/rabl-rails/version.rb����������������������������������������������������������0000644�0001750�0001750�00000000051�12151754461�017734� 0����������������������������������������������������������������������������������������������������ustar �boutil��������������������������boutil�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������module RablRails VERSION = '0.3.2' end ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������rabl-rails-0.3.2/MIT-LICENSE������������������������������������������������������������������������0000644�0001750�0001750�00000002052�12151754461�014563� 0����������������������������������������������������������������������������������������������������ustar �boutil��������������������������boutil�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Copyright 2012 Christopher Cocchi-Perrier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������rabl-rails-0.3.2/.gitignore�������������������������������������������������������������������������0000644�0001750�0001750�00000000076�12151754461�015123� 0����������������������������������������������������������������������������������������������������ustar �boutil��������������������������boutil�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������## General log doc rdoc ## Bundler .bundle pkg Gemfile.lock ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������