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.
Files changed (43) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/ruby.yml +108 -0
  3. data/.gitignore +4 -1
  4. data/Appraisals +25 -0
  5. data/CONTRIBUTING.md +106 -0
  6. data/Gemfile +4 -12
  7. data/MIT-LICENSE +1 -1
  8. data/README.md +171 -45
  9. data/Rakefile +15 -10
  10. data/gemfiles/rails_5_0.gemfile +10 -0
  11. data/gemfiles/rails_5_1.gemfile +10 -0
  12. data/gemfiles/rails_5_2.gemfile +10 -0
  13. data/gemfiles/rails_6_0.gemfile +10 -0
  14. data/gemfiles/rails_6_1.gemfile +10 -0
  15. data/gemfiles/rails_head.gemfile +10 -0
  16. data/jbuilder.gemspec +20 -6
  17. data/lib/generators/rails/jbuilder_generator.rb +13 -2
  18. data/lib/generators/rails/scaffold_controller_generator.rb +9 -3
  19. data/lib/generators/rails/templates/api_controller.rb +63 -0
  20. data/lib/generators/rails/templates/controller.rb +16 -20
  21. data/lib/generators/rails/templates/index.json.jbuilder +1 -4
  22. data/lib/generators/rails/templates/partial.json.jbuilder +16 -0
  23. data/lib/generators/rails/templates/show.json.jbuilder +1 -1
  24. data/lib/jbuilder/blank.rb +11 -0
  25. data/lib/jbuilder/collection_renderer.rb +109 -0
  26. data/lib/jbuilder/dependency_tracker.rb +1 -1
  27. data/lib/jbuilder/errors.rb +24 -0
  28. data/lib/jbuilder/jbuilder.rb +7 -0
  29. data/lib/jbuilder/jbuilder_template.rb +213 -65
  30. data/lib/jbuilder/key_formatter.rb +34 -0
  31. data/lib/jbuilder/railtie.rb +31 -6
  32. data/lib/jbuilder.rb +148 -114
  33. data/test/jbuilder_dependency_tracker_test.rb +3 -4
  34. data/test/jbuilder_generator_test.rb +31 -4
  35. data/test/jbuilder_template_test.rb +313 -195
  36. data/test/jbuilder_test.rb +615 -219
  37. data/test/scaffold_api_controller_generator_test.rb +70 -0
  38. data/test/scaffold_controller_generator_test.rb +62 -19
  39. data/test/test_helper.rb +36 -0
  40. metadata +38 -23
  41. data/.travis.yml +0 -21
  42. data/CHANGELOG.md +0 -89
  43. data/Gemfile.old +0 -14
@@ -1,8 +1,11 @@
1
- require 'test/unit'
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
- def map(&block)
14
- @collection.map(&block)
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
- json = Jbuilder.encode do |json|
57
+ result = jbuild do |json|
21
58
  json.content 'hello'
22
59
  end
23
60
 
24
- assert_equal 'hello', MultiJson.load(json)['content']
61
+ assert_equal 'hello', result['content']
25
62
  end
26
63
 
27
64
  test 'single key with false value' do
28
- json = Jbuilder.encode do |json|
65
+ result = jbuild do |json|
29
66
  json.content false
30
67
  end
31
68
 
32
- assert_equal false, MultiJson.load(json)['content']
69
+ assert_equal false, result['content']
33
70
  end
34
71
 
35
72
  test 'single key with nil value' do
36
- json = Jbuilder.encode do |json|
73
+ result = jbuild do |json|
37
74
  json.content nil
38
75
  end
39
76
 
40
- assert MultiJson.load(json).has_key?('content')
41
- assert_equal nil, MultiJson.load(json)['content']
77
+ assert result.has_key?('content')
78
+ assert_nil result['content']
42
79
  end
43
80
 
44
81
  test 'multiple keys' do
45
- json = Jbuilder.encode do |json|
82
+ result = jbuild do |json|
46
83
  json.title 'hello'
47
84
  json.content 'world'
48
85
  end
49
86
 
50
- parsed = MultiJson.load(json)
51
- assert_equal 'hello', parsed['title']
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
- json = Jbuilder.encode do |json|
94
+ result = jbuild do |json|
59
95
  json.extract! person, :name, :age
60
96
  end
61
97
 
62
- parsed = MultiJson.load(json)
63
- assert_equal 'David', parsed['name']
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 for 1.9' do
102
+ test 'extracting from object using call style' do
68
103
  person = Struct.new(:name, :age).new('David', 32)
69
104
 
70
- json = Jbuilder.encode do |json|
105
+ result = jbuild do |json|
71
106
  json.(person, :name, :age)
72
107
  end
73
108
 
