jbuilder 1.5.3 → 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 (42) 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 -5
  7. data/MIT-LICENSE +2 -2
  8. data/README.md +182 -40
  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 +25 -8
  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 +18 -22
  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 +61 -0
  27. data/lib/jbuilder/errors.rb +24 -0
  28. data/lib/jbuilder/jbuilder.rb +7 -0
  29. data/lib/jbuilder/jbuilder_template.rb +228 -67
  30. data/lib/jbuilder/key_formatter.rb +34 -0
  31. data/lib/jbuilder/railtie.rb +31 -6
  32. data/lib/jbuilder.rb +144 -137
  33. data/test/jbuilder_dependency_tracker_test.rb +72 -0
  34. data/test/jbuilder_generator_test.rb +31 -4
  35. data/test/jbuilder_template_test.rb +319 -163
  36. data/test/jbuilder_test.rb +613 -298
  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 +47 -22
  41. data/.travis.yml +0 -38
  42. data/Gemfile.old +0 -7
@@ -1,295 +1,356 @@
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
 
6
- Comment = Struct.new(:content, :id)
7
-
8
- unless JbuilderProxy.method_defined? :instance_eval
9
- # Faking Object#instance_eval for 1.8 and some newer JRubies
10
- class JbuilderProxy
11
- def instance_eval(code)
12
- eval code
13
- end
14
- end
5
+ def jbuild(*args, &block)
6
+ Jbuilder.new(*args, &block).attributes!
15
7
  end
16
8
 
9
+ Comment = Struct.new(:content, :id)
10
+
17
11
  class NonEnumerable
18
12
  def initialize(collection)
19
13
  @collection = collection
20
14
  end
21
15
 
22
- def map(&block)
23
- @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
26
+ end
27
+ end
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
24
47
  end
25
48
  end
26
49
 
50
+
27
51
  class JbuilderTest < ActiveSupport::TestCase
52
+ teardown do
53
+ Jbuilder.send :class_variable_set, '@@key_formatter', nil
54
+ end
55
+
28
56
  test 'single key' do
29
- json = Jbuilder.encode do |json|
57
+ result = jbuild do |json|
30
58
  json.content 'hello'
31
59
  end
32
60
 
33
- assert_equal 'hello', MultiJson.load(json)['content']
61
+ assert_equal 'hello', result['content']
34
62
  end
35
63
 
36
64
  test 'single key with false value' do
37
- json = Jbuilder.encode do |json|
65
+ result = jbuild do |json|
38
66
  json.content false
39
67
  end
40
68
 
41
- assert_equal false, MultiJson.load(json)['content']
69
+ assert_equal false, result['content']
42
70
  end
43
71
 
44
72
  test 'single key with nil value' do
45
- json = Jbuilder.encode do |json|
73
+ result = jbuild do |json|
46
74
  json.content nil
47
75
  end
48
76
 
49
- assert MultiJson.load(json).has_key?('content')
50
- assert_equal nil, MultiJson.load(json)['content']
77
+ assert result.has_key?('content')
78
+ assert_nil result['content']
51
79
  end
52
80
 
53
81
  test 'multiple keys' do
54
- json = Jbuilder.encode do |json|
82
+ result = jbuild do |json|
55
83
  json.title 'hello'
56
84
  json.content 'world'
57
85
  end
58
86
 
59
- parsed = MultiJson.load(json)
60
- assert_equal 'hello', parsed['title']
61
- assert_equal 'world', parsed['content']
87
+ assert_equal 'hello', result['title']
88
+ assert_equal 'world', result['content']
62
89
  end
63
90
 
64
91
  test 'extracting from object' do
65
92
  person = Struct.new(:name, :age).new('David', 32)
66
93
 
67
- json = Jbuilder.encode do |json|
94
+ result = jbuild do |json|
68
95
  json.extract! person, :name, :age
69
96
  end
70
97
 
