diff --git a/lib/jbuilder.rb b/lib/jbuilder.rb index 1290839..61b7b50 100644 --- a/lib/jbuilder.rb +++ b/lib/jbuilder.rb @@ -7,6 +7,7 @@ require 'jbuilder/errors' require 'json' require 'active_support/core_ext/hash/deep_merge' +require 'active_support/core_ext/object/blank' class Jbuilder @@key_formatter = nil @@ -32,41 +33,12 @@ def self.encode(...) new(...).target! end - BLANK = Blank.new + BLANK = Blank.new.freeze + EMPTY_ARRAY = [].freeze + private_constant :BLANK, :EMPTY_ARRAY def set!(key, value = BLANK, *args, &block) - result = if ::Kernel.block_given? - if !_blank?(value) - # json.comments @post.comments { |comment| ... } - # { "comments": [ { ... }, { ... } ] } - _scope{ array! value, &block } - else - # json.comments { ... } - # { "comments": ... } - _merge_block(key){ yield self } - end - elsif args.empty? - if ::Jbuilder === value - # json.age 32 - # json.person another_jbuilder - # { "age": 32, "person": { ... } - _format_keys(value.attributes!) - else - # json.age 32 - # { "age": 32 } - _format_keys(value) - end - elsif _is_collection?(value) - # json.comments @post.comments, :content, :created_at - # { "comments": [ { "content": "hello", "created_at": "..." }, { "content": "world", "created_at": "..." } ] } - _scope{ array! value, *args } - else - # json.author @post.creator, :name, :email_address - # { "author": { "name": "David", "email_address": "david@loudthinking.com" } } - _merge_block(key){ _extract value, args } - end - - _set_value key, result + _set(key, value, args, &block) end # Specifies formatting to be applied to the key. Passing in a name of a function @@ -206,18 +178,8 @@ def child! # json.array! [1, 2, 3] # # [1,2,3] - def array!(collection = [], *attributes, &block) - array = if collection.nil? - [] - elsif ::Kernel.block_given? - _map_collection(collection, &block) - elsif attributes.any? - _map_collection(collection) { |element| _extract element, attributes } - else - _format_keys(collection.to_a) - end - - @attributes = _merge_values(@attributes, array) + def array!(collection = EMPTY_ARRAY, *attributes, &block) + _array collection, attributes, &block end # Extracts the mentioned attributes or hash elements from the passed object and turns them into attributes of the JSON. @@ -242,8 +204,8 @@ def extract!(object, *attributes) end def call(object, *attributes, &block) - if ::Kernel.block_given? - array! object, &block + if block + _array object, &block else _extract object, attributes end @@ -276,6 +238,55 @@ def target! alias_method :method_missing, :set! + def _set(key, value = BLANK, attributes = nil, &block) + result = if block + if _blank?(value) + # json.comments { ... } + # { "comments": ... } + _merge_block key, &block + else + # json.comments @post.comments { |comment| ... } + # { "comments": [ { ... }, { ... } ] } + _scope { _array value, &block } + end + elsif attributes.empty? + if ::Jbuilder === value + # json.age 32 + # json.person another_jbuilder + # { "age": 32, "person": { ... } + _format_keys(value.attributes!) + else + # json.age 32 + # { "age": 32 } + _format_keys(value) + end + elsif _is_collection?(value) + # json.comments @post.comments, :content, :created_at + # { "comments": [ { "content": "hello", "created_at": "..." }, { "content": "world", "created_at": "..." } ] } + _scope { _array value, attributes } + else + # json.author @post.creator, :name, :email_address + # { "author": { "name": "David", "email_address": "david@loudthinking.com" } } + _merge_block(key) { _extract value, attributes } + end + + _set_value key, result + end + + def _array(collection = EMPTY_ARRAY, attributes = nil, &block) + array = if collection.nil? + EMPTY_ARRAY + elsif block + _map_collection(collection, &block) + elsif attributes.present? + _map_collection(collection) { |element| _extract element, attributes } + else + _format_keys(collection.to_a) + end + + @attributes = _merge_values(@attributes, array) + end + def _extract(object, attributes) if ::Hash === object _extract_hash_values(object, attributes) diff --git a/lib/jbuilder/jbuilder_template.rb b/lib/jbuilder/jbuilder_template.rb index 855dd67..53e4c22 100644 --- a/lib/jbuilder/jbuilder_template.rb +++ b/lib/jbuilder/jbuilder_template.rb @@ -52,7 +52,7 @@ def initialize(context, options = nil) # json.comments @post.comments, partial: "comments/comment", as: :comment, cached: true # def partial!(*args) - if args.one? && _is_active_model?(args.first) + if _is_active_model?(args.first) _render_active_model_partial args.first else options = args.extract_options!.dup @@ -119,25 +119,25 @@ def target! @cached_root || super end - def array!(collection = [], *args) + def array!(collection = EMPTY_ARRAY, *args, &block) options = args.first - if args.one? && _partial_options?(options) + if _partial_options?(options) options = options.dup options[:collection] = collection _render_partial_with_options options else - super + _array collection, args, &block end end - def set!(name, object = BLANK, *args) + def set!(name, object = BLANK, *args, &block) options = args.first - if args.one? && _partial_options?(options) + if _partial_options?(options) _set_inline_partial name, object, options.dup else - super + _set name, object, args, &block end end @@ -151,7 +151,7 @@ def _render_partial_with_options(options) as = options[:as] if as && options.key?(:collection) - collection = options.delete(:collection) || [] + collection = options.delete(:collection) || EMPTY_ARRAY partial = options.delete(:partial) options[:locals][:json] = self collection = EnumerableCompat.new(collection) if collection.respond_to?(:count) && !collection.respond_to?(:size) @@ -169,9 +169,9 @@ def _render_partial_with_options(options) .new(@context.lookup_context, options) { |&block| _scope(&block) } .render_collection_with_partial(collection, partial, @context, nil) - array! if results.respond_to?(:body) && results.body.nil? + _array if results.respond_to?(:body) && results.body.nil? else - array! + _array end else _render_partial options @@ -233,7 +233,7 @@ def _is_active_model?(object) def _set_inline_partial(name, object, options) value = if object.nil? - [] + EMPTY_ARRAY elsif _is_collection?(object) _scope do options[:collection] = object