jbuilder 2.10.0 → 2.11.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a7289a23f21f82bfb3eeccd466ec985e8348a9977ed083be43eb8349551e840c
4
- data.tar.gz: 20d2ba506a7a474d2a32030d91cef03d4345f2332a0753bca685ce6360b30995
3
+ metadata.gz: '039b14d174dd7bfb3a5e00d5a315aee89f1efd46a570244191ab985bb44fc328'
4
+ data.tar.gz: 4eda58d10150903687b7940b858fa02c6f0a2cbca8858cd2ae9866c5be5a4b62
5
5
  SHA512:
6
- metadata.gz: fc04dfeb7c16b4547d0f7949b30ec481eb8517da3318550cf6c9f5e8bbc7bdc107c2d8bccc94e0e506741926d14f6798e783ece3640abb6edb323a39f7a691ae
7
- data.tar.gz: 29a6793c2a3b4318b27b2a0614cc88c2a5e4ca646f39a29cb563d370524bc3c946b9ea8bff114b6a5ae9ea5b4e9d3546e30211f0ada15ad74ff028663933e5d7
6
+ metadata.gz: 1d20f2794455635cbb3272bd4b6e8f91781e2c55aa40c7228d5347b4b6ad049cd3cc22ebc9ef708b8bbe4a05811774de07770b175035342123458a4637b9240f
7
+ data.tar.gz: 383aca1c7489eda135339e4538fa8a9dec80900c7c8bab786729a7c323d63f3a75657889ee6dad6bf91b769ced3eb507e41ef7daa15faa8bb3c5d0261c0c0394
data/.gitignore CHANGED
@@ -1,4 +1,5 @@
1
1
  tmp
