jbuilder 2.6.0 → 2.11.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.github/workflows/ruby.yml +108 -0
- data/.gitignore +2 -0
- data/Appraisals +16 -35
- data/CONTRIBUTING.md +9 -10
- data/Gemfile +0 -1
- data/MIT-LICENSE +1 -1
- data/README.md +100 -20
- data/Rakefile +2 -6
- data/gemfiles/rails_5_0.gemfile +3 -6
- data/gemfiles/rails_5_1.gemfile +10 -0
- data/gemfiles/rails_5_2.gemfile +10 -0
- data/gemfiles/rails_6_0.gemfile +10 -0
- data/gemfiles/rails_6_1.gemfile +10 -0
- data/gemfiles/rails_head.gemfile +10 -0
- data/jbuilder.gemspec +20 -6
- data/lib/generators/rails/jbuilder_generator.rb +12 -2
- data/lib/generators/rails/scaffold_controller_generator.rb +7 -1
- data/lib/generators/rails/templates/api_controller.rb +4 -4
- data/lib/generators/rails/templates/controller.rb +15 -19
- data/lib/generators/rails/templates/index.json.jbuilder +1 -1
- data/lib/generators/rails/templates/partial.json.jbuilder +16 -2
- data/lib/generators/rails/templates/show.json.jbuilder +1 -1
- data/lib/jbuilder/collection_renderer.rb +109 -0
- data/lib/jbuilder/errors.rb +7 -0
- data/lib/jbuilder/jbuilder_template.rb +90 -16
- data/lib/jbuilder/key_formatter.rb +2 -2
- data/lib/jbuilder/railtie.rb +1 -1
- data/lib/jbuilder.rb +70 -28
- data/test/jbuilder_dependency_tracker_test.rb +2 -2
- data/test/jbuilder_generator_test.rb +27 -7
- data/test/jbuilder_template_test.rb +281 -295
- data/test/jbuilder_test.rb +256 -4
- data/test/scaffold_api_controller_generator_test.rb +29 -14
- data/test/scaffold_controller_generator_test.rb +54 -21
- data/test/test_helper.rb +30 -8
- metadata +25 -31
- data/.travis.yml +0 -44
- data/CHANGELOG.md +0 -229
- data/gemfiles/rails_3_0.gemfile +0 -14
- data/gemfiles/rails_3_1.gemfile +0 -14
- data/gemfiles/rails_3_2.gemfile +0 -14
- data/gemfiles/rails_4_0.gemfile +0 -13
- data/gemfiles/rails_4_1.gemfile +0 -13
- data/gemfiles/rails_4_2.gemfile +0 -13
@@ -1,424 +1,410 @@
|
|
1
1
|
require "test_helper"
|
2
|
-
require "mocha/setup"
|
3
|
-
require "active_model"
|
4
|
-
require "action_view"
|
5
2
|
require "action_view/testing/resolvers"
|
6
|
-
require "active_support/cache"
|
7
|
-
require "jbuilder/jbuilder_template"
|
8
|
-
|
9
|
-
BLOG_POST_PARTIAL = <<-JBUILDER
|
10
|
-
json.extract! blog_post, :id, :body
|
11
|
-
json.author do
|
12
|
-
first_name, last_name = blog_post.author_name.split(nil, 2)
|
13
|
-
json.first_name first_name
|
14
|
-
json.last_name last_name
|
15
|
-
end
|
16
|
-
JBUILDER
|
17
3
|
|
18
|
-
|
19
|
-
|
20
|
-
|
4
|
+
class JbuilderTemplateTest < ActiveSupport::TestCase
|
5
|
+
POST_PARTIAL = <<-JBUILDER
|
6
|
+
json.extract! post, :id, :body
|
7
|
+
json.author do
|
8
|
+
first_name, last_name = post.author_name.split(nil, 2)
|
9
|
+
json.first_name first_name
|
10
|
+
json.last_name last_name
|
11
|
+
end
|
12
|
+
JBUILDER
|
21
13
|
|
22
|
-
|
23
|
-
|
24
|
-
JBUILDER
|
14
|
+
COLLECTION_PARTIAL = <<-JBUILDER
|
15
|
+
json.extract! collection, :id, :name
|
16
|
+
JBUILDER
|
25
17
|
|
26
|
-
|
27
|
-
|
28
|
-
|
18
|
+
RACER_PARTIAL = <<-JBUILDER
|
19
|
+
json.extract! racer, :id, :name
|
20
|
+
JBUILDER
|
29
21
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
end
|
22
|
+
PARTIALS = {
|
23
|
+
"_partial.json.jbuilder" => "json.content content",
|
24
|
+
"_post.json.jbuilder" => POST_PARTIAL,
|
25
|
+
"racers/_racer.json.jbuilder" => RACER_PARTIAL,
|
26
|
+
"_collection.json.jbuilder" => COLLECTION_PARTIAL,
|
36
27
|
|
28
|
+
# Ensure we find only Jbuilder partials from within Jbuilder templates.
|
29
|
+
"_post.html.erb" => "Hello world!"
|
30
|
+
}
|
37
31
|
|
38
|
-
|
39
|
-
|
40
|
-
blog_authors = [ "David Heinemeier Hansson", "Pavel Pravosud" ].cycle
|
41
|
-
BLOG_POST_COLLECTION = Array.new(10){ |i| BlogPost.new(i+1, "post body #{i+1}", blog_authors.next) }
|
42
|
-
COLLECTION_COLLECTION = Array.new(5){ |i| Collection.new(i+1, "collection #{i+1}") }
|
32
|
+
AUTHORS = [ "David Heinemeier Hansson", "Pavel Pravosud" ].cycle
|
33
|
+
POSTS = (1..10).collect { |i| Post.new(i, "Post ##{i}", AUTHORS.next) }
|
43
34
|
|
44
|
-
|
35
|
+
setup { Rails.cache.clear }
|
45
36
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
"_collection.json.jbuilder" => COLLECTION_PARTIAL
|
51
|
-
}
|
37
|
+
test "basic template" do
|
38
|
+
result = render('json.content "hello"')
|
39
|
+
assert_equal "hello", result["content"]
|
40
|
+
end
|
52
41
|
|
53
|
-
|
54
|
-
|
55
|
-
|
42
|
+
test "partial by name with top-level locals" do
|
43
|
+
result = render('json.partial! "partial", content: "hello"')
|
44
|
+
assert_equal "hello", result["content"]
|
56
45
|
end
|
57
|
-
end
|
58
46
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
Rails.cache.clear
|
47
|
+
test "partial by name with nested locals" do
|
48
|
+
result = render('json.partial! "partial", locals: { content: "hello" }')
|
49
|
+
assert_equal "hello", result["content"]
|
63
50
|
end
|
64
51
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
partials["test.json.jbuilder"] = source
|
69
|
-
resolver = ActionView::FixtureResolver.new(partials)
|
70
|
-
lookup_context.view_paths = [resolver]
|
71
|
-
template = ActionView::Template.new(source, "test", JbuilderHandler, virtual_path: "test")
|
72
|
-
json = template.render(self, {}).strip
|
73
|
-
MultiJson.load(json)
|
52
|
+
test "partial by options containing nested locals" do
|
53
|
+
result = render('json.partial! partial: "partial", locals: { content: "hello" }')
|
54
|
+
assert_equal "hello", result["content"]
|
74
55
|
end
|
75
56
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
undef_method name.to_sym if method_defined?(name.to_sym)
|
80
|
-
end
|
81
|
-
end
|
57
|
+
test "partial by options containing top-level locals" do
|
58
|
+
result = render('json.partial! partial: "partial", content: "hello"')
|
59
|
+
assert_equal "hello", result["content"]
|
82
60
|
end
|
83
61
|
|
84
|
-
|
85
|
-
result =
|
62
|
+
test "partial for Active Model" do
|
63
|
+
result = render('json.partial! @racer', racer: Racer.new(123, "Chris Harris"))
|
64
|
+
assert_equal 123, result["id"]
|
65
|
+
assert_equal "Chris Harris", result["name"]
|
66
|
+
end
|
86
67
|
|
87
|
-
|
88
|
-
|
89
|
-
assert_equal
|
68
|
+
test "partial collection by name with symbol local" do
|
69
|
+
result = render('json.partial! "post", collection: @posts, as: :post', posts: POSTS)
|
70
|
+
assert_equal 10, result.count
|
71
|
+
assert_equal "Post #5", result[4]["body"]
|
90
72
|
assert_equal "Heinemeier Hansson", result[2]["author"]["last_name"]
|
91
|
-
assert_equal "Pavel",
|
73
|
+
assert_equal "Pavel", result[5]["author"]["first_name"]
|
92
74
|
end
|
93
75
|
|
94
|
-
test "
|
95
|
-
result =
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
assert_equal "
|
76
|
+
test "partial collection by name with string local" do
|
77
|
+
result = render('json.partial! "post", collection: @posts, as: "post"', posts: POSTS)
|
78
|
+
assert_equal 10, result.count
|
79
|
+
assert_equal "Post #5", result[4]["body"]
|
80
|
+
assert_equal "Heinemeier Hansson", result[2]["author"]["last_name"]
|
81
|
+
assert_equal "Pavel", result[5]["author"]["first_name"]
|
100
82
|
end
|
101
83
|
|
102
|
-
test "
|
103
|
-
result =
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
assert_equal ["camelStyle"], result.keys
|
84
|
+
test "partial collection by options" do
|
85
|
+
result = render('json.partial! partial: "post", collection: @posts, as: :post', posts: POSTS)
|
86
|
+
assert_equal 10, result.count
|
87
|
+
assert_equal "Post #5", result[4]["body"]
|
88
|
+
assert_equal "Heinemeier Hansson", result[2]["author"]["last_name"]
|
89
|
+
assert_equal "Pavel", result[5]["author"]["first_name"]
|
109
90
|
end
|
110
91
|
|
111
|
-
test "
|
112
|
-
|
113
|
-
|
114
|
-
json.level1 "one"
|
115
|
-
json.level2 do
|
116
|
-
json.value "two"
|
117
|
-
end
|
118
|
-
JBUILDER
|
92
|
+
test "nil partial collection by name" do
|
93
|
+
assert_equal [], render('json.partial! "post", collection: @posts, as: :post', posts: nil)
|
94
|
+
end
|
119
95
|
|
120
|
-
|
121
|
-
assert_equal "
|
96
|
+
test "nil partial collection by options" do
|
97
|
+
assert_equal [], render('json.partial! partial: "post", collection: @posts, as: :post', posts: nil)
|
122
98
|
end
|
123
99
|
|
124
|
-
test "
|
125
|
-
result =
|
126
|
-
|
127
|
-
|
100
|
+
test "array of partials" do
|
101
|
+
result = render('json.array! @posts, partial: "post", as: :post', posts: POSTS)
|
102
|
+
assert_equal 10, result.count
|
103
|
+
assert_equal "Post #5", result[4]["body"]
|
104
|
+
assert_equal "Heinemeier Hansson", result[2]["author"]["last_name"]
|
105
|
+
assert_equal "Pavel", result[5]["author"]["first_name"]
|
106
|
+
end
|
128
107
|
|
129
|
-
|
108
|
+
test "empty array of partials from nil collection" do
|
109
|
+
assert_equal [], render('json.array! @posts, partial: "post", as: :post', posts: nil)
|
130
110
|
end
|
131
111
|
|
132
|
-
test "
|
133
|
-
result =
|
134
|
-
|
135
|
-
|
112
|
+
test "array of partials under key" do
|
113
|
+
result = render('json.posts @posts, partial: "post", as: :post', posts: POSTS)
|
114
|
+
assert_equal 10, result["posts"].count
|
115
|
+
assert_equal "Post #5", result["posts"][4]["body"]
|
116
|
+
assert_equal "Heinemeier Hansson", result["posts"][2]["author"]["last_name"]
|
117
|
+
assert_equal "Pavel", result["posts"][5]["author"]["first_name"]
|
118
|
+
end
|
136
119
|
|
137
|
-
|
120
|
+
test "empty array of partials under key from nil collection" do
|
121
|
+
result = render('json.posts @posts, partial: "post", as: :post', posts: nil)
|
122
|
+
assert_equal [], result["posts"]
|
138
123
|
end
|
139
124
|
|
140
|
-
test "
|
141
|
-
|
142
|
-
json.
|
125
|
+
test "object fragment caching" do
|
126
|
+
render(<<-JBUILDER)
|
127
|
+
json.cache! "cache-key" do
|
128
|
+
json.name "Hit"
|
129
|
+
end
|
143
130
|
JBUILDER
|
144
131
|
|
145
|
-
|
132
|
+
hit = render('json.cache! "cache-key" do; end')
|
133
|
+
assert_equal "Hit", hit["name"]
|
146
134
|
end
|
147
135
|
|
148
|
-
test "
|
149
|
-
|
150
|
-
json.
|
136
|
+
test "conditional object fragment caching" do
|
137
|
+
render(<<-JBUILDER)
|
138
|
+
json.cache_if! true, "cache-key" do
|
139
|
+
json.a "Hit"
|
140
|
+
end
|
141
|
+
|
142
|
+
json.cache_if! false, "cache-key" do
|
143
|
+
json.b "Hit"
|
144
|
+
end
|
151
145
|
JBUILDER
|
152
146
|
|
153
|
-
|
154
|
-
|
147
|
+
result = render(<<-JBUILDER)
|
148
|
+
json.cache_if! true, "cache-key" do
|
149
|
+
json.a "Miss"
|
150
|
+
end
|
155
151
|
|
156
|
-
|
157
|
-
|
158
|
-
|
152
|
+
json.cache_if! false, "cache-key" do
|
153
|
+
json.b "Miss"
|
154
|
+
end
|
159
155
|
JBUILDER
|
160
156
|
|
161
|
-
|
157
|
+
assert_equal "Hit", result["a"]
|
158
|
+
assert_equal "Miss", result["b"]
|
162
159
|
end
|
163
160
|
|
164
|
-
test "
|
165
|
-
|
166
|
-
|
161
|
+
test "object fragment caching with expiry" do
|
162
|
+
travel_to Time.iso8601("2018-05-12T11:29:00-04:00")
|
163
|
+
|
164
|
+
render <<-JBUILDER
|
165
|
+
json.cache! "cache-key", expires_in: 1.minute do
|
166
|
+
json.name "Hit"
|
167
|
+
end
|
167
168
|
JBUILDER
|
168
169
|
|
169
|
-
|
170
|
-
end
|
170
|
+
travel 30.seconds
|
171
171
|
|
172
|
-
|
173
|
-
|
174
|
-
|
172
|
+
result = render(<<-JBUILDER)
|
173
|
+
json.cache! "cache-key", expires_in: 1.minute do
|
174
|
+
json.name "Miss"
|
175
|
+
end
|
175
176
|
JBUILDER
|
176
177
|
|
177
|
-
assert_equal
|
178
|
-
|
178
|
+
assert_equal "Hit", result["name"]
|
179
|
+
|
180
|
+
travel 31.seconds
|
179
181
|
|
180
|
-
|
181
|
-
|
182
|
-
|
182
|
+
result = render(<<-JBUILDER)
|
183
|
+
json.cache! "cache-key", expires_in: 1.minute do
|
184
|
+
json.name "Miss"
|
185
|
+
end
|
183
186
|
JBUILDER
|
184
187
|
|
185
|
-
|
188
|
+
assert_equal "Miss", result["name"]
|
186
189
|
end
|
187
190
|
|
188
|
-
test "
|
189
|
-
|
190
|
-
json.
|
191
|
+
test "object root caching" do
|
192
|
+
render <<-JBUILDER
|
193
|
+
json.cache_root! "cache-key" do
|
194
|
+
json.name "Hit"
|
195
|
+
end
|
191
196
|
JBUILDER
|
192
197
|
|
193
|
-
assert_equal
|
194
|
-
end
|
198
|
+
assert_equal JSON.dump(name: "Hit"), Rails.cache.read("jbuilder/root/cache-key")
|
195
199
|
|
196
|
-
|
197
|
-
|
198
|
-
|
200
|
+
result = render(<<-JBUILDER)
|
201
|
+
json.cache_root! "cache-key" do
|
202
|
+
json.name "Miss"
|
203
|
+
end
|
199
204
|
JBUILDER
|
200
205
|
|
201
|
-
|
206
|
+
assert_equal "Hit", result["name"]
|
202
207
|
end
|
203
208
|
|
204
|
-
test "
|
205
|
-
|
206
|
-
json.
|
209
|
+
test "array fragment caching" do
|
210
|
+
render <<-JBUILDER
|
211
|
+
json.cache! "cache-key" do
|
212
|
+
json.array! %w[ a b c ]
|
213
|
+
end
|
207
214
|
JBUILDER
|
208
215
|
|
209
|
-
assert_equal [],
|
216
|
+
assert_equal %w[ a b c ], render('json.cache! "cache-key" do; end')
|
210
217
|
end
|
211
218
|
|
212
|
-
test "
|
213
|
-
|
214
|
-
json.
|
219
|
+
test "array root caching" do
|
220
|
+
render <<-JBUILDER
|
221
|
+
json.cache_root! "cache-key" do
|
222
|
+
json.array! %w[ a b c ]
|
223
|
+
end
|
215
224
|
JBUILDER
|
216
225
|
|
217
|
-
|
218
|
-
end
|
226
|
+
assert_equal JSON.dump(%w[ a b c ]), Rails.cache.read("jbuilder/root/cache-key")
|
219
227
|
|
220
|
-
|
221
|
-
|
222
|
-
|
228
|
+
assert_equal %w[ a b c ], render(<<-JBUILDER)
|
229
|
+
json.cache_root! "cache-key" do
|
230
|
+
json.array! %w[ d e f ]
|
231
|
+
end
|
223
232
|
JBUILDER
|
224
|
-
|
225
|
-
assert_equal [], result["posts"]
|
226
233
|
end
|
227
234
|
|
228
|
-
test "cache
|
229
|
-
|
235
|
+
test "failing to cache root after JSON structures have been defined" do
|
236
|
+
assert_raises ActionView::Template::Error, "cache_root! can't be used after JSON structures have been defined" do
|
237
|
+
render <<-JBUILDER
|
238
|
+
json.name "Kaboom"
|
239
|
+
json.cache_root! "cache-key" do
|
240
|
+
json.name "Miss"
|
241
|
+
end
|
242
|
+
JBUILDER
|
243
|
+
end
|
244
|
+
end
|
230
245
|
|
231
|
-
|
232
|
-
|
233
|
-
end
|
234
|
-
JBUILDER
|
246
|
+
test "empty fragment caching" do
|
247
|
+
render 'json.cache! "nothing" do; end'
|
235
248
|
|
236
249
|
result = nil
|
237
250
|
|
238
251
|
assert_nothing_raised do
|
239
|
-
result =
|
252
|
+
result = render(<<-JBUILDER)
|
240
253
|
json.foo "bar"
|
241
|
-
json.cache! "nothing" do
|
242
|
-
end
|
254
|
+
json.cache! "nothing" do; end
|
243
255
|
JBUILDER
|
244
256
|
end
|
245
257
|
|
246
258
|
assert_equal "bar", result["foo"]
|
247
259
|
end
|
248
260
|
|
249
|
-
test "
|
250
|
-
|
261
|
+
test "cache instrumentation" do
|
262
|
+
payloads = {}
|
251
263
|
|
252
|
-
|
253
|
-
|
254
|
-
json.name "Cache"
|
255
|
-
end
|
256
|
-
JBUILDER
|
264
|
+
ActiveSupport::Notifications.subscribe("read_fragment.action_controller") { |*args| payloads[:read] = args.last }
|
265
|
+
ActiveSupport::Notifications.subscribe("write_fragment.action_controller") { |*args| payloads[:write] = args.last }
|
257
266
|
|
258
|
-
|
259
|
-
json.cache! "
|
260
|
-
json.name "
|
267
|
+
render <<-JBUILDER
|
268
|
+
json.cache! "cache-key" do
|
269
|
+
json.name "Cache"
|
261
270
|
end
|
262
271
|
JBUILDER
|
263
272
|
|
264
|
-
assert_equal "
|
273
|
+
assert_equal "jbuilder/cache-key", payloads[:read][:key]
|
274
|
+
assert_equal "jbuilder/cache-key", payloads[:write][:key]
|
265
275
|
end
|
266
276
|
|
267
|
-
test "
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
json.cache_if! true, "cachekey" do
|
272
|
-
json.test1 "Cache"
|
273
|
-
end
|
274
|
-
json.cache_if! false, "cachekey" do
|
275
|
-
json.test2 "Cache"
|
276
|
-
end
|
277
|
-
JBUILDER
|
278
|
-
|
279
|
-
result = jbuild(<<-JBUILDER)
|
280
|
-
json.cache_if! true, "cachekey" do
|
281
|
-
json.test1 "Miss"
|
282
|
-
end
|
283
|
-
json.cache_if! false, "cachekey" do
|
284
|
-
json.test2 "Miss"
|
285
|
-
end
|
277
|
+
test "camelized keys" do
|
278
|
+
result = render(<<-JBUILDER)
|
279
|
+
json.key_format! camelize: [:lower]
|
280
|
+
json.first_name "David"
|
286
281
|
JBUILDER
|
287
282
|
|
288
|
-
assert_equal "
|
289
|
-
assert_equal "Miss", result["test2"]
|
283
|
+
assert_equal "David", result["firstName"]
|
290
284
|
end
|
291
285
|
|
292
|
-
|
293
|
-
|
286
|
+
if JbuilderTemplate::CollectionRenderer.supported?
|
287
|
+
test "returns an empty array for an empty collection" do
|
288
|
+
result = render('json.array! @posts, partial: "post", as: :post, cached: true', posts: [])
|
294
289
|
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
end
|
299
|
-
JBUILDER
|
300
|
-
|
301
|
-
result = jbuild(<<-JBUILDER)
|
302
|
-
json.cache! "cachekey" do
|
303
|
-
json.array! %w[1 2 3]
|
304
|
-
end
|
305
|
-
JBUILDER
|
306
|
-
|
307
|
-
assert_equal %w[a b c], result
|
308
|
-
end
|
309
|
-
|
310
|
-
test "fragment caching works with previous version of cache digests" do
|
311
|
-
undef_context_methods :cache_fragment_name
|
290
|
+
# Do not use #assert_empty as it is important to ensure that the type of the JSON result is an array.
|
291
|
+
assert_equal [], result
|
292
|
+
end
|
312
293
|
|
313
|
-
|
294
|
+
test "works with an enumerable object" do
|
295
|
+
enumerable_class = Class.new do
|
296
|
+
include Enumerable
|
297
|
+
alias length count # Rails 6.1 requires this.
|
314
298
|
|
315
|
-
|
316
|
-
|
317
|
-
|
299
|
+
def each(&block)
|
300
|
+
[].each(&block)
|
301
|
+
end
|
318
302
|
end
|
319
|
-
JBUILDER
|
320
|
-
end
|
321
|
-
|
322
|
-
test "fragment caching works with current cache digests" do
|
323
|
-
undef_context_methods :fragment_name_with_digest
|
324
303
|
|
325
|
-
|
326
|
-
ActiveSupport::Cache.expects :expand_cache_key
|
304
|
+
result = render('json.array! @posts, partial: "post", as: :post, cached: true', posts: enumerable_class.new)
|
327
305
|
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
end
|
332
|
-
JBUILDER
|
333
|
-
end
|
306
|
+
# Do not use #assert_empty as it is important to ensure that the type of the JSON result is an array.
|
307
|
+
assert_equal [], result
|
308
|
+
end
|
334
309
|
|
335
|
-
|
336
|
-
|
310
|
+
test "supports the cached: true option" do
|
311
|
+
result = render('json.array! @posts, partial: "post", as: :post, cached: true', posts: POSTS)
|
337
312
|
|
338
|
-
|
313
|
+
assert_equal 10, result.count
|
314
|
+
assert_equal "Post #5", result[4]["body"]
|
315
|
+
assert_equal "Heinemeier Hansson", result[2]["author"]["last_name"]
|
316
|
+
assert_equal "Pavel", result[5]["author"]["first_name"]
|
339
317
|
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
318
|
+
expected = {
|
319
|
+
"id" => 1,
|
320
|
+
"body" => "Post #1",
|
321
|
+
"author" => {
|
322
|
+
"first_name" => "David",
|
323
|
+
"last_name" => "Heinemeier Hansson"
|
324
|
+
}
|
325
|
+
}
|
346
326
|
|
347
|
-
|
348
|
-
undef_context_methods :fragment_name_with_digest, :cache_fragment_name
|
327
|
+
assert_equal expected, Rails.cache.read("post-1")
|
349
328
|
|
350
|
-
|
351
|
-
ActiveSupport::Notifications.subscribe("read_fragment.action_controller") { |*args| payloads[:read_fragment] = args.last }
|
352
|
-
ActiveSupport::Notifications.subscribe("write_fragment.action_controller") { |*args| payloads[:write_fragment] = args.last }
|
329
|
+
result = render('json.array! @posts, partial: "post", as: :post, cached: true', posts: POSTS)
|
353
330
|
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
331
|
+
assert_equal 10, result.count
|
332
|
+
assert_equal "Post #5", result[4]["body"]
|
333
|
+
assert_equal "Heinemeier Hansson", result[2]["author"]["last_name"]
|
334
|
+
assert_equal "Pavel", result[5]["author"]["first_name"]
|
335
|
+
end
|
359
336
|
|
360
|
-
|
361
|
-
|
362
|
-
end
|
337
|
+
test "supports the cached: ->() {} option" do
|
338
|
+
result = render('json.array! @posts, partial: "post", as: :post, cached: ->(post) { [post, "foo"] }', posts: POSTS)
|
363
339
|
|
364
|
-
|
365
|
-
|
340
|
+
assert_equal 10, result.count
|
341
|
+
assert_equal "Post #5", result[4]["body"]
|
342
|
+
assert_equal "Heinemeier Hansson", result[2]["author"]["last_name"]
|
343
|
+
assert_equal "Pavel", result[5]["author"]["first_name"]
|
366
344
|
|
367
|
-
|
368
|
-
|
345
|
+
expected = {
|
346
|
+
"id" => 1,
|
347
|
+
"body" => "Post #1",
|
348
|
+
"author" => {
|
349
|
+
"first_name" => "David",
|
350
|
+
"last_name" => "Heinemeier Hansson"
|
351
|
+
}
|
352
|
+
}
|
369
353
|
|
370
|
-
|
371
|
-
json.cache! "cachekey", skip_digest: true do
|
372
|
-
json.name "Cache"
|
373
|
-
end
|
374
|
-
JBUILDER
|
375
|
-
end
|
354
|
+
assert_equal expected, Rails.cache.read("post-1/foo")
|
376
355
|
|
377
|
-
|
378
|
-
undef_context_methods :fragment_name_with_digest
|
356
|
+
result = render('json.array! @posts, partial: "post", as: :post, cached: ->(post) { [post, "foo"] }', posts: POSTS)
|
379
357
|
|
380
|
-
|
358
|
+
assert_equal 10, result.count
|
359
|
+
assert_equal "Post #5", result[4]["body"]
|
360
|
+
assert_equal "Heinemeier Hansson", result[2]["author"]["last_name"]
|
361
|
+
assert_equal "Pavel", result[5]["author"]["first_name"]
|
362
|
+
end
|
381
363
|
|
382
|
-
|
383
|
-
|
384
|
-
json.
|
364
|
+
test "raises an error on a render call with the :layout option" do
|
365
|
+
error = assert_raises NotImplementedError do
|
366
|
+
render('json.array! @posts, partial: "post", as: :post, layout: "layout"', posts: POSTS)
|
385
367
|
end
|
386
|
-
JBUILDER
|
387
|
-
end
|
388
368
|
|
389
|
-
|
390
|
-
|
369
|
+
assert_equal "The `:layout' option is not supported in collection rendering.", error.message
|
370
|
+
end
|
391
371
|
|
392
|
-
|
393
|
-
|
394
|
-
json.
|
372
|
+
test "raises an error on a render call with the :spacer_template option" do
|
373
|
+
error = assert_raises NotImplementedError do
|
374
|
+
render('json.array! @posts, partial: "post", as: :post, spacer_template: "template"', posts: POSTS)
|
395
375
|
end
|
396
|
-
JBUILDER
|
397
376
|
|
398
|
-
|
377
|
+
assert_equal "The `:spacer_template' option is not supported in collection rendering.", error.message
|
378
|
+
end
|
399
379
|
end
|
400
380
|
|
401
|
-
|
402
|
-
|
381
|
+
private
|
382
|
+
def render(*args)
|
383
|
+
JSON.load render_without_parsing(*args)
|
384
|
+
end
|
403
385
|
|
404
|
-
|
405
|
-
|
406
|
-
|
386
|
+
def render_without_parsing(source, assigns = {})
|
387
|
+
view = build_view(fixtures: PARTIALS.merge("source.json.jbuilder" => source), assigns: assigns)
|
388
|
+
view.render(template: "source")
|
389
|
+
end
|
407
390
|
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
391
|
+
def build_view(options = {})
|
392
|
+
resolver = ActionView::FixtureResolver.new(options.fetch(:fixtures))
|
393
|
+
lookup_context = ActionView::LookupContext.new([ resolver ], {}, [""])
|
394
|
+
controller = ActionView::TestCase::TestController.new
|
412
395
|
|
413
|
-
|
414
|
-
|
396
|
+
# TODO: Use with_empty_template_cache unconditionally after dropping support for Rails <6.0.
|
397
|
+
view = if ActionView::Base.respond_to?(:with_empty_template_cache)
|
398
|
+
ActionView::Base.with_empty_template_cache.new(lookup_context, options.fetch(:assigns, {}), controller)
|
399
|
+
else
|
400
|
+
ActionView::Base.new(lookup_context, options.fetch(:assigns, {}), controller)
|
401
|
+
end
|
415
402
|
|
416
|
-
|
417
|
-
|
418
|
-
|
403
|
+
def view.view_cache_dependencies; []; end
|
404
|
+
def view.combined_fragment_cache_key(key) [ key ] end
|
405
|
+
def view.cache_fragment_name(key, *) key end
|
406
|
+
def view.fragment_name_with_digest(key) key end
|
419
407
|
|
420
|
-
|
421
|
-
|
422
|
-
assert_equal "Chris Harris", result["name"]
|
423
|
-
end
|
408
|
+
view
|
409
|
+
end
|
424
410
|
end
|