71
- parsed = MultiJson.load(json)
72
- assert_equal 'David', parsed['name']
73
- assert_equal 32, parsed['age']
74
- end
75
-
76
- test 'extracting from object using private method' do
77
- person = Struct.new(:name) do
78
- private
79
- def age
80
- 32
81
- end
82
- end.new('David')
83
-
84
- message = 'Private method :age was used to extract value. This will be' +
85
- ' an error in future versions of Jbuilder'
86
-
87
- ::ActiveSupport::Deprecation.expects(:warn).with(message)
88
- json = Jbuilder.encode do |json|
89
- json.extract! person, :name, :age
90
- end
98
+ assert_equal 'David', result['name']
99
+ assert_equal 32, result['age']
91
100
  end
92
101
 
93
- test 'extracting from object using call style for 1.9' do
102
+ test 'extracting from object using call style' do
94
103
  person = Struct.new(:name, :age).new('David', 32)
95
104
 
96
- json = Jbuilder.encode do |json|
97
- if ::RUBY_VERSION > '1.9'
98
- instance_eval 'json.(person, :name, :age)'
99
- else
100
- instance_eval 'json.call(person, :name, :age)'
101
- end
105
+ result = jbuild do |json|
106
+ json.(person, :name, :age)
102
107
  end
103
108
 
104
- parsed = MultiJson.load(json)
105
- assert_equal 'David', parsed['name']
106
- assert_equal 32, parsed['age']
109
+ assert_equal 'David', result['name']
110
+ assert_equal 32, result['age']
107
111
  end
108
112
 
109
113
  test 'extracting from hash' do
110
114
  person = {:name => 'Jim', :age => 34}
111
115
 
112
- json = Jbuilder.encode do |json|
116
+ result = jbuild do |json|
113
117
  json.extract! person, :name, :age
114
118
  end
115
119
 
116
- parsed = MultiJson.load(json)
117
- assert_equal 'Jim', parsed['name']
118
- assert_equal 34, parsed['age']
120
+ assert_equal 'Jim', result['name']
121
+ assert_equal 34, result['age']
119
122
  end
120
123
 
121
124
  test 'nesting single child with block' do
122
- json = Jbuilder.encode do |json|
125
+ result = jbuild do |json|
123
126
  json.author do
124
127
  json.name 'David'
125
128
  json.age 32
126
129
  end
127
130
  end
128
131
 
129
- parsed = MultiJson.load(json)
130
- assert_equal 'David', parsed['author']['name']
131
- 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
132
230
  end
133
231
 
134
232
  test 'nesting multiple children with block' do
135
- json = Jbuilder.encode do |json|
233
+ result = jbuild do |json|
136
234
  json.comments do
137
235
  json.child! { json.content 'hello' }
138
236
  json.child! { json.content 'world' }
139
237
  end
140
238
  end
141
239
 
142
- parsed = MultiJson.load(json)
143
- assert_equal 'hello', parsed['comments'].first['content']
144
- assert_equal 'world', parsed['comments'].second['content']
240
+ assert_equal 'hello', result['comments'].first['content']
241
+ assert_equal 'world', result['comments'].second['content']
145
242
  end
146
243
 
147
244
  test 'nesting single child with inline extract' do
148
- person = Class.new do
149
- attr_reader :name, :age
150
-
151
- def initialize(name, age)
152
- @name, @age = name, age
153
- end
154
- end.new('David', 32)
245
+ person = Person.new('David', 32)
155
246
 
156
- json = Jbuilder.encode do |json|
247
+ result = jbuild do |json|
157
248
  json.author person, :name, :age
158
249
  end
159
250
 
160
- parsed = MultiJson.load(json)
161
- assert_equal 'David', parsed['author']['name']
162
- assert_equal 32, parsed['author']['age']
251
+ assert_equal 'David', result['author']['name']
252
+ assert_equal 32, result['author']['age']
163
253
  end
164
254
 
165
255
  test 'nesting multiple children from array' do
166
256
  comments = [ Comment.new('hello', 1), Comment.new('world', 2) ]
167
257
 
168
- json = Jbuilder.encode do |json|
258
+ result = jbuild do |json|
169
259
  json.comments comments, :content
170
260
  end
171
261
 
