Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 58 additions & 47 deletions lib/jbuilder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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": "[email protected]" } }
_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
Expand Down Expand Up @@ -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.
Expand All @@ -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
Expand Down Expand Up @@ -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": "[email protected]" } }
_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)
Expand Down
22 changes: 11 additions & 11 deletions lib/jbuilder/jbuilder_template.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand All @@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down