74
- parsed = MultiJson.load(json)
75
- assert_equal 'David', parsed['name']
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
- json = Jbuilder.encode do |json|
116
+ result = jbuild do |json|
83
117
  json.extract! person, :name, :age
84
118
  end
85
119
 
86
- parsed = MultiJson.load(json)
87
- assert_equal 'Jim', parsed['name']
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
- json = Jbuilder.encode do |json|
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
- parsed = MultiJson.load(json)
100
- assert_equal 'David', parsed['author']['name']
101
- assert_equal 32, parsed['author']['age']
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
- json = Jbuilder.encode do |json|
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
- parsed = MultiJson.load(json)
113
- assert_equal 'hello', parsed['comments'].first['content']
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 = Class.new do
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
- json = Jbuilder.encode do |json|
247
+ result = jbuild do |json|
127
248
  json.author person, :name, :age
128
249
  end
129
250
 
130
- parsed = MultiJson.load(json)
131
- assert_equal 'David', parsed['author']['name']
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
- json = Jbuilder.encode do |json|
258
+ result = jbuild do |json|
139
259
  json.comments comments, :content
140
260
  end
141
261
 
142
- parsed = MultiJson.load(json)
143
- assert_equal ['content'], parsed['comments'].first.keys
144
- assert_equal 'hello', parsed['comments'].first['content']
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
- json = Jbuilder.encode do |json|
270
+ result = jbuild do |json|
152
271
  json.name 'Parent'
153
272
  json.comments comments, :content
154
273
  end
155
274
 
156
- parsed = MultiJson.load(json)
157
- assert_equal 'Parent', parsed['name']
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
- json = Jbuilder.encode do |json|
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
- parsed = MultiJson.load(json)
171
- assert_equal ['content'], parsed['comments'].first.keys
172
- assert_equal 'hello', parsed['comments'].first['content']
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
- json = Jbuilder.encode do |json|
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 [], MultiJson.load(json)['comments']
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
- json = Jbuilder.encode do |json|
306
+ result = jbuild do |json|
190
307
  json.comments comments, :content
191
308
  end
192
309
 
193
- parsed = MultiJson.load(json)
194
- assert_equal ['content'], parsed['comments'].first.keys
195
- assert_equal 'hello', parsed['comments'].first['content']
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 chilren from a non-Enumerable that responds to #map with inline loop' do
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
- json = Jbuilder.encode do |json|
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
- parsed = MultiJson.load(json)
209
- assert_equal ['content'], parsed['comments'].first.keys
210
- assert_equal 'hello', parsed['comments'].first['content']
211
- assert_equal 'world', parsed['comments'].second['content']
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
- json = Jbuilder.encode do |json|
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
- parsed = MultiJson.load(json)
224
- assert_equal 'hello', parsed.first['content']
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
- json = Jbuilder.encode do |json|
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
- parsed = MultiJson.load(json)
242
- assert_equal 'hello', parsed['author']['comments'].first['content']
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
- json = Jbuilder.encode do |json|
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', MultiJson.load(json)['comments'].first['authors'].first['name']
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
- json = Jbuilder.encode do |json|
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', MultiJson.load(json)[0]['names'].last
274
- assert_not_equal 'hello', MultiJson.load(json)[0]['not_in_json']
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
- to_nest.nested_value 'Nested Test'
280
- json = Jbuilder.encode do |json|
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
- result = {'value' => 'Test', 'nested' => {'nested_value' => 'Nested Test'}}
286
- assert_equal result, MultiJson.load(json)
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
- to_nest.nested_value 'Nested Test'
292
- json = Jbuilder.encode do |json|
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
- result = {'value' => 'Test', 'nested' => {'nested_value' => 'Nested Test'}}
298
- assert_equal result, MultiJson.load(json)
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
- json = Jbuilder.encode do |json|
305
- json.array!(comments) do |comment|
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
- parsed = MultiJson.load(json)
311
- assert_equal 'hello', parsed.first['content']
312
- assert_equal 'world', parsed.second['content']
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
- json = Jbuilder.encode do |json|
455
+ result = jbuild do |json|
319
456
  json.array! comments, :content, :id
320
457
  end
321
458
 
322
- parsed = MultiJson.load(json)
323
- assert_equal 'hello', parsed.first['content']
324
- assert_equal 1, parsed.first['id']
325
- assert_equal 'world', parsed.second['content']
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
- json = Jbuilder.encode do |json|
333
- json.array!(comments) do |comment|
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 [], MultiJson.load(json)
474
+ assert_equal [], result
339
475
  end
340
476
 
341
477
  test 'dynamically set a key/value' do
342
- json = Jbuilder.encode do |json|
478
+ result = jbuild do |json|
343
479
  json.set! :each, 'stuff'
344
480
  end
345
481
 
