Skip to content

Commit 1feda7e

Browse files
committed
Add new in-place partial invocation support
Fixes #266
1 parent 45f6dac commit 1feda7e

File tree

4 files changed

+61
-28
lines changed

4 files changed

+61
-28
lines changed

lib/jbuilder.rb

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ def self.encode(*args, &block)
2323
end
2424

2525
BLANK = Blank.new
26+
NON_ENUMERABLES = [ ::Struct, ::OpenStruct ].to_set
2627

2728
def set!(key, value = BLANK, *args)
2829
result = if ::Kernel.block_given?
@@ -46,7 +47,7 @@ def set!(key, value = BLANK, *args)
4647
# { "age": 32 }
4748
value
4849
end
49-
elsif _mapable_arguments?(value, *args)
50+
elsif _is_collection?(value)
5051
# json.comments @post.comments, :content, :created_at
5152
# { "comments": [ { "content": "hello", "created_at": "..." }, { "content": "world", "created_at": "..." } ] }
5253
_scope{ array! value, *args }
@@ -59,8 +60,13 @@ def set!(key, value = BLANK, *args)
5960
_set_value key, result
6061
end
6162

62-
alias_method :method_missing, :set!
63-
private :method_missing
63+
def method_missing(*args)
64+
if ::Kernel.block_given?
65+
set! *args, &::Proc.new
66+
else
67+
set! *args
68+
end
69+
end
6470

6571
# Specifies formatting to be applied to the key. Passing in a name of a function
6672
# will cause that function to be called on the key. So :upcase will upper case
@@ -301,13 +307,17 @@ def _scope
301307
@attributes, @key_formatter = parent_attributes, parent_formatter
302308
end
303309

304-
def _mapable_arguments?(value, *args)
305-
value.respond_to?(:map)
310+
def _is_collection?(object)
311+
_object_respond_to?(object, :map, :count) && NON_ENUMERABLES.none?{ |klass| klass === object }
306312
end
307313

308314
def _blank?(value=@attributes)
309315
BLANK == value
310316
end
317+
318+
def _object_respond_to?(object, *methods)
319+
methods.all?{ |m| object.respond_to?(m) }
320+
end
311321
end
312322

313323
require 'jbuilder/railtie' if defined?(Rails)

lib/jbuilder/jbuilder_template.rb

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,6 @@ def partial!(name_or_options, locals = {})
3535
_render_partial_with_options options
3636
end
3737

38-
def array!(collection = [], *attributes)
39-
options = attributes.extract_options!
40-
41-
if options.key?(:partial)
42-
partial! options[:partial], options.merge(collection: collection)
43-
else
44-
super
45-
end
46-
end
47-
4838
# Caches the json constructed within the block passed. Has the same signature as the `cache` helper
4939
# method in `ActionView::Helpers::CacheHelper` and so can be used in the same way.
5040
#
@@ -78,7 +68,34 @@ def cache_if!(condition, *args)
7868
condition ? cache!(*args, &::Proc.new) : yield
7969
end
8070

81-
protected
71+
def array!(collection = [], *attributes)
72+
options = attributes.extract_options!
73+
74+
if options.key?(:partial)
75+
partial! options[:partial], options.merge(collection: collection)
76+
else
77+
super
78+
end
79+
end
80+
81+
def set!(name, object = BLANK, *args)
82+
options = args.first
83+
84+
return super unless _partial_options?(options)
85+
86+
value = if object.nil?
87+
[]
88+
elsif _is_collection?(object)
89+
_scope{ _render_partial_with_options options.merge(collection: object) }
90+
else
91+
locals = ::Hash[options[:as], object]
92+
_scope{ _render_partial options.merge(locals: locals) }
93+
end
94+
95+
super name, value
96+
end
97+
98+
private
8299

83100
def _render_partial_with_options(options)
84101
options.reverse_merge! locals: {}
@@ -111,8 +128,6 @@ def _cache_key(key, options)
111128
::ActiveSupport::Cache.expand_cache_key(key, :jbuilder)
112129
end
113130

114-
private
115-
116131
def _fragment_name_with_digest(key, options)
117132
if @context.respond_to?(:cache_fragment_name)
118133
# Current compatibility, fragment_name_with_digest is private again and cache_fragment_name
@@ -126,10 +141,8 @@ def _fragment_name_with_digest(key, options)
126141
end
127142
end
128143

129-
def _mapable_arguments?(value, *args)
130-
return true if super
131-
options = args.last
132-
::Hash === options && options.key?(:as)
144+
def _partial_options?(options)
145+
::Hash === options && options.key?(:as) && options.key?(:partial)
133146
end
134147
end
135148

test/jbuilder_template_test.rb

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
BLOG_POST_PARTIAL = <<-JBUILDER
99
json.extract! blog_post, :id, :body
1010
json.author do
11-
name = blog_post.author_name.split(nil, 2)
12-
json.first_name name[0]
13-
json.last_name name[1]
11+
first_name, last_name = blog_post.author_name.split(nil, 2)
12+
json.first_name first_name
13+
json.last_name last_name
1414
end
1515
JBUILDER
1616

@@ -338,4 +338,16 @@ def assert_collection_rendered(result, context = nil)
338338

339339
assert_equal Rails.cache.inspect[/entries=(\d+)/, 1], "0"
340340
end
341+
342+
test "invokes templates via params via set!" do
343+
@post = BLOG_POST_COLLECTION.first
344+
345+
result = jbuild(<<-JBUILDER)
346+
json.post @post, partial: "blog_post", as: :blog_post
347+
JBUILDER
348+
349+
assert_equal 1, result["post"]["id"]
350+
assert_equal "post body 1", result["post"]["body"]
351+
assert_equal "David", result["post"]["author"]["first_name"]
352+
end
341353
end

test/jbuilder_test.rb

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,7 @@ def initialize(collection)
1313
@collection = collection
1414
end
1515

16-
def map(&block)
17-
@collection.map(&block)
18-
end
16+
delegate :map, :count, to: :@collection
1917
end
2018

2119
class VeryBasicWrapper < BasicObject

0 commit comments

Comments
 (0)