172
- parsed = MultiJson.load(json)
173
- assert_equal ['content'], parsed['comments'].first.keys
174
- assert_equal 'hello', parsed['comments'].first['content']
175
- 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']
176
265
  end
177
266
 
178
267
  test 'nesting multiple children from array when child array is empty' do
179
268
  comments = []
180
269
 
181
- json = Jbuilder.encode do |json|
270
+ result = jbuild do |json|
182
271
  json.name 'Parent'
183
272
  json.comments comments, :content
184
273
  end
185
274
 
186
- parsed = MultiJson.load(json)
187
- assert_equal 'Parent', parsed['name']
188
- assert_equal [], parsed['comments']
275
+ assert_equal 'Parent', result['name']
276
+ assert_equal [], result['comments']
189
277
  end
190
278
 
191
279
  test 'nesting multiple children from array with inline loop' do
192
280
  comments = [ Comment.new('hello', 1), Comment.new('world', 2) ]
193
281
 
194
- json = Jbuilder.encode do |json|
282
+ result = jbuild do |json|
195
283
  json.comments comments do |comment|
196
284
  json.content comment.content
197
285
  end
198
286
  end
199
287
 
200
- parsed = MultiJson.load(json)
201
- assert_equal ['content'], parsed['comments'].first.keys
202
- assert_equal 'hello', parsed['comments'].first['content']
203
- 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']
204
291
  end
205
292
 
206
293
  test 'handles nil-collections as empty arrays' do
207
- json = Jbuilder.encode do |json|
294
+ result = jbuild do |json|
208
295
  json.comments nil do |comment|
209
296
  json.content comment.content
210
297
  end
211
298
  end
212
299
 
213
- assert_equal [], MultiJson.load(json)['comments']
300
+ assert_equal [], result['comments']
214
301
  end
215
302
 
216
303
  test 'nesting multiple children from a non-Enumerable that responds to #map' do
217
304
  comments = NonEnumerable.new([ Comment.new('hello', 1), Comment.new('world', 2) ])
218
305
 
219
- json = Jbuilder.encode do |json|
306
+ result = jbuild do |json|
220
307
  json.comments comments, :content
221
308
  end
222
309
 
223
- parsed = MultiJson.load(json)
224
- assert_equal ['content'], parsed['comments'].first.keys
225
- assert_equal 'hello', parsed['comments'].first['content']
226
- 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']
227
313
  end
228
314
 
229
- 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
230
316
  comments = NonEnumerable.new([ Comment.new('hello', 1), Comment.new('world', 2) ])
231
317
 
232
- json = Jbuilder.encode do |json|
318
+ result = jbuild do |json|
233
319
  json.comments comments do |comment|
234
320
  json.content comment.content
235
321
  end
236
322
  end
237
323
 
238
- parsed = MultiJson.load(json)
239
- assert_equal ['content'], parsed['comments'].first.keys
240
- assert_equal 'hello', parsed['comments'].first['content']
241
- 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']
242
327
  end
243
328
 
244
- test 'nesting multiple children from array with inline loop with old api' do
245
- comments = [ Comment.new('hello', 1), Comment.new('world', 2) ]
329
+ test 'array! casts array-like objects to array before merging' do
330
+ wrapped_array = VeryBasicWrapper.new(%w[foo bar])
246
331
 
247
- json = Jbuilder.encode do |json|
248
- ::ActiveSupport::Deprecation.silence do
249
- json.comments comments do |json, comment|
250
- json.content comment.content
251
- end
252
- end
332
+ result = jbuild do |json|
333
+ json.array! wrapped_array
253
334
  end
254
335
 
255
- parsed = MultiJson.load(json)
256
- assert_equal ['content'], parsed['comments'].first.keys
257
- assert_equal 'hello', parsed['comments'].first['content']
258
- assert_equal 'world', parsed['comments'].second['content']
336
+ assert_equal %w[foo bar], result
259
337
  end
260
338
 
261
339
  test 'nesting multiple children from array with inline loop on root' do
262
340
  comments = [ Comment.new('hello', 1), Comment.new('world', 2) ]
263
341
 
264
- json = Jbuilder.encode do |json|
342
+ result = jbuild do |json|
265
343
  json.call(comments) do |comment|
