dry-types-json-schema 0.0.1 → 0.0.3

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: 33b4fa74b0afcb773080ea49a615812cb32e2d23fc0a498a5f5f8e3a5d729532
4
- data.tar.gz: d3cddcc29c63e0cdbef859ce7259155cc038e40374690b58c9e5eafaa103dd08
3
+ metadata.gz: b3c596c245aad8cb72bc10fbe904078ac01c7522d14c135b4a6645a7b7f5c769
4
+ data.tar.gz: 5fc2b32368fdcb1135b32fdb7d3db27c2104966ff371f020eea750df08588b4f
5
5
  SHA512:
6
- metadata.gz: 8b10cc09ab9ff064cf2d1fabaa859e1ca4442e247b368f3aa2c73c8f54f46e2b958898549d91c5bdf0884b97914249646a76280deddec3e0a1d7aa9243961bed
7
- data.tar.gz: 380965c43395d6742b673f9926d092fe746a593277b027edf4c90ee3f3995893ce94c838d1257d8e9eaa573dfc26ae78f304075e93d4ad4375c753c1a9397668
6
+ metadata.gz: 50ed4c3ca20040b4d88ae5ecf107c6ccfad7ae7242a724f1c3afd9f015f63a7b203966571af8699614881f7848a657d322554bbb541ff48244194662a73c40e1
7
+ data.tar.gz: 4810a8578cfecb91df93f66b4a9ad6748db42ef0ebd219c1dfce4eeb52c9fd6992027925579dea2f545b57b38f851eca600ef1eb898ba0a5f114e8d42c14537f
@@ -0,0 +1,12 @@
1
+ name: Test
2
+ on: [push, pull_request]
3
+ jobs:
4
+ test:
5
+ runs-on: ubuntu-latest
6
+ steps:
7
+ - uses: actions/checkout@v4
8
+ - uses: ruby/setup-ruby@v1
9
+ with:
10
+ ruby-version: '3.3'
11
+ bundler-cache: true
12
+ - run: bundle exec make test
data/.gitignore CHANGED
@@ -1 +1,2 @@
1
1
  coverage
2
+ *.gem
data/Gemfile.lock CHANGED
@@ -1,17 +1,19 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- dry-types-json-schema (0.0.1)
4
+ dry-types-json-schema (0.0.3)
5
5
  dry-types (~> 1.7.2)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
10
  ast (2.4.2)
11
+ attr_extras (7.1.0)
11
12
  base64 (0.2.0)
12
13
  bigdecimal (3.1.7)
13
14
  coderay (1.1.3)
14
15
  concurrent-ruby (1.2.3)
16
+ diff-lcs (1.5.1)
15
17
  docile (1.4.0)
16
18
  dry-core (1.0.1)
17
19
  concurrent-ruby (~> 1.0)
@@ -45,10 +47,13 @@ GEM
45
47
  language_server-protocol (3.17.0.3)
46
48
  method_source (1.0.0)
47
49
  minitest (5.22.3)
50
+ optimist (3.1.0)
48
51
  parallel (1.24.0)
49
52
  parser (3.3.0.5)
50
53
  ast (~> 2.4.1)
51
54
  racc
55
+ patience_diff (1.2.0)
56
+ optimist (~> 3.0)
52
57
  pry (0.14.2)
53
58
  coderay (~> 1.1)
54
59
  method_source (~> 1.0)
@@ -81,6 +86,10 @@ GEM
81
86
  simplecov_json_formatter (0.1.4)
82
87
  simpleidn (0.2.1)
83
88
  unf (~> 0.1.4)
89
+ super_diff (0.11.0)
90
+ attr_extras (>= 6.2.4)
91
+ diff-lcs
92
+ patience_diff
84
93
  unf (0.1.4)
85
94
  unf_ext
86
95
  unf_ext (0.0.9.1)
@@ -100,6 +109,7 @@ DEPENDENCIES
100
109
  rubocop (~> 1.62.1)
101
110
  rubocop-minitest (~> 0.35.0)
102
111
  simplecov (~> 0.22.0)
112
+ super_diff (~> 0.11.0)
103
113
 
104
114
  BUNDLED WITH
105
115
  2.5.3
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = "dry-types-json-schema"
5
- s.version = "0.0.1"
5
+ s.version = "0.0.3"
6
6
  s.summary = "Generate JSON Schema from dry-types"
7
7
  s.authors = ["elcuervo"]
8
8
  s.licenses = %w[MIT]
@@ -20,4 +20,5 @@ Gem::Specification.new do |s|
20
20
  s.add_development_dependency("simplecov", "~> 0.22.0")
21
21
  s.add_development_dependency("rubocop", "~> 1.62.1")
