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 +4 -4
- data/.devcontainer/devcontainer-lock.json +9 -0
- data/.devcontainer/devcontainer.json +1 -1
- data/.github/workflows/ruby.yml +11 -1
- data/Appraisals +4 -0
- data/README.md +25 -3
- data/gemfiles/rails_8_1.gemfile +10 -0
- data/lib/generators/rails/scaffold_controller_generator.rb +4 -0
- data/lib/generators/rails/templates/api_controller.rb +2 -2
- data/lib/generators/rails/templates/controller.rb +4 -4
- data/lib/jbuilder/jbuilder_template.rb +18 -21
- data/lib/jbuilder/key_formatter.rb +1 -1
- data/lib/jbuilder/version.rb +1 -1
- data/lib/jbuilder.rb +64 -50
- data/test/jbuilder_template_test.rb +26 -2
- data/test/jbuilder_test.rb +8 -0
- data/test/scaffold_api_controller_generator_test.rb +10 -2
- data/test/scaffold_controller_generator_test.rb +14 -4
- data/test/test_helper.rb +1 -1
- metadata +6 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 92a9e6859ee3e4a852307625394f53c94bbf3022adee7534c8f9746abe2e5c28
|
|
4
|
+
data.tar.gz: 9af5dfa8eb02f7a8c2dcbd9342ca5686fe33c2bcf171f34838599e64e23f9292
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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:
|
|
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
|
}
|
data/.github/workflows/ruby.yml
CHANGED
|
@@ -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@
|
|
58
|
+
- uses: actions/checkout@v6
|
|
49
59
|
|
|
50
60
|
- uses: ruby/setup-ruby@v1
|
|
51
61
|
with:
|
data/Appraisals
CHANGED
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": "
|
|
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
|
-
|
|
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
|
-
|
|
346
|
+
key names from the standard ruby_format to camelCase:
|
|
325
347
|
|
|
326
348
|
```ruby
|
|
327
349
|
json.key_format! camelize: :lower
|
|
@@ -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:
|
|
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:
|
|
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:
|
|
37
|
-
format.json { render json: <%= "@#{orm_instance.errors}" %>, status:
|
|
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:
|
|
50
|
-
format.json { render json: <%= "@#{orm_instance.errors}" %>, status:
|
|
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
|
|
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
|
|
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 =
|
|
122
|
+
def array!(collection = EMPTY_ARRAY, *args, &block)
|
|
123
123
|
options = args.first
|
|
124
124
|
|
|
125
|
-
if
|
|
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
|
-
|
|
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
|
|
137
|
+
if _partial_options?(options)
|
|
138
138
|
_set_inline_partial name, object, options.dup
|
|
139
139
|
else
|
|
140
|
-
|
|
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
|
-
|
|
173
|
+
_array if results.respond_to?(:body) && results.body.nil?
|
|
173
174
|
else
|
|
174
|
-
|
|
175
|
+
_array
|
|
175
176
|
end
|
|
176
177
|
else
|
|
177
|
-
|
|
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[
|
|
241
|
+
options[options[:as]] = object
|
|
245
242
|
_render_partial_with_options options
|
|
246
243
|
end
|
|
247
244
|
end
|
data/lib/jbuilder/version.rb
CHANGED
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
|
-
|
|
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 =
|
|
210
|
-
|
|
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
|
|
246
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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"]
|
data/test/jbuilder_test.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
37
|
-
|
|
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
|
-
|
|
44
|
-
|
|
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
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.
|
|
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.
|
|
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.
|
|
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:
|
|
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:
|