266
344
  json.content comment.content
267
345
  end
268
346
  end
269
347
 
270
- parsed = MultiJson.load(json)
271
- assert_equal 'hello', parsed.first['content']
272
- assert_equal 'world', parsed.second['content']
273
- end
274
-
275
- test 'nesting multiple children from array with inline loop on root with old api' do
276
- comments = [ Comment.new('hello', 1), Comment.new('world', 2) ]
277
-
278
- json = Jbuilder.encode do |json|
279
- ::ActiveSupport::Deprecation.silence do
280
- json.call(comments) do |json, comment|
281
- json.content comment.content
282
- end
283
- end
284
- end
285
-
286
- parsed = MultiJson.load(json)
287
- assert_equal 'hello', parsed.first['content']
288
- assert_equal 'world', parsed.second['content']
348
+ assert_equal 'hello', result.first['content']
349
+ assert_equal 'world', result.second['content']
289
350
  end
290
351
 
291
352
  test 'array nested inside nested hash' do
292
- json = Jbuilder.encode do |json|
353
+ result = jbuild do |json|
293
354
  json.author do
294
355
  json.name 'David'
295
356
  json.age 32
@@ -301,13 +362,12 @@ class JbuilderTest < ActiveSupport::TestCase
301
362
  end
302
363
  end
303
364
 
304
- parsed = MultiJson.load(json)
305
- assert_equal 'hello', parsed['author']['comments'].first['content']
306
- 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']
307
367
  end
308
368
 
309
369
  test 'array nested inside array' do
310
- json = Jbuilder.encode do |json|
370
+ result = jbuild do |json|
311
371
  json.comments do
312
372
  json.child! do
313
373
  json.authors do
@@ -319,12 +379,13 @@ class JbuilderTest < ActiveSupport::TestCase
319
379
  end
320
380
  end
321
381
 
322
- assert_equal 'david', MultiJson.load(json)['comments'].first['authors'].first['name']
382
+ assert_equal 'david', result['comments'].first['authors'].first['name']
323
383
  end
324
384
 
325
385
  test 'directly set an array nested in another array' do
326
386
  data = [ { :department => 'QA', :not_in_json => 'hello', :names => ['John', 'David'] } ]
327
- json = Jbuilder.encode do |json|
387
+
388
+ result = jbuild do |json|
328
389
  json.array! data do |object|
329
390
  json.department object[:department]
330
391
  json.names do
@@ -333,294 +394,548 @@ class JbuilderTest < ActiveSupport::TestCase
333
394
  end
334
395
  end
335
396
 
336
- assert_equal 'David', MultiJson.load(json)[0]['names'].last
337
- assert_not_equal 'hello', MultiJson.load(json)[0]['not_in_json']
338
- end
339
-
340
- test 'directly set an array nested in another array with old api' do
341
- data = [ { :department => 'QA', :not_in_json => 'hello', :names => ['John', 'David'] } ]
342
- json = Jbuilder.encode do |json|
343
- ::ActiveSupport::Deprecation.silence do
344
- json.array! data do |json, object|
345
- json.department object[:department]
346
- json.names do
347
- json.array! object[:names]
348
- end
349
- end
350
- end
351
- end
352
-
353
- parsed = MultiJson.load(json)
354
- assert_equal 'David', parsed.first['names'].last
355
- assert_not_equal 'hello', parsed.first['not_in_json']
397
+ assert_equal 'David', result[0]['names'].last
398
+ assert !result[0].key?('not_in_json')
356
399
  end
357
400
 
358
401
  test 'nested jbuilder objects' do
359
- to_nest = Jbuilder.new
360
- to_nest.nested_value 'Nested Test'
361
- json = Jbuilder.encode do |json|
402
+ to_nest = Jbuilder.new{ |json| json.nested_value 'Nested Test' }
403
+
404
+ result = jbuild do |json|
362
405
  json.value 'Test'
363
406
  json.nested to_nest
364
407
  end
365
408
 