22
22
  s.add_development_dependency("rubocop-minitest", "~> 0.35.0")
23
+ s.add_development_dependency("super_diff", "~> 0.11.0")
23
24
  end
@@ -71,14 +71,13 @@ module Dry
71
71
  lt?: { exclusiveMaximum: IDENTITY },
72
72
  lteq?: { maximum: IDENTITY },
73
73
  format?: { format: INSPECT },
74
- included_in?: { enum: TO_ARRAY },
74
+ included_in?: { enum: TO_ARRAY }
75
75
  }.freeze
76
76
 
77
77
  # @return [Set] the set of required keys for the JSON Schema.
78
78
  #
79
79
  attr_reader :required
80
80
 
81
-
82
81
  # Initializes a new instance of the JSONSchema class.
83
82
  # @param root [Boolean] whether this schema is the root schema.
84
83
  # @param loose [Boolean] whether to ignore unknown predicates.
@@ -141,10 +140,7 @@ module Dry
141
140
  type, meta = node
142
141
 
143
142
  if opts.fetch(:key, false)
144
- if meta.any?
145
- @keys[opts[:key]] ||= {}
146
- @keys[opts[:key]].merge!(meta.slice(*ALLOWED_TYPES_META_OVERRIDES))
147
- end
143
+ visit_nominal_with_key(node, opts)
148
144
  else
149
145
  @keys.merge!(type: CLASS_TO_TYPE[type.to_s.to_sym])
150
146
  @keys.merge!(meta.slice(*ALLOWED_TYPES_META_OVERRIDES)) if meta.any?
@@ -175,20 +171,20 @@ module Dry
175
171
  @keys[ctx].merge!(definition)
176
172
  end
177
173
 
178
- def visit_sum(node, opts = EMPTY_HASH)
174
+ def visit_intersection(node, opts = EMPTY_HASH)
179
175
  *types, _ = node
180
176
 
181
- # FIXME: cleaner way to generate individual types
182
- #
183
- process = -> (type) do
184
- self.class.new
185
- .tap { |target| target.visit(type, opts) }
186
- .to_hash
187
- .values
188
- .first
189
- end
177
+ result = types.map { |type| compile_type(type) }
190
178
 
191
- result = types.map(&process).uniq
179
+ @keys[opts[:key]] = deep_merge_items(result)
180
+ end
181
+
182
+ def visit_sum(node, opts = EMPTY_HASH)
183
+ *types, _ = node
184
+
185
+ result = types
186
+ .map { |type| compile_value(type, opts.merge(sum: true)) }
187
+ .uniq
192
188
 
193
189
  return @keys[opts[:key]] = result.first if result.count == 1
194
190
 
@@ -216,7 +212,10 @@ module Dry
216
212
 
217
213
  def visit_struct(node, opts = EMPTY_HASH)
218
214
  _, schema = node
219
- visit(schema, opts)
215
+
216
+ return visit(schema, opts) unless opts[:key]
217
+
218
+ @keys[opts[:key]] = compile_type(schema)
220
219
  end
221
220
 
222
221
  def visit_array(node, opts = EMPTY_HASH)
@@ -254,6 +253,49 @@ module Dry
254
253
 
255
254
  visit(rest, opts.merge(key: name))
256
255
  end
256
+
257
+ private
258
+
259
+ def deep_merge_items(items)
260
+ items.reduce({}) do |current, target|
261
+ current.merge(target) do |_, from, to|
262
+ case [from.class, to.class]
263
+ when [::Hash, ::Hash]
264
+ deep_merge_items([from, to])
265
+ when [::Array, ::Array]
266
+ from | to
267
+ else
268
+ to
269
+ end
270
+ end
271
+ end
272
+ end
273
+
274
+ def compile_type(type, opts = EMPTY_HASH)
275
+ self.class.new
276
+ .tap { |target| target.visit(type, opts) }
277
+ .to_hash
278
+ end
279
+
280
+ def compile_value(type, opts = EMPTY_HASH)
281
+ compile_type(type, opts)
282
+ .values
283
+ .first
284
+ end
285
+
286
+ def visit_nominal_with_key(node, opts = EMPTY_HASH)
287
+ type, meta = node
288
+
289
+ if opts[:array] && !opts[:sum]
290
+ @keys[opts[:key]] ||= {}
291
+ @keys[opts[:key]].merge!(items: { type: CLASS_TO_TYPE[type.to_s.to_sym] })
292
+ end
293
+
294
+ if meta.any?
295
+ @keys[opts[:key]] ||= {}
296
+ @keys[opts[:key]].merge!(meta.slice(*ALLOWED_TYPES_META_OVERRIDES))
297
+ end
298
+ end
257
299
  end
258
300
 
259
301
  # The `Builder` module provides a method to generate a JSON Schema hash from dry-types definitions.
