jbuilder 2.13.0 → 2.15.1
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 +4 -4
- data/.devcontainer/devcontainer-lock.json +9 -0
- data/.devcontainer/devcontainer.json +25 -0
- data/.github/workflows/ruby.yml +23 -6
- data/Appraisals +25 -12
- data/Gemfile +2 -0
- data/README.md +63 -22
- data/Rakefile +2 -0
- data/bin/test +1 -1
- data/gemfiles/rails_7_0.gemfile +1 -0
- data/gemfiles/rails_7_2.gemfile +10 -0
- data/gemfiles/rails_8_0.gemfile +10 -0
- data/gemfiles/rails_8_1.gemfile +10 -0
- data/jbuilder.gemspec +5 -3
- data/lib/generators/rails/jbuilder_generator.rb +2 -0
- data/lib/generators/rails/scaffold_controller_generator.rb +6 -0
- data/lib/generators/rails/templates/api_controller.rb +2 -2
- data/lib/generators/rails/templates/controller.rb +6 -6
- data/lib/jbuilder/blank.rb +2 -0
- data/lib/jbuilder/collection_renderer.rb +19 -77
- data/lib/jbuilder/errors.rb +3 -1
- data/lib/jbuilder/jbuilder.rb +3 -1
- data/lib/jbuilder/jbuilder_dependency_tracker.rb +2 -0
- data/lib/jbuilder/jbuilder_template.rb +41 -67
- data/lib/jbuilder/key_formatter.rb +19 -21
- data/lib/jbuilder/railtie.rb +15 -17
- data/lib/jbuilder/version.rb +4 -2
- data/lib/jbuilder.rb +101 -82
- data/test/jbuilder_generator_test.rb +6 -8
- data/test/jbuilder_template_test.rb +116 -80
- data/test/jbuilder_test.rb +21 -8
- data/test/scaffold_api_controller_generator_test.rb +56 -56
- data/test/scaffold_controller_generator_test.rb +37 -33
- data/test/test_helper.rb +2 -2
- metadata +15 -14
data/lib/jbuilder.rb
CHANGED
|
@@ -1,75 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'active_support'
|
|
2
4
|
require 'jbuilder/jbuilder'
|
|
3
5
|
require 'jbuilder/blank'
|
|
4
6
|
require 'jbuilder/key_formatter'
|
|
5
7
|
require 'jbuilder/errors'
|
|
6
|
-
require 'jbuilder/version'
|
|
7
8
|
require 'json'
|
|
8
9
|
require 'active_support/core_ext/hash/deep_merge'
|
|
10
|
+
require 'active_support/core_ext/object/blank'
|
|
9
11
|
|
|
10
12
|
class Jbuilder
|
|
11
13
|
@@key_formatter = nil
|
|
12
14
|
@@ignore_nil = false
|
|
13
15
|
@@deep_format_keys = false
|
|
14
16
|
|
|
15
|
-
def initialize(
|
|
17
|
+
def initialize(
|
|
18
|
+
key_formatter: @@key_formatter,
|
|
19
|
+
ignore_nil: @@ignore_nil,
|
|
20
|
+
deep_format_keys: @@deep_format_keys,
|
|
21
|
+
&block
|
|
22
|
+
)
|
|
16
23
|
@attributes = {}
|
|
24
|
+
@key_formatter = key_formatter
|
|
25
|
+
@ignore_nil = ignore_nil
|
|
26
|
+
@deep_format_keys = deep_format_keys
|
|
17
27
|
|
|
18
|
-
|
|
19
|
-
@ignore_nil = options.fetch(:ignore_nil, @@ignore_nil)
|
|
20
|
-
@deep_format_keys = options.fetch(:deep_format_keys, @@deep_format_keys)
|
|
21
|
-
|
|
22
|
-
yield self if ::Kernel.block_given?
|
|
28
|
+
yield self if block
|
|
23
29
|
end
|
|
24
30
|
|
|
25
31
|
# Yields a builder and automatically turns the result into a JSON string
|
|
26
|
-
def self.encode(
|
|
27
|
-
new(
|
|
32
|
+
def self.encode(...)
|
|
33
|
+
new(...).target!
|
|
28
34
|
end
|
|
29
35
|
|
|
30
|
-
BLANK = Blank.new
|
|
36
|
+
BLANK = Blank.new.freeze
|
|
37
|
+
EMPTY_ARRAY = [].freeze
|
|
38
|
+
private_constant :BLANK, :EMPTY_ARRAY
|
|
31
39
|
|
|
32
40
|
def set!(key, value = BLANK, *args, &block)
|
|
33
|
-
|
|
34
|
-
if !_blank?(value)
|
|
35
|
-
# json.comments @post.comments { |comment| ... }
|
|
36
|
-
# { "comments": [ { ... }, { ... } ] }
|
|
37
|
-
_scope{ array! value, &block }
|
|
38
|
-
else
|
|
39
|
-
# json.comments { ... }
|
|
40
|
-
# { "comments": ... }
|
|
41
|
-
_merge_block(key){ yield self }
|
|
42
|
-
end
|
|
43
|
-
elsif args.empty?
|
|
44
|
-
if ::Jbuilder === value
|
|
45
|
-
# json.age 32
|
|
46
|
-
# json.person another_jbuilder
|
|
47
|
-
# { "age": 32, "person": { ... }
|
|
48
|
-
_format_keys(value.attributes!)
|
|
49
|
-
else
|
|
50
|
-
# json.age 32
|
|
51
|
-
# { "age": 32 }
|
|
52
|
-
_format_keys(value)
|
|
53
|
-
end
|
|
54
|
-
elsif _is_collection?(value)
|
|
55
|
-
# json.comments @post.comments, :content, :created_at
|
|
56
|
-
# { "comments": [ { "content": "hello", "created_at": "..." }, { "content": "world", "created_at": "..." } ] }
|
|
57
|
-
_scope{ array! value, *args }
|
|
58
|
-
else
|
|
59
|
-
# json.author @post.creator, :name, :email_address
|
|
60
|
-
# { "author": { "name": "David", "email_address": "david@loudthinking.com" } }
|
|
61
|
-
_merge_block(key){ extract! value, *args }
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
_set_value key, result
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
def method_missing(*args, &block)
|
|
68
|
-
if ::Kernel.block_given?
|
|
69
|
-
set!(*args, &block)
|
|
70
|
-
else
|
|
71
|
-
set!(*args)
|
|
72
|
-
end
|
|
41
|
+
_set(key, value, args, &block)
|
|
73
42
|
end
|
|
74
43
|
|
|
75
44
|
# Specifies formatting to be applied to the key. Passing in a name of a function
|
|
@@ -100,13 +69,13 @@ class Jbuilder
|
|
|
100
69
|
#
|
|
101
70
|
# { "_first_name": "David" }
|
|
102
71
|
#
|
|
103
|
-
def key_format!(
|
|
104
|
-
@key_formatter = KeyFormatter.new(
|
|
72
|
+
def key_format!(...)
|
|
73
|
+
@key_formatter = KeyFormatter.new(...)
|
|
105
74
|
end
|
|
106
75
|
|
|
107
76
|
# Same as the instance method key_format! except sets the default.
|
|
108
|
-
def self.key_format(
|
|
109
|
-
@@key_formatter = KeyFormatter.new(
|
|
77
|
+
def self.key_format(...)
|
|
78
|
+
@@key_formatter = KeyFormatter.new(...)
|
|
110
79
|
end
|
|
111
80
|
|
|
112
81
|
# If you want to skip adding nil values to your JSON hash. This is useful
|
|
@@ -209,18 +178,8 @@ class Jbuilder
|
|
|
209
178
|
# json.array! [1, 2, 3]
|
|
210
179
|
#
|
|
211
180
|
# [1,2,3]
|
|
212
|
-
def array!(collection =
|
|
213
|
-
|
|
214
|
-
[]
|
|
215
|
-
elsif ::Kernel.block_given?
|
|
216
|
-
_map_collection(collection, &block)
|
|
217
|
-
elsif attributes.any?
|
|
218
|
-
_map_collection(collection) { |element| extract! element, *attributes }
|
|
219
|
-
else
|
|
220
|
-
_format_keys(collection.to_a)
|
|
221
|
-
end
|
|
222
|
-
|
|
223
|
-
@attributes = _merge_values(@attributes, array)
|
|
181
|
+
def array!(collection = EMPTY_ARRAY, *attributes, &block)
|
|
182
|
+
_array collection, attributes, &block
|
|
224
183
|
end
|
|
225
184
|
|
|
226
185
|
# Extracts the mentioned attributes or hash elements from the passed object and turns them into attributes of the JSON.
|
|
@@ -241,18 +200,14 @@ class Jbuilder
|
|
|
241
200
|
#
|
|
242
201
|
# json.(@person, :name, :age)
|
|
243
202
|
def extract!(object, *attributes)
|
|
244
|
-
|
|
245
|
-
_extract_hash_values(object, attributes)
|
|
246
|
-
else
|
|
247
|
-
_extract_method_values(object, attributes)
|
|
248
|
-
end
|
|
203
|
+
_extract object, attributes
|
|
249
204
|
end
|
|
250
205
|
|
|
251
206
|
def call(object, *attributes, &block)
|
|
252
|
-
if
|
|
253
|
-
|
|
207
|
+
if block
|
|
208
|
+
_array object, &block
|
|
254
209
|
else
|
|
255
|
-
|
|
210
|
+
_extract object, attributes
|
|
256
211
|
end
|
|
257
212
|
end
|
|
258
213
|
|
|
@@ -279,8 +234,68 @@ class Jbuilder
|
|
|
279
234
|
@attributes.to_json
|
|
280
235
|
end
|
|
281
236
|
|
|
237
|
+
alias_method :method_missing, :set!
|
|
238
|
+
private :method_missing
|
|
239
|
+
|
|
282
240
|
private
|
|
283
241
|
|
|
242
|
+
def _set(key, value = BLANK, attributes = nil, &block)
|
|
243
|
+
result = if block
|
|
244
|
+
if _blank?(value)
|
|
245
|
+
# json.comments { ... }
|
|
246
|
+
# { "comments": ... }
|
|
247
|
+
_merge_block key, &block
|
|
248
|
+
else
|
|
249
|
+
# json.comments @post.comments { |comment| ... }
|
|
250
|
+
# { "comments": [ { ... }, { ... } ] }
|
|
251
|
+
_scope { _array value, &block }
|
|
252
|
+
end
|
|
253
|
+
elsif attributes.empty?
|
|
254
|
+
if ::Jbuilder === value
|
|
255
|
+
# json.age 32
|
|
256
|
+
# json.person another_jbuilder
|
|
257
|
+
# { "age": 32, "person": { ... }
|
|
258
|
+
_format_keys(value.attributes!)
|
|
259
|
+
else
|
|
260
|
+
# json.age 32
|
|
261
|
+
# { "age": 32 }
|
|
262
|
+
_format_keys(value)
|
|
263
|
+
end
|
|
264
|
+
elsif _is_collection?(value)
|
|
265
|
+
# json.comments @post.comments, :content, :created_at
|
|
266
|
+
# { "comments": [ { "content": "hello", "created_at": "..." }, { "content": "world", "created_at": "..." } ] }
|
|
267
|
+
_scope { _array value, attributes }
|
|
268
|
+
else
|
|
269
|
+
# json.author @post.creator, :name, :email_address
|
|
270
|
+
# { "author": { "name": "David", "email_address": "david@loudthinking.com" } }
|
|
271
|
+
_merge_block(key) { _extract value, attributes }
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
_set_value key, result
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
def _array(collection = EMPTY_ARRAY, attributes = nil, &block)
|
|
278
|
+
array = if collection.nil?
|
|
279
|
+
EMPTY_ARRAY
|
|
280
|
+
elsif block
|
|
281
|
+
_map_collection(collection, &block)
|
|
282
|
+
elsif attributes.present?
|
|
283
|
+
_map_collection(collection) { |element| _extract element, attributes }
|
|
284
|
+
else
|
|
285
|
+
_format_keys(collection.to_a)
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
@attributes = _merge_values(@attributes, array)
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
def _extract(object, attributes)
|
|
292
|
+
if ::Hash === object
|
|
293
|
+
_extract_hash_values(object, attributes)
|
|
294
|
+
else
|
|
295
|
+
_extract_method_values(object, attributes)
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
|
|
284
299
|
def _extract_hash_values(object, attributes)
|
|
285
300
|
attributes.each{ |key| _set_value key, _format_keys(object.fetch(key)) }
|
|
286
301
|
end
|
|
@@ -311,7 +326,13 @@ class Jbuilder
|
|
|
311
326
|
end
|
|
312
327
|
|
|
313
328
|
def _key(key)
|
|
314
|
-
|
|
329
|
+
if @key_formatter
|
|
330
|
+
@key_formatter.format(key)
|
|
331
|
+
elsif key.is_a?(::Symbol)
|
|
332
|
+
key.name
|
|
333
|
+
else
|
|
334
|
+
key.to_s
|
|
335
|
+
end
|
|
315
336
|
end
|
|
316
337
|
|
|
317
338
|
def _format_keys(hash_or_array)
|
|
@@ -335,9 +356,11 @@ class Jbuilder
|
|
|
335
356
|
end
|
|
336
357
|
|
|
337
358
|
def _map_collection(collection)
|
|
338
|
-
collection.map do |element|
|
|
359
|
+
collection = collection.map do |element|
|
|
339
360
|
_scope{ yield element }
|
|
340
|
-
end
|
|
361
|
+
end
|
|
362
|
+
collection.delete(BLANK)
|
|
363
|
+
collection
|
|
341
364
|
end
|
|
342
365
|
|
|
343
366
|
def _scope
|
|
@@ -350,16 +373,12 @@ class Jbuilder
|
|
|
350
373
|
end
|
|
351
374
|
|
|
352
375
|
def _is_collection?(object)
|
|
353
|
-
|
|
376
|
+
object.respond_to?(:map) && object.respond_to?(:count) && !(::Struct === object)
|
|
354
377
|
end
|
|
355
378
|
|
|
356
379
|
def _blank?(value=@attributes)
|
|
357
380
|
BLANK == value
|
|
358
381
|
end
|
|
359
|
-
|
|
360
|
-
def _object_respond_to?(object, *methods)
|
|
361
|
-
methods.all?{ |m| object.respond_to?(m) }
|
|
362
|
-
end
|
|
363
382
|
end
|
|
364
383
|
|
|
365
384
|
require 'jbuilder/railtie' if defined?(Rails)
|
|
@@ -56,15 +56,13 @@ class JbuilderGeneratorTest < Rails::Generators::TestCase
|
|
|
56
56
|
end
|
|
57
57
|
end
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
run_generator %w(Message content:rich_text video:attachment photos:attachments)
|
|
59
|
+
test 'handles virtual attributes' do
|
|
60
|
+
run_generator %w(Message content:rich_text video:attachment photos:attachments)
|
|
62
61
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
end
|
|
62
|
+
assert_file 'app/views/messages/_message.json.jbuilder' do |content|
|
|
63
|
+
assert_match %r{json\.content message\.content\.to_s}, content
|
|
64
|
+
assert_match %r{json\.video url_for\(message\.video\)}, content
|
|
65
|
+
assert_match %r{json\.photos do\n json\.array!\(message\.photos\) do \|photo\|\n json\.id photo\.id\n json\.url url_for\(photo\)\n end\nend}, content
|
|
68
66
|
end
|
|
69
67
|
end
|
|
70
68
|
end
|
|
@@ -4,6 +4,7 @@ require "action_view/testing/resolvers"
|
|
|
4
4
|
class JbuilderTemplateTest < ActiveSupport::TestCase
|
|
5
5
|
POST_PARTIAL = <<-JBUILDER
|
|
6
6
|
json.extract! post, :id, :body
|
|
7
|
+
json.title post.title if local_assigns.fetch(:include_title, false)
|
|
7
8
|
json.author do
|
|
8
9
|
first_name, last_name = post.author_name.split(nil, 2)
|
|
9
10
|
json.first_name first_name
|
|
@@ -17,6 +18,7 @@ class JbuilderTemplateTest < ActiveSupport::TestCase
|
|
|
17
18
|
|
|
18
19
|
RACER_PARTIAL = <<-JBUILDER
|
|
19
20
|
json.extract! racer, :id, :name
|
|
21
|
+
json.highlighted local_assigns.fetch(:highlighted, false)
|
|
20
22
|
JBUILDER
|
|
21
23
|
|
|
22
24
|
PARTIALS = {
|
|
@@ -30,7 +32,7 @@ class JbuilderTemplateTest < ActiveSupport::TestCase
|
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
AUTHORS = [ "David Heinemeier Hansson", "Pavel Pravosud" ].cycle
|
|
33
|
-
POSTS = (1..10).collect { |i| Post.new(i, "Post ##{i}", AUTHORS.next) }
|
|
35
|
+
POSTS = (1..10).collect { |i| Post.new(i, "Title #{i}", "Post ##{i}", AUTHORS.next) }
|
|
34
36
|
|
|
35
37
|
setup { Rails.cache.clear }
|
|
36
38
|
|
|
@@ -39,6 +41,11 @@ class JbuilderTemplateTest < ActiveSupport::TestCase
|
|
|
39
41
|
assert_equal "hello", result["content"]
|
|
40
42
|
end
|
|
41
43
|
|
|
44
|
+
test "method_missing can be used as a key" do
|
|
45
|
+
result = render('json.method_missing "hello"')
|
|
46
|
+
assert_equal({ "method_missing" => "hello" }, result)
|
|
47
|
+
end
|
|
48
|
+
|
|
42
49
|
test "partial by name with top-level locals" do
|
|
43
50
|
result = render('json.partial! "partial", content: "hello"')
|
|
44
51
|
assert_equal "hello", result["content"]
|
|
@@ -49,6 +56,17 @@ class JbuilderTemplateTest < ActiveSupport::TestCase
|
|
|
49
56
|
assert_equal "hello", result["content"]
|
|
50
57
|
end
|
|
51
58
|
|
|
59
|
+
test "partial by name with hash value omission (punning) as last statement [3.1+]" do
|
|
60
|
+
major, minor, _ = RUBY_VERSION.split(".").map(&:to_i)
|
|
61
|
+
return unless (major == 3 && minor >= 1) || major > 3
|
|
62
|
+
|
|
63
|
+
result = render(<<-JBUILDER)
|
|
64
|
+
content = "hello"
|
|
65
|
+
json.partial! "partial", content:
|
|
66
|
+
JBUILDER
|
|
67
|
+
assert_equal "hello", result["content"]
|
|
68
|
+
end
|
|
69
|
+
|
|
52
70
|
test "partial by options containing nested locals" do
|
|
53
71
|
result = render('json.partial! partial: "partial", locals: { content: "hello" }')
|
|
54
72
|
assert_equal "hello", result["content"]
|
|
@@ -65,6 +83,13 @@ class JbuilderTemplateTest < ActiveSupport::TestCase
|
|
|
65
83
|
assert_equal "Chris Harris", result["name"]
|
|
66
84
|
end
|
|
67
85
|
|
|
86
|
+
test "partial for Active Model preserves extra locals" do
|
|
87
|
+
result = render('json.partial! @racer, highlighted: true', racer: Racer.new(123, "Chris Harris"))
|
|
88
|
+
assert_equal 123, result["id"]
|
|
89
|
+
assert_equal "Chris Harris", result["name"]
|
|
90
|
+
assert_equal true, result["highlighted"]
|
|
91
|
+
end
|
|
92
|
+
|
|
68
93
|
test "partial collection by name with symbol local" do
|
|
69
94
|
result = render('json.partial! "post", collection: @posts, as: :post', posts: POSTS)
|
|
70
95
|
assert_equal 10, result.count
|
|
@@ -125,6 +150,18 @@ class JbuilderTemplateTest < ActiveSupport::TestCase
|
|
|
125
150
|
assert_equal [], render('json.array! @posts, partial: "post", as: :post', posts: nil)
|
|
126
151
|
end
|
|
127
152
|
|
|
153
|
+
test "single partial under key" do
|
|
154
|
+
result = render('json.post @post, partial: "post", as: :post', post: POSTS.first)
|
|
155
|
+
assert_equal "Post #1", result["post"]["body"]
|
|
156
|
+
assert_equal "Heinemeier Hansson", result["post"]["author"]["last_name"]
|
|
157
|
+
assert_equal "David", result["post"]["author"]["first_name"]
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
test 'single partial under key with local' do
|
|
161
|
+
result = render('json.post @post, partial: "post", as: :post, include_title: true', post: POSTS.first)
|
|
162
|
+
assert_equal "Title 1", result["post"]["title"]
|
|
163
|
+
end
|
|
164
|
+
|
|
128
165
|
test "array of partials under key" do
|
|
129
166
|
result = render('json.posts @posts, partial: "post", as: :post', posts: POSTS)
|
|
130
167
|
assert_equal 10, result["posts"].count
|
|
@@ -133,13 +170,19 @@ class JbuilderTemplateTest < ActiveSupport::TestCase
|
|
|
133
170
|
assert_equal "Pavel", result["posts"][5]["author"]["first_name"]
|
|
134
171
|
end
|
|
135
172
|
|
|
173
|
+
test "array of partials under key with local" do
|
|
174
|
+
result = render('json.posts @posts, partial: "post", as: :post, include_title: true', posts: POSTS)
|
|
175
|
+
assert_equal "Title 1", result["posts"][0]["title"]
|
|
176
|
+
assert_equal "Title 2", result["posts"][1]["title"]
|
|
177
|
+
end
|
|
178
|
+
|
|
136
179
|
test "empty array of partials under key from nil collection" do
|
|
137
180
|
Jbuilder::CollectionRenderer.expects(:new).never
|
|
138
181
|
result = render('json.posts @posts, partial: "post", as: :post', posts: nil)
|
|
139
182
|
assert_equal [], result["posts"]
|
|
140
183
|
end
|
|
141
184
|
|
|
142
|
-
test "empty array of partials under key from an
|
|
185
|
+
test "empty array of partials under key from an empty collection" do
|
|
143
186
|
Jbuilder::CollectionRenderer.expects(:new).never
|
|
144
187
|
result = render('json.posts @posts, partial: "post", as: :post', posts: [])
|
|
145
188
|
assert_equal [], result["posts"]
|
|
@@ -306,99 +349,97 @@ class JbuilderTemplateTest < ActiveSupport::TestCase
|
|
|
306
349
|
assert_equal "David", result["firstName"]
|
|
307
350
|
end
|
|
308
351
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
result = render('json.array! @posts, partial: "post", as: :post, cached: true', posts: [])
|
|
352
|
+
test "returns an empty array for an empty collection" do
|
|
353
|
+
Jbuilder::CollectionRenderer.expects(:new).never
|
|
354
|
+
result = render('json.array! @posts, partial: "post", as: :post, cached: true', posts: [])
|
|
313
355
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
356
|
+
# Do not use #assert_empty as it is important to ensure that the type of the JSON result is an array.
|
|
357
|
+
assert_equal [], result
|
|
358
|
+
end
|
|
317
359
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
360
|
+
test "works with an enumerable object" do
|
|
361
|
+
enumerable_class = Class.new do
|
|
362
|
+
include Enumerable
|
|
321
363
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
end
|
|
364
|
+
def each(&block)
|
|
365
|
+
[].each(&block)
|
|
325
366
|
end
|
|
367
|
+
end
|
|
326
368
|
|
|
327
|
-
|
|
369
|
+
result = render('json.array! @posts, partial: "post", as: :post, cached: true', posts: enumerable_class.new)
|
|
328
370
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
371
|
+
# Do not use #assert_empty as it is important to ensure that the type of the JSON result is an array.
|
|
372
|
+
assert_equal [], result
|
|
373
|
+
end
|
|
332
374
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
}
|
|
375
|
+
test "supports the cached: true option" do
|
|
376
|
+
result = render('json.array! @posts, partial: "post", as: :post, cached: true', posts: POSTS)
|
|
377
|
+
|
|
378
|
+
assert_equal 10, result.count
|
|
379
|
+
assert_equal "Post #5", result[4]["body"]
|
|
380
|
+
assert_equal "Heinemeier Hansson", result[2]["author"]["last_name"]
|
|
381
|
+
assert_equal "Pavel", result[5]["author"]["first_name"]
|
|
382
|
+
|
|
383
|
+
expected = {
|
|
384
|
+
"id" => 1,
|
|
385
|
+
"body" => "Post #1",
|
|
386
|
+
"author" => {
|
|
387
|
+
"first_name" => "David",
|
|
388
|
+
"last_name" => "Heinemeier Hansson"
|
|
348
389
|
}
|
|
390
|
+
}
|
|
349
391
|
|
|
350
|
-
|
|
392
|
+
assert_equal expected, Rails.cache.read("post-1")
|
|
351
393
|
|
|
352
|
-
|
|
394
|
+
result = render('json.array! @posts, partial: "post", as: :post, cached: true', posts: POSTS)
|
|
353
395
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
396
|
+
assert_equal 10, result.count
|
|
397
|
+
assert_equal "Post #5", result[4]["body"]
|
|
398
|
+
assert_equal "Heinemeier Hansson", result[2]["author"]["last_name"]
|
|
399
|
+
assert_equal "Pavel", result[5]["author"]["first_name"]
|
|
400
|
+
end
|
|
359
401
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
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
|
-
|
|
368
|
-
expected = {
|
|
369
|
-
"id" => 1,
|
|
370
|
-
"body" => "Post #1",
|
|
371
|
-
"author" => {
|
|
372
|
-
"first_name" => "David",
|
|
373
|
-
"last_name" => "Heinemeier Hansson"
|
|
374
|
-
}
|
|
375
|
-
}
|
|
402
|
+
test "supports the cached: ->() {} option" do
|
|
403
|
+
result = render('json.array! @posts, partial: "post", as: :post, cached: ->(post) { [post, "foo"] }', posts: POSTS)
|
|
376
404
|
|
|
377
|
-
|
|
405
|
+
assert_equal 10, result.count
|
|
406
|
+
assert_equal "Post #5", result[4]["body"]
|
|
407
|
+
assert_equal "Heinemeier Hansson", result[2]["author"]["last_name"]
|
|
408
|
+
assert_equal "Pavel", result[5]["author"]["first_name"]
|
|
378
409
|
|
|
379
|
-
|
|
410
|
+
expected = {
|
|
411
|
+
"id" => 1,
|
|
412
|
+
"body" => "Post #1",
|
|
413
|
+
"author" => {
|
|
414
|
+
"first_name" => "David",
|
|
415
|
+
"last_name" => "Heinemeier Hansson"
|
|
416
|
+
}
|
|
417
|
+
}
|
|
380
418
|
|
|
381
|
-
|
|
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
|
|
419
|
+
assert_equal expected, Rails.cache.read("post-1/foo")
|
|
386
420
|
|
|
387
|
-
|
|
388
|
-
error = assert_raises NotImplementedError do
|
|
389
|
-
render('json.array! @posts, partial: "post", as: :post, layout: "layout"', posts: POSTS)
|
|
390
|
-
end
|
|
421
|
+
result = render('json.array! @posts, partial: "post", as: :post, cached: ->(post) { [post, "foo"] }', posts: POSTS)
|
|
391
422
|
|
|
392
|
-
|
|
423
|
+
assert_equal 10, result.count
|
|
424
|
+
assert_equal "Post #5", result[4]["body"]
|
|
425
|
+
assert_equal "Heinemeier Hansson", result[2]["author"]["last_name"]
|
|
426
|
+
assert_equal "Pavel", result[5]["author"]["first_name"]
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
test "raises an error on a render call with the :layout option" do
|
|
430
|
+
error = assert_raises NotImplementedError do
|
|
431
|
+
render('json.array! @posts, partial: "post", as: :post, layout: "layout"', posts: POSTS)
|
|
393
432
|
end
|
|
394
433
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
render('json.array! @posts, partial: "post", as: :post, spacer_template: "template"', posts: POSTS)
|
|
398
|
-
end
|
|
434
|
+
assert_equal "The `:layout' option is not supported in collection rendering.", error.message
|
|
435
|
+
end
|
|
399
436
|
|
|
400
|
-
|
|
437
|
+
test "raises an error on a render call with the :spacer_template option" do
|
|
438
|
+
error = assert_raises NotImplementedError do
|
|
439
|
+
render('json.array! @posts, partial: "post", as: :post, spacer_template: "template"', posts: POSTS)
|
|
401
440
|
end
|
|
441
|
+
|
|
442
|
+
assert_equal "The `:spacer_template' option is not supported in collection rendering.", error.message
|
|
402
443
|
end
|
|
403
444
|
|
|
404
445
|
private
|
|
@@ -416,12 +457,7 @@ class JbuilderTemplateTest < ActiveSupport::TestCase
|
|
|
416
457
|
lookup_context = ActionView::LookupContext.new([ resolver ], {}, [""])
|
|
417
458
|
controller = ActionView::TestCase::TestController.new
|
|
418
459
|
|
|
419
|
-
|
|
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
|
|
460
|
+
view = ActionView::Base.with_empty_template_cache.new(lookup_context, options.fetch(:assigns, {}), controller)
|
|
425
461
|
|
|
426
462
|
def view.view_cache_dependencies; []; end
|
|
427
463
|
def view.combined_fragment_cache_key(key) [ key ] end
|
data/test/jbuilder_test.rb
CHANGED
|
@@ -61,6 +61,14 @@ class JbuilderTest < ActiveSupport::TestCase
|
|
|
61
61
|
assert_equal 'hello', result['content']
|
|
62
62
|
end
|
|
63
63
|
|
|
64
|
+
test 'method_missing key' do
|
|
65
|
+
result = jbuild do |json|
|
|
66
|
+
json.method_missing 'hello'
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
assert_equal 'hello', result['method_missing']
|
|
70
|
+
end
|
|
71
|
+
|
|
64
72
|
test 'single key with false value' do
|
|
65
73
|
result = jbuild do |json|
|
|
66
74
|
json.content false
|
|
@@ -784,12 +792,12 @@ class JbuilderTest < ActiveSupport::TestCase
|
|
|
784
792
|
assert_equal ['camelStyle'], result.keys
|
|
785
793
|
end
|
|
786
794
|
|
|
787
|
-
test '
|
|
795
|
+
test 'use default key formatter when configured' do
|
|
788
796
|
Jbuilder.key_format
|
|
789
797
|
jbuild{ |json| json.key 'value' }
|
|
790
798
|
formatter = Jbuilder.send(:class_variable_get, '@@key_formatter')
|
|
791
799
|
cache = formatter.instance_variable_get('@cache')
|
|
792
|
-
|
|
800
|
+
assert_includes cache, :key
|
|
793
801
|
end
|
|
794
802
|
|
|
795
803
|
test 'ignore_nil! without a parameter' do
|
|
@@ -930,12 +938,17 @@ class JbuilderTest < ActiveSupport::TestCase
|
|
|
930
938
|
end
|
|
931
939
|
end
|
|
932
940
|
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
941
|
+
test "respects JSON encoding customizations" do
|
|
942
|
+
# Active Support overrides Time#as_json for custom formatting.
|
|
943
|
+
# Ensure we call #to_json on the final attributes instead of JSON.dump.
|
|
944
|
+
result = JSON.load(Jbuilder.encode { |json| json.time Time.parse("2018-05-13 11:51:00.485 -0400") })
|
|
945
|
+
assert_equal "2018-05-13T11:51:00.485-04:00", result["time"]
|
|
946
|
+
end
|
|
947
|
+
|
|
948
|
+
test "encode forwards options to new" do
|
|
949
|
+
Jbuilder.encode(key_formatter: 1, ignore_nil: 2) do |json|
|
|
950
|
+
assert_equal 1, json.instance_eval{ @key_formatter }
|
|
951
|
+
assert_equal 2, json.instance_eval{ @ignore_nil }
|
|
939
952
|
end
|
|
940
953
|
end
|
|
941
954
|
end
|