diff --git a/lib/jbuilder.rb b/lib/jbuilder.rb index d1e3140..aa4bf46 100644 --- a/lib/jbuilder.rb +++ b/lib/jbuilder.rb @@ -32,7 +32,7 @@ def set!(key, value = BLANK, *args, &block) else # json.comments { ... } # { "comments": ... } - _scope{ yield self } + _merge_block(key){ yield self } end elsif args.empty? if ::Jbuilder === value @@ -52,7 +52,7 @@ def set!(key, value = BLANK, *args, &block) else # json.author @post.creator, :name, :email_address # { "author": { "name": "David", "email_address": "david@loudthinking.com" } } - _scope{ extract! value, *args } + _merge_block(key){ extract! value, *args } end _set_value key, result @@ -174,13 +174,15 @@ def child! # # [1,2,3] def array!(collection = [], *attributes, &block) - @attributes = if block + array = if block _map_collection(collection, &block) elsif attributes.any? _map_collection(collection) { |element| extract! element, *attributes } else collection end + + merge! array end # Extracts the mentioned attributes or hash elements from the passed object and turns them into attributes of the JSON. @@ -230,12 +232,7 @@ def attributes! # Merges hash or array into current builder. def merge!(hash_or_array) - if ::Array === hash_or_array - @attributes = [] unless ::Array === @attributes - @attributes.concat hash_or_array - else - @attributes.update hash_or_array - end + @attributes = _merge_values(@attributes, hash_or_array) end # Encodes the current builder as JSON. @@ -253,12 +250,29 @@ def _extract_method_values(object, *attributes) attributes.each{ |key| _set_value key, object.public_send(key) } end + def _merge_block(key, &block) + current_value = _read(key, {}) + raise NullError.build(key) if current_value.nil? + value = _scope{ yield self } + value.nil? ? value : _merge_values(current_value, value) + end + + def _read(key, default = nil) + @attributes.fetch(_key(key)){ default } + end + + def _write(key, value) + @attributes[_key(key)] = value + end + + def _key(key) + @key_formatter.format(key) + end + def _set_value(key, value) raise NullError.build(key) if @attributes.nil? return if @ignore_nil && value.nil? - - key = @key_formatter.format(key) - @attributes[key] = value + _write key, value end def _map_collection(collection) @@ -281,6 +295,19 @@ def _scope def _mapable_arguments?(value, *args) value.respond_to?(:map) end + + def _merge_values(attributes, hash_or_array) + attributes = attributes.dup + + if ::Array === hash_or_array + attributes = [] unless ::Array === attributes + attributes.concat hash_or_array + else + attributes.update hash_or_array + end + + attributes + end end require 'jbuilder/jbuilder_template' if defined?(ActionView::Template) diff --git a/test/jbuilder_test.rb b/test/jbuilder_test.rb index ddb1129..66b77fe 100644 --- a/test/jbuilder_test.rb +++ b/test/jbuilder_test.rb @@ -121,6 +121,42 @@ class JbuilderTest < ActiveSupport::TestCase assert_equal 32, result['author']['age'] end + test 'blocks are additive' do + result = jbuild do |json| + json.author do + json.name 'David' + end + + json.author do + json.age 32 + end + end + + assert_equal 'David', result['author']['name'] + assert_equal 32, result['author']['age'] + end + + test 'blocks are additive via extract syntax' do + person = Person.new('Pavel', 27) + + result = jbuild do |json| + json.author person, :age + json.author person, :name + end + + assert_equal 'Pavel', result['author']['name'] + assert_equal 27, result['author']['age'] + end + + test 'arrays are additive' do + result = jbuild do |json| + json.array! %w[foo] + json.array! %w[bar] + end + + assert_equal %w[foo bar], result + end + test 'nesting multiple children with block' do result = jbuild do |json| json.comments do @@ -531,9 +567,40 @@ class JbuilderTest < ActiveSupport::TestCase assert_nil result end - test 'throws meaningfull error when on trying to add properties to null' do + test 'null! in a block' do + result = jbuild do |json| + json.author do + json.name 'David' + end + + json.author do + json.null! + end + end + + assert result.key?('author') + assert_nil result['author'] + end + + test 'throws NullError when trying to add properties to null' do json = Jbuilder.new json.null! - assert_raise(Jbuilder::NullError) { json.foo 'bar' } + assert_raise Jbuilder::NullError do + json.foo 'bar' + end + end + + test 'throws NullError when trying to add properties to null using block syntax' do + assert_raise Jbuilder::NullError do + jbuild do |json| + json.author do + json.null! + end + + json.author do + json.name "Pavel" + end + end + end end end