Skip to content

Commit 4d5bf7d

Browse files
committed
Add implicit partial rendering for AM::Models
Fixes #265
1 parent ce410ce commit 4d5bf7d

File tree

9 files changed

+104
-40
lines changed

9 files changed

+104
-40
lines changed

Appraisals

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,40 @@
11
appraise "rails-3-0" do
22
gem "test-unit"
3-
gem "railties", "~> 3.0.0"
4-
gem "actionpack", "~> 3.0.0"
3+
gem "railties", "~> 3.0.0"
4+
gem "actionpack", "~> 3.0.0"
5+
gem "activemodel", "~> 3.0.0"
56
end
67

78
appraise "rails-3-1" do
89
gem "test-unit"
9-
gem "railties", "~> 3.1.0"
10-
gem "actionpack", "~> 3.1.0"
10+
gem "railties", "~> 3.1.0"
11+
gem "actionpack", "~> 3.1.0"
12+
gem "activemodel", "~> 3.1.0"
1113
end
1214

1315
appraise "rails-3-2" do
1416
gem "test-unit"
15-
gem "railties", "~> 3.2.0"
16-
gem "actionpack", "~> 3.2.0"
17+
gem "railties", "~> 3.2.0"
18+
gem "actionpack", "~> 3.2.0"
19+
gem "activemodel", "~> 3.2.0"
1720
end
1821

1922
appraise "rails-4-0" do
20-
gem "railties", "~> 4.0.0"
21-
gem "actionpack", "~> 4.0.0"
23+
gem "railties", "~> 4.0.0"
24+
gem "actionpack", "~> 4.0.0"
25+
gem "activemodel", "~> 4.0.0"
2226
end
2327

2428
appraise "rails-4-1" do
25-
gem "railties", "~> 4.1.0"
26-
gem "actionpack", "~> 4.1.0"
29+
gem "railties", "~> 4.1.0"
30+
gem "actionpack", "~> 4.1.0"
31+
gem "activemodel", "~> 4.1.0"
2732
end
2833

2934
appraise "rails-4-2" do
30-
gem "railties", "~> 4.2.0"
31-
gem "actionpack", "~> 4.2.0"
35+
gem "railties", "~> 4.2.0"
36+
gem "actionpack", "~> 4.2.0"
37+
gem "activemodel", "~> 4.2.0"
3238
end
3339

3440
appraise "rails-edge" do

gemfiles/rails_3_0.gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ gem "pry"
99
gem "test-unit"
1010
gem "railties", "~> 3.0.0"
1111
gem "actionpack", "~> 3.0.0"
12+
gem "activemodel", "~> 3.0.0"
1213

1314
gemspec :path => "../"

gemfiles/rails_3_1.gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ gem "pry"
99
gem "test-unit"
1010
gem "railties", "~> 3.1.0"
1111
gem "actionpack", "~> 3.1.0"
12+
gem "activemodel", "~> 3.1.0"
1213

1314
gemspec :path => "../"

gemfiles/rails_3_2.gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ gem "pry"
99
gem "test-unit"
1010
gem "railties", "~> 3.2.0"
1111
gem "actionpack", "~> 3.2.0"
12+
gem "activemodel", "~> 3.2.0"
1213

1314
gemspec :path => "../"

gemfiles/rails_4_0.gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@ gem "appraisal"
88
gem "pry"
99
gem "railties", "~> 4.0.0"
1010
gem "actionpack", "~> 4.0.0"
11+
gem "activemodel", "~> 4.0.0"
1112

1213
gemspec :path => "../"

gemfiles/rails_4_1.gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@ gem "appraisal"
88
gem "pry"
99
gem "railties", "~> 4.1.0"
1010
gem "actionpack", "~> 4.1.0"
11+
gem "activemodel", "~> 4.1.0"
1112

1213
gemspec :path => "../"

gemfiles/rails_4_2.gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@ gem "appraisal"
88
gem "pry"
99
gem "railties", "~> 4.2.0"
1010
gem "actionpack", "~> 4.2.0"
11+
gem "activemodel", "~> 4.2.0"
1112

1213
gemspec :path => "../"

lib/jbuilder/jbuilder_template.rb

Lines changed: 50 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -11,28 +11,15 @@ class << self
1111

1212
def initialize(context, *args)
1313
@context = context
14-
super(*args)
14+
super *args
1515
end
1616

