jbuilder 2.0.6 → 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.
Files changed (43) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/ruby.yml +108 -0
  3. data/.gitignore +4 -1
  4. data/Appraisals +25 -0
  5. data/CONTRIBUTING.md +106 -0
  6. data/Gemfile +4 -12
  7. data/MIT-LICENSE +1 -1
  8. data/README.md +171 -45
  9. data/Rakefile +15 -10
  10. data/gemfiles/rails_5_0.gemfile +10 -0
  11. data/gemfiles/rails_5_1.gemfile +10 -0
  12. data/gemfiles/rails_5_2.gemfile +10 -0
  13. data/gemfiles/rails_6_0.gemfile +10 -0
  14. data/gemfiles/rails_6_1.gemfile +10 -0
  15. data/gemfiles/rails_head.gemfile +10 -0
  16. data/jbuilder.gemspec +20 -6
  17. data/lib/generators/rails/jbuilder_generator.rb +13 -2
  18. data/lib/generators/rails/scaffold_controller_generator.rb +9 -3
  19. data/lib/generators/rails/templates/api_controller.rb +63 -0
  20. data/lib/generators/rails/templates/controller.rb +16 -20
  21. data/lib/generators/rails/templates/index.json.jbuilder +1 -4
  22. data/lib/generators/rails/templates/partial.json.jbuilder +16 -0
  23. data/lib/generators/rails/templates/show.json.jbuilder +1 -1
  24. data/lib/jbuilder/blank.rb +11 -0
  25. data/lib/jbuilder/collection_renderer.rb +109 -0
  26. data/lib/jbuilder/dependency_tracker.rb +1 -1
  27. data/lib/jbuilder/errors.rb +24 -0
  28. data/lib/jbuilder/jbuilder.rb +7 -0
  29. data/lib/jbuilder/jbuilder_template.rb +213 -65
  30. data/lib/jbuilder/key_formatter.rb +34 -0
  31. data/lib/jbuilder/railtie.rb +31 -6
  32. data/lib/jbuilder.rb +148 -114
  33. data/test/jbuilder_dependency_tracker_test.rb +3 -4
  34. data/test/jbuilder_generator_test.rb +31 -4
  35. data/test/jbuilder_template_test.rb +313 -195
  36. data/test/jbuilder_test.rb +615 -219
  37. data/test/scaffold_api_controller_generator_test.rb +70 -0
  38. data/test/scaffold_controller_generator_test.rb +62 -19
  39. data/test/test_helper.rb +36 -0
  40. metadata +38 -23
  41. data/.travis.yml +0 -21
  42. data/CHANGELOG.md +0 -89
  43. data/Gemfile.old +0 -14
@@ -1,292 +1,410 @@
1
- require 'test/unit'
2
- require 'mocha/setup'
3
- require 'action_view'
4
- require 'action_view/testing/resolvers'
5
- require 'active_support/cache'
6
- require 'jbuilder/jbuilder_template'
7
-
8
-
9
- BLOG_POST_PARTIAL = <<-JBUILDER
10
- json.extract! blog_post, :id, :body
11
- json.author do
12
- name = blog_post.author_name.split(nil, 2)
13
- json.first_name name[0]
14
- json.last_name name[1]
1
+ require "test_helper"
2
+ require "action_view/testing/resolvers"
3
+
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
13
+
14
+ COLLECTION_PARTIAL = <<-JBUILDER
15
+ json.extract! collection, :id, :name
16
+ JBUILDER
17
+
18
+ RACER_PARTIAL = <<-JBUILDER
19
+ json.extract! racer, :id, :name
20
+ JBUILDER
21
+
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,
27
+
28
+ # Ensure we find only Jbuilder partials from within Jbuilder templates.
29
+ "_post.html.erb" => "Hello world!"
30
+ }
31
+
32
+ AUTHORS = [ "David Heinemeier Hansson", "Pavel Pravosud" ].cycle
33
+ POSTS = (1..10).collect { |i| Post.new(i, "Post ##{i}", AUTHORS.next) }
34
+
35
+ setup { Rails.cache.clear }
36
+
37
+ test "basic template" do
38
+ result = render('json.content "hello"')
39
+ assert_equal "hello", result["content"]
15
40
  end
16
- JBUILDER
17
41
 
18
- BlogPost = Struct.new(:id, :body, :author_name)
19
- blog_authors = [ 'David Heinemeier Hansson', 'Pavel Pravosud' ].cycle
20
- BLOG_POST_COLLECTION = 10.times.map{ |i| BlogPost.new(i+1, "post body #{i+1}", blog_authors.next) }
42
+ test "partial by name with top-level locals" do
43
+ result = render('json.partial! "partial", content: "hello"')
44
+ assert_equal "hello", result["content"]
45
+ end
21
46
 
22
- module Rails
23
- def self.cache
24
- @cache ||= ActiveSupport::Cache::MemoryStore.new
47
+ test "partial by name with nested locals" do
48
+ result = render('json.partial! "partial", locals: { content: "hello" }')
49
+ assert_equal "hello", result["content"]
25
50
  end
26
- end
27
51
 
28
- class JbuilderTemplateTest < ActionView::TestCase
29
- setup do
30
- @context = self
31
- Rails.cache.clear
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"]
32
55
  end
33
56
 
34
- def partials
35
- {
36
- '_partial.json.jbuilder' => 'json.content "hello"',
37
- '_blog_post.json.jbuilder' => BLOG_POST_PARTIAL
38
- }
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"]
39
60
  end
40
61
 
41
- def render_jbuilder(source)
42
- @rendered = []
43
- lookup_context.view_paths = [ActionView::FixtureResolver.new(partials.merge('test.json.jbuilder' => source))]
44
- ActionView::Template.new(source, 'test', JbuilderHandler, :virtual_path => 'test').render(self, {}).strip
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"]
45
66
  end
46
67
 
47
- def undef_context_methods(*names)
48
- self.class_eval do
49
- names.each do |name|
50
- undef_method name.to_sym if self.method_defined?(name.to_sym)
51
- end
52
- end
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"]
72
+ assert_equal "Heinemeier Hansson", result[2]["author"]["last_name"]
73
+ assert_equal "Pavel", result[5]["author"]["first_name"]
53
74
  end
54
75
 
55
- def assert_collection_rendered(json, context = nil)
56
- result = MultiJson.load(json)
57
- result = result.fetch(context) if context
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"]
82
+ end
58
83
 
59
- assert_equal 10, result.length
60
- assert_equal Array, result.class
61
- assert_equal 'post body 5', result[4]['body']
62
- assert_equal 'Heinemeier Hansson', result[2]['author']['last_name']
63
- assert_equal 'Pavel', result[5]['author']['first_name']
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"]
64
90
  end
65
91
 
66
- test 'rendering' do
67
- json = render_jbuilder <<-JBUILDER
68
- json.content 'hello'
69
- JBUILDER
92
+ test "nil partial collection by name" do
93
+ assert_equal [], render('json.partial! "post", collection: @posts, as: :post', posts: nil)
94
+ end
70
95
 
71
- assert_equal 'hello', MultiJson.load(json)['content']
96
+ test "nil partial collection by options" do
97
+ assert_equal [], render('json.partial! partial: "post", collection: @posts, as: :post', posts: nil)
72
98
  end
73
99
 
74
- test 'key_format! with parameter' do
75
- json = render_jbuilder <<-JBUILDER
76
- json.key_format! :camelize => [:lower]
77
- json.camel_style 'for JS'
78
- JBUILDER
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
79
107
 
80
- assert_equal ['camelStyle'], MultiJson.load(json).keys
108
+ test "empty array of partials from nil collection" do
109
+ assert_equal [], render('json.array! @posts, partial: "post", as: :post', posts: nil)
81
110
  end
82
111
 
83
- test 'key_format! propagates to child elements' do
84
- json = render_jbuilder <<-JBUILDER
85
- json.key_format! :upcase
86
- json.level1 'one'
87
- json.level2 do
88
- json.value 'two'
89
- end
90
- JBUILDER
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
91
119
 
92
- result = MultiJson.load(json)
93
- assert_equal 'one', result['LEVEL1']
94
- assert_equal 'two', result['LEVEL2']['VALUE']
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"]
95
123
  end