366
- result = {'value' => 'Test', 'nested' => {'nested_value' => 'Nested Test'}}
367
- assert_equal result, MultiJson.load(json)
409
+ expected = {'value' => 'Test', 'nested' => {'nested_value' => 'Nested Test'}}
410
+ assert_equal expected, result
368
411
  end
369
412
 
370
413
  test 'nested jbuilder object via set!' do
371
- to_nest = Jbuilder.new
372
- to_nest.nested_value 'Nested Test'
373
- json = Jbuilder.encode do |json|
414
+ to_nest = Jbuilder.new{ |json| json.nested_value 'Nested Test' }
415
+
416
+ result = jbuild do |json|
374
417
  json.value 'Test'
375
418
  json.set! :nested, to_nest
376
419
  end
377
420
 
378
- result = {'value' => 'Test', 'nested' => {'nested_value' => 'Nested Test'}}
379
- assert_equal result, MultiJson.load(json)
421
+ expected = {'value' => 'Test', 'nested' => {'nested_value' => 'Nested Test'}}
422
+ assert_equal expected, result
380
423
  end
381
424
 
382
425
  test 'top-level array' do
383
426
  comments = [ Comment.new('hello', 1), Comment.new('world', 2) ]
384
427
 
385
- json = Jbuilder.encode do |json|
386
- json.array!(comments) do |comment|
428
+ result = jbuild do |json|
429
+ json.array! comments do |comment|
387
430
  json.content comment.content
388
431
  end
389
432
  end
390
433
 
391
- parsed = MultiJson.load(json)
392
- assert_equal 'hello', parsed.first['content']
393
- assert_equal 'world', parsed.second['content']
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
443
+ json.content comment.content
444
+ end
445
+ end
446
+
447
+ assert_equal 2, result.length
448
+ assert_equal 'hello', result.first['content']
449
+ assert_equal 'world', result.second['content']
394
450
  end
395
451
 
396
452
  test 'extract attributes directly from array' do
397
453
  comments = [ Comment.new('hello', 1), Comment.new('world', 2) ]
398
454
 
399
- json = Jbuilder.encode do |json|
455
+ result = jbuild do |json|
400
456
  json.array! comments, :content, :id
401
457
  end
402
458
 
403
- parsed = MultiJson.load(json)
404
- assert_equal 'hello', parsed.first['content']
405
- assert_equal 1, parsed.first['id']
406
- assert_equal 'world', parsed.second['content']
407
- 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']
408
463
  end
409
464
 
410
465
  test 'empty top-level array' do
411
466
  comments = []
412
467
 
413
- json = Jbuilder.encode do |json|
414
- json.array!(comments) do |comment|
468
+ result = jbuild do |json|
469
+ json.array! comments do |comment|
415
470
  json.content comment.content
416
471
  end
417
472
  end
418
473
 
419
- assert_equal [], MultiJson.load(json)
474
+ assert_equal [], result
420
475
  end
421
476
 
422
477
  test 'dynamically set a key/value' do
423
- json = Jbuilder.encode do |json|
478
+ result = jbuild do |json|
424
479
  json.set! :each, 'stuff'
425
480
  end
426
481
 
427
- assert_equal 'stuff', MultiJson.load(json)['each']
482
+ assert_equal 'stuff', result['each']
428
483
  end
429
484
 
430
485
  test 'dynamically set a key/nested child with block' do
431
- json = Jbuilder.encode do |json|
432
- json.set!(:author) do
486
+ result = jbuild do |json|
487
+ json.set! :author do
433
488
  json.name 'David'
434
489
  json.age 32
435
490
  end
436
491
  end
437
492
 
438
- parsed = MultiJson.load(json)
439
- assert_equal 'David', parsed['author']['name']
440
- assert_equal 32, parsed['author']['age']
493
+ assert_equal 'David', result['author']['name']
494
+ assert_equal 32, result['author']['age']
441
495
  end
442
496
 
443
497
  test 'dynamically sets a collection' do
444
498
  comments = [ Comment.new('hello', 1), Comment.new('world', 2) ]
445
499
 
446
- json = Jbuilder.encode do |json|
500
+ result = jbuild do |json|
447
501
  json.set! :comments, comments, :content
