jbuilder 2.14.1 → 2.15.0

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: 7980c25163c5711962622d39c236ac262bfd7917160eb5012c6e36a06c797984
4
- data.tar.gz: 7b6881e873c5e3d6be33cf5e7a122de51c99a33ec61fa34b30baa5c5fa1c2c63
3
+ metadata.gz: 92a9e6859ee3e4a852307625394f53c94bbf3022adee7534c8f9746abe2e5c28
4
+ data.tar.gz: 9af5dfa8eb02f7a8c2dcbd9342ca5686fe33c2bcf171f34838599e64e23f9292
5
5
  SHA512:
6
- metadata.gz: 6c8477608caf773c5ef0123798df81cdcd8003081fa5dccd79f72a356f58b31c05a06bb1df16725150e01477f611332973e8e870db5239f6077501e9d560daed
7
- data.tar.gz: 108438d96749fabc48b4432eaa5c9b6dcf47df1f0adcdf0ff3de68bb602f49e3bb0bfe542cd13e041501bcddb80f8a3fd7ff7ffed9882235ff6f6d4c0d1aa464
6
+ metadata.gz: b03941877b974e49054bb41d50924520cf4a255daa73a107b32176b2db1c60cb6da117668195c5f6650be39e6405689c7f4958cc166ac1826d2bef4071a0b77a
7
+ data.tar.gz: 0b668f703e2e082152a986b2574f78740c5d68e017683ede8be4bc5e6e1e432ef49e34ef2c4c9989b1a67181ef02fd3728688b34c88b60f829f4bb6f3cf0edc9
@@ -0,0 +1,9 @@
1
+ {
2
+ "features": {
3
+ "ghcr.io/devcontainers/features/github-cli:1": {
4
+ "version": "1.1.0",
5
+ "resolved": "ghcr.io/devcontainers/features/github-cli@sha256:d22f50b70ed75339b4eed1ba9ecde3a1791f90e88d37936517e3bace0bbad671",
6
+ "integrity": "sha256:d22f50b70ed75339b4eed1ba9ecde3a1791f90e88d37936517e3bace0bbad671"
7
+ }
8
+ }
9
+ }
@@ -3,7 +3,7 @@
3
3
  {
4
4
  "name": "jbuilder",
5
5
  // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
6
- "image": "ghcr.io/rails/devcontainer/images/ruby:3.4.5",
6
+ "image": "ghcr.io/rails/devcontainer/images/ruby:4.0.4",
7
7
  "features": {
8
8
  "ghcr.io/devcontainers/features/github-cli:1": {}
9
9
  }
@@ -20,12 +20,14 @@ jobs:
20
20
  - "3.2"
21
21
  - "3.3"
22
22
  - "3.4"
23
+ - "4.0"
23
24
 
24
25
  gemfile:
25
26
  - "rails_7_0"
26
27
  - "rails_7_1"
27
28
  - "rails_7_2"
28
29
  - "rails_8_0"
30
+ - "rails_8_1"
29
31
  - "rails_head"
30
32
 
31
33
  exclude:
@@ -33,19 +35,27 @@ jobs:
33
35
  gemfile: rails_7_2
34
36
  - ruby: '3.0'
35
37
  gemfile: rails_8_0
38
+ - ruby: '3.0'
39
+ gemfile: rails_8_1
36
40
  - ruby: '3.0'
37
41
  gemfile: rails_head
38
42
  - ruby: '3.1'
39
43
  gemfile: rails_7_2
40
44
  - ruby: '3.1'
41
45
  gemfile: rails_8_0
46
+ - ruby: '3.1'
47
+ gemfile: rails_8_1
42
48
  - ruby: '3.1'
43
49
  gemfile: rails_head
50
+ - ruby: '3.2'
51
+ gemfile: rails_head
44
52
  - ruby: '3.4'
45
53
  gemfile: rails_7_0
54
+ - ruby: '4.0'
55
+ gemfile: rails_7_0
46
56
 
47
57
  steps:
48
- - uses: actions/checkout@v4
58
+ - uses: actions/checkout@v6
49
59
 
50
60
  - uses: ruby/setup-ruby@v1
51
61
  with:
data/Appraisals CHANGED
@@ -17,6 +17,10 @@ appraise "rails-8-0" do
17
17
  gem "rails", "~> 8.0.0"
18
18
  end
19
19
 
20
+ appraise "rails-8-1" do
21
+ gem "rails", "~> 8.1.0"
22
+ end
23
+
20
24
  appraise "rails-head" do
21
25
  gem "rails", github: "rails/rails", branch: "main"
22
26
  end
data/README.md CHANGED
@@ -100,7 +100,7 @@ json.array! @comments do |comment|
100
100
  end
101
101
  end
102
102
 
103
- # => [ { "body": "great post...", "author": { "first_name": "Joe", "last_name": "Bloe" }} ]
103
+ # => [ { "body": "great post...", "author": { "first_name": "Joe", "last_name": "Blow" }} ]
104
104
  ```
105
105
 
106
106
  ## Array Attributes
@@ -209,6 +209,13 @@ the partial.
209
209
  json.partial! 'comments/comments', comments: @message.comments
210
210
  ```
211
211
 
212
+ You can also render an object to a partial inline under a key.
213
+
214
+ ```ruby
215
+ json.post @post, partial: 'posts/post', as: :post
216
+ # => { "post": { "title": "Hello World!", "author": { "name": "David" } } }
217
+ ```
218
+
212
219
  It's also possible to render collections of partials:
213
220
 
214
221
  ```ruby
@@ -219,9 +226,24 @@ json.partial! 'posts/post', collection: @posts, as: :post
219
226
 
220
227
  # or
221
228
  json.partial! partial: 'posts/post', collection: @posts, as: :post
229
+ ```
222
230
 
223
- # or
231
+ You can also render to a collection of partials inline under a key.
232
+
233
+ ```ruby
224
234
  json.comments @post.comments, partial: 'comments/comment', as: :comment
235
+ # => { "comments": [{ "content": "Hello everyone!" }, { "content": "To you my good sir!" }] }
236
+ ```
237
+
238
+ You can also provide other locals to the partial you're rendering to.
239
+
240
+ ```ruby
241
+ # Provide the `include_body` local to the partial when rendering a single object
242
+ json.post @post, partial: 'posts/post', as: :post, include_body: true
243
+
244
+ # Provide a local to the partial when rendering a collection.
245
+ # Each item in the collection will render with `include_author: true`.
246
+ json.comments @post.comments, partial: 'comments/comment', as: :comment, include_author: true
225
247
  ```
226
248
 
227
249
  The `as: :some_symbol` is used with partials. It will take care of mapping the passed in object to a variable for the
@@ -321,7 +343,7 @@ This will include both records as part of the cache key and updating either of t
321
343
  ## Formatting Keys
322
344
 
323
345
  Keys can be auto formatted using `key_format!`, this can be used to convert
324
- keynames from the standard ruby_format to camelCase:
346
+ key names from the standard ruby_format to camelCase:
325
347
 
326
348
  ```ruby
327
349
  json.key_format! camelize: :lower
@@ -0,0 +1,10 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rake"
6
+ gem "mocha", require: false
7
+ gem "appraisal"
8
+ gem "rails", "~> 8.1.0"
9
+
10
+ gemspec path: "../"
@@ -15,6 +15,10 @@ module Rails
15
15
  def permitted_params
16
16
  attributes_names.map { |name| ":#{name}" }.join(', ')
17
17
  end unless private_method_defined? :permitted_params
18
+
19
+ def status_unprocessable_content
20
+ ::Rack::Utils::SYMBOL_TO_STATUS_CODE.key(422) rescue :unprocessable_content
21
+ end
18
22
  end
19
23
  end
20
24
  end
@@ -25,7 +25,7 @@ class <%= controller_class_name %>Controller < ApplicationController
25
25
  if @<%= orm_instance.save %>
26
26
  render :show, status: :created, location: <%= "@#{singular_table_name}" %>
27
27
  else
28
- render json: <%= "@#{orm_instance.errors}" %>, status: :unprocessable_entity
28
+ render json: <%= "@#{orm_instance.errors}" %>, status: :<%= status_unprocessable_content.to_s %>
29
29
  end
30
30
  end
31
31
 
@@ -35,7 +35,7 @@ class <%= controller_class_name %>Controller < ApplicationController
35
35
  if @<%= orm_instance.update("#{singular_table_name}_params") %>
36
36
  render :show, status: :ok, location: <%= "@#{singular_table_name}" %>
37
37
  else
38
- render json: <%= "@#{orm_instance.errors}" %>, status: :unprocessable_entity
38
+ render json: <%= "@#{orm_instance.errors}" %>, status: :<%= status_unprocessable_content.to_s %>
39
39
  end
40
40
  end
41
41
 
@@ -33,8 +33,8 @@ class <%= controller_class_name %>Controller < ApplicationController
33
33
  format.html { redirect_to <%= redirect_resource_name %>, notice: <%= %("#{human_name} was successfully created.") %> }
34
34
  format.json { render :show, status: :created, location: <%= "@#{singular_table_name}" %> }
35
35
  else
36
- format.html { render :new, status: :unprocessable_entity }
37
- format.json { render json: <%= "@#{orm_instance.errors}" %>, status: :unprocessable_entity }
36
+ format.html { render :new, status: :<%= status_unprocessable_content.to_s %> }
37
+ format.json { render json: <%= "@#{orm_instance.errors}" %>, status: :<%= status_unprocessable_content.to_s %> }
38
38
  end
39
39
  end
40
40
  end
@@ -46,8 +46,8 @@ class <%= controller_class_name %>Controller < ApplicationController
46
46
  format.html { redirect_to <%= redirect_resource_name %>, notice: <%= %("#{human_name} was successfully updated.") %>, status: :see_other }
47
47
  format.json { render :show, status: :ok, location: <%= "@#{singular_table_name}" %> }
48
48
  else
49
- format.html { render :edit, status: :unprocessable_entity }
50
- format.json { render json: <%= "@#{orm_instance.errors}" %>, status: :unprocessable_entity }
49
+ format.html { render :edit, status: :<%= status_unprocessable_content.to_s %> }
50
+ format.json { render json: <%= "@#{orm_instance.errors}" %>, status: :<%= status_unprocessable_content.to_s %> }
51
51
  end
52
52
  end
53
53
  end
@@ -27,7 +27,7 @@ class JbuilderTemplate < Jbuilder
27
27
  #
28
28
  # json.partial! 'comments/comments', comments: @message.comments
29
29
  #
30
- # There are multiple ways to generate a collection of elements as JSON, as ilustrated below:
30
+ # There are multiple ways to generate a collection of elements as JSON, as illustrated below:
31
31
  #
32
32
  # Example:
33
33
  #
@@ -52,7 +52,7 @@ class JbuilderTemplate < Jbuilder
52
52
  # json.comments @post.comments, partial: "comments/comment", as: :comment, cached: true
53
53
  #
54
54
  def partial!(*args)
55
- if args.one? && _is_active_model?(args.first)
55
+ if _is_active_model?(args.first)
56
56
  _render_active_model_partial args.first
57
57
  else
58
58
  options = args.extract_options!.dup
@@ -119,31 +119,32 @@ class JbuilderTemplate < Jbuilder
119
119
  @cached_root || super
120
120
  end
121
121
 
122
- def array!(collection = [], *args)
122
+ def array!(collection = EMPTY_ARRAY, *args, &block)
123
123
  options = args.first
124
124
 
125
- if args.one? && _partial_options?(options)
125
+ if _partial_options?(options)
126
126
  options = options.dup
127
127
  options[:collection] = collection
128
128
  _render_partial_with_options options
129
129
  else
130
- super
130
+ _array collection, args, &block
131
131
  end
132
132
  end
133
133
 
134
- def set!(name, object = BLANK, *args)
134
+ def set!(name, object = BLANK, *args, &block)
135
135
  options = args.first
136
136
 
137
- if args.one? && _partial_options?(options)
137
+ if _partial_options?(options)
138
138
  _set_inline_partial name, object, options.dup
139
139
  else
140
- super
140
+ _set name, object, args, &block
141
141
  end
142
142
  end
143
143
 
144
- private
145
-
146
144
  alias_method :method_missing, :set!
145
+ private :method_missing
146
+
147
+ private
147
148
 
148
149
  def _render_partial_with_options(options)
149
150
  options[:locals] ||= options.except(:partial, :as, :collection, :cached)
@@ -151,7 +152,7 @@ class JbuilderTemplate < Jbuilder
151
152
  as = options[:as]
152
153
 
153
154
  if as && options.key?(:collection)
154
- collection = options.delete(:collection) || []
155
+ collection = options.delete(:collection) || EMPTY_ARRAY
155
156
  partial = options.delete(:partial)
156
157
  options[:locals][:json] = self
157
158
  collection = EnumerableCompat.new(collection) if collection.respond_to?(:count) && !collection.respond_to?(:size)
@@ -169,20 +170,16 @@ class JbuilderTemplate < Jbuilder
169
170
  .new(@context.lookup_context, options) { |&block| _scope(&block) }
170
171
  .render_collection_with_partial(collection, partial, @context, nil)
171
172
 
172
- array! if results.respond_to?(:body) && results.body.nil?
173
+ _array if results.respond_to?(:body) && results.body.nil?
173
174
  else
174
- array!
175
+ _array
175
176
  end
176
177
  else
177
- _render_partial options
178
+ options[:locals][:json] = self
179
+ @context.render options
178
180
  end
179
181
  end
180
182
 
181
- def _render_partial(options)
182
- options[:locals][:json] = self
183
- @context.render options
184
- end
185
-
186
183
  def _cache_fragment_for(key, options, &block)
187
184
  key = _cache_key(key, options)
188
185
  _read_fragment_cache(key, options) || _write_fragment_cache(key, options, &block)
@@ -233,7 +230,7 @@ class JbuilderTemplate < Jbuilder
233
230
 
234
231
  def _set_inline_partial(name, object, options)
235
232
  value = if object.nil?
236
- []
233
+ EMPTY_ARRAY
237
234
  elsif _is_collection?(object)
238
235
  _scope do
239
236
  options[:collection] = object
@@ -241,7 +238,7 @@ class JbuilderTemplate < Jbuilder
241
238
  end
242
239
  else
243
240
  _scope do
244
- options[:locals] = { options[:as] => object }
241
+ options[options[:as]] = object
245
242
  _render_partial_with_options options
246
243
  end
247
244
  end
@@ -12,7 +12,7 @@ class Jbuilder
12
12
  end
13
13
 
14
14
  def format(key)
15
- @mutex.synchronize do
15
+ @cache[key] || @mutex.synchronize do
16
16
  @cache[key] ||= begin
17
17
  value = key.is_a?(Symbol) ? key.name : key.to_s
18
18
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Jbuilder < BasicObject
4
- VERSION = "2.14.1"
4
+ VERSION = "2.15.0"
5
5
  end
data/lib/jbuilder.rb CHANGED
@@ -7,6 +7,7 @@ require 'jbuilder/key_formatter'
7
7
  require 'jbuilder/errors'
8
8
  require 'json'
9
9
  require 'active_support/core_ext/hash/deep_merge'
10
+ require 'active_support/core_ext/object/blank'
10
11
 
11
12
  class Jbuilder
12
13
  @@key_formatter = nil
@@ -32,41 +33,12 @@ class Jbuilder
32
33
  new(...).target!
33
34
  end
34
35
 
35
- BLANK = Blank.new
36
+ BLANK = Blank.new.freeze
37
+ EMPTY_ARRAY = [].freeze
38
+ private_constant :BLANK, :EMPTY_ARRAY
36
39
 
37
40
  def set!(key, value = BLANK, *args, &block)
38
- result = if ::Kernel.block_given?
39
- if !_blank?(value)
40
- # json.comments @post.comments { |comment| ... }
41
- # { "comments": [ { ... }, { ... } ] }
42
- _scope{ array! value, &block }
43
- else
44
- # json.comments { ... }
45
- # { "comments": ... }
46
- _merge_block(key){ yield self }
47
- end
48
- elsif args.empty?
49
- if ::Jbuilder === value
50
- # json.age 32
51
- # json.person another_jbuilder
52
- # { "age": 32, "person": { ... }
53
- _format_keys(value.attributes!)
54
- else
55
- # json.age 32
56
- # { "age": 32 }
57
- _format_keys(value)
58
- end
59
- elsif _is_collection?(value)
60
- # json.comments @post.comments, :content, :created_at
61
- # { "comments": [ { "content": "hello", "created_at": "..." }, { "content": "world", "created_at": "..." } ] }
62
- _scope{ array! value, *args }
63
- else
64
- # json.author @post.creator, :name, :email_address
65
- # { "author": { "name": "David", "email_address": "david@loudthinking.com" } }
66
- _merge_block(key){ _extract value, args }
67
- end
68
-
69
- _set_value key, result
41
+ _set(key, value, args, &block)
70
42
  end
71
43
 
72
44
  # Specifies formatting to be applied to the key. Passing in a name of a function
@@ -206,18 +178,8 @@ class Jbuilder
206
178
  # json.array! [1, 2, 3]
207
179
  #
208
180
  # [1,2,3]
209
- def array!(collection = [], *attributes, &block)
210
- array = if collection.nil?
211
- []
212
- elsif ::Kernel.block_given?
213
- _map_collection(collection, &block)
214
- elsif attributes.any?
215
- _map_collection(collection) { |element| _extract element, attributes }
216
- else
217
- _format_keys(collection.to_a)
218
- end
219
-
220
- @attributes = _merge_values(@attributes, array)
181
+ def array!(collection = EMPTY_ARRAY, *attributes, &block)
182
+ _array collection, attributes, &block
221
183
  end
222
184
 
223
185
  # Extracts the mentioned attributes or hash elements from the passed object and turns them into attributes of the JSON.
@@ -242,8 +204,8 @@ class Jbuilder
242
204
  end
243
205
 
244
206
  def call(object, *attributes, &block)
245
- if ::Kernel.block_given?
246
- array! object, &block
207
+ if block
208
+ _array object, &block
247
209
  else
248
210
  _extract object, attributes
249
211
  end
@@ -272,9 +234,59 @@ class Jbuilder
272
234
  @attributes.to_json
273
235
  end
274
236
 
237
+ alias_method :method_missing, :set!
238
+ private :method_missing
239
+
275
240
  private
276
241
 
277
- alias_method :method_missing, :set!
242
+ def _set(key, value = BLANK, attributes = nil, &block)
243
+ result = if block
244
+ if _blank?(value)
245
+ # json.comments { ... }
246
+ # { "comments": ... }
247
+ _merge_block key, &block
248
+ else
249
+ # json.comments @post.comments { |comment| ... }
250
+ # { "comments": [ { ... }, { ... } ] }
251
+ _scope { _array value, &block }
252
+ end
253
+ elsif attributes.empty?
254
+ if ::Jbuilder === value
255
+ # json.age 32
256
+ # json.person another_jbuilder
257
+ # { "age": 32, "person": { ... }
258
+ _format_keys(value.attributes!)
259
+ else
260
+ # json.age 32
261
+ # { "age": 32 }
262
+ _format_keys(value)
263
+ end
264
+ elsif _is_collection?(value)
265
+ # json.comments @post.comments, :content, :created_at
266
+ # { "comments": [ { "content": "hello", "created_at": "..." }, { "content": "world", "created_at": "..." } ] }
267
+ _scope { _array value, attributes }
268
+ else
269
+ # json.author @post.creator, :name, :email_address
270
+ # { "author": { "name": "David", "email_address": "david@loudthinking.com" } }
271
+ _merge_block(key) { _extract value, attributes }
272
+ end
273
+
274
+ _set_value key, result
275
+ end
276
+
277
+ def _array(collection = EMPTY_ARRAY, attributes = nil, &block)
278
+ array = if collection.nil?
279
+ EMPTY_ARRAY
280
+ elsif block
281
+ _map_collection(collection, &block)
282
+ elsif attributes.present?
283
+ _map_collection(collection) { |element| _extract element, attributes }
284
+ else
285
+ _format_keys(collection.to_a)
286
+ end
287
+
288
+ @attributes = _merge_values(@attributes, array)
289
+ end
278
290
 
279
291
  def _extract(object, attributes)
280
292
  if ::Hash === object
@@ -344,9 +356,11 @@ class Jbuilder
344
356
  end
345
357
 
346
358
  def _map_collection(collection)
347
- collection.map do |element|
359
+ collection = collection.map do |element|
348
360
  _scope{ yield element }
349
- end - [BLANK]
361
+ end
362
+ collection.delete(BLANK)
363
+ collection
350
364
  end
351
365
 
352
366
  def _scope
@@ -4,6 +4,7 @@ require "action_view/testing/resolvers"
4
4
  class JbuilderTemplateTest < ActiveSupport::TestCase
5
5
  POST_PARTIAL = <<-JBUILDER
6
6
  json.extract! post, :id, :body
7
+ json.title post.title if local_assigns.fetch(:include_title, false)
7
8
  json.author do
8
9
  first_name, last_name = post.author_name.split(nil, 2)
9
10
  json.first_name first_name
@@ -30,7 +31,7 @@ class JbuilderTemplateTest < ActiveSupport::TestCase
30
31
  }
31
32
 
32
33
  AUTHORS = [ "David Heinemeier Hansson", "Pavel Pravosud" ].cycle
33
- POSTS = (1..10).collect { |i| Post.new(i, "Post ##{i}", AUTHORS.next) }
34
+ POSTS = (1..10).collect { |i| Post.new(i, "Title #{i}", "Post ##{i}", AUTHORS.next) }
34
35
 
35
36
  setup { Rails.cache.clear }
36
37
 
@@ -39,6 +40,11 @@ class JbuilderTemplateTest < ActiveSupport::TestCase
39
40
  assert_equal "hello", result["content"]
40
41
  end
41
42
 
43
+ test "method_missing can be used as a key" do
44
+ result = render('json.method_missing "hello"')
45
+ assert_equal({ "method_missing" => "hello" }, result)
46
+ end
47
+
42
48
  test "partial by name with top-level locals" do
43
49
  result = render('json.partial! "partial", content: "hello"')
44
50
  assert_equal "hello", result["content"]
@@ -136,6 +142,18 @@ class JbuilderTemplateTest < ActiveSupport::TestCase
136
142
  assert_equal [], render('json.array! @posts, partial: "post", as: :post', posts: nil)
137
143
  end
138
144
 
145
+ test "single partial under key" do
146
+ result = render('json.post @post, partial: "post", as: :post', post: POSTS.first)
147
+ assert_equal "Post #1", result["post"]["body"]
148
+ assert_equal "Heinemeier Hansson", result["post"]["author"]["last_name"]
149
+ assert_equal "David", result["post"]["author"]["first_name"]
150
+ end
151
+
152
+ test 'single partial under key with local' do
153
+ result = render('json.post @post, partial: "post", as: :post, include_title: true', post: POSTS.first)
154
+ assert_equal "Title 1", result["post"]["title"]
155
+ end
156
+
139
157
  test "array of partials under key" do
140
158
  result = render('json.posts @posts, partial: "post", as: :post', posts: POSTS)
141
159
  assert_equal 10, result["posts"].count
@@ -144,13 +162,19 @@ class JbuilderTemplateTest < ActiveSupport::TestCase
144
162
  assert_equal "Pavel", result["posts"][5]["author"]["first_name"]
145
163
  end
146
164
 
165
+ test "array of partials under key with local" do
166
+ result = render('json.posts @posts, partial: "post", as: :post, include_title: true', posts: POSTS)
167
+ assert_equal "Title 1", result["posts"][0]["title"]
168
+ assert_equal "Title 2", result["posts"][1]["title"]
169
+ end
170
+
147
171
  test "empty array of partials under key from nil collection" do
148
172
  Jbuilder::CollectionRenderer.expects(:new).never
149
173
  result = render('json.posts @posts, partial: "post", as: :post', posts: nil)
150
174
  assert_equal [], result["posts"]
151
175
  end
152
176
 
153
- test "empty array of partials under key from an empy collection" do
177
+ test "empty array of partials under key from an empty collection" do
154
178
  Jbuilder::CollectionRenderer.expects(:new).never
155
179
  result = render('json.posts @posts, partial: "post", as: :post', posts: [])
156
180
  assert_equal [], result["posts"]
@@ -61,6 +61,14 @@ class JbuilderTest < ActiveSupport::TestCase
61
61
  assert_equal 'hello', result['content']
62
62
  end
63
63
 
64
+ test 'method_missing key' do
65
+ result = jbuild do |json|
66
+ json.method_missing 'hello'
67
+ end
68
+
69
+ assert_equal 'hello', result['method_missing']
70
+ end
71
+
64
72
  test 'single key with false value' do
65
73
  result = jbuild do |json|
66
74
  json.content false
@@ -24,12 +24,20 @@ class ScaffoldApiControllerGeneratorTest < Rails::Generators::TestCase
24
24
  assert_match %r{@post = Post\.new\(post_params\)}, m
25
25
  assert_match %r{@post\.save}, m
26
26
  assert_match %r{render :show, status: :created, location: @post}, m
27
- assert_match %r{render json: @post\.errors, status: :unprocessable_entity}, m
27
+ if Gem::Version.new(Rack::RELEASE) < Gem::Version.new("3.1")
28
+ assert_match %r{render json: @post\.errors, status: :unprocessable_entity}, m
29
+ else
30
+ assert_match %r{render json: @post\.errors, status: :unprocessable_content}, m
31
+ end
28
32
  end
29
33
 
30
34
  assert_instance_method :update, content do |m|
31
35
  assert_match %r{render :show, status: :ok, location: @post}, m
32
- assert_match %r{render json: @post.errors, status: :unprocessable_entity}, m
36
+ if Gem::Version.new(Rack::RELEASE) < Gem::Version.new("3.1")
37
+ assert_match %r{render json: @post.errors, status: :unprocessable_entity}, m
38
+ else
39
+ assert_match %r{render json: @post.errors, status: :unprocessable_content}, m
40
+ end
33
41
  end
34
42
 
35
43
  assert_instance_method :destroy, content do |m|
@@ -33,15 +33,25 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase
33
33
  assert_match %r{@post\.save}, m
34
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, status: :unprocessable_entity \}}, m
37
- assert_match %r{format\.json \{ render json: @post\.errors, status: :unprocessable_entity \}}, m
36
+ if Gem::Version.new(Rack::RELEASE) < Gem::Version.new("3.1")
37
+ assert_match %r{format\.html \{ render :new, status: :unprocessable_entity \}}, m
38
+ assert_match %r{format\.json \{ render json: @post\.errors, status: :unprocessable_entity \}}, m
39
+ else
40
+ assert_match %r{format\.html \{ render :new, status: :unprocessable_content \}}, m
41
+ assert_match %r{format\.json \{ render json: @post\.errors, status: :unprocessable_content \}}, m
42
+ end
38
43
  end
39
44
 
40
45
  assert_instance_method :update, content do |m|
41
46
  assert_match %r{format\.html \{ redirect_to @post, notice: "Post was successfully updated\.", status: :see_other \}}, m
42
47
  assert_match %r{format\.json \{ render :show, status: :ok, location: @post \}}, m
43
- assert_match %r{format\.html \{ render :edit, status: :unprocessable_entity \}}, m
44
- assert_match %r{format\.json \{ render json: @post.errors, status: :unprocessable_entity \}}, m
48
+ if Gem::Version.new(Rack::RELEASE) < Gem::Version.new("3.1")
49
+ assert_match %r{format\.html \{ render :edit, status: :unprocessable_entity \}}, m
50
+ assert_match %r{format\.json \{ render json: @post.errors, status: :unprocessable_entity \}}, m
51
+ else
52
+ assert_match %r{format\.html \{ render :edit, status: :unprocessable_content \}}, m
53
+ assert_match %r{format\.json \{ render json: @post.errors, status: :unprocessable_content \}}, m
54
+ end
45
55
  end
46
56
 
47
57
  assert_instance_method :destroy, content do |m|
data/test/test_helper.rb CHANGED
@@ -26,7 +26,7 @@ end
26
26
 
27
27
  Jbuilder::CollectionRenderer.collection_cache = Rails.cache
28
28
 
29
- class Post < Struct.new(:id, :body, :author_name)
29
+ class Post < Struct.new(:id, :title, :body, :author_name)
30
30
  def cache_key
31
31
  "post-#{id}"
32
32
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jbuilder
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.14.1
4
+ version: 2.15.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
@@ -42,6 +42,7 @@ executables: []
42
42
  extensions: []
43
43
  extra_rdoc_files: []
44
44
  files:
45
+ - ".devcontainer/devcontainer-lock.json"
45
46
  - ".devcontainer/devcontainer.json"
46
47
  - ".github/workflows/ruby.yml"
47
48
  - ".gitignore"
@@ -57,6 +58,7 @@ files:
57
58
  - gemfiles/rails_7_1.gemfile
58
59
  - gemfiles/rails_7_2.gemfile
59
60
  - gemfiles/rails_8_0.gemfile
61
+ - gemfiles/rails_8_1.gemfile
60
62
  - gemfiles/rails_head.gemfile
61
63
  - jbuilder.gemspec
62
64
  - lib/generators/rails/jbuilder_generator.rb
@@ -88,9 +90,9 @@ licenses:
88
90
  - MIT
89
91
  metadata:
90
92
  bug_tracker_uri: https://github.com/rails/jbuilder/issues
91
- changelog_uri: https://github.com/rails/jbuilder/releases/tag/v2.14.1
93
+ changelog_uri: https://github.com/rails/jbuilder/releases/tag/v2.15.0
92
94
  mailing_list_uri: https://discuss.rubyonrails.org/c/rubyonrails-talk
93
- source_code_uri: https://github.com/rails/jbuilder/tree/v2.14.1
95
+ source_code_uri: https://github.com/rails/jbuilder/tree/v2.15.0
94
96
  rubygems_mfa_required: 'true'
95
97
  rdoc_options: []
96
98
  require_paths:
@@ -106,7 +108,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
106
108
  - !ruby/object:Gem::Version
107
109
  version: '0'
108
110
  requirements: []
109
- rubygems_version: 3.6.9
111
+ rubygems_version: 4.0.10
110
112
  specification_version: 4
111
113
  summary: Create JSON structures via a Builder-style DSL
112
114
  test_files: