diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d5c73bd..d1a734a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,9 @@ * Alias to\_msgpack to to\_mpac * Cache template sources for faster partial lookups (thanks cj) * Adds BSON format support (thanks Antiarchitect) - * Use Rails template lookup mechanism to find templates (in Rails 3) + * Use template lookup mechanism to find templates in Rails 3 (thanks blakewatters) + * Adds a 'object_root' option to collection (thanks blakewatters) + * Adds a 'root_name' option to collection ## 0.5.4 diff --git a/README.md b/README.md index 2b6e8fb6..06fc70e9 100644 --- a/README.md +++ b/README.md @@ -216,13 +216,20 @@ collection @users # => [ { "user" : { ... } } ] ``` -or even specify a root node label for the collection: +or specify a root node label for the collection: ```ruby collection @users => :people # => { "people" : [ { "person" : { ... } } ] } ``` +or even specify both the child and root labels for a collection: + +```ruby +collection @users, :root_name => "people", :object_root => "user" +# => { "people" : [ { "user" : { ... } } ] } +``` + and this will be used as the default data for the rendering. There can also be odd cases where the root-level of the response doesn't map directly to any object: diff --git a/lib/rabl/builder.rb b/lib/rabl/builder.rb index 75750b03..f3216425 100644 --- a/lib/rabl/builder.rb +++ b/lib/rabl/builder.rb @@ -3,25 +3,26 @@ class Builder include Rabl::Partials # Constructs a new rabl hash based on given object and options - # options = { :format => "json", :attributes, :root => true, - # :child_root => true, :node, :child, :glue, :extends } + # options = { :format => "json", :root => true, :child_root => true, + # :attributes, :node, :child, :glue, :extends } + # def initialize(options={}, &block) @options = options @_scope = options[:scope] end # Given an object and options, returns the hash representation - # build(@user, :format => "json", :attributes => { ... }) - def build(data, options={}) - @_data = data - @_object = data_object(data) + # build(@user, :format => "json", :attributes => { ... }, :root_name => "user") + def build(object, options={}) + @_object = object compile_hash(options) end protected # Returns a hash representation of the data object - # compile_hash(:root => true) + # compile_hash(:root_name => false) + # compile_hash(:root_name => "user") def compile_hash(options={}) @_result = {} # Extends @@ -46,8 +47,8 @@ def compile_hash(options={}) end if @options.has_key?(:glue) # Wrap result in root - if @options[:root] || options[:root] - @_root_name ||= data_name(@_data) + if options[:root_name].present? + @_root_name = options[:root_name] else # no root @_root_name = nil end diff --git a/lib/rabl/engine.rb b/lib/rabl/engine.rb index f13fa06f..a9f6596d 100644 --- a/lib/rabl/engine.rb +++ b/lib/rabl/engine.rb @@ -31,17 +31,15 @@ def render(scope, locals, &block) # to_hash(:root => true, :child_root => true) def to_hash(options={}) options = @_options.merge(options) - data = data_object(@_data) + data, root_name = data_object(@_data), data_name(@_data) builder = Rabl::Builder.new(options) if is_object?(data) || !data # object @user - builder.build(@_data, options) + options[:root_name] = root_name if options[:root] + builder.build(data, options) elsif is_collection?(data) # collection @users - if options[:root] # only calculate root name if needed - object_name = data_name(@_data).to_s.singularize # @users => :users - data.map { |object| builder.build({ object => object_name }, options) } - else # skip root name - data.map { |object| builder.build(object, options) } - end + options[:root_name] = object_root_name if object_root_name + options[:root_name] ||= root_name.to_s.singularize if options[:root] + data.map { |object| builder.build(object, options) } end end @@ -99,8 +97,12 @@ def object(data) # Sets the object as a collection casted to a simple array # collection @users # collection @users => :people - def collection(data) - @_collection_name = data.values.first if data.respond_to?(:each_pair) + # collection @users, :root => :person + # collection @users, :object_root => :person + def collection(data, options={}) + @_collection_name = options[:root] if options[:root] + @_collection_name ||= data.values.first if data.respond_to?(:each_pair) + @_object_root_name = options[:object_root] if options[:object_root] self.object(data_object(data).to_a) if data end diff --git a/test/builder_test.rb b/test/builder_test.rb index 14c2e1d2..36e7ce50 100644 --- a/test/builder_test.rb +++ b/test/builder_test.rb @@ -28,7 +28,7 @@ setup { builder({ :attributes => { :name => :name } }) } asserts "that the object is set properly" do - topic.build(User.new, :root => true) + topic.build(User.new, :root_name => "user") end.equivalent_to({ "user" => { :name => "rabl" } }) end @@ -37,7 +37,7 @@ setup { builder({ :attributes => { :name => :name } }) } asserts "that the object is set properly" do - topic.build({ User.new => "person" }, :root => true) + topic.build(User.new, :root_name => "person") end.equivalent_to({ "person" => { :name => "rabl" } }) end diff --git a/test/engine_test.rb b/test/engine_test.rb index a344d979..0afc2804 100644 --- a/test/engine_test.rb +++ b/test/engine_test.rb @@ -263,15 +263,32 @@ template.render(scope) end.equals "[{},{}]" - asserts "that it sets root node for objects" do + asserts "that it sets root node for objects using hash" do template = rabl %{ - collection @users => :person + collection @users => :people } scope = Object.new scope.instance_variable_set :@users, [User.new, User.new] template.render(scope) - end.equals "{\"person\":[{},{}]}" + end.equals "{\"people\":[{},{}]}" + asserts "that it sets root node for objects using root option" do + template = rabl %{ + collection @users, :root => :people + } + scope = Object.new + scope.instance_variable_set :@users, [User.new, User.new] + template.render(scope) + end.equals "{\"people\":[{},{}]}" + + asserts "that it sets root node for objects using object_root option" do + template = rabl %{ + collection @users, :root => :humans, :object_root => :person + } + scope = Object.new + scope.instance_variable_set :@users, [User.new, User.new] + template.render(scope) + end.equals %Q^{"humans":[{"person":{}},{"person":{}}]}^ end context "#attribute" do