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