448
502
  end
449
503
 
450
- parsed = MultiJson.load(json)
451
- assert_equal ['content'], parsed['comments'].first.keys
452
- assert_equal 'hello', parsed['comments'].first['content']
453
- 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']
454
507
  end
455
508
 
456
509
  test 'query like object' do
457
- class Person
458
- attr_reader :name, :age
459
-
460
- def initialize(name, age)
461
- @name, @age = name, age
462
- end
463
- end
464
- class RelationMock
465
- include Enumerable
466
-
467
- def each(&block)
468
- [Person.new('Bob', 30), Person.new('Frank', 50)].each(&block)
469
- end
470
- def empty?
471
- false
472
- end
473
- end
474
-
475
- result = Jbuilder.encode do |json|
510
+ result = jbuild do |json|
476
511
  json.relations RelationMock.new, :name, :age
477
512
  end
478
513
 
479
- parsed = MultiJson.load(result)
480
- assert_equal 2, parsed['relations'].length
481
- assert_equal 'Bob', parsed['relations'][0]['name']
482
- 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']
483
517
  end
484
518
 
485
519
  test 'initialize via options hash' do
486
- jbuilder = Jbuilder.new(:key_formatter => 1, :ignore_nil => 2)
487
- assert_equal 1, jbuilder.instance_eval('@key_formatter')
488
- assert_equal 2, jbuilder.instance_eval('@ignore_nil')
520
+ jbuilder = Jbuilder.new(key_formatter: 1, ignore_nil: 2)
521
+ assert_equal 1, jbuilder.instance_eval{ @key_formatter }
522
+ assert_equal 2, jbuilder.instance_eval{ @ignore_nil }
489
523
  end
490
524
 
491
525
  test 'key_format! with parameter' do
492
- json = Jbuilder.new
493
- json.key_format! :camelize => [:lower]
494
- json.camel_style 'for JS'
526
+ result = jbuild do |json|
527
+ json.key_format! camelize: [:lower]
528
+ json.camel_style 'for JS'
529
+ end
495
530
 
496
- assert_equal ['camelStyle'], json.attributes!.keys
531
+ assert_equal ['camelStyle'], result.keys
497
532
  end
498
533
 
499
534
  test 'key_format! with parameter not as an array' do
500
- json = Jbuilder.new
501
- json.key_format! :camelize => :lower
502
- json.camel_style 'for JS'
535
+ result = jbuild do |json|
536
+ json.key_format! :camelize => :lower
537
+ json.camel_style 'for JS'
538
+ end
503
539
 
504
- assert_equal ['camelStyle'], json.attributes!.keys
540
+ assert_equal ['camelStyle'], result.keys
505
541
  end
506
542
 
507
543
  test 'key_format! propagates to child elements' do
508
- json = Jbuilder.new
509
- json.key_format! :upcase
510
- json.level1 'one'
511
- json.level2 do
512
- 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
513
550
  end
514
551
 
515
- result = json.attributes!
516
552
  assert_equal 'one', result['LEVEL1']
517
553
  assert_equal 'two', result['LEVEL2']['VALUE']
518
554
  end
519
555
 
520
556
  test 'key_format! resets after child element' do
521
- json = Jbuilder.new
522
- json.level2 do
523
- json.key_format! :upcase
524
- 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'
525
563
  end
526
- json.level1 'one'
527
564
 
528
- result = json.attributes!
529
565
  assert_equal 'two', result['level2']['VALUE']
530
566
  assert_equal 'one', result['level1']
531
567
  end
532
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
+
533
599
  test 'key_format! with no parameter' do
534
- json = Jbuilder.new
535
- json.key_format! :upcase
536
- json.lower 'Value'
600
+ result = jbuild do |json|
601
+ json.key_format! :upcase
602
+ json.lower 'Value'
603
+ end
537
604
 
538
- assert_equal ['LOWER'], json.attributes!.keys
605
+ assert_equal ['LOWER'], result.keys
539
606
  end
540
607
 
541
608
  test 'key_format! with multiple steps' do