96
124
 
97
- test 'partial! renders partial' do
98
- json = render_jbuilder <<-JBUILDER
99
- json.partial! 'partial'
125
+ test "object fragment caching" do
126
+ render(<<-JBUILDER)
127
+ json.cache! "cache-key" do
128
+ json.name "Hit"
129
+ end
100
130
  JBUILDER
101
131
 
102
- assert_equal 'hello', MultiJson.load(json)['content']
132
+ hit = render('json.cache! "cache-key" do; end')
133
+ assert_equal "Hit", hit["name"]
103
134
  end
104
135
 
105
- test 'partial! renders collections' do
106
- json = render_jbuilder <<-JBUILDER
107
- json.partial! 'blog_post', :collection => BLOG_POST_COLLECTION, :as => :blog_post
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
108
145
  JBUILDER
109
146
 
110
- assert_collection_rendered json
111
- end
147
+ result = render(<<-JBUILDER)
148
+ json.cache_if! true, "cache-key" do
149
+ json.a "Miss"
150
+ end
112
151
 
113
- test 'partial! renders as empty array for nil-collection' do
114
- json = render_jbuilder <<-JBUILDER
115
- json.partial! 'blog_post', :collection => nil, :as => :blog_post
152
+ json.cache_if! false, "cache-key" do
153
+ json.b "Miss"
154
+ end
116
155
  JBUILDER
117
156
 
118
- assert_equal '[]', json
157
+ assert_equal "Hit", result["a"]
158
+ assert_equal "Miss", result["b"]
119
159
  end
120
160
 
121
- test 'partial! renders collection (alt. syntax)' do
122
- json = render_jbuilder <<-JBUILDER
123
- json.partial! :partial => 'blog_post', :collection => BLOG_POST_COLLECTION, :as => :blog_post
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
124
168
  JBUILDER
125
169
 
126
- assert_collection_rendered json
127
- end
170
+ travel 30.seconds
128
171
 
129
- test 'partial! renders as empty array for nil-collection (alt. syntax)' do
130
- json = render_jbuilder <<-JBUILDER
131
- json.partial! :partial => 'blog_post', :collection => nil, :as => :blog_post
172
+ result = render(<<-JBUILDER)
173
+ json.cache! "cache-key", expires_in: 1.minute do
174
+ json.name "Miss"
175
+ end
132
176
  JBUILDER
133
177
 
134
- assert_equal '[]', json
135
- end
178
+ assert_equal "Hit", result["name"]
179
+
180
+ travel 31.seconds
136
181
 
137
- test 'render array of partials' do
138
- json = render_jbuilder <<-JBUILDER
139
- json.array! BLOG_POST_COLLECTION, :partial => 'blog_post', :as => :blog_post
182
+ result = render(<<-JBUILDER)
183
+ json.cache! "cache-key", expires_in: 1.minute do
184
+ json.name "Miss"
185
+ end
140
186
  JBUILDER
141
187
 
142
- assert_collection_rendered json
188
+ assert_equal "Miss", result["name"]
143
189
  end
144
190
 
145
- test 'render array of partials as empty array with nil-collection' do
146
- json = render_jbuilder <<-JBUILDER
147
- json.array! nil, :partial => 'blog_post', :as => :blog_post
191
+ test "object root caching" do
192
+ render <<-JBUILDER
193
+ json.cache_root! "cache-key" do
194
+ json.name "Hit"
195
+ end
148
196
  JBUILDER
149
197
 
150
- assert_equal '[]', json
151
- end
198
+ assert_equal JSON.dump(name: "Hit"), Rails.cache.read("jbuilder/root/cache-key")
152
199
 
153
- test 'render array if partials as a value' do
154
- json = render_jbuilder <<-JBUILDER
155
- json.posts BLOG_POST_COLLECTION, :partial => 'blog_post', :as => :blog_post
200
+ result = render(<<-JBUILDER)
201
+ json.cache_root! "cache-key" do
202
+ json.name "Miss"
203
+ end
156
204
  JBUILDER
157
205
 
158
- assert_collection_rendered json, 'posts'
206
+ assert_equal "Hit", result["name"]
159
207
  end
160
208
 
