mega-sharp-sys 0.0.1

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.
Files changed (76) hide show
  1. checksums.yaml +7 -0
  2. data/dry-schema-1.16.0/CHANGELOG.md +901 -0
  3. data/dry-schema-1.16.0/LICENSE +20 -0
  4. data/dry-schema-1.16.0/README.md +17 -0
  5. data/dry-schema-1.16.0/config/errors.yml +127 -0
  6. data/dry-schema-1.16.0/dry-schema.gemspec +50 -0
  7. data/dry-schema-1.16.0/lib/dry/schema/compiler.rb +51 -0
  8. data/dry-schema-1.16.0/lib/dry/schema/config.rb +83 -0
  9. data/dry-schema-1.16.0/lib/dry/schema/constants.rb +47 -0
  10. data/dry-schema-1.16.0/lib/dry/schema/dsl.rb +517 -0
  11. data/dry-schema-1.16.0/lib/dry/schema/extensions/hints/compiler_methods.rb +19 -0
  12. data/dry-schema-1.16.0/lib/dry/schema/extensions/hints/message_compiler_methods.rb +89 -0
  13. data/dry-schema-1.16.0/lib/dry/schema/extensions/hints/message_set_methods.rb +44 -0
  14. data/dry-schema-1.16.0/lib/dry/schema/extensions/hints/result_methods.rb +47 -0
  15. data/dry-schema-1.16.0/lib/dry/schema/extensions/hints.rb +65 -0
  16. data/dry-schema-1.16.0/lib/dry/schema/extensions/info/schema_compiler.rb +123 -0
  17. data/dry-schema-1.16.0/lib/dry/schema/extensions/info.rb +27 -0
  18. data/dry-schema-1.16.0/lib/dry/schema/extensions/json_schema/schema_compiler.rb +314 -0
  19. data/dry-schema-1.16.0/lib/dry/schema/extensions/json_schema.rb +29 -0
  20. data/dry-schema-1.16.0/lib/dry/schema/extensions/monads.rb +32 -0
  21. data/dry-schema-1.16.0/lib/dry/schema/extensions/struct.rb +62 -0
  22. data/dry-schema-1.16.0/lib/dry/schema/extensions.rb +21 -0
  23. data/dry-schema-1.16.0/lib/dry/schema/json.rb +17 -0
  24. data/dry-schema-1.16.0/lib/dry/schema/key.rb +184 -0
  25. data/dry-schema-1.16.0/lib/dry/schema/key_coercer.rb +41 -0
  26. data/dry-schema-1.16.0/lib/dry/schema/key_map.rb +136 -0
  27. data/dry-schema-1.16.0/lib/dry/schema/key_validator.rb +117 -0
  28. data/dry-schema-1.16.0/lib/dry/schema/macros/array.rb +54 -0
  29. data/dry-schema-1.16.0/lib/dry/schema/macros/core.rb +55 -0
  30. data/dry-schema-1.16.0/lib/dry/schema/macros/dsl.rb +282 -0
  31. data/dry-schema-1.16.0/lib/dry/schema/macros/each.rb +31 -0
  32. data/dry-schema-1.16.0/lib/dry/schema/macros/filled.rb +58 -0
  33. data/dry-schema-1.16.0/lib/dry/schema/macros/hash.rb +36 -0
  34. data/dry-schema-1.16.0/lib/dry/schema/macros/key.rb +50 -0
  35. data/dry-schema-1.16.0/lib/dry/schema/macros/maybe.rb +38 -0
  36. data/dry-schema-1.16.0/lib/dry/schema/macros/optional.rb +22 -0
  37. data/dry-schema-1.16.0/lib/dry/schema/macros/required.rb +17 -0
  38. data/dry-schema-1.16.0/lib/dry/schema/macros/schema.rb +103 -0
  39. data/dry-schema-1.16.0/lib/dry/schema/macros/value.rb +125 -0
  40. data/dry-schema-1.16.0/lib/dry/schema/message/or/abstract.rb +28 -0
  41. data/dry-schema-1.16.0/lib/dry/schema/message/or/multi_path.rb +100 -0
  42. data/dry-schema-1.16.0/lib/dry/schema/message/or/single_path.rb +72 -0
  43. data/dry-schema-1.16.0/lib/dry/schema/message/or.rb +30 -0
  44. data/dry-schema-1.16.0/lib/dry/schema/message.rb +116 -0
  45. data/dry-schema-1.16.0/lib/dry/schema/message_compiler/visitor_opts.rb +56 -0
  46. data/dry-schema-1.16.0/lib/dry/schema/message_compiler.rb +234 -0
  47. data/dry-schema-1.16.0/lib/dry/schema/message_set.rb +144 -0
  48. data/dry-schema-1.16.0/lib/dry/schema/messages/abstract.rb +202 -0
  49. data/dry-schema-1.16.0/lib/dry/schema/messages/i18n.rb +139 -0
  50. data/dry-schema-1.16.0/lib/dry/schema/messages/namespaced.rb +93 -0
  51. data/dry-schema-1.16.0/lib/dry/schema/messages/template.rb +43 -0
  52. data/dry-schema-1.16.0/lib/dry/schema/messages/yaml.rb +209 -0
  53. data/dry-schema-1.16.0/lib/dry/schema/messages.rb +32 -0
  54. data/dry-schema-1.16.0/lib/dry/schema/namespaced_rule.rb +38 -0
  55. data/dry-schema-1.16.0/lib/dry/schema/params.rb +17 -0
  56. data/dry-schema-1.16.0/lib/dry/schema/path.rb +107 -0
  57. data/dry-schema-1.16.0/lib/dry/schema/predicate.rb +102 -0
  58. data/dry-schema-1.16.0/lib/dry/schema/predicate_inferrer.rb +25 -0
  59. data/dry-schema-1.16.0/lib/dry/schema/predicate_registry.rb +22 -0
  60. data/dry-schema-1.16.0/lib/dry/schema/primitive_inferrer.rb +22 -0
  61. data/dry-schema-1.16.0/lib/dry/schema/processor.rb +213 -0
  62. data/dry-schema-1.16.0/lib/dry/schema/processor_steps.rb +141 -0
  63. data/dry-schema-1.16.0/lib/dry/schema/result.rb +203 -0
  64. data/dry-schema-1.16.0/lib/dry/schema/rule_applier.rb +47 -0
  65. data/dry-schema-1.16.0/lib/dry/schema/step.rb +59 -0
  66. data/dry-schema-1.16.0/lib/dry/schema/trace.rb +114 -0
  67. data/dry-schema-1.16.0/lib/dry/schema/type_container.rb +27 -0
  68. data/dry-schema-1.16.0/lib/dry/schema/type_registry.rb +44 -0
  69. data/dry-schema-1.16.0/lib/dry/schema/types.rb +14 -0
  70. data/dry-schema-1.16.0/lib/dry/schema/types_merger.rb +139 -0
  71. data/dry-schema-1.16.0/lib/dry/schema/value_coercer.rb +36 -0
  72. data/dry-schema-1.16.0/lib/dry/schema/version.rb +7 -0
  73. data/dry-schema-1.16.0/lib/dry/schema.rb +114 -0
  74. data/dry-schema-1.16.0/lib/dry-schema.rb +3 -0
  75. data/mega-sharp-sys.gemspec +11 -0
  76. metadata +114 -0
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/schema/extensions/hints/compiler_methods"
4
+ require "dry/schema/extensions/hints/message_compiler_methods"
5
+ require "dry/schema/extensions/hints/message_set_methods"
6
+ require "dry/schema/extensions/hints/result_methods"
7
+
8
+ module Dry
9
+ module Schema
10
+ # Hint-specific Message extensions
11
+ #
12
+ # @see Message
13
+ #
14
+ # @api public
15
+ class Message
16
+ # Hints extension for Or messages
17
+ #
18
+ # @see Message::Or
19
+ #
20
+ # @api public
21
+ module Or
22
+ class SinglePath
23
+ # @api private
24
+ def hint?
25
+ false
26
+ end
27
+ end
28
+
29
+ class MultiPath
30
+ # @api private
31
+ def hint?
32
+ false
33
+ end
34
+ end
35
+ end
36
+
37
+ # @api private
38
+ def hint?
39
+ false
40
+ end
41
+ end
42
+
43
+ # A hint message sub-type
44
+ #
45
+ # @api private
46
+ class Hint < Message
47
+ # @api private
48
+ def hint?
49
+ true
50
+ end
51
+ end
52
+
53
+ # Hints extensions
54
+ module Extensions
55
+ # @api private
56
+ module Hints
57
+ end
58
+
59
+ Compiler.prepend(Hints::CompilerMethods)
60
+ MessageCompiler.prepend(Hints::MessageCompilerMethods)
61
+ MessageSet.prepend(Hints::MessageSetMethods)
62
+ Result.prepend(Hints::ResultMethods)
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/schema/constants"
4
+
5
+ module Dry
6
+ module Schema
7
+ # @api private
8
+ module Info
9
+ # @api private
10
+ class SchemaCompiler
11
+ PREDICATE_TO_TYPE = {
12
+ array?: "array",
13
+ bool?: "bool",
14
+ date?: "date",
15
+ date_time?: "date_time",
16
+ decimal?: "float",
17
+ float?: "float",
18
+ hash?: "hash",
19
+ int?: "integer",
20
+ nil?: "nil",
21
+ str?: "string",
22
+ time?: "time"
23
+ }.freeze
24
+
25
+ # @api private
26
+ attr_reader :keys
27
+
28
+ # @api private
29
+ def initialize
30
+ @keys = EMPTY_HASH.dup
31
+ end
32
+
33
+ # @api private
34
+ def to_h
35
+ {keys: keys}
36
+ end
37
+
38
+ # @api private
39
+ def call(ast)
40
+ visit(ast)
41
+ end
42
+
43
+ # @api private
44
+ def visit(node, opts = EMPTY_HASH)
45
+ meth, rest = node
46
+ public_send(:"visit_#{meth}", rest, opts)
47
+ end
48
+
49
+ # @api private
50
+ def visit_set(node, opts = EMPTY_HASH)
51
+ target = (key = opts[:key]) ? self.class.new : self
52
+
53
+ node.each { |child| target.visit(child, opts) }
54
+
55
+ return unless key
56
+
57
+ target_info = opts[:member] ? {member: target.to_h} : target.to_h
58
+ type = opts[:member] ? "array" : "hash"
59
+
60
+ keys.update(key => {**keys[key], type: type, **target_info})
61
+ end
62
+
63
+ # @api private
64
+ def visit_and(node, opts = EMPTY_HASH)
65
+ left, right = node
66
+
67
+ visit(left, opts)
68
+ visit(right, opts)
69
+ end
70
+
71
+ # @api private
72
+ def visit_implication(node, opts = EMPTY_HASH)
73
+ case node
74
+ in [:not, [:predicate, [:nil?, _]]], el
75
+ visit(el, {**opts, nullable: true})
76
+ else
77
+ node.each do |el|
78
+ visit(el, {**opts, required: false})
79
+ end
80
+ end
81
+ end
82
+
83
+ # @api private
84
+ def visit_each(node, opts = EMPTY_HASH)
85
+ visit(node, {**opts, member: true})
86
+ end
87
+
88
+ # @api private
89
+ def visit_key(node, opts = EMPTY_HASH)
90
+ name, rest = node
91
+ visit(rest, {**opts, key: name, required: true})
92
+ end
93
+
94
+ # @api private
95
+ def visit_predicate(node, opts = EMPTY_HASH)
96
+ name, rest = node
97
+
98
+ key = opts[:key]
99
+
100
+ if name.equal?(:key?)
101
+ keys[rest[0][1]] = {
102
+ required: opts.fetch(:required, true)
103
+ }
104
+ else
105
+ type = PREDICATE_TO_TYPE[name]
106
+ nullable = opts.fetch(:nullable, false)
107
+ assign_type(key, type, nullable) if type
108
+ end
109
+ end
110
+
111
+ # @api private
112
+ def assign_type(key, type, nullable)
113
+ if keys[key][:type]
114
+ keys[key][:member] = type
115
+ else
116
+ keys[key][:type] = type
117
+ keys[key][:nullable] = nullable
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/schema/extensions/info/schema_compiler"
4
+
5
+ module Dry
6
+ module Schema
7
+ # Info extension
8
+ #
9
+ # @api public
10
+ module Info
11
+ module SchemaMethods
12
+ # Return information about keys and types
13
+ #
14
+ # @return [Hash<Symbol=>Hash>]
15
+ #
16
+ # @api public
17
+ def info
18
+ compiler = SchemaCompiler.new
19
+ compiler.call(to_ast)
20
+ compiler.to_h
21
+ end
22
+ end
23
+ end
24
+
25
+ Processor.include(Info::SchemaMethods)
26
+ end
27
+ end
@@ -0,0 +1,314 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/schema/constants"
4
+
5
+ module Dry
6
+ module Schema
7
+ # @api private
8
+ module JSONSchema
9
+ # @api private
10
+ class SchemaCompiler
11
+ # An error raised when a predicate cannot be converted
12
+ UnknownConversionError = ::Class.new(::StandardError)
13
+
14
+ IDENTITY = ->(v, _) { v }.freeze
15
+ TO_INTEGER = ->(v, _) { v.to_i }.freeze
16
+
17
+ PREDICATE_TO_TYPE = {
18
+ array?: {type: "array"},
19
+ bool?: {type: "boolean"},
20
+ date?: {type: "string", format: "date"},
21
+ date_time?: {type: "string", format: "date-time"},
22
+ decimal?: {type: "number"},
23
+ float?: {type: "number"},
24
+ hash?: {type: "object"},
25
+ int?: {type: "integer"},
26
+ nil?: {type: "null"},
27
+ str?: {type: "string"},
28
+ time?: {type: "string", format: "time"},
29
+ min_size?: {minLength: TO_INTEGER},
30
+ max_size?: {maxLength: TO_INTEGER},
31
+ size?: {maxLength: TO_INTEGER, minLength: TO_INTEGER},
32
+ format?: {
33
+ pattern: proc do |x|
34
+ x.to_s.delete_prefix("(?-mix:").delete_suffix(")")
35
+ end
36
+ },
37
+ true?: {},
38
+ false?: {},
39
+ included_in?: {enum: ->(v, _) { v.to_a }},
40
+ eql?: {const: IDENTITY},
41
+ filled?: EMPTY_HASH,
42
+ uri?: {format: "uri"},
43
+ uuid_v1?: {
44
+ pattern: "^[0-9A-F]{8}-[0-9A-F]{4}-1[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$"
45
+ },
46
+ uuid_v2?: {
47
+ pattern: "^[0-9A-F]{8}-[0-9A-F]{4}-2[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$"
48
+ },
49
+ uuid_v3?: {
50
+ pattern: "^[0-9A-F]{8}-[0-9A-F]{4}-3[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$"
51
+ },
52
+ uuid_v4?: {
53
+ pattern: "^[a-f0-9]{8}-?[a-f0-9]{4}-?4[a-f0-9]{3}-?[89ab][a-f0-9]{3}-?[a-f0-9]{12}$"
54
+ },
55
+ uuid_v5?: {
56
+ pattern: "^[0-9A-F]{8}-[0-9A-F]{4}-5[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$"
57
+ },
58
+ uuid_v6?: {
59
+ pattern: "^[0-9A-F]{8}-[0-9A-F]{4}-6[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$"
60
+ },
61
+ uuid_v7?: {
62
+ pattern: "^[0-9A-F]{8}-[0-9A-F]{4}-7[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$"
63
+ },
64
+ uuid_v8?: {
65
+ pattern: "^[0-9A-F]{8}-[0-9A-F]{4}-8[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$"
66
+ },
67
+ gt?: {exclusiveMinimum: IDENTITY},
68
+ gteq?: {minimum: IDENTITY},
69
+ lt?: {exclusiveMaximum: IDENTITY},
70
+ lteq?: {maximum: IDENTITY},
71
+ odd?: {type: "integer", not: {multipleOf: 2}},
72
+ even?: {type: "integer", multipleOf: 2}
73
+ }.freeze
74
+
75
+ # @api private
76
+ attr_reader :keys, :required
77
+
78
+ # @api private
79
+ def initialize(root: false, loose: false)
80
+ @keys = EMPTY_HASH.dup
81
+ @required = Set.new
82
+ @root = root
83
+ @loose = loose
84
+ end
85
+
86
+ # @api private
87
+ def to_hash
88
+ result = {}
89
+ result[:$schema] = "http://json-schema.org/draft-06/schema#" if root?
90
+ result.merge!(type: "object", properties: keys, required: required.to_a)
91
+ result
92
+ end
93
+
94
+ alias_method :to_h, :to_hash
95
+
96
+ # @api private
97
+ def call(ast)
98
+ visit(ast)
99
+ end
100
+
101
+ # @api private
102
+ def visit(node, opts = EMPTY_HASH)
103
+ meth, rest = node
104
+ public_send(:"visit_#{meth}", rest, opts)
105
+ end
106
+
107
+ # @api private
108
+ def visit_set(node, opts = EMPTY_HASH)
109
+ target = (key = opts[:key]) ? self.class.new(loose: loose?) : self
110
+
111
+ node.map { |child| target.visit(child, opts.except(:member)) }
112
+
113
+ return unless key
114
+
115
+ target_info = opts[:member] ? {items: target.to_h} : target.to_h
116
+ type = opts[:member] ? "array" : "object"
117
+
118
+ merge_opts!(keys[key], {type: type, **target_info})
119
+ end
120
+
121
+ # @api private
122
+ def visit_and(node, opts = EMPTY_HASH)
123
+ left, right = node
124
+
125
+ # We need to know the type first to apply filled macro
126
+ if left[1][0] == :filled?
127
+ visit(right, opts)
128
+ visit(left, opts)
129
+ else
130
+ visit(left, opts)
131
+ visit(right, opts)
132
+ end
133
+ end
134
+
135
+ # @api private
136
+ def visit_or(node, opts = EMPTY_HASH)
137
+ any_of = []
138
+ node.each do |child|
139
+ c = self.class.new(loose: loose?)
140
+ c.keys.update(subschema: {})
141
+ c.visit(child, opts.except(:member).merge(key: :subschema))
142
+ any_of << c.keys[:subschema]
143
+ end
144
+
145
+ target = keys[opts[:key]]
146
+ if target[:type]&.include?("array")
147
+ target[:items] = {anyOf: any_of}
148
+ else
149
+ target[:anyOf] = any_of
150
+ end
151
+ end
152
+
153
+ # @api private
154
+ def visit_implication(node, opts = EMPTY_HASH)
155
+ node.each do |el|
156
+ visit(el, **opts, required: false)
157
+ end
158
+ end
159
+
160
+ # @api private
161
+ def visit_each(node, opts = EMPTY_HASH)
162
+ visit(node, opts.merge(member: true))
163
+ end
164
+
165
+ # @api private
166
+ def visit_key(node, opts = EMPTY_HASH)
167
+ name, rest = node
168
+
169
+ if opts.fetch(:required, :true)
170
+ required << name.to_s
171
+ else
172
+ opts.delete(:required)
173
+ end
174
+
175
+ visit(rest, opts.merge(key: name))
176
+ end
177
+
178
+ # @api private
179
+ def visit_not(node, opts = EMPTY_HASH)
180
+ _name, rest = node
181
+
182
+ visit_predicate(rest, opts)
183
+ end
184
+
185
+ # @api private
186
+ def visit_predicate(node, opts = EMPTY_HASH)
187
+ name, rest = node
188
+
189
+ if name.equal?(:key?)
190
+ handle_key_predicate(rest)
191
+ else
192
+ handle_value_predicate(name, rest, opts)
193
+ end
194
+ end
195
+
196
+ # @api private
197
+ def handle_key_predicate(rest)
198
+ prop_name = rest[0][1]
199
+ keys[prop_name] = {}
200
+ end
201
+
202
+ # @api private
203
+ def handle_value_predicate(name, rest, opts)
204
+ target = keys[opts[:key]]
205
+ type_opts = fetch_type_opts_for_predicate(name, rest, target)
206
+
207
+ if array_with_size_predicate?(target, name, opts)
208
+ apply_array_size_constraint(target, name, rest)
209
+ elsif target[:type]&.include?("array")
210
+ apply_array_item_constraint(target, type_opts)
211
+ else
212
+ merge_opts!(target, type_opts)
213
+ end
214
+ end
215
+
216
+ # @api private
217
+ def array_with_size_predicate?(target, name, opts)
218
+ target[:type]&.include?("array") && array_size_predicate?(name) && !opts[:member]
219
+ end
220
+
221
+ # @api private
222
+ def apply_array_size_constraint(target, name, rest)
223
+ array_type_opts = convert_array_size_predicate(name, rest)
224
+ merge_opts!(target, array_type_opts)
225
+ end
226
+
227
+ # @api private
228
+ def apply_array_item_constraint(target, type_opts)
229
+ target[:items] ||= {}
230
+ merge_opts!(target[:items], type_opts)
231
+ end
232
+
233
+ # @api private
234
+ def array_size_predicate?(name)
235
+ name == :min_size? || name == :max_size?
236
+ end
237
+
238
+ # @api private
239
+ def convert_array_size_predicate(name, rest)
240
+ value = rest[0][1].to_i
241
+ case name
242
+ when :min_size?
243
+ {minItems: value}
244
+ when :max_size?
245
+ {maxItems: value}
246
+ else
247
+ {}
248
+ end
249
+ end
250
+
251
+ # @api private
252
+ def fetch_type_opts_for_predicate(name, rest, target)
253
+ type_opts = PREDICATE_TO_TYPE.fetch(name) do
254
+ raise_unknown_conversion_error!(:predicate, name) unless loose?
255
+
256
+ EMPTY_HASH
257
+ end.dup
258
+ type_opts.transform_values! { |v| v.respond_to?(:call) ? v.call(rest[0][1], target) : v }
259
+ type_opts.merge!(fetch_filled_options(target[:type], target)) if name == :filled?
260
+ type_opts
261
+ end
262
+
263
+ # @api private
264
+ def fetch_filled_options(type, _target)
265
+ case type
266
+ when "string"
267
+ {minLength: 1}
268
+ when "array"
269
+ raise_unknown_conversion_error!(:type, :array) unless loose?
270
+
271
+ {not: {type: "null"}}
272
+ else
273
+ {not: {type: "null"}}
274
+ end
275
+ end
276
+
277
+ # @api private
278
+ def merge_opts!(orig_opts, new_opts)
279
+ new_type = new_opts[:type]
280
+ orig_type = orig_opts[:type]
281
+
282
+ if orig_type && new_type && orig_type != new_type
283
+ new_opts[:type] = [orig_type, new_type].flatten.uniq
284
+ end
285
+
286
+ orig_opts.merge!(new_opts)
287
+ end
288
+
289
+ # @api private
290
+ def root?
291
+ @root
292
+ end
293
+
294
+ # @api private
295
+ def loose?
296
+ @loose
297
+ end
298
+
299
+ def raise_unknown_conversion_error!(type, name)
300
+ message = <<~MSG
301
+ Could not find an equivalent conversion for #{type} #{name.inspect}.
302
+
303
+ This means that your generated JSON schema may be missing this validation.
304
+
305
+ You can ignore this by generating the schema in "loose" mode, i.e.:
306
+ my_schema.json_schema(loose: true)
307
+ MSG
308
+
309
+ raise UnknownConversionError, message.chomp
310
+ end
311
+ end
312
+ end
313
+ end
314
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/schema/extensions/json_schema/schema_compiler"
4
+
5
+ module Dry
6
+ module Schema
7
+ # JSONSchema extension
8
+ #
9
+ # @api public
10
+ module JSONSchema
11
+ module SchemaMethods
12
+ # Convert the schema into a JSON schema hash
13
+ #
14
+ # @param [Symbol] loose Compile the schema in "loose" mode
15
+ #
16
+ # @return [Hash<Symbol=>Hash>]
17
+ #
18
+ # @api public
19
+ def json_schema(loose: false)
20
+ compiler = SchemaCompiler.new(root: true, loose: loose)
21
+ compiler.call(to_ast)
22
+ compiler.to_hash
23
+ end
24
+ end
25
+ end
26
+
27
+ Processor.include(JSONSchema::SchemaMethods)
28
+ end
29
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/monads"
4
+
5
+ # preload monads
6
+ Dry::Monads[:result]
7
+
8
+ module Dry
9
+ module Schema
10
+ # Monad extension for Result
11
+ #
12
+ # @api public
13
+ class Result
14
+ include ::Dry::Monads::Result::Mixin
15
+
16
+ # Turn result into a monad
17
+ #
18
+ # This makes result objects work with dry-monads (or anything with a compatible interface)
19
+ #
20
+ # @return [Dry::Monads::Success,Dry::Monads::Failure]
21
+ #
22
+ # @api public
23
+ def to_monad
24
+ if success?
25
+ Success(self)
26
+ else
27
+ Failure(self)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/struct"
4
+
5
+ module Dry
6
+ module Schema
7
+ module Macros
8
+ class StructToSchema < ::Dry::Struct::Compiler
9
+ def call(struct)
10
+ visit(struct.to_ast)
11
+ end
12
+
13
+ # strip away structs from AST
14
+ def visit_struct(node)
15
+ _, ast = node
16
+ visit(ast)
17
+ end
18
+ end
19
+
20
+ Hash.option :struct_compiler, default: proc { StructToSchema.new(schema_dsl.config.types) }
21
+
22
+ Hash.prepend(::Module.new {
23
+ def call(*args)
24
+ if args.size >= 1 && struct?(args[0])
25
+ if block_given?
26
+ raise ArgumentError, "blocks are not supported when using " \
27
+ "a struct class (#{name.inspect} => #{args[0]})"
28
+ end
29
+
30
+ struct_class = extract_struct_class(args[0])
31
+ schema = struct_compiler.(struct_class)
32
+
33
+ super(schema, *args.drop(1))
34
+ type(schema_dsl.types[name].constructor(schema))
35
+ else
36
+ super
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def struct?(type)
43
+ (type.is_a?(::Class) && type <= ::Dry::Struct) ||
44
+ (type.is_a?(::Dry::Types::Constructor) && type.primitive <= ::Dry::Struct)
45
+ end
46
+
47
+ def extract_struct_class(type)
48
+ if type.is_a?(::Class) && type <= ::Dry::Struct
49
+ type
50
+ elsif type.is_a?(::Dry::Types::Constructor) && type.primitive <= ::Dry::Struct
51
+ type.primitive
52
+ else
53
+ type
54
+ end
55
+ end
56
+ })
57
+ end
58
+
59
+ PredicateInferrer::Compiler.alias_method(:visit_struct, :visit_hash)
60
+ PrimitiveInferrer::Compiler.alias_method(:visit_struct, :visit_hash)
61
+ end
62
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ Dry::Schema.register_extension(:monads) do
4
+ require "dry/schema/extensions/monads"
5
+ end
6
+
7
+ Dry::Schema.register_extension(:hints) do
8
+ require "dry/schema/extensions/hints"
9
+ end
10
+
11
+ Dry::Schema.register_extension(:struct) do
12
+ require "dry/schema/extensions/struct"
13
+ end
14
+
15
+ Dry::Schema.register_extension(:info) do
16
+ require "dry/schema/extensions/info"
17
+ end
18
+
19
+ Dry::Schema.register_extension(:json_schema) do
20
+ require "dry/schema/extensions/json_schema"
21
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dry
4
+ module Schema
5
+ # JSON schema type
6
+ #
7
+ # @see Processor
8
+ # @see Schema#JSON
9
+ #
10
+ # @api public
11
+ class JSON < Processor
12
+ config.key_map_type = :stringified
13
+ config.type_registry_namespace = :json
14
+ config.filter_empty_string = false
15
+ end
16
+ end
17
+ end