542
- json = Jbuilder.new
543
- json.key_format! :upcase, :pluralize
544
- json.pill ''
609
+ result = jbuild do |json|
610
+ json.key_format! :upcase, :pluralize
611
+ json.pill 'foo'
612
+ end
545
613
 
546
- assert_equal ['PILLs'], json.attributes!.keys
614
+ assert_equal ['PILLs'], result.keys
547
615
  end
548
616
 
549
617
  test 'key_format! with lambda/proc' do
550
- json = Jbuilder.new
551
- json.key_format! lambda { |key| key + ' and friends' }
552
- json.oats ''
618
+ result = jbuild do |json|
619
+ json.key_format! ->(key){ key + ' and friends' }
620
+ json.oats 'foo'
621
+ end
553
622
 
554
- assert_equal ['oats and friends'], json.attributes!.keys
623
+ assert_equal ['oats and friends'], result.keys
555
624
  end
556
625
 
557
- test 'default key_format!' do
558
- Jbuilder.key_format :camelize => :lower
559
- json = Jbuilder.new
560
- 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
561
632
 
562
- assert_equal ['camelStyle'], json.attributes!.keys
563
- Jbuilder.send(:class_variable_set, '@@key_formatter', Jbuilder::KeyFormatter.new)
633
+ assert_equal %i[first_name last_name], result['allNames'].keys
564
634
  end
565
635
 
566
- test 'do not use default key formatter directly' do
567
- json = Jbuilder.new
568
- 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
569
646
 
570
- assert_equal [], Jbuilder.send(:class_variable_get, '@@key_formatter').instance_variable_get('@cache').keys
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
780
+
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
571
793
  end
572
794
 
573
795
  test 'ignore_nil! without a parameter' do
574
- json = Jbuilder.new
575
- json.ignore_nil!
576
- json.test nil
796
+ result = jbuild do |json|
797
+ json.ignore_nil!
798
+ json.test nil
799
+ end
577
800
 
578
- assert_equal [], json.attributes!.keys
801
+ assert_empty result.keys
579
802
  end
580
803
 
581
804
  test 'ignore_nil! with parameter' do
582
- json = Jbuilder.new
583
- json.ignore_nil! true
584
- json.name 'Bob'
585
- json.dne nil
805
+ result = jbuild do |json|
806
+ json.ignore_nil! true
807
+ json.name 'Bob'
808
+ json.dne nil
809
+ end
586
810
 
587
- assert_equal ['name'], json.attributes!.keys
811
+ assert_equal ['name'], result.keys
588
812
 
589
- json = Jbuilder.new
590
- json.ignore_nil! false
591
- json.name 'Bob'
592
- json.dne nil
813
+ result = jbuild do |json|
814
+ json.ignore_nil! false
815
+ json.name 'Bob'
816
+ json.dne nil
817
+ end
593
818
 
594
- assert_equal ['name', 'dne'], json.attributes!.keys
819
+ assert_equal ['name', 'dne'], result.keys
595
820
  end
596
821
 
597
822
  test 'default ignore_nil!' do
598
823
  Jbuilder.ignore_nil
599
- json = Jbuilder.new
600
- json.name 'Bob'
601
- json.dne nil
602
824
 
603
- 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
604
831
  Jbuilder.send(:class_variable_set, '@@ignore_nil', false)
605
832
  end
606
833
 
607
834
  test 'nil!' do
608
- json = Jbuilder.new
609
- json.key 'value'
610
- json.nil!
611
- assert_nil json.attributes!
835
+ result = jbuild do |json|
836
+ json.key 'value'
837
+ json.nil!
838
+ end
839
+
840
+ assert_nil result
612
841
  end
613
842
 
614
843
  test 'null!' do
615
- json = Jbuilder.new
616
- json.key 'value'
617
- json.null!
618
- 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?
872
+ end
873
+
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
619
890
  end
620
891
 
621
- test 'throws meaningfull error when on trying to add properties to null' do
622
- json = Jbuilder.new
623
- json.null!
624
- assert_raise(Jbuilder::NullError) { json.foo 'bar' }
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
625
940
  end
626
941
  end