161
- test 'render as empty array if partials as a nil value' do
162
- json = render_jbuilder <<-JBUILDER
163
- json.posts nil, :partial => 'blog_post', :as => :blog_post
209
+ test "array fragment caching" do
210
+ render <<-JBUILDER
211
+ json.cache! "cache-key" do
212
+ json.array! %w[ a b c ]
213
+ end
164
214
  JBUILDER
165
215
 
166
- assert_equal '{"posts":[]}', json
216
+ assert_equal %w[ a b c ], render('json.cache! "cache-key" do; end')
167
217
  end
168
218
 
169
- test 'fragment caching a JSON object' do
170
- undef_context_methods :fragment_name_with_digest, :cache_fragment_name
171
-
172
- render_jbuilder <<-JBUILDER
173
- json.cache! 'cachekey' do
174
- json.name 'Cache'
219
+ test "array root caching" do
220
+ render <<-JBUILDER
221
+ json.cache_root! "cache-key" do
222
+ json.array! %w[ a b c ]
175
223
  end
176
224
  JBUILDER
177
225
 
178
- json = render_jbuilder <<-JBUILDER
179
- json.cache! 'cachekey' do
180
- json.name 'Miss'
226
+ assert_equal JSON.dump(%w[ a b c ]), Rails.cache.read("jbuilder/root/cache-key")
227
+
228
+ assert_equal %w[ a b c ], render(<<-JBUILDER)
229
+ json.cache_root! "cache-key" do
230
+ json.array! %w[ d e f ]
181
231
  end
182
232
  JBUILDER
233
+ end
183
234
 
184
- parsed = MultiJson.load(json)
185
- assert_equal 'Cache', parsed['name']
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
186
244
  end
187
245
 
188
- test 'conditionally fragment caching a JSON object' do
189
- undef_context_methods :fragment_name_with_digest, :cache_fragment_name
246
+ test "empty fragment caching" do
247
+ render 'json.cache! "nothing" do; end'
190
248
 
191
- render_jbuilder <<-JBUILDER
192
- json.cache_if! true, 'cachekey' do
193
- json.test1 'Cache'
194
- end
195
- json.cache_if! false, 'cachekey' do
196
- json.test2 'Cache'
197
- end
198
- JBUILDER
249
+ result = nil
199
250
 
200
- json = render_jbuilder <<-JBUILDER
201
- json.cache_if! true, 'cachekey' do
202
- json.test1 'Miss'
203
- end
204
- json.cache_if! false, 'cachekey' do
205
- json.test2 'Miss'
206
- end
207
- JBUILDER
251
+ assert_nothing_raised do
252
+ result = render(<<-JBUILDER)
253
+ json.foo "bar"
254
+ json.cache! "nothing" do; end
255
+ JBUILDER
256
+ end
208
257
 
209
- parsed = MultiJson.load(json)
210
- assert_equal 'Cache', parsed['test1']
211
- assert_equal 'Miss', parsed['test2']
258
+ assert_equal "bar", result["foo"]
212
259
  end
213
260
 
214
- test 'fragment caching deserializes an array' do
215
- undef_context_methods :fragment_name_with_digest, :cache_fragment_name
261
+ test "cache instrumentation" do
262
+ payloads = {}
216
263
 
217
- render_jbuilder <<-JBUILDER
218
- json.cache! 'cachekey' do
219
- json.array! %w(a b c)
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 }
266
+
267
+ render <<-JBUILDER
268
+ json.cache! "cache-key" do
269
+ json.name "Cache"
220
270
  end
221
271
  JBUILDER
222
272
 
223
- json = render_jbuilder <<-JBUILDER
224
- json.cache! 'cachekey' do
225
- json.array! %w(1 2 3)
226
- end
273
+ assert_equal "jbuilder/cache-key", payloads[:read][:key]
274
+ assert_equal "jbuilder/cache-key", payloads[:write][:key]
275
+ end
276
+
277
+ test "camelized keys" do
278
+ result = render(<<-JBUILDER)
279
+ json.key_format! camelize: [:lower]
280
+ json.first_name "David"
227
281
  JBUILDER
228
282
 