2
+ gemfiles/.bundle
2
3
  gemfiles/*.lock
3
4
  Gemfile.lock
4
5
  .ruby-version
@@ -3,17 +3,16 @@ language: ruby
3
3
  cache: bundler
4
4
 
5
5
  before_install:
6
- - "gem update --system 2.7.9"
7
6
  - "gem install bundler -v '<2'"
8
7
 
9
8
  rvm:
10
9
  - 2.2.10
11
10
  - 2.3.8
12
- - 2.4.6
13
- - 2.5.5
14
- - 2.6.2
11
+ - 2.4.10
12
+ - 2.5.8
13
+ - 2.6.6
14
+ - 2.7.1
15
15
  - ruby-head
16
- - rbx-3.107
17
16
 
18
17
  gemfile:
19
18
  - gemfiles/rails_5_0.gemfile
@@ -24,21 +23,27 @@ gemfile:
24
23
 
25
24
  matrix:
26
25
  exclude:
26
+ - rvm: 2.7.1
27
+ gemfile: gemfiles/rails_5_0.gemfile
28
+ - rvm: 2.7.1
29
+ gemfile: gemfiles/rails_5_1.gemfile
30
+ - rvm: 2.2.10
31
+ gemfile: gemfiles/rails_5_2.gemfile
32
+ - rvm: 2.7.1
33
+ gemfile: gemfiles/rails_5_2.gemfile
27
34
  - rvm: 2.2.10
28
35
  gemfile: gemfiles/rails_6_0.gemfile
29
36
  - rvm: 2.3.8
30
37
  gemfile: gemfiles/rails_6_0.gemfile
31
- - rvm: 2.4.6
38
+ - rvm: 2.4.10
32
39
  gemfile: gemfiles/rails_6_0.gemfile
33
40
  - rvm: 2.2.10
34
41
  gemfile: gemfiles/rails_head.gemfile
35
42
  - rvm: 2.3.8
36
43
  gemfile: gemfiles/rails_head.gemfile
37
- - rvm: 2.4.6
44
+ - rvm: 2.4.10
38
45
  gemfile: gemfiles/rails_head.gemfile
39
46
  allow_failures:
40
- - rvm: jruby-19mode
41
- - rvm: rbx-3.107
42
47
  - rvm: ruby-head
43
48
  - gemfile: gemfiles/rails_head.gemfile
44
49
  fast_finish: true
@@ -1,7 +1,35 @@
1
1
  # Changelog
2
2
 
3
+ 2.11.2
4
+ ------
5
+
6
+ * [Improve key formatting for nested hashes and disable by default](https://github.com/rails/jbuilder/pull/497)
7
+
8
+ 2.11.1
9
+ ------
10
+
11
+ * Use symbols instead of strings for before_action filters [DHH]
12
+ * Slim down comments in generated scaffold code [DHH]
13
+
14
+ 2.11.0
15
+ ------
16
+
17
+ * [Allow jbuilder instance to be passed to #merge!](https://github.com/rails/jbuilder/pull/485)
18
+ * [Fix for key_format! when using nested hashes](https://github.com/rails/jbuilder/pull/486)
19
+ * [Include rich_text, attachment, and attachments fields in json partial](https://github.com/rails/jbuilder/pull/459)
20
+
21
+ 2.10.2
22
+ ------
23
+
24
+ * Update scaffold generator to use double quotes, 422 form error responds, and modern string-of-arrays syntax [DHH]
25
+
26
+ 2.10.1
27
+ ------
28
+
29
+ * Fix keyword arguments warning on Ruby 2.7
30
+
3
31
  2.10.0
4
- ----------
32
+ ------
5
33
 
6
34
  * Requires Rails 5+ and Ruby 2.2+
7
35
  * Nested hashes are deep-merged
@@ -4,18 +4,16 @@ Contributing to Jbuilder
4
4
  [![Build Status](https://api.travis-ci.org/rails/jbuilder.svg?branch=master)][travis]
5
5
  [![Gem Version](https://badge.fury.io/rb/jbuilder.svg)][gem]
6
6
  [![Code Climate](https://codeclimate.com/github/rails/jbuilder/badges/gpa.svg)][codeclimate]
7
- [![Dependencies Status](https://gemnasium.com/rails/jbuilder.svg)][gemnasium]
8
7
 
9
8
  [travis]: https://travis-ci.org/rails/jbuilder
10
9
  [gem]: https://rubygems.org/gems/jbuilder
11
10
  [codeclimate]: https://codeclimate.com/github/rails/jbuilder
12
- [gemnasium]: https://gemnasium.com/rails/jbuilder
13
11
 
14
12
  Jbuilder is work of [many contributors](https://github.com/rails/jbuilder/graphs/contributors). You're encouraged to submit [pull requests](https://github.com/rails/jbuilder/pulls), [propose features and discuss issues](https://github.com/rails/jbuilder/issues).
15
13
 
16
14
  #### Fork the Project
17
15
 
18
- Fork the [project on Github](https://github.com/rails/jbuilder) and check out your copy.
16
+ Fork the [project on GitHub](https://github.com/rails/jbuilder) and check out your copy.
19
17
 
20
18
  ```
21
19
  git clone https://github.com/contributor/jbuilder.git
data/README.md CHANGED
@@ -274,6 +274,25 @@ environment.rb for example):
274
274
  Jbuilder.key_format camelize: :lower
275
275
  ```
276
276
 
277
+ By default, key format is not applied to keys of hashes that are
278
+ passed to methods like `set!`, `array!` or `merge!`. You can opt into
279
+ deeply transforming these as well:
280
+
281
+ ``` ruby
282
+ json.key_format! camelize: :lower
283
+ json.deep_format_keys!
284
+ json.settings([{some_value: "abc"}])
285
+
286
+ # => { "settings": [{ "someValue": "abc" }]}
287
+ ```
288
+
289
+ You can set this globally with the class method `deep_format_keys` (from inside your
290
+ environment.rb for example):
291
+
292
+ ``` ruby
293
+ Jbuilder.deep_format_keys true
294
+ ```
295
+
277
296
  ## Contributing to Jbuilder
278
297
 
279
298
  Jbuilder is the work of many contributors. You're encouraged to submit pull requests, propose
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'jbuilder'
3
- s.version = '2.10.0'
3
+ s.version = '2.11.2'
4
4
  s.authors = 'David Heinemeier Hansson'
5
5
  s.email = 'david@basecamp.com'
6
6
  s.summary = 'Create JSON structures via a Builder-style DSL'
@@ -11,6 +11,12 @@ Gem::Specification.new do |s|
11
11
 
12
12
  s.add_dependency 'activesupport', '>= 5.0.0'
13
13
 
14
+ if RUBY_ENGINE == 'rbx'
15
+ s.add_development_dependency('racc')
16
+ s.add_development_dependency('json')
17
+ s.add_development_dependency('rubysl')
18
+ end
19
+
14
20
  s.files = `git ls-files`.split("\n")
15
21
  s.test_files = `git ls-files -- test/*`.split("\n")
16
22
  end
@@ -50,6 +50,10 @@ module Rails
50
50
 
51
51
  attributes.map { |a| ":#{a}"} * ', '
52
52
  end
53
+
54
+ def virtual_attributes
55
+ attributes.select {|name| name.respond_to?(:virtual?) && name.virtual? }
56
+ end
53
57
  end
54
58
  end
55
59
  end
@@ -1,10 +1,10 @@
1
1
  <% if namespaced? -%>
2
- require_dependency "<%= namespaced_file_path %>/application_controller"
2
+ require_dependency "<%= namespaced_path %>/application_controller"
3
3
 
4
4
  <% end -%>
5
5
  <% module_namespacing do -%>
6
6
  class <%= controller_class_name %>Controller < ApplicationController
7
- before_action :set_<%= singular_table_name %>, only: [:show, :update, :destroy]
7
+ before_action :set_<%= singular_table_name %>, only: %i[ show update destroy ]
8
8
 
9
9
  # GET <%= route_url %>
10
10
  # GET <%= route_url %>.json
@@ -1,19 +1,17 @@
1
1
  <% if namespaced? -%>
2
- require_dependency "<%= namespaced_file_path %>/application_controller"
2
+ require_dependency "<%= namespaced_path %>/application_controller"
3
3
 
4
4
  <% end -%>
5
5
  <% module_namespacing do -%>
6
6
  class <%= controller_class_name %>Controller < ApplicationController
7
- before_action :set_<%= singular_table_name %>, only: [:show, :edit, :update, :destroy]
7
+ before_action :set_<%= singular_table_name %>, only: %i[ show edit update destroy ]
8
8
 
9
- # GET <%= route_url %>
10
- # GET <%= route_url %>.json
9
+ # GET <%= route_url %> or <%= route_url %>.json
11
10
  def index
12
11
  @<%= plural_table_name %> = <%= orm_class.all(class_name) %>
13
12
  end
14
13
 
15
- # GET <%= route_url %>/1
16
- # GET <%= route_url %>/1.json
14
+ # GET <%= route_url %>/1 or <%= route_url %>/1.json
17
15
  def show
18
16
  end
19
17
 
@@ -26,42 +24,39 @@ class <%= controller_class_name %>Controller < ApplicationController
26
24
  def edit
27
25
  end
28
26
 
29
- # POST <%= route_url %>
30
- # POST <%= route_url %>.json
27
+ # POST <%= route_url %> or <%= route_url %>.json
31
28
  def create
32
29
  @<%= singular_table_name %> = <%= orm_class.build(class_name, "#{singular_table_name}_params") %>
33
30
 
34
31
  respond_to do |format|
35
32
  if @<%= orm_instance.save %>
36
- format.html { redirect_to @<%= singular_table_name %>, notice: <%= "'#{human_name} was successfully created.'" %> }
33
+ format.html { redirect_to @<%= singular_table_name %>, notice: <%= %("#{human_name} was successfully created.") %> }
37
34
  format.json { render :show, status: :created, location: <%= "@#{singular_table_name}" %> }
38
35
  else
39
- format.html { render :new }
36
+ format.html { render :new, status: :unprocessable_entity }
40
37
  format.json { render json: <%= "@#{orm_instance.errors}" %>, status: :unprocessable_entity }
41
38
  end
42
39
  end
43
40
  end
44
41
 
45
- # PATCH/PUT <%= route_url %>/1
46
- # PATCH/PUT <%= route_url %>/1.json
42
+ # PATCH/PUT <%= route_url %>/1 or <%= route_url %>/1.json
47
43
  def update
48
44
  respond_to do |format|
49
45
  if @<%= orm_instance.update("#{singular_table_name}_params") %>
50
- format.html { redirect_to @<%= singular_table_name %>, notice: <%= "'#{human_name} was successfully updated.'" %> }
46
+ format.html { redirect_to @<%= singular_table_name %>, notice: <%= %("#{human_name} was successfully updated.") %> }
51
47
  format.json { render :show, status: :ok, location: <%= "@#{singular_table_name}" %> }
52
48
  else
53
- format.html { render :edit }
49
+ format.html { render :edit, status: :unprocessable_entity }
54
50
  format.json { render json: <%= "@#{orm_instance.errors}" %>, status: :unprocessable_entity }
55
51
  end
56
52
  end
57
53
  end
58
54
 
59
- # DELETE <%= route_url %>/1
60
- # DELETE <%= route_url %>/1.json
55
+ # DELETE <%= route_url %>/1 or <%= route_url %>/1.json
61
56
  def destroy
62
57
  @<%= orm_instance.destroy %>
63
58
  respond_to do |format|
64
- format.html { redirect_to <%= index_helper %>_url, notice: <%= "'#{human_name} was successfully destroyed.'" %> }
59
+ format.html { redirect_to <%= index_helper %>_url, notice: <%= %("#{human_name} was successfully destroyed.") %> }
65
60
  format.json { head :no_content }
66
61
  end
67
62
  end
@@ -1,2 +1,16 @@
1
1
  json.extract! <%= singular_table_name %>, <%= full_attributes_list %>
2
2
  json.url <%= singular_table_name %>_url(<%= singular_table_name %>, format: :json)
3
+ <%- virtual_attributes.each do |attribute| -%>
4
+ <%- if attribute.type == :rich_text -%>
5
+ json.<%= attribute.name %> <%= singular_table_name %>.<%= attribute.name %>.to_s
6
+ <%- elsif attribute.type == :attachment -%>
7
+ json.<%= attribute.name %> url_for(<%= singular_table_name %>.<%= attribute.name %>)
8
+ <%- elsif attribute.type == :attachments -%>
9
+ json.<%= attribute.name %> do
10
+ json.array!(<%= singular_table_name %>.<%= attribute.name %>) do |<%= attribute.singular_name %>|
11
+ json.id <%= attribute.singular_name %>.id
12
+ json.url url_for(<%= attribute.singular_name %>)
13
+ end
14
+ end
15
+ <%- end -%>
16
+ <%- end -%>
@@ -9,12 +9,14 @@ require 'active_support/core_ext/hash/deep_merge'
9
9
  class Jbuilder
10
10
  @@key_formatter = nil
11
11
  @@ignore_nil = false
12
+ @@deep_format_keys = false
12
13
 
13
14
  def initialize(options = {})
14
15
  @attributes = {}
15
16
 
16
17
  @key_formatter = options.fetch(:key_formatter){ @@key_formatter ? @@key_formatter.clone : nil}
17
18
  @ignore_nil = options.fetch(:ignore_nil, @@ignore_nil)
19
+ @deep_format_keys = options.fetch(:deep_format_keys, @@deep_format_keys)
18
20
 
19
21
  yield self if ::Kernel.block_given?
20
22
  end
@@ -43,11 +45,11 @@ class Jbuilder
43
45
  # json.age 32
44
46
  # json.person another_jbuilder
45
47
  # { "age": 32, "person": { ... }
46
- value.attributes!
48
+ _format_keys(value.attributes!)
47
49
  else
48
50
  # json.age 32
49
51
  # { "age": 32 }
50
- value
52
+ _format_keys(value)
51
53
  end
52
54
  elsif _is_collection?(value)
53
55
  # json.comments @post.comments, :content, :created_at
@@ -131,6 +133,31 @@ class Jbuilder
131
133
  @@ignore_nil = value
132
134
  end
133
135
 
136
+ # Deeply apply key format to nested hashes and arrays passed to
137
+ # methods like set!, merge! or array!.
138
+ #
139
+ # Example:
140
+ #
141
+ # json.key_format! camelize: :lower
142
+ # json.settings({some_value: "abc"})
143
+ #
144
+ # { "settings": { "some_value": "abc" }}
145
+ #
146
+ # json.key_format! camelize: :lower
147
+ # json.deep_format_keys!
148
+ # json.settings({some_value: "abc"})
149
+ #
150
+ # { "settings": { "someValue": "abc" }}
151
+ #
152
+ def deep_format_keys!(value = true)
153
+ @deep_format_keys = value
154
+ end
155
+
156
+ # Same as instance method deep_format_keys! except sets the default.
157
+ def self.deep_format_keys(value = true)
158
+ @@deep_format_keys = value
159
+ end
160
+
134
161
  # Turns the current element into an array and yields a builder to add a hash.
135
162
  #
136
163
  # Example:
@@ -190,10 +217,10 @@ class Jbuilder
190
217
  elsif attributes.any?
191
218
  _map_collection(collection) { |element| extract! element, *attributes }
192
219
  else
193
- collection.to_a
220
+ _format_keys(collection.to_a)
194
221
  end
195
222
 
196
- merge! array
223
+ @attributes = _merge_values(@attributes, array)
197
224
  end
198
225
 
199
226
  # Extracts the mentioned attributes or hash elements from the passed object and turns them into attributes of the JSON.
@@ -241,9 +268,10 @@ class Jbuilder
241
268
  @attributes
242
269
  end
243
270
 
244
- # Merges hash or array into current builder.
245
- def merge!(hash_or_array)
246
- @attributes = _merge_values(@attributes, hash_or_array)
271
+ # Merges hash, array, or Jbuilder instance into current builder.
272
+ def merge!(object)
273
+ hash_or_array = ::Jbuilder === object ? object.attributes! : object
274
+ @attributes = _merge_values(@attributes, _format_keys(hash_or_array))
247
275
  end
248
276
 
249
277
  # Encodes the current builder as JSON.
@@ -254,11 +282,11 @@ class Jbuilder
254
282
  private
255
283
 
256
284
  def _extract_hash_values(object, attributes)
257
- attributes.each{ |key| _set_value key, object.fetch(key) }
285
+ attributes.each{ |key| _set_value key, _format_keys(object.fetch(key)) }
258
286
  end
259
287
 
260
288
  def _extract_method_values(object, attributes)
261
- attributes.each{ |key| _set_value key, object.public_send(key) }
289
+ attributes.each{ |key| _set_value key, _format_keys(object.public_send(key)) }
262
290
  end
263
291
 
264
292
  def _merge_block(key)
@@ -286,6 +314,18 @@ class Jbuilder
286
314
  @key_formatter ? @key_formatter.format(key) : key.to_s
287
315
  end
288
316
 
317
+ def _format_keys(hash_or_array)
318
+ return hash_or_array unless @deep_format_keys
319
+
320
+ if ::Array === hash_or_array
321
+ hash_or_array.map { |value| _format_keys(value) }
322
+ elsif ::Hash === hash_or_array
323
+ ::Hash[hash_or_array.collect { |k, v| [_key(k), _format_keys(v)] }]
324
+ else
325
+ hash_or_array
326
+ end
327
+ end
328
+
289
329
  def _set_value(key, value)
290
330
  raise NullError.build(key) if @attributes.nil?
291
331
  raise ArrayError.build(key) if ::Array === @attributes
@@ -301,12 +341,12 @@ class Jbuilder
301
341
  end
302
342
 
303
343
  def _scope
304
- parent_attributes, parent_formatter = @attributes, @key_formatter
344
+ parent_attributes, parent_formatter, parent_deep_format_keys = @attributes, @key_formatter, @deep_format_keys
305
345
  @attributes = BLANK
306
346
  yield
307
347
  @attributes
308
348
  ensure
309
- @attributes, @key_formatter = parent_attributes, parent_formatter
349
+ @attributes, @key_formatter, @deep_format_keys = parent_attributes, parent_formatter, parent_deep_format_keys
310
350
  end
311
351
 
312
352
  def _is_collection?(object)
@@ -164,7 +164,7 @@ class JbuilderTemplate < Jbuilder
164
164
  if @context.respond_to?(:cache_fragment_name)
165
165
  # Current compatibility, fragment_name_with_digest is private again and cache_fragment_name
166
166
  # should be used instead.
167
- @context.cache_fragment_name(key, options)
167
+ @context.cache_fragment_name(key, **options)
168
168
  elsif @context.respond_to?(:fragment_name_with_digest)
169
169
  # Backwards compatibility for period of time when fragment_name_with_digest was made public.
170
170
  @context.fragment_name_with_digest(key)
@@ -43,4 +43,16 @@ class JbuilderGeneratorTest < Rails::Generators::TestCase
43
43
  assert_no_match %r{:created_at, :updated_at}, content
44
44
  end
45
45
  end
46
+
47
+ if Rails::VERSION::MAJOR >= 6
48
+ test 'handles virtual attributes' do
49
+ run_generator %w(Message content:rich_text video:attachment photos:attachments)
50
+
51
+ assert_file 'app/views/messages/_message.json.jbuilder' do |content|
52
+ assert_match %r{json\.content message\.content\.to_s}, content
53
+ assert_match %r{json\.video url_for\(message\.video\)}, content
54
+ assert_match %r{json\.photos do\n json\.array!\(message\.photos\) do \|photo\|\n json\.id photo\.id\n json\.url url_for\(photo\)\n end\nend}, content
55
+ end
56
+ end
57
+ end
46
58
  end
@@ -196,6 +196,18 @@ class JbuilderTest < ActiveSupport::TestCase
196
196
  assert_equal 'Pavel', result['author']['name']
197
197
  end
198
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
+
199
211
  test 'blocks are additive via extract syntax' do
200
212
  person = Person.new('Pavel', 27)
201
213
 
@@ -554,6 +566,36 @@ class JbuilderTest < ActiveSupport::TestCase
554
566
  assert_equal 'one', result['level1']
555
567
  end
556
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
+
557
599
  test 'key_format! with no parameter' do
558
600
  result = jbuild do |json|
559
601
  json.key_format! :upcase
@@ -581,6 +623,161 @@ class JbuilderTest < ActiveSupport::TestCase
581
623
  assert_equal ['oats and friends'], result.keys
582
624
  end
583
625
 
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
632
+
633
+ assert_equal %i[first_name last_name], result['allNames'].keys
634
+ end
635
+
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
780
+
584
781
  test 'default key_format!' do
585
782
  Jbuilder.key_format camelize: :lower
586
783
  result = jbuild{ |json| json.camel_style 'for JS' }
@@ -55,5 +55,16 @@ if Rails::VERSION::MAJOR > 4
55
55
  assert_match %r{params\.fetch\(:post, \{\}\)}, content
56
56
  end
57
57
  end
58
+
59
+
60
+ if Rails::VERSION::MAJOR >= 6
61
+ test 'handles virtual attributes' do
62
+ run_generator ["Message", "content:rich_text", "video:attachment", "photos:attachments"]
63
+
64
+ assert_file 'app/controllers/messages_controller.rb' do |content|
65
+ assert_match %r{params\.require\(:message\)\.permit\(:content, :video, photos: \[\]\)}, content
66
+ end
67
+ end
68
+ end
58
69
  end
59
70
  end
@@ -31,22 +31,22 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase
31
31
  assert_instance_method :create, content do |m|
32
32
  assert_match %r{@post = Post\.new\(post_params\)}, m
33
33
  assert_match %r{@post\.save}, m
34
- assert_match %r{format\.html \{ redirect_to @post, notice: 'Post was successfully created\.' \}}, m
34
+ assert_match %r{format\.html \{ redirect_to @post, notice: "Post was successfully created\." \}}, m
35
35
  assert_match %r{format\.json \{ render :show, status: :created, location: @post \}}, m
36
- assert_match %r{format\.html \{ render :new \}}, m
36
+ assert_match %r{format\.html \{ render :new, status: :unprocessable_entity \}}, m
37
37
  assert_match %r{format\.json \{ render json: @post\.errors, status: :unprocessable_entity \}}, m
38
38
  end
39
39
 
40
40
  assert_instance_method :update, content do |m|
41
- assert_match %r{format\.html \{ redirect_to @post, notice: 'Post was successfully updated\.' \}}, m
41
+ assert_match %r{format\.html \{ redirect_to @post, notice: "Post was successfully updated\." \}}, m
42
42
  assert_match %r{format\.json \{ render :show, status: :ok, location: @post \}}, m
43
- assert_match %r{format\.html \{ render :edit \}}, m
43
+ assert_match %r{format\.html \{ render :edit, status: :unprocessable_entity \}}, m
44
44
  assert_match %r{format\.json \{ render json: @post.errors, status: :unprocessable_entity \}}, m
45
45
  end
46
46
 
47
47
  assert_instance_method :destroy, content do |m|
48
48
  assert_match %r{@post\.destroy}, m
49
- assert_match %r{format\.html \{ redirect_to posts_url, notice: 'Post was successfully destroyed\.' \}}, m
49
+ assert_match %r{format\.html \{ redirect_to posts_url, notice: "Post was successfully destroyed\." \}}, m
50
50
  assert_match %r{format\.json \{ head :no_content \}}, m
51
51
  end
52
52
 
@@ -67,4 +67,14 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase
67
67
  assert_match %r{params\.fetch\(:post, \{\}\)}, content
68
68
  end
69
69
  end
70
+
71
+ if Rails::VERSION::MAJOR >= 6
72
+ test 'handles virtual attributes' do
73
+ run_generator %w(Message content:rich_text video:attachment photos:attachments)
74
+
75
+ assert_file 'app/controllers/messages_controller.rb' do |content|
76
+ assert_match %r{params\.require\(:message\)\.permit\(:content, :video, photos: \[\]\)}, content
77
+ end
78
+ end
79
+ end
70
80
  end
@@ -11,7 +11,7 @@ require "rails/version"
11
11
  require "jbuilder"
12
12
 
13
13
  require "active_support/testing/autorun"
14
- require "mocha/setup"
14
+ require "mocha/minitest"
15
15
 
16
16
  ActiveSupport.test_order = :random
17
17
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jbuilder
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.10.0
4
+ version: 2.11.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-02-10 00:00:00.000000000 Z
11
+ date: 2021-01-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -86,7 +86,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
86
86
  - !ruby/object:Gem::Version
87
87
  version: '0'
88
88
  requirements: []
89
- rubygems_version: 3.0.6
89
+ rubygems_version: 3.1.2
90
90
  signing_key:
91
91
  specification_version: 4
92
92
  summary: Create JSON structures via a Builder-style DSL