jbuilder 2.10.0 → 2.11.2

Sign up to get free protection for your applications and to get access to all the features.
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