jbuilder 2.0.6 → 2.11.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.github/workflows/ruby.yml +108 -0
- data/.gitignore +4 -1
- data/Appraisals +25 -0
- data/CONTRIBUTING.md +106 -0
- data/Gemfile +4 -12
- data/MIT-LICENSE +1 -1
- data/README.md +171 -45
- data/Rakefile +15 -10
- data/gemfiles/rails_5_0.gemfile +10 -0
- 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 +13 -2
- data/lib/generators/rails/scaffold_controller_generator.rb +9 -3
- data/lib/generators/rails/templates/api_controller.rb +63 -0
- data/lib/generators/rails/templates/controller.rb +16 -20
- data/lib/generators/rails/templates/index.json.jbuilder +1 -4
- data/lib/generators/rails/templates/partial.json.jbuilder +16 -0
- data/lib/generators/rails/templates/show.json.jbuilder +1 -1
- data/lib/jbuilder/blank.rb +11 -0
- data/lib/jbuilder/collection_renderer.rb +109 -0
- data/lib/jbuilder/dependency_tracker.rb +1 -1
- data/lib/jbuilder/errors.rb +24 -0
- data/lib/jbuilder/jbuilder.rb +7 -0
- data/lib/jbuilder/jbuilder_template.rb +213 -65
- data/lib/jbuilder/key_formatter.rb +34 -0
- data/lib/jbuilder/railtie.rb +31 -6
- data/lib/jbuilder.rb +148 -114
- data/test/jbuilder_dependency_tracker_test.rb +3 -4
- data/test/jbuilder_generator_test.rb +31 -4
- data/test/jbuilder_template_test.rb +313 -195
- data/test/jbuilder_test.rb +615 -219
- data/test/scaffold_api_controller_generator_test.rb +70 -0
- data/test/scaffold_controller_generator_test.rb +62 -19
- data/test/test_helper.rb +36 -0
- metadata +38 -23
- data/.travis.yml +0 -21
- data/CHANGELOG.md +0 -89
- data/Gemfile.old +0 -14
data/test/jbuilder_test.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
|
-
require '
|
2
|
-
require 'active_support/test_case'
|
1
|
+
require 'test_helper'
|
3
2
|
require 'active_support/inflector'
|
4
3
|
require 'jbuilder'
|
5
4
|
|
5
|
+
def jbuild(*args, &block)
|
6
|
+
Jbuilder.new(*args, &block).attributes!
|
7
|
+
end
|
8
|
+
|
6
9
|
Comment = Struct.new(:content, :id)
|
7
10
|
|
8
11
|
class NonEnumerable
|
@@ -10,223 +13,344 @@ class NonEnumerable
|
|
10
13
|
@collection = collection
|
11
14
|
end
|
12
15
|
|
13
|
-
|
14
|
-
|
16
|
+
delegate :map, :count, to: :@collection
|
17
|
+
end
|
18
|
+
|
19
|
+
class VeryBasicWrapper < BasicObject
|
20
|
+
def initialize(thing)
|
21
|
+
@thing = thing
|
22
|
+
end
|
23
|
+
|
24
|
+
def method_missing(name, *args, &block)
|
25
|
+
@thing.send name, *args, &block
|
15
26
|
end
|
16
27
|
end
|
17
28
|
|
29
|
+
# This is not Struct, because structs are Enumerable
|
30
|
+
class Person
|
31
|
+
attr_reader :name, :age
|
32
|
+
|
33
|
+
def initialize(name, age)
|
34
|
+
@name, @age = name, age
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class RelationMock
|
39
|
+
include Enumerable
|
40
|
+
|
41
|
+
def each(&block)
|
42
|
+
[Person.new('Bob', 30), Person.new('Frank', 50)].each(&block)
|
43
|
+
end
|
44
|
+
|
45
|
+
def empty?
|
46
|
+
false
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
|
18
51
|
class JbuilderTest < ActiveSupport::TestCase
|
52
|
+
teardown do
|
53
|
+
Jbuilder.send :class_variable_set, '@@key_formatter', nil
|
54
|
+
end
|
55
|
+
|
19
56
|
test 'single key' do
|
20
|
-
|
57
|
+
result = jbuild do |json|
|
21
58
|
json.content 'hello'
|
22
59
|
end
|
23
60
|
|
24
|
-
assert_equal 'hello',
|
61
|
+
assert_equal 'hello', result['content']
|
25
62
|
end
|
26
63
|
|
27
64
|
test 'single key with false value' do
|
28
|
-
|
65
|
+
result = jbuild do |json|
|
29
66
|
json.content false
|
30
67
|
end
|
31
68
|
|
32
|
-
assert_equal false,
|
69
|
+
assert_equal false, result['content']
|
33
70
|
end
|
34
71
|
|
35
72
|
test 'single key with nil value' do
|
36
|
-
|
73
|
+
result = jbuild do |json|
|
37
74
|
json.content nil
|
38
75
|
end
|
39
76
|
|
40
|
-
assert
|
41
|
-
|
77
|
+
assert result.has_key?('content')
|
78
|
+
assert_nil result['content']
|
42
79
|
end
|
43
80
|
|
44
81
|
test 'multiple keys' do
|
45
|
-
|
82
|
+
result = jbuild do |json|
|
46
83
|
json.title 'hello'
|
47
84
|
json.content 'world'
|
48
85
|
end
|
49
86
|
|
50
|
-
|
51
|
-
assert_equal '
|
52
|
-
assert_equal 'world', parsed['content']
|
87
|
+
assert_equal 'hello', result['title']
|
88
|
+
assert_equal 'world', result['content']
|
53
89
|
end
|
54
90
|
|
55
91
|
test 'extracting from object' do
|
56
92
|
person = Struct.new(:name, :age).new('David', 32)
|
57
93
|
|
58
|
-
|
94
|
+
result = jbuild do |json|
|
59
95
|
json.extract! person, :name, :age
|
60
96
|
end
|
61
97
|
|
62
|
-
|
63
|
-
assert_equal
|
64
|
-
assert_equal 32, parsed['age']
|
98
|
+
assert_equal 'David', result['name']
|
99
|
+
assert_equal 32, result['age']
|
65
100
|
end
|
66
101
|
|
67
|
-
test 'extracting from object using call style
|
102
|
+
test 'extracting from object using call style' do
|
68
103
|
person = Struct.new(:name, :age).new('David', 32)
|
69
104
|
|
70
|
-
|
105
|
+
result = jbuild do |json|
|
71
106
|
json.(person, :name, :age)
|
72
107
|
end
|
73
108
|
|
74
|
-
|
75
|
-
assert_equal
|
76
|
-
assert_equal 32, parsed['age']
|
109
|
+
assert_equal 'David', result['name']
|
110
|
+
assert_equal 32, result['age']
|
77
111
|
end
|
78
112
|
|
79
113
|
test 'extracting from hash' do
|
80
114
|
person = {:name => 'Jim', :age => 34}
|
81
115
|
|
82
|
-
|
116
|
+
result = jbuild do |json|
|
83
117
|
json.extract! person, :name, :age
|
84
118
|
end
|
85
119
|
|
86
|
-
|
87
|
-
assert_equal
|
88
|
-
assert_equal 34, parsed['age']
|
120
|
+
assert_equal 'Jim', result['name']
|
121
|
+
assert_equal 34, result['age']
|
89
122
|
end
|
90
123
|
|
91
124
|
test 'nesting single child with block' do
|
92
|
-
|
125
|
+
result = jbuild do |json|
|
93
126
|
json.author do
|
94
127
|
json.name 'David'
|
95
128
|
json.age 32
|
96
129
|
end
|
97
130
|
end
|
98
131
|
|
99
|
-
|
100
|
-
assert_equal
|
101
|
-
|
132
|
+
assert_equal 'David', result['author']['name']
|
133
|
+
assert_equal 32, result['author']['age']
|
134
|
+
end
|
135
|
+
|
136
|
+
test 'empty block handling' do
|
137
|
+
result = jbuild do |json|
|
138
|
+
json.foo 'bar'
|
139
|
+
json.author do
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
assert_equal 'bar', result['foo']
|
144
|
+
assert !result.key?('author')
|
145
|
+
end
|
146
|
+
|
147
|
+
test 'blocks are additive' do
|
148
|
+
result = jbuild do |json|
|
149
|
+
json.author do
|
150
|
+
json.name 'David'
|
151
|
+
end
|
152
|
+
|
153
|
+
json.author do
|
154
|
+
json.age 32
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
assert_equal 'David', result['author']['name']
|
159
|
+
assert_equal 32, result['author']['age']
|
160
|
+
end
|
161
|
+
|
162
|
+
test 'nested blocks are additive' do
|
163
|
+
result = jbuild do |json|
|
164
|
+
json.author do
|
165
|
+
json.name do
|
166
|
+
json.first 'David'
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
json.author do
|
171
|
+
json.name do
|
172
|
+
json.last 'Heinemeier Hansson'
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
assert_equal 'David', result['author']['name']['first']
|
178
|
+
assert_equal 'Heinemeier Hansson', result['author']['name']['last']
|
179
|
+
end
|
180
|
+
|
181
|
+
test 'support merge! method' do
|
182
|
+
result = jbuild do |json|
|
183
|
+
json.merge! 'foo' => 'bar'
|
184
|
+
end
|
185
|
+
|
186
|
+
assert_equal 'bar', result['foo']
|
187
|
+
end
|
188
|
+
|
189
|
+
test 'support merge! method in a block' do
|
190
|
+
result = jbuild do |json|
|
191
|
+
json.author do
|
192
|
+
json.merge! 'name' => 'Pavel'
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
assert_equal 'Pavel', result['author']['name']
|
197
|
+
end
|
198
|
+
|
199
|
+
test 'support merge! method with Jbuilder instance' do
|
200
|
+
obj = jbuild do |json|
|
201
|
+
json.foo 'bar'
|
202
|
+
end
|
203
|
+
|
204
|
+
result = jbuild do |json|
|
205
|
+
json.merge! obj
|
206
|
+
end
|
207
|
+
|
208
|
+
assert_equal 'bar', result['foo']
|
209
|
+
end
|
210
|
+
|
211
|
+
test 'blocks are additive via extract syntax' do
|
212
|
+
person = Person.new('Pavel', 27)
|
213
|
+
|
214
|
+
result = jbuild do |json|
|
215
|
+
json.author person, :age
|
216
|
+
json.author person, :name
|
217
|
+
end
|
218
|
+
|
219
|
+
assert_equal 'Pavel', result['author']['name']
|
220
|
+
assert_equal 27, result['author']['age']
|
221
|
+
end
|
222
|
+
|
223
|
+
test 'arrays are additive' do
|
224
|
+
result = jbuild do |json|
|
225
|
+
json.array! %w[foo]
|
226
|
+
json.array! %w[bar]
|
227
|
+
end
|
228
|
+
|
229
|
+
assert_equal %w[foo bar], result
|
102
230
|
end
|
103
231
|
|
104
232
|
test 'nesting multiple children with block' do
|
105
|
-
|
233
|
+
result = jbuild do |json|
|
106
234
|
json.comments do
|
107
235
|
json.child! { json.content 'hello' }
|
108
236
|
json.child! { json.content 'world' }
|
109
237
|
end
|
110
238
|
end
|
111
239
|
|
112
|
-
|
113
|
-
assert_equal '
|
114
|
-
assert_equal 'world', parsed['comments'].second['content']
|
240
|
+
assert_equal 'hello', result['comments'].first['content']
|
241
|
+
assert_equal 'world', result['comments'].second['content']
|
115
242
|
end
|
116
243
|
|
117
244
|
test 'nesting single child with inline extract' do
|
118
|
-
person =
|
119
|
-
attr_reader :name, :age
|
120
|
-
|
121
|
-
def initialize(name, age)
|
122
|
-
@name, @age = name, age
|
123
|
-
end
|
124
|
-
end.new('David', 32)
|
245
|
+
person = Person.new('David', 32)
|
125
246
|
|
126
|
-
|
247
|
+
result = jbuild do |json|
|
127
248
|
json.author person, :name, :age
|
128
249
|
end
|
129
250
|
|
130
|
-
|
131
|
-
assert_equal
|
132
|
-
assert_equal 32, parsed['author']['age']
|
251
|
+
assert_equal 'David', result['author']['name']
|
252
|
+
assert_equal 32, result['author']['age']
|
133
253
|
end
|
134
254
|
|
135
255
|
test 'nesting multiple children from array' do
|
136
256
|
comments = [ Comment.new('hello', 1), Comment.new('world', 2) ]
|
137
257
|
|
138
|
-
|
258
|
+
result = jbuild do |json|
|
139
259
|
json.comments comments, :content
|
140
260
|
end
|
141
261
|
|
142
|
-
|
143
|
-
assert_equal
|
144
|
-
assert_equal '
|
145
|
-
assert_equal 'world', parsed['comments'].second['content']
|
262
|
+
assert_equal ['content'], result['comments'].first.keys
|
263
|
+
assert_equal 'hello', result['comments'].first['content']
|
264
|
+
assert_equal 'world', result['comments'].second['content']
|
146
265
|
end
|
147
266
|
|
148
267
|
test 'nesting multiple children from array when child array is empty' do
|
149
268
|
comments = []
|
150
269
|
|
151
|
-
|
270
|
+
result = jbuild do |json|
|
152
271
|
json.name 'Parent'
|
153
272
|
json.comments comments, :content
|
154
273
|
end
|
155
274
|
|
156
|
-
|
157
|
-
assert_equal
|
158
|
-
assert_equal [], parsed['comments']
|
275
|
+
assert_equal 'Parent', result['name']
|
276
|
+
assert_equal [], result['comments']
|
159
277
|
end
|
160
278
|
|
161
279
|
test 'nesting multiple children from array with inline loop' do
|
162
280
|
comments = [ Comment.new('hello', 1), Comment.new('world', 2) ]
|
163
281
|
|
164
|
-
|
282
|
+
result = jbuild do |json|
|
165
283
|
json.comments comments do |comment|
|
166
284
|
json.content comment.content
|
167
285
|
end
|
168
286
|
end
|
169
287
|
|
170
|
-
|
171
|
-
assert_equal
|
172
|
-
assert_equal '
|
173
|
-
assert_equal 'world', parsed['comments'].second['content']
|
288
|
+
assert_equal ['content'], result['comments'].first.keys
|
289
|
+
assert_equal 'hello', result['comments'].first['content']
|
290
|
+
assert_equal 'world', result['comments'].second['content']
|
174
291
|
end
|
175
292
|
|
176
293
|
test 'handles nil-collections as empty arrays' do
|
177
|
-
|
294
|
+
result = jbuild do |json|
|
178
295
|
json.comments nil do |comment|
|
179
296
|
json.content comment.content
|
180
297
|
end
|
181
298
|
end
|
182
299
|
|
183
|
-
assert_equal [],
|
300
|
+
assert_equal [], result['comments']
|
184
301
|
end
|
185
302
|
|
186
303
|
test 'nesting multiple children from a non-Enumerable that responds to #map' do
|
187
304
|
comments = NonEnumerable.new([ Comment.new('hello', 1), Comment.new('world', 2) ])
|
188
305
|
|
189
|
-
|
306
|
+
result = jbuild do |json|
|
190
307
|
json.comments comments, :content
|
191
308
|
end
|
192
309
|
|
193
|
-
|
194
|
-
assert_equal
|
195
|
-
assert_equal '
|
196
|
-
assert_equal 'world', parsed['comments'].second['content']
|
310
|
+
assert_equal ['content'], result['comments'].first.keys
|
311
|
+
assert_equal 'hello', result['comments'].first['content']
|
312
|
+
assert_equal 'world', result['comments'].second['content']
|
197
313
|
end
|
198
314
|
|
199
|
-
test 'nesting multiple
|
315
|
+
test 'nesting multiple children from a non-Enumerable that responds to #map with inline loop' do
|
200
316
|
comments = NonEnumerable.new([ Comment.new('hello', 1), Comment.new('world', 2) ])
|
201
317
|
|
202
|
-
|
318
|
+
result = jbuild do |json|
|
203
319
|
json.comments comments do |comment|
|
204
320
|
json.content comment.content
|
205
321
|
end
|
206
322
|
end
|
207
323
|
|
208
|
-
|
209
|
-
assert_equal
|
210
|
-
assert_equal '
|
211
|
-
|
324
|
+
assert_equal ['content'], result['comments'].first.keys
|
325
|
+
assert_equal 'hello', result['comments'].first['content']
|
326
|
+
assert_equal 'world', result['comments'].second['content']
|
327
|
+
end
|
328
|
+
|
329
|
+
test 'array! casts array-like objects to array before merging' do
|
330
|
+
wrapped_array = VeryBasicWrapper.new(%w[foo bar])
|
331
|
+
|
332
|
+
result = jbuild do |json|
|
333
|
+
json.array! wrapped_array
|
334
|
+
end
|
335
|
+
|
336
|
+
assert_equal %w[foo bar], result
|
212
337
|
end
|
213
338
|
|
214
339
|
test 'nesting multiple children from array with inline loop on root' do
|
215
340
|
comments = [ Comment.new('hello', 1), Comment.new('world', 2) ]
|
216
341
|
|
217
|
-
|
342
|
+
result = jbuild do |json|
|
218
343
|
json.call(comments) do |comment|
|
219
344
|
json.content comment.content
|
220
345
|
end
|
221
346
|
end
|
222
347
|
|
223
|
-
|
224
|
-
assert_equal '
|
225
|
-
assert_equal 'world', parsed.second['content']
|
348
|
+
assert_equal 'hello', result.first['content']
|
349
|
+
assert_equal 'world', result.second['content']
|
226
350
|
end
|
227
351
|
|
228
352
|
test 'array nested inside nested hash' do
|
229
|
-
|
353
|
+
result = jbuild do |json|
|
230
354
|
json.author do
|
231
355
|
json.name 'David'
|
232
356
|
json.age 32
|
@@ -238,13 +362,12 @@ class JbuilderTest < ActiveSupport::TestCase
|
|
238
362
|
end
|
239
363
|
end
|
240
364
|
|
241
|
-
|
242
|
-
assert_equal '
|
243
|
-
assert_equal 'world', parsed['author']['comments'].second['content']
|
365
|
+
assert_equal 'hello', result['author']['comments'].first['content']
|
366
|
+
assert_equal 'world', result['author']['comments'].second['content']
|
244
367
|
end
|
245
368
|
|
246
369
|
test 'array nested inside array' do
|
247
|
-
|
370
|
+
result = jbuild do |json|
|
248
371
|
json.comments do
|
249
372
|
json.child! do
|
250
373
|
json.authors do
|
@@ -256,12 +379,13 @@ class JbuilderTest < ActiveSupport::TestCase
|
|
256
379
|
end
|
257
380
|
end
|
258
381
|
|
259
|
-
assert_equal 'david',
|
382
|
+
assert_equal 'david', result['comments'].first['authors'].first['name']
|
260
383
|
end
|
261
384
|
|
262
385
|
test 'directly set an array nested in another array' do
|
263
386
|
data = [ { :department => 'QA', :not_in_json => 'hello', :names => ['John', 'David'] } ]
|
264
|
-
|
387
|
+
|
388
|
+
result = jbuild do |json|
|
265
389
|
json.array! data do |object|
|
266
390
|
json.department object[:department]
|
267
391
|
json.names do
|
@@ -270,276 +394,548 @@ class JbuilderTest < ActiveSupport::TestCase
|
|
270
394
|
end
|
271
395
|
end
|
272
396
|
|
273
|
-
assert_equal 'David',
|
274
|
-
|
397
|
+
assert_equal 'David', result[0]['names'].last
|
398
|
+
assert !result[0].key?('not_in_json')
|
275
399
|
end
|
276
400
|
|
277
401
|
test 'nested jbuilder objects' do
|
278
|
-
to_nest = Jbuilder.new
|
279
|
-
|
280
|
-
|
402
|
+
to_nest = Jbuilder.new{ |json| json.nested_value 'Nested Test' }
|
403
|
+
|
404
|
+
result = jbuild do |json|
|
281
405
|
json.value 'Test'
|
282
406
|
json.nested to_nest
|
283
407
|
end
|
284
408
|
|
285
|
-
|
286
|
-
assert_equal
|
409
|
+
expected = {'value' => 'Test', 'nested' => {'nested_value' => 'Nested Test'}}
|
410
|
+
assert_equal expected, result
|
287
411
|
end
|
288
412
|
|
289
413
|
test 'nested jbuilder object via set!' do
|
290
|
-
to_nest = Jbuilder.new
|
291
|
-
|
292
|
-
|
414
|
+
to_nest = Jbuilder.new{ |json| json.nested_value 'Nested Test' }
|
415
|
+
|
416
|
+
result = jbuild do |json|
|
293
417
|
json.value 'Test'
|
294
418
|
json.set! :nested, to_nest
|
295
419
|
end
|
296
420
|
|
297
|
-
|
298
|
-
assert_equal
|
421
|
+
expected = {'value' => 'Test', 'nested' => {'nested_value' => 'Nested Test'}}
|
422
|
+
assert_equal expected, result
|
299
423
|
end
|
300
424
|
|
301
425
|
test 'top-level array' do
|
302
426
|
comments = [ Comment.new('hello', 1), Comment.new('world', 2) ]
|
303
427
|
|
304
|
-
|
305
|
-
json.array!
|
428
|
+
result = jbuild do |json|
|
429
|
+
json.array! comments do |comment|
|
430
|
+
json.content comment.content
|
431
|
+
end
|
432
|
+
end
|
433
|
+
|
434
|
+
assert_equal 'hello', result.first['content']
|
435
|
+
assert_equal 'world', result.second['content']
|
436
|
+
end
|
437
|
+
|
438
|
+
test 'it allows using next in array block to skip value' do
|
439
|
+
comments = [ Comment.new('hello', 1), Comment.new('skip', 2), Comment.new('world', 3) ]
|
440
|
+
result = jbuild do |json|
|
441
|
+
json.array! comments do |comment|
|
442
|
+
next if comment.id == 2
|
306
443
|
json.content comment.content
|
307
444
|
end
|
308
445
|
end
|
309
446
|
|
310
|
-
|
311
|
-
assert_equal 'hello',
|
312
|
-
assert_equal 'world',
|
447
|
+
assert_equal 2, result.length
|
448
|
+
assert_equal 'hello', result.first['content']
|
449
|
+
assert_equal 'world', result.second['content']
|
313
450
|
end
|
314
451
|
|
315
452
|
test 'extract attributes directly from array' do
|
316
453
|
comments = [ Comment.new('hello', 1), Comment.new('world', 2) ]
|
317
454
|
|
318
|
-
|
455
|
+
result = jbuild do |json|
|
319
456
|
json.array! comments, :content, :id
|
320
457
|
end
|
321
458
|
|
322
|
-
|
323
|
-
assert_equal
|
324
|
-
assert_equal
|
325
|
-
assert_equal
|
326
|
-
assert_equal 2, parsed.second['id']
|
459
|
+
assert_equal 'hello', result.first['content']
|
460
|
+
assert_equal 1, result.first['id']
|
461
|
+
assert_equal 'world', result.second['content']
|
462
|
+
assert_equal 2, result.second['id']
|
327
463
|
end
|
328
464
|
|
329
465
|
test 'empty top-level array' do
|
330
466
|
comments = []
|
331
467
|
|
332
|
-
|
333
|
-
json.array!
|
468
|
+
result = jbuild do |json|
|
469
|
+
json.array! comments do |comment|
|
334
470
|
json.content comment.content
|
335
471
|
end
|
336
472
|
end
|
337
473
|
|
338
|
-
assert_equal [],
|
474
|
+
assert_equal [], result
|
339
475
|
end
|
340
476
|
|
341
477
|
test 'dynamically set a key/value' do
|
342
|
-
|
478
|
+
result = jbuild do |json|
|
343
479
|
json.set! :each, 'stuff'
|
344
480
|
end
|
345
481
|
|
346
|
-
assert_equal 'stuff',
|
482
|
+
assert_equal 'stuff', result['each']
|
347
483
|
end
|
348
484
|
|
349
485
|
test 'dynamically set a key/nested child with block' do
|
350
|
-
|
351
|
-
json.set!
|
486
|
+
result = jbuild do |json|
|
487
|
+
json.set! :author do
|
352
488
|
json.name 'David'
|
353
489
|
json.age 32
|
354
490
|
end
|
355
491
|
end
|
356
492
|
|
357
|
-
|
358
|
-
assert_equal
|
359
|
-
assert_equal 32, parsed['author']['age']
|
493
|
+
assert_equal 'David', result['author']['name']
|
494
|
+
assert_equal 32, result['author']['age']
|
360
495
|
end
|
361
496
|
|
362
497
|
test 'dynamically sets a collection' do
|
363
498
|
comments = [ Comment.new('hello', 1), Comment.new('world', 2) ]
|
364
499
|
|
365
|
-
|
500
|
+
result = jbuild do |json|
|
366
501
|
json.set! :comments, comments, :content
|
367
502
|
end
|
368
503
|
|
369
|
-
|
370
|
-
assert_equal
|
371
|
-
assert_equal '
|
372
|
-
assert_equal 'world', parsed['comments'].second['content']
|
504
|
+
assert_equal ['content'], result['comments'].first.keys
|
505
|
+
assert_equal 'hello', result['comments'].first['content']
|
506
|
+
assert_equal 'world', result['comments'].second['content']
|
373
507
|
end
|
374
508
|
|
375
509
|
test 'query like object' do
|
376
|
-
|
377
|
-
attr_reader :name, :age
|
378
|
-
|
379
|
-
def initialize(name, age)
|
380
|
-
@name, @age = name, age
|
381
|
-
end
|
382
|
-
end
|
383
|
-
class RelationMock
|
384
|
-
include Enumerable
|
385
|
-
|
386
|
-
def each(&block)
|
387
|
-
[Person.new('Bob', 30), Person.new('Frank', 50)].each(&block)
|
388
|
-
end
|
389
|
-
def empty?
|
390
|
-
false
|
391
|
-
end
|
392
|
-
end
|
393
|
-
|
394
|
-
result = Jbuilder.encode do |json|
|
510
|
+
result = jbuild do |json|
|
395
511
|
json.relations RelationMock.new, :name, :age
|
396
512
|
end
|
397
513
|
|
398
|
-
|
399
|
-
assert_equal
|
400
|
-
assert_equal
|
401
|
-
assert_equal 50, parsed['relations'][1]['age']
|
514
|
+
assert_equal 2, result['relations'].length
|
515
|
+
assert_equal 'Bob', result['relations'][0]['name']
|
516
|
+
assert_equal 50, result['relations'][1]['age']
|
402
517
|
end
|
403
518
|
|
404
519
|
test 'initialize via options hash' do
|
405
|
-
jbuilder = Jbuilder.new(:
|
520
|
+
jbuilder = Jbuilder.new(key_formatter: 1, ignore_nil: 2)
|
406
521
|
assert_equal 1, jbuilder.instance_eval{ @key_formatter }
|
407
522
|
assert_equal 2, jbuilder.instance_eval{ @ignore_nil }
|
408
523
|
end
|
409
524
|
|
410
525
|
test 'key_format! with parameter' do
|
411
|
-
|
412
|
-
|
413
|
-
|
526
|
+
result = jbuild do |json|
|
527
|
+
json.key_format! camelize: [:lower]
|
528
|
+
json.camel_style 'for JS'
|
529
|
+
end
|
414
530
|
|
415
|
-
assert_equal ['camelStyle'],
|
531
|
+
assert_equal ['camelStyle'], result.keys
|
416
532
|
end
|
417
533
|
|
418
534
|
test 'key_format! with parameter not as an array' do
|
419
|
-
|
420
|
-
|
421
|
-
|
535
|
+
result = jbuild do |json|
|
536
|
+
json.key_format! :camelize => :lower
|
537
|
+
json.camel_style 'for JS'
|
538
|
+
end
|
422
539
|
|
423
|
-
assert_equal ['camelStyle'],
|
540
|
+
assert_equal ['camelStyle'], result.keys
|
424
541
|
end
|
425
542
|
|
426
543
|
test 'key_format! propagates to child elements' do
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
544
|
+
result = jbuild do |json|
|
545
|
+
json.key_format! :upcase
|
546
|
+
json.level1 'one'
|
547
|
+
json.level2 do
|
548
|
+
json.value 'two'
|
549
|
+
end
|
432
550
|
end
|
433
551
|
|
434
|
-
result = json.attributes!
|
435
552
|
assert_equal 'one', result['LEVEL1']
|
436
553
|
assert_equal 'two', result['LEVEL2']['VALUE']
|
437
554
|
end
|
438
555
|
|
439
556
|
test 'key_format! resets after child element' do
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
557
|
+
result = jbuild do |json|
|
558
|
+
json.level2 do
|
559
|
+
json.key_format! :upcase
|
560
|
+
json.value 'two'
|
561
|
+
end
|
562
|
+
json.level1 'one'
|
444
563
|
end
|
445
|
-
json.level1 'one'
|
446
564
|
|
447
|
-
result = json.attributes!
|
448
565
|
assert_equal 'two', result['level2']['VALUE']
|
449
566
|
assert_equal 'one', result['level1']
|
450
567
|
end
|
451
568
|
|
569
|
+
test 'key_format! can be changed in child elements' do
|
570
|
+
result = jbuild do |json|
|
571
|
+
json.key_format! camelize: :lower
|
572
|
+
|
573
|
+
json.level_one do
|
574
|
+
json.key_format! :upcase
|
575
|
+
json.value 'two'
|
576
|
+
end
|
577
|
+
end
|
578
|
+
|
579
|
+
assert_equal ['levelOne'], result.keys
|
580
|
+
assert_equal ['VALUE'], result['levelOne'].keys
|
581
|
+
end
|
582
|
+
|
583
|
+
test 'key_format! can be changed in array!' do
|
584
|
+
result = jbuild do |json|
|
585
|
+
json.key_format! camelize: :lower
|
586
|
+
|
587
|
+
json.level_one do
|
588
|
+
json.array! [{value: 'two'}] do |object|
|
589
|
+
json.key_format! :upcase
|
590
|
+
json.value object[:value]
|
591
|
+
end
|
592
|
+
end
|
593
|
+
end
|
594
|
+
|
595
|
+
assert_equal ['levelOne'], result.keys
|
596
|
+
assert_equal ['VALUE'], result['levelOne'][0].keys
|
597
|
+
end
|
598
|
+
|
452
599
|
test 'key_format! with no parameter' do
|
453
|
-
|
454
|
-
|
455
|
-
|
600
|
+
result = jbuild do |json|
|
601
|
+
json.key_format! :upcase
|
602
|
+
json.lower 'Value'
|
603
|
+
end
|
456
604
|
|
457
|
-
assert_equal ['LOWER'],
|
605
|
+
assert_equal ['LOWER'], result.keys
|
458
606
|
end
|
459
607
|
|
460
608
|
test 'key_format! with multiple steps' do
|
461
|
-
|
462
|
-
|
463
|
-
|
609
|
+
result = jbuild do |json|
|
610
|
+
json.key_format! :upcase, :pluralize
|
611
|
+
json.pill 'foo'
|
612
|
+
end
|
464
613
|
|
465
|
-
assert_equal ['PILLs'],
|
614
|
+
assert_equal ['PILLs'], result.keys
|
466
615
|
end
|
467
616
|
|
468
617
|
test 'key_format! with lambda/proc' do
|
469
|
-
|
470
|
-
|
471
|
-
|
618
|
+
result = jbuild do |json|
|
619
|
+
json.key_format! ->(key){ key + ' and friends' }
|
620
|
+
json.oats 'foo'
|
621
|
+
end
|
472
622
|
|
473
|
-
assert_equal ['oats and friends'],
|
623
|
+
assert_equal ['oats and friends'], result.keys
|
474
624
|
end
|
475
625
|
|
476
|
-
test '
|
477
|
-
|
478
|
-
|
479
|
-
|
626
|
+
test 'key_format! is not applied deeply by default' do
|
627
|
+
names = { first_name: 'camel', last_name: 'case' }
|
628
|
+
result = jbuild do |json|
|
629
|
+
json.key_format! camelize: :lower
|
630
|
+
json.set! :all_names, names
|
631
|
+
end
|
480
632
|
|
481
|
-
assert_equal ['
|
482
|
-
Jbuilder.send(:class_variable_set, '@@key_formatter', Jbuilder::KeyFormatter.new)
|
633
|
+
assert_equal %i[first_name last_name], result['allNames'].keys
|
483
634
|
end
|
484
635
|
|
485
|
-
test '
|
486
|
-
|
487
|
-
json
|
636
|
+
test 'applying key_format! deeply can be enabled per scope' do
|
637
|
+
names = { first_name: 'camel', last_name: 'case' }
|
638
|
+
result = jbuild do |json|
|
639
|
+
json.key_format! camelize: :lower
|
640
|
+
json.scope do
|
641
|
+
json.deep_format_keys!
|
642
|
+
json.set! :all_names, names
|
643
|
+
end
|
644
|
+
json.set! :all_names, names
|
645
|
+
end
|
646
|
+
|
647
|
+
assert_equal %w[firstName lastName], result['scope']['allNames'].keys
|
648
|
+
assert_equal %i[first_name last_name], result['allNames'].keys
|
649
|
+
end
|
650
|
+
|
651
|
+
test 'applying key_format! deeply can be disabled per scope' do
|
652
|
+
names = { first_name: 'camel', last_name: 'case' }
|
653
|
+
result = jbuild do |json|
|
654
|
+
json.key_format! camelize: :lower
|
655
|
+
json.deep_format_keys!
|
656
|
+
json.set! :all_names, names
|
657
|
+
json.scope do
|
658
|
+
json.deep_format_keys! false
|
659
|
+
json.set! :all_names, names
|
660
|
+
end
|
661
|
+
end
|
662
|
+
|
663
|
+
assert_equal %w[firstName lastName], result['allNames'].keys
|
664
|
+
assert_equal %i[first_name last_name], result['scope']['allNames'].keys
|
665
|
+
end
|
666
|
+
|
667
|
+
test 'applying key_format! deeply can be enabled globally' do
|
668
|
+
names = { first_name: 'camel', last_name: 'case' }
|
669
|
+
|
670
|
+
Jbuilder.deep_format_keys true
|
671
|
+
result = jbuild do |json|
|
672
|
+
json.key_format! camelize: :lower
|
673
|
+
json.set! :all_names, names
|
674
|
+
end
|
675
|
+
|
676
|
+
assert_equal %w[firstName lastName], result['allNames'].keys
|
677
|
+
Jbuilder.send(:class_variable_set, '@@deep_format_keys', false)
|
678
|
+
end
|
679
|
+
|
680
|
+
test 'deep key_format! with merge!' do
|
681
|
+
hash = { camel_style: 'for JS' }
|
682
|
+
result = jbuild do |json|
|
683
|
+
json.key_format! camelize: :lower
|
684
|
+
json.deep_format_keys!
|
685
|
+
json.merge! hash
|
686
|
+
end
|
687
|
+
|
688
|
+
assert_equal ['camelStyle'], result.keys
|
689
|
+
end
|
690
|
+
|
691
|
+
test 'deep key_format! with merge! deep' do
|
692
|
+
hash = { camel_style: { sub_attr: 'for JS' } }
|
693
|
+
result = jbuild do |json|
|
694
|
+
json.key_format! camelize: :lower
|
695
|
+
json.deep_format_keys!
|
696
|
+
json.merge! hash
|
697
|
+
end
|
698
|
+
|
699
|
+
assert_equal ['subAttr'], result['camelStyle'].keys
|
700
|
+
end
|
701
|
+
|
702
|
+
test 'deep key_format! with set! array of hashes' do
|
703
|
+
names = [{ first_name: 'camel', last_name: 'case' }]
|
704
|
+
result = jbuild do |json|
|
705
|
+
json.key_format! camelize: :lower
|
706
|
+
json.deep_format_keys!
|
707
|
+
json.set! :names, names
|
708
|
+
end
|
709
|
+
|
710
|
+
assert_equal %w[firstName lastName], result['names'][0].keys
|
711
|
+
end
|
712
|
+
|
713
|
+
test 'deep key_format! with set! extracting hash from object' do
|
714
|
+
comment = Struct.new(:author).new({ first_name: 'camel', last_name: 'case' })
|
715
|
+
result = jbuild do |json|
|
716
|
+
json.key_format! camelize: :lower
|
717
|
+
json.deep_format_keys!
|
718
|
+
json.set! :comment, comment, :author
|
719
|
+
end
|
720
|
+
|
721
|
+
assert_equal %w[firstName lastName], result['comment']['author'].keys
|
722
|
+
end
|
723
|
+
|
724
|
+
test 'deep key_format! with array! of hashes' do
|
725
|
+
names = [{ first_name: 'camel', last_name: 'case' }]
|
726
|
+
result = jbuild do |json|
|
727
|
+
json.key_format! camelize: :lower
|
728
|
+
json.deep_format_keys!
|
729
|
+
json.array! names
|
730
|
+
end
|
731
|
+
|
732
|
+
assert_equal %w[firstName lastName], result[0].keys
|
733
|
+
end
|
734
|
+
|
735
|
+
test 'deep key_format! with merge! array of hashes' do
|
736
|
+
names = [{ first_name: 'camel', last_name: 'case' }]
|
737
|
+
new_names = [{ first_name: 'snake', last_name: 'case' }]
|
738
|
+
result = jbuild do |json|
|
739
|
+
json.key_format! camelize: :lower
|
740
|
+
json.deep_format_keys!
|
741
|
+
json.array! names
|
742
|
+
json.merge! new_names
|
743
|
+
end
|
744
|
+
|
745
|
+
assert_equal %w[firstName lastName], result[1].keys
|
746
|
+
end
|
747
|
+
|
748
|
+
test 'deep key_format! is applied to hash extracted from object' do
|
749
|
+
comment = Struct.new(:author).new({ first_name: 'camel', last_name: 'case' })
|
750
|
+
result = jbuild do |json|
|
751
|
+
json.key_format! camelize: :lower
|
752
|
+
json.deep_format_keys!
|
753
|
+
json.extract! comment, :author
|
754
|
+
end
|
755
|
+
|
756
|
+
assert_equal %w[firstName lastName], result['author'].keys
|
757
|
+
end
|
758
|
+
|
759
|
+
test 'deep key_format! is applied to hash extracted from hash' do
|
760
|
+
comment = {author: { first_name: 'camel', last_name: 'case' }}
|
761
|
+
result = jbuild do |json|
|
762
|
+
json.key_format! camelize: :lower
|
763
|
+
json.deep_format_keys!
|
764
|
+
json.extract! comment, :author
|
765
|
+
end
|
766
|
+
|
767
|
+
assert_equal %w[firstName lastName], result['author'].keys
|
768
|
+
end
|
769
|
+
|
770
|
+
test 'deep key_format! is applied to hash extracted directly from array' do
|
771
|
+
comments = [Struct.new(:author).new({ first_name: 'camel', last_name: 'case' })]
|
772
|
+
result = jbuild do |json|
|
773
|
+
json.key_format! camelize: :lower
|
774
|
+
json.deep_format_keys!
|
775
|
+
json.array! comments, :author
|
776
|
+
end
|
777
|
+
|
778
|
+
assert_equal %w[firstName lastName], result[0]['author'].keys
|
779
|
+
end
|
488
780
|
|
489
|
-
|
781
|
+
test 'default key_format!' do
|
782
|
+
Jbuilder.key_format camelize: :lower
|
783
|
+
result = jbuild{ |json| json.camel_style 'for JS' }
|
784
|
+
assert_equal ['camelStyle'], result.keys
|
785
|
+
end
|
786
|
+
|
787
|
+
test 'do not use default key formatter directly' do
|
788
|
+
Jbuilder.key_format
|
789
|
+
jbuild{ |json| json.key 'value' }
|
790
|
+
formatter = Jbuilder.send(:class_variable_get, '@@key_formatter')
|
791
|
+
cache = formatter.instance_variable_get('@cache')
|
792
|
+
assert_empty cache
|
490
793
|
end
|
491
794
|
|
492
795
|
test 'ignore_nil! without a parameter' do
|
493
|
-
|
494
|
-
|
495
|
-
|
796
|
+
result = jbuild do |json|
|
797
|
+
json.ignore_nil!
|
798
|
+
json.test nil
|
799
|
+
end
|
496
800
|
|
497
|
-
|
801
|
+
assert_empty result.keys
|
498
802
|
end
|
499
803
|
|
500
804
|
test 'ignore_nil! with parameter' do
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
805
|
+
result = jbuild do |json|
|
806
|
+
json.ignore_nil! true
|
807
|
+
json.name 'Bob'
|
808
|
+
json.dne nil
|
809
|
+
end
|
505
810
|
|
506
|
-
assert_equal ['name'],
|
811
|
+
assert_equal ['name'], result.keys
|
507
812
|
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
813
|
+
result = jbuild do |json|
|
814
|
+
json.ignore_nil! false
|
815
|
+
json.name 'Bob'
|
816
|
+
json.dne nil
|
817
|
+
end
|
512
818
|
|
513
|
-
assert_equal ['name', 'dne'],
|
819
|
+
assert_equal ['name', 'dne'], result.keys
|
514
820
|
end
|
515
821
|
|
516
822
|
test 'default ignore_nil!' do
|
517
823
|
Jbuilder.ignore_nil
|
518
|
-
json = Jbuilder.new
|
519
|
-
json.name 'Bob'
|
520
|
-
json.dne nil
|
521
824
|
|
522
|
-
|
825
|
+
result = jbuild do |json|
|
826
|
+
json.name 'Bob'
|
827
|
+
json.dne nil
|
828
|
+
end
|
829
|
+
|
830
|
+
assert_equal ['name'], result.keys
|
523
831
|
Jbuilder.send(:class_variable_set, '@@ignore_nil', false)
|
524
832
|
end
|
525
833
|
|
526
834
|
test 'nil!' do
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
835
|
+
result = jbuild do |json|
|
836
|
+
json.key 'value'
|
837
|
+
json.nil!
|
838
|
+
end
|
839
|
+
|
840
|
+
assert_nil result
|
531
841
|
end
|
532
842
|
|
533
843
|
test 'null!' do
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
844
|
+
result = jbuild do |json|
|
845
|
+
json.key 'value'
|
846
|
+
json.null!
|
847
|
+
end
|
848
|
+
|
849
|
+
assert_nil result
|
850
|
+
end
|
851
|
+
|
852
|
+
test 'null! in a block' do
|
853
|
+
result = jbuild do |json|
|
854
|
+
json.author do
|
855
|
+
json.name 'David'
|
856
|
+
end
|
857
|
+
|
858
|
+
json.author do
|
859
|
+
json.null!
|
860
|
+
end
|
861
|
+
end
|
862
|
+
|
863
|
+
assert result.key?('author')
|
864
|
+
assert_nil result['author']
|
865
|
+
end
|
866
|
+
|
867
|
+
test 'empty attributes respond to empty?' do
|
868
|
+
attributes = Jbuilder.new.attributes!
|
869
|
+
assert attributes.empty?
|
870
|
+
assert attributes.blank?
|
871
|
+
assert !attributes.present?
|
538
872
|
end
|
539
873
|
|
540
|
-
test 'throws
|
541
|
-
|
542
|
-
|
543
|
-
|
874
|
+
test 'throws ArrayError when trying to add a key to an array' do
|
875
|
+
assert_raise Jbuilder::ArrayError do
|
876
|
+
jbuild do |json|
|
877
|
+
json.array! %w[foo bar]
|
878
|
+
json.fizz "buzz"
|
879
|
+
end
|
880
|
+
end
|
881
|
+
end
|
882
|
+
|
883
|
+
test 'throws NullError when trying to add properties to null' do
|
884
|
+
assert_raise Jbuilder::NullError do
|
885
|
+
jbuild do |json|
|
886
|
+
json.null!
|
887
|
+
json.foo 'bar'
|
888
|
+
end
|
889
|
+
end
|
890
|
+
end
|
891
|
+
|
892
|
+
test 'throws NullError when trying to add properties to null using block syntax' do
|
893
|
+
assert_raise Jbuilder::NullError do
|
894
|
+
jbuild do |json|
|
895
|
+
json.author do
|
896
|
+
json.null!
|
897
|
+
end
|
898
|
+
|
899
|
+
json.author do
|
900
|
+
json.name "Pavel"
|
901
|
+
end
|
902
|
+
end
|
903
|
+
end
|
904
|
+
end
|
905
|
+
|
906
|
+
test "throws MergeError when trying to merge array with non-empty hash" do
|
907
|
+
assert_raise Jbuilder::MergeError do
|
908
|
+
jbuild do |json|
|
909
|
+
json.name "Daniel"
|
910
|
+
json.merge! []
|
911
|
+
end
|
912
|
+
end
|
913
|
+
end
|
914
|
+
|
915
|
+
test "throws MergeError when trying to merge hash with array" do
|
916
|
+
assert_raise Jbuilder::MergeError do
|
917
|
+
jbuild do |json|
|
918
|
+
json.array!
|
919
|
+
json.merge!({})
|
920
|
+
end
|
921
|
+
end
|
922
|
+
end
|
923
|
+
|
924
|
+
test "throws MergeError when trying to merge invalid objects" do
|
925
|
+
assert_raise Jbuilder::MergeError do
|
926
|
+
jbuild do |json|
|
927
|
+
json.name "Daniel"
|
928
|
+
json.merge! "Nope"
|
929
|
+
end
|
930
|
+
end
|
931
|
+
end
|
932
|
+
|
933
|
+
if RUBY_VERSION >= "2.2.10"
|
934
|
+
test "respects JSON encoding customizations" do
|
935
|
+
# Active Support overrides Time#as_json for custom formatting.
|
936
|
+
# Ensure we call #to_json on the final attributes instead of JSON.dump.
|
937
|
+
result = JSON.load(Jbuilder.encode { |json| json.time Time.parse("2018-05-13 11:51:00.485 -0400") })
|
938
|
+
assert_equal "2018-05-13T11:51:00.485-04:00", result["time"]
|
939
|
+
end
|
544
940
|
end
|
545
941
|
end
|