346
- assert_equal 'stuff', MultiJson.load(json)['each']
482
+ assert_equal 'stuff', result['each']
347
483
  end
348
484
 
349
485
  test 'dynamically set a key/nested child with block' do
350
- json = Jbuilder.encode do |json|
351
- json.set!(:author) do
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
- parsed = MultiJson.load(json)
358
- assert_equal 'David', parsed['author']['name']
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
- json = Jbuilder.encode do |json|
500
+ result = jbuild do |json|
366
501
  json.set! :comments, comments, :content
367
502
  end
368
503
 
369
- parsed = MultiJson.load(json)
370
- assert_equal ['content'], parsed['comments'].first.keys
371
- assert_equal 'hello', parsed['comments'].first['content']
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
- class Person
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
- parsed = MultiJson.load(result)
399
- assert_equal 2, parsed['relations'].length
400
- assert_equal 'Bob', parsed['relations'][0]['name']
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(:key_formatter => 1, :ignore_nil => 2)
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
- json = Jbuilder.new
412
- json.key_format! :camelize => [:lower]
413
- json.camel_style 'for JS'
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'], json.attributes!.keys
531
+ assert_equal ['camelStyle'], result.keys
416
532
  end
417
533
 
418
534
  test 'key_format! with parameter not as an array' do
419
- json = Jbuilder.new
420
- json.key_format! :camelize => :lower
421
- json.camel_style 'for JS'
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'], json.attributes!.keys
540
+ assert_equal ['camelStyle'], result.keys
424
541
  end
425
542
 
426
543
  test 'key_format! propagates to child elements' do
427
- json = Jbuilder.new
428
- json.key_format! :upcase
429
- json.level1 'one'
430
- json.level2 do
431
- json.value 'two'
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
- json = Jbuilder.new
441
- json.level2 do
442
- json.key_format! :upcase
443
- json.value 'two'
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
- json = Jbuilder.new
454
- json.key_format! :upcase
455
- json.lower 'Value'
600
+ result = jbuild do |json|
601
+ json.key_format! :upcase
602
+ json.lower 'Value'
603
+ end
456
604
 
457
- assert_equal ['LOWER'], json.attributes!.keys
605
+ assert_equal ['LOWER'], result.keys
458
606
  end
459
607
 
460
608
  test 'key_format! with multiple steps' do
461
- json = Jbuilder.new
462
- json.key_format! :upcase, :pluralize
463
- json.pill ''
609
+ result = jbuild do |json|
610
+ json.key_format! :upcase, :pluralize
611
+ json.pill 'foo'
612
+ end
464
613
 
465
- assert_equal ['PILLs'], json.attributes!.keys
614
+ assert_equal ['PILLs'], result.keys
466
615
  end
467
616
 
468
617
  test 'key_format! with lambda/proc' do
469
- json = Jbuilder.new
470
- json.key_format! lambda { |key| key + ' and friends' }
471
- json.oats ''
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'], json.attributes!.keys
623
+ assert_equal ['oats and friends'], result.keys
474
624
  end
475
625
 
476
- test 'default key_format!' do
477
- Jbuilder.key_format :camelize => :lower
478
- json = Jbuilder.new
479
- json.camel_style 'for JS'
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 ['camelStyle'], json.attributes!.keys
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 'do not use default key formatter directly' do
486
- json = Jbuilder.new
487
- json.key 'value'
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
- assert_equal [], Jbuilder.send(:class_variable_get, '@@key_formatter').instance_variable_get('@cache').keys
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
- json = Jbuilder.new
494
- json.ignore_nil!
495
- json.test nil
796
+ result = jbuild do |json|
797
+ json.ignore_nil!
798
+ json.test nil
799
+ end
496
800
 
497
- assert_equal [], json.attributes!.keys
801
+ assert_empty result.keys
498
802
  end
499
803
 
500
804
  test 'ignore_nil! with parameter' do
501
- json = Jbuilder.new
502
- json.ignore_nil! true
503
- json.name 'Bob'
504
- json.dne nil
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'], json.attributes!.keys
811
+ assert_equal ['name'], result.keys
507
812
 
508
- json = Jbuilder.new
509
- json.ignore_nil! false
510
- json.name 'Bob'
511
- json.dne nil
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'], json.attributes!.keys
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
- assert_equal ['name'], json.attributes!.keys
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
- json = Jbuilder.new
528
- json.key 'value'
529
- json.nil!
530
- assert_nil json.attributes!
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
- json = Jbuilder.new
535
- json.key 'value'
536
- json.null!
537
- assert_nil json.attributes!
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 meaningfull error when on trying to add properties to null' do
541
- json = Jbuilder.new
542
- json.null!
543
- assert_raise(Jbuilder::NullError) { json.foo 'bar' }
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