229
- parsed = MultiJson.load(json)
230
- assert_equal %w(a b c), parsed
283
+ assert_equal "David", result["firstName"]
231
284
  end
232
285
 
233
- test 'fragment caching works with previous version of cache digests' do
234
- undef_context_methods :cache_fragment_name
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: [])
289
+
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
235
293
 
236
- @context.expects :fragment_name_with_digest
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.
237
298
 
238
- render_jbuilder <<-JBUILDER
239
- json.cache! 'cachekey' do
240
- json.name 'Cache'
299
+ def each(&block)
300
+ [].each(&block)
301
+ end
241
302
  end
242
- JBUILDER
243
- end
244
303
 
245
- test 'fragment caching works with current cache digests' do
246
- undef_context_methods :fragment_name_with_digest
304
+ result = render('json.array! @posts, partial: "post", as: :post, cached: true', posts: enumerable_class.new)
247
305
 
248
- @context.expects :cache_fragment_name
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
249
309
 
250
- render_jbuilder <<-JBUILDER
251
- json.cache! 'cachekey' do
252
- json.name 'Cache'
253
- end
254
- JBUILDER
255
- end
310
+ test "supports the cached: true option" do
311
+ result = render('json.array! @posts, partial: "post", as: :post, cached: true', posts: POSTS)
312
+
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"]
317
+
318
+ expected = {
319
+ "id" => 1,
320
+ "body" => "Post #1",
321
+ "author" => {
322
+ "first_name" => "David",
323
+ "last_name" => "Heinemeier Hansson"
324
+ }
325
+ }
256
326
 
257
- test 'current cache digest option accepts options' do
258
- undef_context_methods :fragment_name_with_digest
327
+ assert_equal expected, Rails.cache.read("post-1")
259
328
 
260
- @context.expects(:cache_fragment_name).with('cachekey', skip_digest: true)
329
+ result = render('json.array! @posts, partial: "post", as: :post, cached: true', posts: POSTS)
330
+
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
261
336
 
262
- render_jbuilder <<-JBUILDER
263
- json.cache! 'cachekey', skip_digest: true do
264
- json.name 'Cache'
337
+ test "supports the cached: ->() {} option" do
338
+ result = render('json.array! @posts, partial: "post", as: :post, cached: ->(post) { [post, "foo"] }', posts: POSTS)
339
+
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"]
344
+
345
+ expected = {
346
+ "id" => 1,
347
+ "body" => "Post #1",
348
+ "author" => {
349
+ "first_name" => "David",
350
+ "last_name" => "Heinemeier Hansson"
351
+ }
352
+ }
353
+
354
+ assert_equal expected, Rails.cache.read("post-1/foo")
355
+
356
+ result = render('json.array! @posts, partial: "post", as: :post, cached: ->(post) { [post, "foo"] }', posts: POSTS)
357
+
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
363
+
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)
265
367
  end
266
- JBUILDER
267
- end
268
368
 
269
- test 'does not perform caching when controller.perform_caching is false' do
270
- controller.perform_caching = false
271
- render_jbuilder <<-JBUILDER
272
- json.cache! 'cachekey' do
273
- json.name 'Cache'
369
+ assert_equal "The `:layout' option is not supported in collection rendering.", error.message
370
+ end
371
+
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)
274
375
  end
275
- JBUILDER
276
376
 
277
- assert_equal Rails.cache.inspect[/entries=(\d+)/, 1], '0'
377
+ assert_equal "The `:spacer_template' option is not supported in collection rendering.", error.message
378
+ end
278
379
  end
279
380
 
280
- test 'fragment caching falls back on ActiveSupport::Cache.expand_cache_key' do
281
- undef_context_methods :fragment_name_with_digest, :cache_fragment_name
381
+ private
382
+ def render(*args)
383
+ JSON.load render_without_parsing(*args)
384
+ end
385
+
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
282
390
 
283
- ActiveSupport::Cache.expects :expand_cache_key
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
284
395
 
285
- render_jbuilder <<-JBUILDER
286
- json.cache! 'cachekey' do
287
- json.name 'Cache'
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)
288
401
  end
289
- JBUILDER
290
- end
291
402
 
292
- end
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
407
+
408
+ view
409
+ end
410
+ end