@@ -263,8 +305,8 @@ module Dry
263
305
  # @param options [Hash] Initialization options passed to `JSONSchema.new`
264
306
  # @return [Hash] The generated JSON Schema as a hash.
265
307
  #
266
- def json_schema(**)
267
- compiler = JSONSchema.new(**)
308
+ def json_schema(root: false, loose: false)
309
+ compiler = JSONSchema.new(root: root, loose: loose)
268
310
  compiler.call(to_ast)
269
311
  compiler.to_hash
270
312
  end
@@ -66,6 +66,13 @@ describe Dry::Types::JSONSchema do
66
66
  .constrained(format: /\A[\w+\-.]+@[a-z\d-]+(\.[a-z]+)*\.[a-z]+\z/i)
67
67
  .meta(description: "The internally used pattern")
68
68
 
69
+ ArrayOfStrings = Types::Array
70
+ .of(Types::String)
71
+ .constrained(min_size: 1)
72
+
73
+ BasicHash = Types::Hash.schema(name: Types::String)
74
+ ExtendedHash = Types::Hash.schema(age: Types::Integer) & BasicHash
75
+
69
76
  attribute :data, Types::String | Types::Hash
70
77
  attribute :string, Types::String.constrained(min_size: 1, max_size: 255)
71
78
  attribute :list, VariableList
@@ -76,6 +83,12 @@ describe Dry::Types::JSONSchema do
76
83
  attribute? :epoch, Types::Time
77
84
  attribute? :meta, Types::String.meta(format: :email)
78
85
  attribute? :enum, Types::String.enum(*%w[draft published archived])
86
+ attribute? :array, ArrayOfStrings
87
+ attribute? :inter, ExtendedHash
88
+
89
+ attribute? :nested do
90
+ attribute :deep, Types::Integer
91
+ end
79
92
  end
80
93
 
81
94
  let(:type) { StructTest }
@@ -146,6 +159,31 @@ describe Dry::Types::JSONSchema do
146
159
  enum: {
147
160
  type: :string,
148
161
  enum: %w[draft published archived]
162
+ },
163
+
164
+ array: {
165
+ type: :array,
166
+ minItems: 1,
167
+ items: { type: :string }
168
+ },
169
+
170
+ inter: {
171
+ type: :object,
172
+ properties: {
173
+ age: { type: :integer },
174
+ name: { type: :string }
175
+ },
176
+ required: %i[age name]
177
+ },
178
+
179
+ nested: {
180
+ type: :object,
181
+ properties: {
182
+ deep: { type: :integer }
183
+ },
184
+ required: [:deep],
185
+ title: "Title",
186
+ description: "description"
149
187
  }
150
188
  },
151
189
 
data/spec/spec_helper.rb CHANGED
@@ -6,6 +6,7 @@ SimpleCov.start
6
6
 
7
7
  require "minitest/autorun"
8
8
  require "json_schemer"
9
+ require "super_diff"
9
10
 
10
11
  require "dry/struct"
11
12
  require "dry/types"
@@ -13,13 +14,22 @@ require "dry/types/extensions"
13
14
 
14
15
  Dry::Types.load_extensions(:json_schema)
15
16
 
17
+ module Minitest::Assertions
18
+ def assert_equal_diff(expected, actual, msg = nil)
19
+ assert_equal(expected, actual, msg)
20
+ rescue Minitest::Assertion => e
21
+ puts SuperDiff::Differs::Main.call(expected, actual)
22
+ raise e
23
+ end
24
+ end
25
+
16
26
  class Minitest::Spec
17
27
  class << self
18
28
  def it_conforms_definition(&block)
19
29
  instance_exec(&block) if block
20
30
 
21
31
  describe "conforms the schema definition" do
22
- it { assert_equal type.json_schema, definition }
32
+ it { assert_equal_diff type.json_schema, definition }
23
33
  it { assert JSONSchemer.schema(type.json_schema.to_json).valid_schema? }
24
34
  end
25
35
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dry-types-json-schema
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - elcuervo
@@ -122,6 +122,20 @@ dependencies:
122
122
  - - "~>"
123
123
  - !ruby/object:Gem::Version
124
124
  version: 0.35.0
125
+ - !ruby/object:Gem::Dependency
126
+ name: super_diff
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: 0.11.0
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: 0.11.0
125
139
  description:
126
140
  email:
127
141
  - elcuervo@elcuervo.net
@@ -129,6 +143,7 @@ executables: []
129
143
  extensions: []
130
144
  extra_rdoc_files: []
131
145
  files:
146
+ - ".github/workflows/test.yml"
132
147
  - ".gitignore"
133
148
  - ".rubocop.yml"
134
149
  - Gemfile