jbuilder 2.0.6 → 2.11.5

Sign up to get free protection for your applications and to get access to all the features.
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