dry-types-json-schema 0.0.1 → 0.0.3

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: 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