17-
def partial!(name_or_options, locals = {})
18-
case name_or_options
19-
when ::Hash
20-
# partial! partial: 'name', foo: 'bar'
21-
options = name_or_options
17+
def partial!(*args)
18+
if args.one? && _is_active_model?(args.first)
19+
_render_active_model_partial args.first
2220
else
23-
# partial! 'name', locals: {foo: 'bar'}
24-
if locals.one? && (locals.keys.first == :locals)
25-
options = locals.merge(partial: name_or_options)
26-
else
27-
options = { partial: name_or_options, locals: locals }
28-
end
29-
# partial! 'name', foo: 'bar'
30-
as = locals.delete(:as)
31-
options[:as] = as if as.present?
32-
options[:collection] = locals[:collection] if locals.key?(:collection)
21+
_render_explicit_partial *args
3322
end
34-
35-
_render_partial_with_options options
3623
end
3724

3825
# Caches the json constructed within the block passed. Has the same signature as the `cache` helper
@@ -81,18 +68,11 @@ def array!(collection = [], *args)
8168
def set!(name, object = BLANK, *args)
8269
options = args.first
8370

84-
return super unless args.one? && _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) }
71+
if args.one? && _partial_options?(options)
72+
_set_inline_partial name, object, options
9073
else
91-
locals = ::Hash[options[:as], object]
92-
_scope{ _render_partial options.merge(locals: locals) }
74+
super
9375
end
94-
95-
super name, value
9676
end
9777

9878
private
@@ -144,6 +124,48 @@ def _fragment_name_with_digest(key, options)
144124
def _partial_options?(options)
145125
::Hash === options && options.key?(:as) && options.key?(:partial)
146126
end
127+
128+
def _is_active_model?(object)
129+
object.class.respond_to?(:model_name) && object.respond_to?(:to_partial_path)
130+
end
131+
132+
def _set_inline_partial(name, object, options)
133+
value = if object.nil?
134+
[]
135+
elsif _is_collection?(object)
136+
_scope{ _render_partial_with_options options.merge(collection: object) }
137+
else
138+
locals = ::Hash[options[:as], object]
139+
_scope{ _render_partial options.merge(locals: locals) }
140+
end
141+
142+
set! name, value
143+
end
144+
145+
def _render_explicit_partial(name_or_options, locals = {})
146+
case name_or_options
147+
when ::Hash
148+
# partial! partial: 'name', foo: 'bar'
149+
options = name_or_options
150+
else
151+
# partial! 'name', locals: {foo: 'bar'}
152+
if locals.one? && (locals.keys.first == :locals)
153+
options = locals.merge(partial: name_or_options)
154+
else
155+
options = { partial: name_or_options, locals: locals }
156+
end
157+
# partial! 'name', foo: 'bar'
158+
as = locals.delete(:as)
159+
options[:as] = as if as.present?
160+
options[:collection] = locals[:collection] if locals.key?(:collection)
161+
end
162+
163+
_render_partial_with_options options
164+
end
165+
166+
def _render_active_model_partial(object)
167+
@context.render object, json: self
168+
end
147169
end
148170

149171
class JbuilderHandler

test/jbuilder_template_test.rb

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
require "test_helper"
22
require "mocha/setup"
3+
require "active_model"
34
require "action_view"
45
require "action_view/testing/resolvers"
56
require "active_support/cache"
@@ -18,6 +19,22 @@
1819
json.extract! collection, :id, :name
1920
JBUILDER
2021

22+
RACER_PARTIAL = <<-JBUILDER
23+
json.extract! racer, :id, :name
24+
JBUILDER
25+
26+
class Racer
27+
extend ActiveModel::Naming
28+
include ActiveModel::Conversion
29+
30+
def initialize(id, name)
31+
@id, @name = id, name
32+
end
33+
34+
attr_reader :id, :name
35+
end
36+
37+
2138
BlogPost = Struct.new(:id, :body, :author_name)
2239
Collection = Struct.new(:id, :name)
2340
blog_authors = [ "David Heinemeier Hansson", "Pavel Pravosud" ].cycle
@@ -29,6 +46,7 @@
2946
PARTIALS = {
3047
"_partial.json.jbuilder" => "foo ||= 'hello'; json.content foo",
3148
"_blog_post.json.jbuilder" => BLOG_POST_PARTIAL,
49+
"racers/_racer.json.jbuilder" => RACER_PARTIAL,
3250
"_collection.json.jbuilder" => COLLECTION_PARTIAL
3351
}
3452

@@ -350,4 +368,16 @@ def assert_collection_rendered(result, context = nil)
350368
assert_equal "post body 1", result["post"]["body"]
351369
assert_equal "David", result["post"]["author"]["first_name"]
352370
end
371+
372+
test "invokes templates implicitly for ActiveModel objects" do
373+
@racer = Racer.new(123, "Chris Harris")
374+
375+
result = jbuild(<<-JBUILDER)
376+
json.partial! @racer
377+
JBUILDER
378+
379+
assert_equal %w[id name], result.keys
380+
assert_equal 123, result["id"]
381+
assert_equal "Chris Harris", result["name"]
382+
end
353383
end

0 commit comments

Comments
 (0)