jbuilder 2.6.1 → 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 +4 -6
- data/Gemfile +0 -1
- data/MIT-LICENSE +1 -1
- data/README.md +99 -19
- 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 +65 -16
- 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 +25 -5
- data/test/jbuilder_template_test.rb +280 -323
- data/test/jbuilder_test.rb +256 -4
- data/test/scaffold_api_controller_generator_test.rb +18 -3
- data/test/scaffold_controller_generator_test.rb +41 -8
- data/test/test_helper.rb +30 -8
- metadata +25 -31
- data/.travis.yml +0 -44
- data/CHANGELOG.md +0 -234
- 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,453 +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
|
-
|
|
251
|
-
|
|
252
|
-
jbuild <<-JBUILDER
|
|
253
|
-
json.cache! "cachekey" do
|
|
254
|
-
json.name "Cache"
|
|
255
|
-
end
|
|
256
|
-
JBUILDER
|
|
257
|
-
|
|
258
|
-
result = jbuild(<<-JBUILDER)
|
|
259
|
-
json.cache! "cachekey" do
|
|
260
|
-
json.name "Miss"
|
|
261
|
-
end
|
|
262
|
-
JBUILDER
|
|
263
|
-
|
|
264
|
-
assert_equal "Cache", result["name"]
|
|
265
|
-
end
|
|
266
|
-
|
|
267
|
-
test "conditionally fragment caching a JSON object" do
|
|
268
|
-
undef_context_methods :fragment_name_with_digest, :cache_fragment_name
|
|
261
|
+
test "cache instrumentation" do
|
|
262
|
+
payloads = {}
|
|
269
263
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
json.test1 "Cache"
|
|
273
|
-
end
|
|
274
|
-
json.cache_if! false, "cachekey" do
|
|
275
|
-
json.test2 "Cache"
|
|
276
|
-
end
|
|
277
|
-
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 }
|
|
278
266
|
|
|
279
|
-
|
|
280
|
-
json.
|
|
281
|
-
json.
|
|
282
|
-
end
|
|
283
|
-
json.cache_if! false, "cachekey" do
|
|
284
|
-
json.test2 "Miss"
|
|
267
|
+
render <<-JBUILDER
|
|
268
|
+
json.cache! "cache-key" do
|
|
269
|
+
json.name "Cache"
|
|
285
270
|
end
|
|
286
271
|
JBUILDER
|
|
287
272
|
|
|
288
|
-
assert_equal "
|
|
289
|
-
assert_equal "
|
|
273
|
+
assert_equal "jbuilder/cache-key", payloads[:read][:key]
|
|
274
|
+
assert_equal "jbuilder/cache-key", payloads[:write][:key]
|
|
290
275
|
end
|
|
291
276
|
|
|
292
|
-
test "
|
|
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
|
|
277
|
+
test "camelized keys" do
|
|
278
|
+
result = render(<<-JBUILDER)
|
|
279
|
+
json.key_format! camelize: [:lower]
|
|
280
|
+
json.first_name "David"
|
|
305
281
|
JBUILDER
|
|
306
282
|
|
|
307
|
-
assert_equal
|
|
283
|
+
assert_equal "David", result["firstName"]
|
|
308
284
|
end
|
|
309
285
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
@context.expects :fragment_name_with_digest
|
|
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: [])
|
|
314
289
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
end
|
|
319
|
-
JBUILDER
|
|
320
|
-
end
|
|
321
|
-
|
|
322
|
-
test "fragment caching works with current cache digests" do
|
|
323
|
-
undef_context_methods :fragment_name_with_digest
|
|
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
|
|
324
293
|
|
|
325
|
-
|
|
326
|
-
|
|
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.
|
|
327
298
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
299
|
+
def each(&block)
|
|
300
|
+
[].each(&block)
|
|
301
|
+
end
|
|
331
302
|
end
|
|
332
|
-
JBUILDER
|
|
333
|
-
end
|
|
334
303
|
|
|
335
|
-
|
|
336
|
-
undef_context_methods :fragment_name_with_digest, :cache_fragment_name
|
|
304
|
+
result = render('json.array! @posts, partial: "post", as: :post, cached: true', posts: enumerable_class.new)
|
|
337
305
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
json.cache! "cachekey" do
|
|
342
|
-
json.name "Cache"
|
|
343
|
-
end
|
|
344
|
-
JBUILDER
|
|
345
|
-
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
|
|
346
309
|
|
|
347
|
-
|
|
348
|
-
|
|
310
|
+
test "supports the cached: true option" do
|
|
311
|
+
result = render('json.array! @posts, partial: "post", as: :post, cached: true', posts: POSTS)
|
|
349
312
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
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"]
|
|
353
317
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
318
|
+
expected = {
|
|
319
|
+
"id" => 1,
|
|
320
|
+
"body" => "Post #1",
|
|
321
|
+
"author" => {
|
|
322
|
+
"first_name" => "David",
|
|
323
|
+
"last_name" => "Heinemeier Hansson"
|
|
324
|
+
}
|
|
325
|
+
}
|
|
359
326
|
|
|
360
|
-
|
|
361
|
-
assert_equal "jbuilder/cachekey", payloads[:write_fragment][:key]
|
|
362
|
-
end
|
|
327
|
+
assert_equal expected, Rails.cache.read("post-1")
|
|
363
328
|
|
|
364
|
-
|
|
365
|
-
undef_context_methods :fragment_name_with_digest
|
|
329
|
+
result = render('json.array! @posts, partial: "post", as: :post, cached: true', posts: POSTS)
|
|
366
330
|
|
|
367
|
-
|
|
368
|
-
|
|
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
|
|
369
336
|
|
|
370
|
-
|
|
371
|
-
json.
|
|
372
|
-
json.name "Cache"
|
|
373
|
-
end
|
|
374
|
-
JBUILDER
|
|
375
|
-
end
|
|
337
|
+
test "supports the cached: ->() {} option" do
|
|
338
|
+
result = render('json.array! @posts, partial: "post", as: :post, cached: ->(post) { [post, "foo"] }', posts: POSTS)
|
|
376
339
|
|
|
377
|
-
|
|
378
|
-
|
|
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"]
|
|
379
344
|
|
|
380
|
-
|
|
345
|
+
expected = {
|
|
346
|
+
"id" => 1,
|
|
347
|
+
"body" => "Post #1",
|
|
348
|
+
"author" => {
|
|
349
|
+
"first_name" => "David",
|
|
350
|
+
"last_name" => "Heinemeier Hansson"
|
|
351
|
+
}
|
|
352
|
+
}
|
|
381
353
|
|
|
382
|
-
|
|
383
|
-
json.cache! "cachekey", expires_in: 1.minute do
|
|
384
|
-
json.name "Cache"
|
|
385
|
-
end
|
|
386
|
-
JBUILDER
|
|
387
|
-
end
|
|
354
|
+
assert_equal expected, Rails.cache.read("post-1/foo")
|
|
388
355
|
|
|
389
|
-
|
|
390
|
-
undef_context_methods :fragment_name_with_digest, :cache_fragment_name
|
|
356
|
+
result = render('json.array! @posts, partial: "post", as: :post, cached: ->(post) { [post, "foo"] }', posts: POSTS)
|
|
391
357
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
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
|
|
397
363
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
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)
|
|
401
367
|
end
|
|
402
|
-
JBUILDER
|
|
403
368
|
|
|
404
|
-
|
|
405
|
-
end
|
|
406
|
-
|
|
407
|
-
test "failing to cache root after attributes have been defined" do
|
|
408
|
-
assert_raises ActionView::Template::Error, "cache_root! can't be used after JSON structures have been defined" do
|
|
409
|
-
jbuild <<-JBUILDER
|
|
410
|
-
json.name "Kaboom"
|
|
411
|
-
json.cache_root! "cachekey" do
|
|
412
|
-
json.name "Miss"
|
|
413
|
-
end
|
|
414
|
-
JBUILDER
|
|
369
|
+
assert_equal "The `:layout' option is not supported in collection rendering.", error.message
|
|
415
370
|
end
|
|
416
|
-
end
|
|
417
371
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
jbuild <<-JBUILDER
|
|
422
|
-
json.cache! "cachekey" do
|
|
423
|
-
json.name "Cache"
|
|
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)
|
|
424
375
|
end
|
|
425
|
-
JBUILDER
|
|
426
376
|
|
|
427
|
-
|
|
377
|
+
assert_equal "The `:spacer_template' option is not supported in collection rendering.", error.message
|
|
378
|
+
end
|
|
428
379
|
end
|
|
429
380
|
|
|
430
|
-
|
|
431
|
-
|
|
381
|
+
private
|
|
382
|
+
def render(*args)
|
|
383
|
+
JSON.load render_without_parsing(*args)
|
|
384
|
+
end
|
|
432
385
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
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
|
|
436
390
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
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
|
|
441
395
|
|
|
442
|
-
|
|
443
|
-
|
|
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
|
|
444
402
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
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
|
|
448
407
|
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
assert_equal "Chris Harris", result["name"]
|
|
452
|
-
end
|
|
408
|
+
view
|
|
409
|
+
end
|
|
453
410
|
end
|