jbuilder-schema 2.0.4 → 2.2.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/Gemfile.lock +107 -93
- data/README.md +0 -3
- data/lib/jbuilder/schema/renderer.rb +19 -5
- data/lib/jbuilder/schema/template.rb +89 -189
- data/lib/jbuilder/schema/version.rb +1 -1
- data/lib/jbuilder/schema.rb +15 -2
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 220162d597f3e157552d3ca783f9b4aba191f21ce2fb69172f24608d1439e840
|
|
4
|
+
data.tar.gz: 5ffd2a9d013ab15ff47d3d98dc4bcad5f79c735892f6733286d29032ee5d08f8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 86cedff5f98aa84fe8399361058ecff9cb29cbfe63f3b2b2d9894b238b87348692b7354effbef595e83e9b86c01ceeac7f0dd2499c3d20b66e6874653cfcec4f
|
|
7
|
+
data.tar.gz: a40c484ba860d6f50669c83535deb0047c908f47c82bb59f757752d4ec6e1d30f1872bcfa9cb4636cd8df6de6a5a73d6ac290397c9ca9c28c26df70a17d34725
|
data/Gemfile.lock
CHANGED
|
@@ -1,95 +1,98 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
jbuilder-schema (2.0
|
|
4
|
+
jbuilder-schema (2.2.0)
|
|
5
5
|
jbuilder
|
|
6
6
|
rails (>= 5.0.0)
|
|
7
7
|
|
|
8
8
|
GEM
|
|
9
9
|
remote: https://rubygems.org/
|
|
10
10
|
specs:
|
|
11
|
-
actioncable (7.0.4)
|
|
12
|
-
actionpack (= 7.0.4)
|
|
13
|
-
activesupport (= 7.0.4)
|
|
11
|
+
actioncable (7.0.4.3)
|
|
12
|
+
actionpack (= 7.0.4.3)
|
|
13
|
+
activesupport (= 7.0.4.3)
|
|
14
14
|
nio4r (~> 2.0)
|
|
15
15
|
websocket-driver (>= 0.6.1)
|
|
16
|
-
actionmailbox (7.0.4)
|
|
17
|
-
actionpack (= 7.0.4)
|
|
18
|
-
activejob (= 7.0.4)
|
|
19
|
-
activerecord (= 7.0.4)
|
|
20
|
-
activestorage (= 7.0.4)
|
|
21
|
-
activesupport (= 7.0.4)
|
|
16
|
+
actionmailbox (7.0.4.3)
|
|
17
|
+
actionpack (= 7.0.4.3)
|
|
18
|
+
activejob (= 7.0.4.3)
|
|
19
|
+
activerecord (= 7.0.4.3)
|
|
20
|
+
activestorage (= 7.0.4.3)
|
|
21
|
+
activesupport (= 7.0.4.3)
|
|
22
22
|
mail (>= 2.7.1)
|
|
23
23
|
net-imap
|
|
24
24
|
net-pop
|
|
25
25
|
net-smtp
|
|
26
|
-
actionmailer (7.0.4)
|
|
27
|
-
actionpack (= 7.0.4)
|
|
28
|
-
actionview (= 7.0.4)
|
|
29
|
-
activejob (= 7.0.4)
|
|
30
|
-
activesupport (= 7.0.4)
|
|
26
|
+
actionmailer (7.0.4.3)
|
|
27
|
+
actionpack (= 7.0.4.3)
|
|
28
|
+
actionview (= 7.0.4.3)
|
|
29
|
+
activejob (= 7.0.4.3)
|
|
30
|
+
activesupport (= 7.0.4.3)
|
|
31
31
|
mail (~> 2.5, >= 2.5.4)
|
|
32
32
|
net-imap
|
|
33
33
|
net-pop
|
|
34
34
|
net-smtp
|
|
35
35
|
rails-dom-testing (~> 2.0)
|
|
36
|
-
actionpack (7.0.4)
|
|
37
|
-
actionview (= 7.0.4)
|
|
38
|
-
activesupport (= 7.0.4)
|
|
36
|
+
actionpack (7.0.4.3)
|
|
37
|
+
actionview (= 7.0.4.3)
|
|
38
|
+
activesupport (= 7.0.4.3)
|
|
39
39
|
rack (~> 2.0, >= 2.2.0)
|
|
40
40
|
rack-test (>= 0.6.3)
|
|
41
41
|
rails-dom-testing (~> 2.0)
|
|
42
42
|
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
|
43
|
-
actiontext (7.0.4)
|
|
44
|
-
actionpack (= 7.0.4)
|
|
45
|
-
activerecord (= 7.0.4)
|
|
46
|
-
activestorage (= 7.0.4)
|
|
47
|
-
activesupport (= 7.0.4)
|
|
43
|
+
actiontext (7.0.4.3)
|
|
44
|
+
actionpack (= 7.0.4.3)
|
|
45
|
+
activerecord (= 7.0.4.3)
|
|
46
|
+
activestorage (= 7.0.4.3)
|
|
47
|
+
activesupport (= 7.0.4.3)
|
|
48
48
|
globalid (>= 0.6.0)
|
|
49
49
|
nokogiri (>= 1.8.5)
|
|
50
|
-
actionview (7.0.4)
|
|
51
|
-
activesupport (= 7.0.4)
|
|
50
|
+
actionview (7.0.4.3)
|
|
51
|
+
activesupport (= 7.0.4.3)
|
|
52
52
|
builder (~> 3.1)
|
|
53
53
|
erubi (~> 1.4)
|
|
54
54
|
rails-dom-testing (~> 2.0)
|
|
55
55
|
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
|
56
|
-
activejob (7.0.4)
|
|
57
|
-
activesupport (= 7.0.4)
|
|
56
|
+
activejob (7.0.4.3)
|
|
57
|
+
activesupport (= 7.0.4.3)
|
|
58
58
|
globalid (>= 0.3.6)
|
|
59
|
-
activemodel (7.0.4)
|
|
60
|
-
activesupport (= 7.0.4)
|
|
61
|
-
activerecord (7.0.4)
|
|
62
|
-
activemodel (= 7.0.4)
|
|
63
|
-
activesupport (= 7.0.4)
|
|
64
|
-
activestorage (7.0.4)
|
|
65
|
-
actionpack (= 7.0.4)
|
|
66
|
-
activejob (= 7.0.4)
|
|
67
|
-
activerecord (= 7.0.4)
|
|
68
|
-
activesupport (= 7.0.4)
|
|
59
|
+
activemodel (7.0.4.3)
|
|
60
|
+
activesupport (= 7.0.4.3)
|
|
61
|
+
activerecord (7.0.4.3)
|
|
62
|
+
activemodel (= 7.0.4.3)
|
|
63
|
+
activesupport (= 7.0.4.3)
|
|
64
|
+
activestorage (7.0.4.3)
|
|
65
|
+
actionpack (= 7.0.4.3)
|
|
66
|
+
activejob (= 7.0.4.3)
|
|
67
|
+
activerecord (= 7.0.4.3)
|
|
68
|
+
activesupport (= 7.0.4.3)
|
|
69
69
|
marcel (~> 1.0)
|
|
70
70
|
mini_mime (>= 1.1.0)
|
|
71
|
-
activesupport (7.0.4)
|
|
71
|
+
activesupport (7.0.4.3)
|
|
72
72
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
73
73
|
i18n (>= 1.6, < 2)
|
|
74
74
|
minitest (>= 5.1)
|
|
75
75
|
tzinfo (~> 2.0)
|
|
76
76
|
ast (2.4.2)
|
|
77
77
|
builder (3.2.4)
|
|
78
|
-
concurrent-ruby (1.
|
|
78
|
+
concurrent-ruby (1.2.2)
|
|
79
79
|
crass (1.0.6)
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
date (3.3.3)
|
|
81
|
+
erubi (1.12.0)
|
|
82
|
+
globalid (1.1.0)
|
|
82
83
|
activesupport (>= 5.0)
|
|
83
|
-
i18n (1.
|
|
84
|
+
i18n (1.13.0)
|
|
84
85
|
concurrent-ruby (~> 1.0)
|
|
85
86
|
jbuilder (2.11.5)
|
|
86
87
|
actionview (>= 5.0.0)
|
|
87
88
|
activesupport (>= 5.0.0)
|
|
88
|
-
json (2.6.
|
|
89
|
-
|
|
89
|
+
json (2.6.3)
|
|
90
|
+
language_server-protocol (3.17.0.3)
|
|
91
|
+
lint_roller (1.0.0)
|
|
92
|
+
loofah (2.20.0)
|
|
90
93
|
crass (~> 1.0.2)
|
|
91
94
|
nokogiri (>= 1.5.9)
|
|
92
|
-
mail (2.8.
|
|
95
|
+
mail (2.8.1)
|
|
93
96
|
mini_mime (>= 0.1.1)
|
|
94
97
|
net-imap
|
|
95
98
|
net-pop
|
|
@@ -97,88 +100,99 @@ GEM
|
|
|
97
100
|
marcel (1.0.2)
|
|
98
101
|
method_source (1.0.0)
|
|
99
102
|
mini_mime (1.1.2)
|
|
100
|
-
mini_portile2 (2.8.
|
|
101
|
-
minitest (5.
|
|
102
|
-
mocha (
|
|
103
|
-
|
|
103
|
+
mini_portile2 (2.8.2)
|
|
104
|
+
minitest (5.18.0)
|
|
105
|
+
mocha (2.0.2)
|
|
106
|
+
ruby2_keywords (>= 0.0.5)
|
|
107
|
+
net-imap (0.3.4)
|
|
108
|
+
date
|
|
104
109
|
net-protocol
|
|
105
110
|
net-pop (0.1.2)
|
|
106
111
|
net-protocol
|
|
107
|
-
net-protocol (0.2.
|
|
112
|
+
net-protocol (0.2.1)
|
|
108
113
|
timeout
|
|
109
114
|
net-smtp (0.3.3)
|
|
110
115
|
net-protocol
|
|
111
|
-
nio4r (2.5.
|
|
112
|
-
nokogiri (1.
|
|
116
|
+
nio4r (2.5.9)
|
|
117
|
+
nokogiri (1.14.3)
|
|
113
118
|
mini_portile2 (~> 2.8.0)
|
|
114
119
|
racc (~> 1.4)
|
|
115
|
-
parallel (1.
|
|
116
|
-
parser (3.
|
|
120
|
+
parallel (1.23.0)
|
|
121
|
+
parser (3.2.2.1)
|
|
117
122
|
ast (~> 2.4.1)
|
|
118
|
-
racc (1.6.
|
|
119
|
-
rack (2.2.
|
|
120
|
-
rack-test (2.0
|
|
123
|
+
racc (1.6.2)
|
|
124
|
+
rack (2.2.7)
|
|
125
|
+
rack-test (2.1.0)
|
|
121
126
|
rack (>= 1.3)
|
|
122
|
-
rails (7.0.4)
|
|
123
|
-
actioncable (= 7.0.4)
|
|
124
|
-
actionmailbox (= 7.0.4)
|
|
125
|
-
actionmailer (= 7.0.4)
|
|
126
|
-
actionpack (= 7.0.4)
|
|
127
|
-
actiontext (= 7.0.4)
|
|
128
|
-
actionview (= 7.0.4)
|
|
129
|
-
activejob (= 7.0.4)
|
|
130
|
-
activemodel (= 7.0.4)
|
|
131
|
-
activerecord (= 7.0.4)
|
|
132
|
-
activestorage (= 7.0.4)
|
|
133
|
-
activesupport (= 7.0.4)
|
|
127
|
+
rails (7.0.4.3)
|
|
128
|
+
actioncable (= 7.0.4.3)
|
|
129
|
+
actionmailbox (= 7.0.4.3)
|
|
130
|
+
actionmailer (= 7.0.4.3)
|
|
131
|
+
actionpack (= 7.0.4.3)
|
|
132
|
+
actiontext (= 7.0.4.3)
|
|
133
|
+
actionview (= 7.0.4.3)
|
|
134
|
+
activejob (= 7.0.4.3)
|
|
135
|
+
activemodel (= 7.0.4.3)
|
|
136
|
+
activerecord (= 7.0.4.3)
|
|
137
|
+
activestorage (= 7.0.4.3)
|
|
138
|
+
activesupport (= 7.0.4.3)
|
|
134
139
|
bundler (>= 1.15.0)
|
|
135
|
-
railties (= 7.0.4)
|
|
140
|
+
railties (= 7.0.4.3)
|
|
136
141
|
rails-dom-testing (2.0.3)
|
|
137
142
|
activesupport (>= 4.2.0)
|
|
138
143
|
nokogiri (>= 1.6)
|
|
139
|
-
rails-html-sanitizer (1.
|
|
140
|
-
loofah (~> 2.
|
|
141
|
-
railties (7.0.4)
|
|
142
|
-
actionpack (= 7.0.4)
|
|
143
|
-
activesupport (= 7.0.4)
|
|
144
|
+
rails-html-sanitizer (1.5.0)
|
|
145
|
+
loofah (~> 2.19, >= 2.19.1)
|
|
146
|
+
railties (7.0.4.3)
|
|
147
|
+
actionpack (= 7.0.4.3)
|
|
148
|
+
activesupport (= 7.0.4.3)
|
|
144
149
|
method_source
|
|
145
150
|
rake (>= 12.2)
|
|
146
151
|
thor (~> 1.0)
|
|
147
152
|
zeitwerk (~> 2.5)
|
|
148
153
|
rainbow (3.1.1)
|
|
149
154
|
rake (13.0.6)
|
|
150
|
-
regexp_parser (2.
|
|
155
|
+
regexp_parser (2.8.0)
|
|
151
156
|
rexml (3.2.5)
|
|
152
|
-
rubocop (1.
|
|
157
|
+
rubocop (1.50.2)
|
|
153
158
|
json (~> 2.3)
|
|
154
159
|
parallel (~> 1.10)
|
|
155
|
-
parser (>= 3.
|
|
160
|
+
parser (>= 3.2.0.0)
|
|
156
161
|
rainbow (>= 2.2.2, < 4.0)
|
|
157
162
|
regexp_parser (>= 1.8, < 3.0)
|
|
158
163
|
rexml (>= 3.2.5, < 4.0)
|
|
159
|
-
rubocop-ast (>= 1.
|
|
164
|
+
rubocop-ast (>= 1.28.0, < 2.0)
|
|
160
165
|
ruby-progressbar (~> 1.7)
|
|
161
|
-
unicode-display_width (>=
|
|
162
|
-
rubocop-ast (1.
|
|
163
|
-
parser (>= 3.
|
|
164
|
-
rubocop-performance (1.
|
|
166
|
+
unicode-display_width (>= 2.4.0, < 3.0)
|
|
167
|
+
rubocop-ast (1.28.1)
|
|
168
|
+
parser (>= 3.2.1.0)
|
|
169
|
+
rubocop-performance (1.16.0)
|
|
165
170
|
rubocop (>= 1.7.0, < 2.0)
|
|
166
171
|
rubocop-ast (>= 0.4.0)
|
|
167
|
-
ruby-progressbar (1.
|
|
168
|
-
|
|
172
|
+
ruby-progressbar (1.13.0)
|
|
173
|
+
ruby2_keywords (0.0.5)
|
|
174
|
+
sqlite3 (1.6.2)
|
|
169
175
|
mini_portile2 (~> 2.8.0)
|
|
170
|
-
standard (1.
|
|
171
|
-
|
|
172
|
-
|
|
176
|
+
standard (1.28.2)
|
|
177
|
+
language_server-protocol (~> 3.17.0.2)
|
|
178
|
+
lint_roller (~> 1.0)
|
|
179
|
+
rubocop (~> 1.50.2)
|
|
180
|
+
standard-custom (~> 1.0.0)
|
|
181
|
+
standard-performance (~> 1.0.1)
|
|
182
|
+
standard-custom (1.0.0)
|
|
183
|
+
lint_roller (~> 1.0)
|
|
184
|
+
standard-performance (1.0.1)
|
|
185
|
+
lint_roller (~> 1.0)
|
|
186
|
+
rubocop-performance (~> 1.16.0)
|
|
173
187
|
thor (1.2.1)
|
|
174
|
-
timeout (0.3.
|
|
175
|
-
tzinfo (2.0.
|
|
188
|
+
timeout (0.3.2)
|
|
189
|
+
tzinfo (2.0.6)
|
|
176
190
|
concurrent-ruby (~> 1.0)
|
|
177
|
-
unicode-display_width (2.2
|
|
191
|
+
unicode-display_width (2.4.2)
|
|
178
192
|
websocket-driver (0.7.5)
|
|
179
193
|
websocket-extensions (>= 0.1.0)
|
|
180
194
|
websocket-extensions (0.1.5)
|
|
181
|
-
zeitwerk (2.6.
|
|
195
|
+
zeitwerk (2.6.8)
|
|
182
196
|
|
|
183
197
|
PLATFORMS
|
|
184
198
|
ruby
|
|
@@ -191,4 +205,4 @@ DEPENDENCIES
|
|
|
191
205
|
standard (~> 1.3)
|
|
192
206
|
|
|
193
207
|
BUNDLED WITH
|
|
194
|
-
2.
|
|
208
|
+
2.4.10
|
data/README.md
CHANGED
|
@@ -2,9 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
Generate JSON Schema compatible with OpenAPI 3 specs from Jbuilder files
|
|
4
4
|
|
|
5
|
-
[](https://github.com/bullet-train-co/jbuilder-schema/actions)
|
|
6
|
-
[](https://github.com/bullet-train-co/jbuilder-schema/actions)
|
|
7
|
-
|
|
8
5
|
## Installation
|
|
9
6
|
|
|
10
7
|
In your Gemfile, put `gem "jbuilder-schema"` after Jbuilder:
|
|
@@ -3,6 +3,7 @@ require_relative "template"
|
|
|
3
3
|
|
|
4
4
|
class Jbuilder::Schema::Renderer
|
|
5
5
|
@@view_renderer = ActionView::Base.with_empty_template_cache
|
|
6
|
+
@@view_renderer.prefix_partial_path_with_controller_namespace = false
|
|
6
7
|
|
|
7
8
|
def initialize(paths, default_locals = nil)
|
|
8
9
|
@view_renderer = @@view_renderer.with_view_paths(paths)
|
|
@@ -18,19 +19,32 @@ class Jbuilder::Schema::Renderer
|
|
|
18
19
|
end
|
|
19
20
|
|
|
20
21
|
def render(object = nil, title: nil, description: nil, assigns: nil, **options)
|
|
21
|
-
if
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
@view_renderer.assign assigns if assigns
|
|
23
|
+
|
|
24
|
+
partial_path = %i[to_partial_path_for_jbuilder_schema to_partial_path].map { object.public_send(_1) if object.respond_to?(_1) }.compact.first
|
|
25
|
+
if partial_path
|
|
26
|
+
options[:partial] = partial_path
|
|
27
|
+
options[:object] = object
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
json = if partial_path
|
|
31
|
+
original_render(options.dup, options.dup)
|
|
32
|
+
else
|
|
33
|
+
original_render(object || options.dup, options.dup)
|
|
24
34
|
end
|
|
25
35
|
|
|
26
36
|
options[:locals] ||= {}
|
|
27
37
|
options[:locals].merge! @default_locals if @default_locals
|
|
28
|
-
options[:locals][:__jbuilder_schema_options] = {
|
|
38
|
+
options[:locals][:__jbuilder_schema_options] = {json: json, object: object, title: title, description: description}
|
|
29
39
|
|
|
30
|
-
@view_renderer.assign assigns if assigns
|
|
31
40
|
@view_renderer.render(options)
|
|
32
41
|
end
|
|
33
42
|
|
|
43
|
+
# Thin wrapper around the regular Jbuilder JSON output render, which also parses it into a hash.
|
|
44
|
+
def original_render(options = {}, locals = {})
|
|
45
|
+
JSON.parse @view_renderer.render(options, locals)
|
|
46
|
+
end
|
|
47
|
+
|
|
34
48
|
private
|
|
35
49
|
|
|
36
50
|
def normalize(schema)
|
|
@@ -5,9 +5,6 @@ require "active_support/inflections"
|
|
|
5
5
|
|
|
6
6
|
class Jbuilder::Schema
|
|
7
7
|
class Template < ::JbuilderTemplate
|
|
8
|
-
attr_reader :attributes, :type
|
|
9
|
-
attr_reader :model_scope
|
|
10
|
-
|
|
11
8
|
class Handler < ::JbuilderHandler
|
|
12
9
|
def self.call(template, source = nil)
|
|
13
10
|
super.sub("JbuilderTemplate.new(self", "Jbuilder::Schema::Template.build(self, local_assigns")
|
|
@@ -26,34 +23,34 @@ class Jbuilder::Schema
|
|
|
26
23
|
end
|
|
27
24
|
end
|
|
28
25
|
|
|
29
|
-
|
|
30
|
-
def
|
|
31
|
-
|
|
32
|
-
@scope = model&.name&.underscore&.pluralize
|
|
26
|
+
class Configuration < ::Struct.new(:object, :title, :description, keyword_init: true)
|
|
27
|
+
def self.build(object: nil, object_title: nil, object_description: nil, **)
|
|
28
|
+
new(object: object, title: object_title, description: object_description)
|
|
33
29
|
end
|
|
34
30
|
|
|
35
|
-
def
|
|
36
|
-
|
|
31
|
+
def title
|
|
32
|
+
super || translate(Jbuilder::Schema.title_name)
|
|
37
33
|
end
|
|
38
34
|
|
|
39
|
-
def
|
|
40
|
-
|
|
35
|
+
def description
|
|
36
|
+
super || translate(Jbuilder::Schema.description_name)
|
|
41
37
|
end
|
|
42
38
|
|
|
43
39
|
def translate_field(key)
|
|
44
|
-
|
|
40
|
+
translate("fields.#{key}.#{Jbuilder::Schema.description_name}")
|
|
45
41
|
end
|
|
46
|
-
end
|
|
47
42
|
|
|
48
|
-
|
|
49
|
-
@type = :object
|
|
50
|
-
@inline_array = false
|
|
51
|
-
@collection = false
|
|
43
|
+
private
|
|
52
44
|
|
|
53
|
-
|
|
45
|
+
def translate(key)
|
|
46
|
+
I18n.t(key, scope: @scope ||= object&.class&.name&.underscore&.pluralize)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
54
49
|
|
|
50
|
+
def initialize(context, json: nil, **options)
|
|
51
|
+
@json = json
|
|
52
|
+
@configuration = Configuration.new(**options)
|
|
55
53
|
super(context)
|
|
56
|
-
|
|
57
54
|
@ignore_nil = false
|
|
58
55
|
end
|
|
59
56
|
|
|
@@ -62,176 +59,116 @@ class Jbuilder::Schema
|
|
|
62
59
|
end
|
|
63
60
|
|
|
64
61
|
def schema!
|
|
65
|
-
|
|
62
|
+
if ([@attributes] + @attributes.each_value.grep(::Hash)).any? { _1[:type] == :array && _1.key?(:items) }
|
|
63
|
+
@attributes
|
|
64
|
+
else
|
|
65
|
+
_object(@attributes, _required!(@attributes.keys))
|
|
66
|
+
end.merge(example: @json).compact
|
|
66
67
|
end
|
|
67
68
|
|
|
68
|
-
def set!(key, value = BLANK, *args, schema:
|
|
69
|
-
|
|
70
|
-
if !_blank?(value)
|
|
71
|
-
# OBJECTS ARRAY:
|
|
72
|
-
# json.comments @article.comments { |comment| ... }
|
|
73
|
-
# { "comments": [ { ... }, { ... } ] }
|
|
74
|
-
_scope { array! value, &block }
|
|
75
|
-
else
|
|
76
|
-
# BLOCK:
|
|
77
|
-
# json.comments { ... }
|
|
78
|
-
# { "comments": ... }
|
|
79
|
-
@inline_array = true
|
|
80
|
-
|
|
81
|
-
_with_model_scope(**schema) do
|
|
82
|
-
_merge_block(key) { yield self }
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
elsif args.empty?
|
|
86
|
-
if ::Jbuilder === value
|
|
87
|
-
# ATTRIBUTE1:
|
|
88
|
-
# json.age 32
|
|
89
|
-
# json.person another_jbuilder
|
|
90
|
-
# { "age": 32, "person": { ... }
|
|
91
|
-
_schema(key, _format_keys(value.attributes!), **schema)
|
|
92
|
-
elsif _is_collection_array?(value)
|
|
93
|
-
# ATTRIBUTE2:
|
|
94
|
-
_scope { array! value }
|
|
95
|
-
# json.articles @articles
|
|
96
|
-
else
|
|
97
|
-
# json.age 32
|
|
98
|
-
# { "age": 32 }
|
|
99
|
-
_schema(key, _format_keys(value), **schema)
|
|
100
|
-
end
|
|
101
|
-
elsif _is_collection?(value)
|
|
102
|
-
# COLLECTION:
|
|
103
|
-
# json.comments @article.comments, :content, :created_at
|
|
104
|
-
# { "comments": [ { "content": "hello", "created_at": "..." }, { "content": "world", "created_at": "..." } ] }
|
|
105
|
-
@inline_array = true
|
|
106
|
-
@collection = true
|
|
107
|
-
|
|
108
|
-
_scope { array! value, *args }
|
|
109
|
-
else
|
|
110
|
-
# EXTRACT!:
|
|
111
|
-
# json.author @article.creator, :name, :email_address
|
|
112
|
-
# { "author": { "name": "David", "email_address": "david@loudthinking.com" } }
|
|
113
|
-
_with_model_scope(**schema) do
|
|
114
|
-
_merge_block(key) { extract! value, *args, schema: schema }
|
|
115
|
-
end
|
|
116
|
-
end
|
|
69
|
+
def set!(key, value = BLANK, *args, schema: nil, **options, &block)
|
|
70
|
+
old_configuration, @configuration = @configuration, Configuration.build(**schema) if schema&.dig(:object)
|
|
117
71
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
end
|
|
72
|
+
_with_schema_overrides(key => schema) do
|
|
73
|
+
keys = args.presence || _extract_possible_keys(value)
|
|
121
74
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
75
|
+
# Detect `json.articles user.articles` to override Jbuilder's logic, which wouldn't hit `array!` and set a `type: :array, items: {"$ref": "#/components/schemas/article"}` ref.
|
|
76
|
+
if block.nil? && keys.blank? && _is_collection?(value) && (value.empty? || value.all? { _is_active_model?(_1) })
|
|
77
|
+
_set_value(key, _scope { _set_ref(key.to_s.singularize, array: true) })
|
|
78
|
+
else
|
|
79
|
+
super(key, value, *keys, **options, &block)
|
|
80
|
+
end
|
|
127
81
|
end
|
|
82
|
+
ensure
|
|
83
|
+
@configuration = old_configuration if old_configuration
|
|
128
84
|
end
|
|
85
|
+
alias_method :method_missing, :set! # TODO: Remove once Jbuilder passes keyword arguments along to `set!` in its `method_missing`.
|
|
129
86
|
|
|
130
|
-
def array!(collection = [], *args, schema:
|
|
87
|
+
def array!(collection = [], *args, schema: nil, **options, &block)
|
|
131
88
|
if _partial_options?(options)
|
|
132
|
-
|
|
133
|
-
_set_ref(options[:partial].split("/").last)
|
|
89
|
+
partial!(collection: collection, **options)
|
|
134
90
|
else
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
if @inline_array
|
|
138
|
-
@attributes = {}
|
|
139
|
-
_set_value(:type, :array)
|
|
140
|
-
_set_value(:items, array)
|
|
141
|
-
elsif _is_collection_array?(array)
|
|
142
|
-
@attributes = {}
|
|
143
|
-
@inline_array = true
|
|
144
|
-
@collection = true
|
|
145
|
-
array! array, *array.first&.attribute_names(&:to_sym)
|
|
146
|
-
else
|
|
147
|
-
@type = :array
|
|
148
|
-
@attributes = {}
|
|
149
|
-
_set_value(:items, array)
|
|
91
|
+
_with_schema_overrides(schema) do
|
|
92
|
+
_attributes.merge! type: :array, items: _scope { super(collection, *args, &block) }
|
|
150
93
|
end
|
|
151
94
|
end
|
|
152
95
|
end
|
|
153
96
|
|
|
154
|
-
def
|
|
155
|
-
|
|
97
|
+
def extract!(object, *attributes, schema: nil)
|
|
98
|
+
_with_schema_overrides(schema) { super(object, *attributes) }
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def partial!(model = nil, *args, partial: nil, collection: nil, **options)
|
|
102
|
+
if args.none? && _is_active_model?(model)
|
|
156
103
|
# TODO: Find where it is being used
|
|
157
|
-
_render_active_model_partial
|
|
158
|
-
elsif args.first.is_a?(::Hash)
|
|
159
|
-
_set_ref(args.first[:partial].split("/").last)
|
|
104
|
+
_render_active_model_partial model
|
|
160
105
|
else
|
|
161
|
-
|
|
162
|
-
_set_ref(args.first&.split("/")&.last)
|
|
106
|
+
_set_ref(partial || model, array: collection&.any?)
|
|
163
107
|
end
|
|
164
108
|
end
|
|
165
109
|
|
|
166
110
|
def merge!(object)
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
if hash_or_array.is_a?(::Hash)
|
|
170
|
-
hash_or_array = hash_or_array.each_with_object({}) do |(key, value), a|
|
|
171
|
-
result = _schema(key, value)
|
|
172
|
-
result = _set_description(key, result) if model_scope.model
|
|
173
|
-
a[key] = result
|
|
174
|
-
end
|
|
175
|
-
end
|
|
176
|
-
@attributes = _merge_values(@attributes, hash_or_array)
|
|
111
|
+
object = object.to_h { [_1, _schema(_1, _2)] } if object.is_a?(::Hash)
|
|
112
|
+
super
|
|
177
113
|
end
|
|
178
114
|
|
|
179
115
|
def cache!(key = nil, **options)
|
|
180
116
|
yield # TODO: Our schema generation breaks Jbuilder's fragment caching.
|
|
181
117
|
end
|
|
182
118
|
|
|
183
|
-
def method_missing(*args, **options, &block) # standard:disable Style/MissingRespondToMissing
|
|
184
|
-
# TODO: Remove once Jbuilder passes keyword arguments along to `set!` in its `method_missing`.
|
|
185
|
-
set!(*args, **options, &block)
|
|
186
|
-
end
|
|
187
|
-
|
|
188
119
|
private
|
|
189
120
|
|
|
190
|
-
def
|
|
191
|
-
|
|
121
|
+
def _extract_possible_keys(value)
|
|
122
|
+
value.first.as_json.keys if _is_collection?(value) && _is_active_model?(value.first)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def _with_schema_overrides(overrides)
|
|
126
|
+
old_schema_overrides, @schema_overrides = @schema_overrides, overrides if overrides
|
|
192
127
|
yield
|
|
193
128
|
ensure
|
|
194
|
-
@
|
|
129
|
+
@schema_overrides = old_schema_overrides if overrides
|
|
195
130
|
end
|
|
196
131
|
|
|
197
|
-
def _object(
|
|
132
|
+
def _object(attributes, required)
|
|
198
133
|
{
|
|
199
134
|
type: :object,
|
|
200
|
-
title:
|
|
201
|
-
description:
|
|
202
|
-
required:
|
|
203
|
-
properties: attributes
|
|
135
|
+
title: @configuration.title,
|
|
136
|
+
description: @configuration.description,
|
|
137
|
+
required: required,
|
|
138
|
+
properties: _nullify_non_required_types(attributes, required)
|
|
204
139
|
}
|
|
205
140
|
end
|
|
206
141
|
|
|
142
|
+
def _nullify_non_required_types(attributes, required)
|
|
143
|
+
attributes.transform_values! { _1[:type] = [_1[:type], "null"] unless required.include?(attributes.key(_1)); _1 }
|
|
144
|
+
end
|
|
145
|
+
|
|
207
146
|
def _set_description(key, value)
|
|
208
|
-
|
|
209
|
-
description =
|
|
210
|
-
value = {description: description}.merge! value
|
|
147
|
+
if !value.key?(:description) && @configuration.object
|
|
148
|
+
value[:description] = @configuration.translate_field(key)
|
|
211
149
|
end
|
|
212
|
-
value
|
|
213
150
|
end
|
|
214
151
|
|
|
215
|
-
def _set_ref(
|
|
216
|
-
|
|
152
|
+
def _set_ref(part, array: false)
|
|
153
|
+
ref = {"$ref": "#/#{::Jbuilder::Schema.components_path}/#{part.split("/").last}"}
|
|
217
154
|
|
|
218
|
-
if
|
|
219
|
-
|
|
220
|
-
_set_value(:type, :array)
|
|
221
|
-
_set_value(:items, {:$ref => component_path})
|
|
222
|
-
else
|
|
223
|
-
_set_value(:type, :object)
|
|
224
|
-
_set_value(:$ref, component_path)
|
|
225
|
-
end
|
|
155
|
+
if array
|
|
156
|
+
_attributes.merge! type: :array, items: ref
|
|
226
157
|
else
|
|
227
|
-
|
|
228
|
-
_set_value(:items, {:$ref => component_path})
|
|
158
|
+
_attributes.merge! type: :object, **ref
|
|
229
159
|
end
|
|
230
160
|
end
|
|
231
161
|
|
|
162
|
+
def _attributes
|
|
163
|
+
@attributes = {} if _blank?
|
|
164
|
+
@attributes
|
|
165
|
+
end
|
|
166
|
+
|
|
232
167
|
FORMATS = {::DateTime => "date-time", ::ActiveSupport::TimeWithZone => "date-time", ::Date => "date", ::Time => "time"}
|
|
233
168
|
|
|
234
169
|
def _schema(key, value, **options)
|
|
170
|
+
options = @schema_overrides&.dig(key).to_h if options.empty?
|
|
171
|
+
|
|
235
172
|
unless options[:type]
|
|
236
173
|
options[:type] = _primitive_type value
|
|
237
174
|
|
|
@@ -243,15 +180,16 @@ class Jbuilder::Schema
|
|
|
243
180
|
format = FORMATS[value.class] and options[:format] ||= format
|
|
244
181
|
end
|
|
245
182
|
|
|
246
|
-
if (
|
|
183
|
+
if (klass = @configuration.object&.class) && (defined_enum = klass.try(:defined_enums)&.dig(key.to_s))
|
|
247
184
|
options[:enum] = defined_enum.keys
|
|
248
185
|
end
|
|
249
186
|
|
|
187
|
+
_set_description key, options
|
|
250
188
|
options
|
|
251
189
|
end
|
|
252
190
|
|
|
253
|
-
def _primitive_type(
|
|
254
|
-
case
|
|
191
|
+
def _primitive_type(value)
|
|
192
|
+
case value
|
|
255
193
|
when ::Array then :array
|
|
256
194
|
when ::Float, ::BigDecimal then :number
|
|
257
195
|
when true, false then :boolean
|
|
@@ -261,47 +199,21 @@ class Jbuilder::Schema
|
|
|
261
199
|
end
|
|
262
200
|
end
|
|
263
201
|
|
|
264
|
-
def
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
_map_collection(collection, &block)
|
|
269
|
-
elsif args.any?
|
|
270
|
-
_map_collection(collection) { |element| extract! element, *args, schema: schema }
|
|
271
|
-
else
|
|
272
|
-
_format_keys(collection.to_a)
|
|
273
|
-
end
|
|
274
|
-
end
|
|
275
|
-
|
|
276
|
-
def _is_collection_array?(object)
|
|
277
|
-
object.is_a?(::Array) && object.all? { _is_active_model? _1 }
|
|
202
|
+
def _set_value(key, value)
|
|
203
|
+
value = _schema(key, value) unless value.is_a?(::Hash) && value.key?(:type)
|
|
204
|
+
_set_description(key, value)
|
|
205
|
+
super
|
|
278
206
|
end
|
|
279
207
|
|
|
280
208
|
def _required!(keys)
|
|
281
|
-
presence_validated_attributes =
|
|
282
|
-
keys & [_key(:id), *presence_validated_attributes.
|
|
209
|
+
presence_validated_attributes = @configuration.object&.class.try(:validators).to_a.flat_map { _1.attributes if _1.is_a?(::ActiveRecord::Validations::PresenceValidator) }
|
|
210
|
+
keys & [_key(:id), *presence_validated_attributes.flat_map { [_key(_1), _key("#{_1}_id")] }]
|
|
283
211
|
end
|
|
284
212
|
|
|
285
213
|
###
|
|
286
214
|
# Jbuilder methods
|
|
287
215
|
###
|
|
288
216
|
|
|
289
|
-
def _extract_hash_values(object, attributes, schema:)
|
|
290
|
-
attributes.each do |key|
|
|
291
|
-
result = _schema(key, _format_keys(object.fetch(key)), **schema[key] || {})
|
|
292
|
-
result = _set_description(key, result) if model_scope.model
|
|
293
|
-
_set_value key, result
|
|
294
|
-
end
|
|
295
|
-
end
|
|
296
|
-
|
|
297
|
-
def _extract_method_values(object, attributes, schema:)
|
|
298
|
-
attributes.each do |key|
|
|
299
|
-
result = _schema(key, _format_keys(object.public_send(key)), **schema[key] || {})
|
|
300
|
-
result = _set_description(key, result) if model_scope.model
|
|
301
|
-
_set_value key, result
|
|
302
|
-
end
|
|
303
|
-
end
|
|
304
|
-
|
|
305
217
|
def _map_collection(collection)
|
|
306
218
|
super.first
|
|
307
219
|
end
|
|
@@ -311,20 +223,8 @@ class Jbuilder::Schema
|
|
|
311
223
|
raise NullError.build(key) if current_value.nil?
|
|
312
224
|
|
|
313
225
|
value = _scope { yield self }
|
|
314
|
-
value = _object(
|
|
226
|
+
value = _object(value, _required!(value.keys)) unless value[:type] == :array || value.key?(:$ref)
|
|
315
227
|
_merge_values(current_value, value)
|
|
316
228
|
end
|
|
317
229
|
end
|
|
318
230
|
end
|
|
319
|
-
|
|
320
|
-
class Jbuilder
|
|
321
|
-
module SkipFormatting
|
|
322
|
-
SCHEMA_KEYS = %i[type items properties]
|
|
323
|
-
|
|
324
|
-
def format(key)
|
|
325
|
-
SCHEMA_KEYS.include?(key) ? key : super
|
|
326
|
-
end
|
|
327
|
-
end
|
|
328
|
-
|
|
329
|
-
KeyFormatter.prepend SkipFormatting
|
|
330
|
-
end
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
# We can't use the standard `Jbuilder::Schema::VERSION =` because
|
|
2
2
|
# `Jbuilder` isn't a regular module namespace, but a class …which also loads Active Support.
|
|
3
3
|
# So we use trickery, and assign the proper version once `jbuilder/schema.rb` is loaded.
|
|
4
|
-
JBUILDER_SCHEMA_VERSION = "2.0
|
|
4
|
+
JBUILDER_SCHEMA_VERSION = "2.2.0"
|
data/lib/jbuilder/schema.rb
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "
|
|
3
|
+
require "jbuilder"
|
|
4
4
|
require "jbuilder/schema/version"
|
|
5
|
+
require "active_support/core_ext/module/delegation"
|
|
5
6
|
|
|
6
7
|
class Jbuilder::Schema
|
|
7
|
-
VERSION =
|
|
8
|
+
VERSION = JBUILDER_SCHEMA_VERSION # See `jbuilder/schema/version.rb`
|
|
8
9
|
|
|
9
10
|
module IgnoreSchemaMeta
|
|
10
11
|
::Jbuilder.prepend self
|
|
@@ -12,6 +13,18 @@ class Jbuilder::Schema
|
|
|
12
13
|
def method_missing(*args, schema: nil, **options, &block) # standard:disable Style/MissingRespondToMissing
|
|
13
14
|
super(*args, **options, &block)
|
|
14
15
|
end
|
|
16
|
+
|
|
17
|
+
def set!(*args, schema: nil, **options, &block)
|
|
18
|
+
super(*args, **options, &block)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def array!(*args, schema: nil, **options, &block)
|
|
22
|
+
super(*args, **options, &block)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def extract!(*args, schema: nil, **options, &block)
|
|
26
|
+
super(*args, **options, &block)
|
|
27
|
+
end
|
|
15
28
|
end
|
|
16
29
|
|
|
17
30
|
singleton_class.alias_method :configure, :tap
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: jbuilder-schema
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.0
|
|
4
|
+
version: 2.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yuri Sidorov
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2023-05-05 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: jbuilder
|
|
@@ -76,7 +76,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
76
76
|
- !ruby/object:Gem::Version
|
|
77
77
|
version: '0'
|
|
78
78
|
requirements: []
|
|
79
|
-
rubygems_version: 3.
|
|
79
|
+
rubygems_version: 3.4.10
|
|
80
80
|
signing_key:
|
|
81
81
|
specification_version: 4
|
|
82
82
|
summary: Generate JSON Schema from Jbuilder files
|