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,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dry
4
+ module Schema
5
+ module Messages
6
+ # Namespaced messages backend
7
+ #
8
+ # @api public
9
+ class Namespaced < ::Dry::Schema::Messages::Abstract
10
+ # @api private
11
+ attr_reader :namespace
12
+
13
+ # @api private
14
+ attr_reader :messages
15
+
16
+ # @api private
17
+ attr_reader :config
18
+
19
+ # @api private
20
+ attr_reader :call_opts
21
+
22
+ # @api private
23
+ def initialize(namespace, messages)
24
+ super()
25
+ @config = messages.config
26
+ @namespace = namespace
27
+ @messages = messages
28
+ @call_opts = {namespace: namespace}.freeze
29
+ end
30
+
31
+ # Get a message for the given key and its options
32
+ #
33
+ # @param [Symbol] key
34
+ # @param [Hash] options
35
+ #
36
+ # @return [String]
37
+ #
38
+ # @api public
39
+ def get(key, options = {})
40
+ messages.get(key, options)
41
+ end
42
+
43
+ # @api public
44
+ def call(key, options = {})
45
+ super(key, options.empty? ? call_opts : options.merge(call_opts))
46
+ end
47
+ alias_method :[], :call
48
+
49
+ # Check if given key is defined
50
+ #
51
+ # @return [Boolean]
52
+ #
53
+ # @api public
54
+ def key?(key, *args)
55
+ messages.key?(key, *args)
56
+ end
57
+
58
+ # @api private
59
+ def filled_lookup_paths(tokens)
60
+ super(tokens.merge(root: "#{tokens[:root]}.#{namespace}")) + super
61
+ end
62
+
63
+ # @api private
64
+ def rule_lookup_paths(tokens)
65
+ base_paths = messages.rule_lookup_paths(tokens)
66
+ base_paths.map { |key|
67
+ key.sub(config.top_namespace, "#{config.top_namespace}.#{namespace}")
68
+ } + base_paths
69
+ end
70
+
71
+ # @api private
72
+ def cache_key(predicate, options)
73
+ messages.cache_key(predicate, options)
74
+ end
75
+
76
+ # @api private
77
+ def interpolatable_data(key, options, **data)
78
+ messages.interpolatable_data(key, options, **data)
79
+ end
80
+
81
+ # @api private
82
+ def interpolate(key, options, **data)
83
+ messages.interpolate(key, options, **data)
84
+ end
85
+
86
+ # @api private
87
+ def translate(key, **args)
88
+ messages.translate(key, **args)
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/initializer"
4
+ require "dry/core/equalizer"
5
+
6
+ require "dry/schema/constants"
7
+
8
+ module Dry
9
+ module Schema
10
+ module Messages
11
+ # @api private
12
+ class Template
13
+ extend ::Dry::Initializer
14
+ include ::Dry::Equalizer(:messages, :key, :options)
15
+
16
+ option :messages
17
+ option :key
18
+ option :options
19
+
20
+ # @api private
21
+ def data(data = EMPTY_HASH)
22
+ ensure_message!
23
+ messages.interpolatable_data(key, options, **options, **data)
24
+ end
25
+
26
+ # @api private
27
+ def call(data = EMPTY_HASH)
28
+ ensure_message!
29
+ messages.interpolate(key, options, **data)
30
+ end
31
+ alias_method :[], :call
32
+
33
+ private
34
+
35
+ def ensure_message!
36
+ return if messages.key?(key, options)
37
+
38
+ raise KeyError, "No message found for template, template=#{inspect}"
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,209 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+ require "pathname"
5
+
6
+ require "dry/schema/constants"
7
+
8
+ module Dry
9
+ module Schema
10
+ module Messages
11
+ # Plain YAML message backend
12
+ #
13
+ # @api public
14
+ class YAML < Abstract
15
+ LOCALE_TOKEN = "%<locale>s"
16
+ TOKEN_REGEXP = /%{(\w*)}/
17
+ EMPTY_CONTEXT = ::Object.new.tap { |ctx|
18
+ def ctx.context
19
+ binding
20
+ end
21
+ }.freeze.context
22
+
23
+ include ::Dry::Equalizer(:data)
24
+
25
+ # Loaded localized message templates
26
+ #
27
+ # @return [Hash]
28
+ attr_reader :data
29
+
30
+ # Translation function
31
+ #
32
+ # @return [Proc]
33
+ attr_reader :t
34
+
35
+ # @api private
36
+ def self.build(options = EMPTY_HASH)
37
+ super do |config|
38
+ config.default_locale = :en unless config.default_locale
39
+
40
+ config.root = -"%<locale>s.#{config.root}"
41
+
42
+ config.rule_lookup_paths = config.rule_lookup_paths.map { |path|
43
+ -"%<locale>s.#{path}"
44
+ }
45
+ end
46
+ end
47
+
48
+ # @api private
49
+ # rubocop: disable Metrics/PerceivedComplexity
50
+ def self.flat_hash(hash, path = EMPTY_ARRAY, keys = {})
51
+ hash.each do |key, value|
52
+ flat_hash(value, [*path, key], keys) if value.is_a?(Hash)
53
+
54
+ if value.is_a?(String) && hash["text"] != value
55
+ keys[[*path, key].join(DOT)] = {
56
+ text: value,
57
+ meta: EMPTY_HASH
58
+ }
59
+ elsif value.is_a?(Hash) && value["text"].is_a?(String)
60
+ keys[[*path, key].join(DOT)] = {
61
+ text: value["text"],
62
+ meta: value.reject { _1.eql?("text") }.transform_keys(&:to_sym)
63
+ }
64
+ end
65
+ end
66
+
67
+ keys
68
+ end
69
+ # rubocop: enable Metrics/PerceivedComplexity
70
+
71
+ # @api private
72
+ def self.cache
73
+ @cache ||= Concurrent::Map.new do |h, k|
74
+ h.compute_if_absent(k) { Concurrent::Map.new }
75
+ end
76
+ end
77
+
78
+ # @api private
79
+ def self.source_cache
80
+ @source_cache ||= Concurrent::Map.new
81
+ end
82
+
83
+ # @api private
84
+ def initialize(data: EMPTY_HASH, config: nil)
85
+ super()
86
+ @data = data
87
+ @__config__ = config if config
88
+ @t = proc { |key, locale: default_locale| get("%<locale>s.#{key}", locale: locale) }
89
+ end
90
+
91
+ # Get an array of looked up paths
92
+ #
93
+ # @param [Symbol] predicate
94
+ # @param [Hash] options
95
+ #
96
+ # @return [String]
97
+ #
98
+ # @api public
99
+ def looked_up_paths(predicate, options)
100
+ super.map { |path| path % {locale: options[:locale] || default_locale} }
101
+ end
102
+
103
+ # Get a message for the given key and its options
104
+ #
105
+ # @param [Symbol] key
106
+ # @param [Hash] options
107
+ #
108
+ # @return [String]
109
+ #
110
+ # @api public
111
+ def get(key, options = EMPTY_HASH)
112
+ data[evaluated_key(key, options)]
113
+ end
114
+
115
+ # Check if given key is defined
116
+ #
117
+ # @return [Boolean]
118
+ #
119
+ # @api public
120
+ def key?(key, options = EMPTY_HASH)
121
+ data.key?(evaluated_key(key, options))
122
+ end
123
+
124
+ # Merge messages from an additional path
125
+ #
126
+ # @param [String] overrides
127
+ #
128
+ # @return [Messages::I18n]
129
+ #
130
+ # @api public
131
+ def merge(overrides)
132
+ if overrides.is_a?(Hash)
133
+ self.class.new(
134
+ data: data.merge(self.class.flat_hash(overrides)),
135
+ config: config
136
+ )
137
+ else
138
+ self.class.new(
139
+ data: Array(overrides).reduce(data) { |a, e| a.merge(load_translations(e)) },
140
+ config: config
141
+ )
142
+ end
143
+ end
144
+
145
+ # @api private
146
+ def prepare
147
+ @data = config.load_paths.map { |path| load_translations(path) }.reduce({}, :merge)
148
+ self
149
+ end
150
+
151
+ # @api private
152
+ def interpolatable_data(key, options, **data)
153
+ tokens = evaluation_context(key, options).fetch(:tokens)
154
+ data.select { |k,| tokens.include?(k) }
155
+ end
156
+
157
+ # @api private
158
+ def interpolate(key, options, **data)
159
+ evaluator = evaluation_context(key, options).fetch(:evaluator)
160
+ data.empty? ? evaluator.() : evaluator.(**data)
161
+ end
162
+
163
+ private
164
+
165
+ # @api private
166
+ def evaluation_context(key, options)
167
+ cache.fetch_or_store(get(key, options).fetch(:text)) do |input|
168
+ tokens = input.scan(TOKEN_REGEXP).flatten(1).to_set(&:to_sym)
169
+ text = input.gsub("%", "#")
170
+
171
+ # rubocop:disable Security/Eval
172
+ evaluator = eval(<<~RUBY, EMPTY_CONTEXT, __FILE__, __LINE__ + 1)
173
+ -> (#{tokens.map { |token| "#{token}:" }.join(", ")}) { "#{text}" } # -> (a:, b:) { "Translation #\{a} #\{b}" }
174
+ RUBY
175
+ # rubocop:enable Security/Eval
176
+
177
+ {
178
+ tokens: tokens,
179
+ evaluator: evaluator
180
+ }
181
+ end
182
+ end
183
+
184
+ # @api private
185
+ def cache
186
+ @cache ||= self.class.cache[self]
187
+ end
188
+
189
+ # @api private
190
+ def load_translations(path)
191
+ data = self.class.source_cache.fetch_or_store(path) do
192
+ self.class.flat_hash(::YAML.load_file(path)).freeze
193
+ end
194
+
195
+ return data unless custom_top_namespace?(path)
196
+
197
+ data.transform_keys { _1.gsub(DEFAULT_MESSAGES_ROOT, config.top_namespace) }
198
+ end
199
+
200
+ # @api private
201
+ def evaluated_key(key, options)
202
+ return key unless key.include?(LOCALE_TOKEN)
203
+
204
+ key % {locale: options[:locale] || default_locale}
205
+ end
206
+ end
207
+ end
208
+ end
209
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dry
4
+ module Schema
5
+ # An API for configuring message backends
6
+ #
7
+ # @api private
8
+ module Messages
9
+ BACKENDS = {
10
+ i18n: "I18n",
11
+ yaml: "YAML"
12
+ }.freeze
13
+
14
+ module_function
15
+
16
+ public def setup(config)
17
+ backend_class = BACKENDS.fetch(config.backend) do
18
+ raise "+#{config.backend}+ is not a valid messages identifier"
19
+ end
20
+
21
+ namespace = config.namespace
22
+ options = config.to_h.select { |k, _| Abstract.setting_names.include?(k) }
23
+
24
+ messages = Messages.const_get(backend_class).build(options)
25
+
26
+ return messages.namespaced(namespace) if namespace
27
+
28
+ messages
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dry
4
+ module Schema
5
+ # A special rule type that is configured under a specified namespace
6
+ #
7
+ # This is used internally to create rules that can be properly handled
8
+ # by the message compiler in situations where a schema reuses another schema
9
+ # but it is configured to use a message namespace
10
+ #
11
+ # @api private
12
+ class NamespacedRule
13
+ # @api private
14
+ attr_reader :rule
15
+
16
+ # @api private
17
+ attr_reader :namespace
18
+
19
+ # @api private
20
+ def initialize(namespace, rule)
21
+ @namespace = namespace
22
+ @rule = rule
23
+ end
24
+
25
+ # @api private
26
+ def call(input)
27
+ result = rule.call(input)
28
+ Logic::Result.new(result.success?) { [:namespace, [namespace, result.to_ast]] }
29
+ end
30
+
31
+ # @api private
32
+ def ast(input = Undefined)
33
+ [:namespace, [namespace, rule.ast(input)]]
34
+ end
35
+ alias_method :to_ast, :ast
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dry
4
+ module Schema
5
+ # Params schema type
6
+ #
7
+ # @see Processor
8
+ # @see Schema#Params
9
+ #
10
+ # @api public
11
+ class Params < Processor
12
+ config.key_map_type = :stringified
13
+ config.type_registry_namespace = :params
14
+ config.filter_empty_string = true
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/schema/constants"
4
+
5
+ module Dry
6
+ module Schema
7
+ # Path represents a list of keys in a hash
8
+ #
9
+ # @api private
10
+ class Path
11
+ include ::Dry.Equalizer(:keys)
12
+ include ::Comparable
13
+ include ::Enumerable
14
+
15
+ # @return [Array<Symbol>]
16
+ attr_reader :keys
17
+
18
+ alias_method :root, :first
19
+
20
+ # Coerce a spec into a path object
21
+ #
22
+ # @param [Path, Symbol, String, Hash, Array<Symbol>] spec
23
+ #
24
+ # @return [Path]
25
+ #
26
+ # @api private
27
+ def self.call(spec)
28
+ case spec
29
+ when ::Symbol, ::Array
30
+ new([*spec])
31
+ when ::String
32
+ new(spec.split(DOT).map(&:to_sym))
33
+ when ::Hash
34
+ new(keys_from_hash(spec))
35
+ when self
36
+ spec
37
+ else
38
+ raise ::ArgumentError, "+spec+ must be either a Symbol, Array, Hash or a #{name}"
39
+ end
40
+ end
41
+
42
+ # @api private
43
+ def self.[](spec)
44
+ call(spec)
45
+ end
46
+
47
+ # Extract a list of keys from a hash
48
+ #
49
+ # @api private
50
+ def self.keys_from_hash(hash)
51
+ hash.inject([]) { |a, (k, v)|
52
+ v.is_a?(::Hash) ? a.push(k, *keys_from_hash(v)) : a.push(k, v)
53
+ }
54
+ end
55
+
56
+ # @api private
57
+ def initialize(keys)
58
+ @keys = keys
59
+ end
60
+
61
+ # @api private
62
+ def to_h(value = EMPTY_ARRAY.dup)
63
+ value = [value] unless value.is_a?(::Array)
64
+
65
+ keys.reverse_each.reduce(value) { |result, key| {key => result} }
66
+ end
67
+
68
+ # @api private
69
+ def each(&)
70
+ keys.each(&)
71
+ end
72
+
73
+ # @api private
74
+ def include?(other)
75
+ keys[0, other.keys.length].eql?(other.keys)
76
+ end
77
+
78
+ # @api private
79
+ def <=>(other)
80
+ return keys.length <=> other.keys.length if include?(other) || other.include?(self)
81
+
82
+ first_uncommon_index = (self & other).keys.length
83
+
84
+ keys[first_uncommon_index] <=> other.keys[first_uncommon_index]
85
+ end
86
+
87
+ # @api private
88
+ def &(other)
89
+ self.class.new(
90
+ keys.take_while.with_index { |key, index| other.keys[index].eql?(key) }
91
+ )
92
+ end
93
+
94
+ # @api private
95
+ def last
96
+ keys.last
97
+ end
98
+
99
+ # @api private
100
+ def same_root?(other)
101
+ root.equal?(other.root)
102
+ end
103
+
104
+ EMPTY = new(EMPTY_ARRAY).freeze
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/core/equalizer"
4
+ require "dry/logic/operators"
5
+
6
+ module Dry
7
+ module Schema
8
+ # Predicate objects used within the DSL
9
+ #
10
+ # @api public
11
+ class Predicate
12
+ # A negated predicate
13
+ #
14
+ # @api private
15
+ class Negation
16
+ include ::Dry::Logic::Operators
17
+
18
+ # @api private
19
+ attr_reader :predicate
20
+
21
+ # @api private
22
+ def initialize(predicate)
23
+ @predicate = predicate
24
+ end
25
+
26
+ # Dump negated predicate to an AST
27
+ #
28
+ # @return [Array]
29
+ #
30
+ # @api private
31
+ def to_ast(...)
32
+ [:not, predicate.to_ast(...)]
33
+ end
34
+ alias_method :ast, :to_ast
35
+ end
36
+
37
+ include ::Dry::Logic::Operators
38
+ include ::Dry::Equalizer(:name, :args, :block)
39
+
40
+ # @api private
41
+ attr_reader :compiler
42
+
43
+ # @api private
44
+ attr_reader :name
45
+
46
+ # @api private
47
+ attr_reader :args
48
+
49
+ # @api private
50
+ attr_reader :arity
51
+
52
+ # @api private
53
+ attr_reader :block
54
+
55
+ # @api private
56
+ def initialize(compiler, name, args, block)
57
+ @compiler = compiler
58
+ @name = name
59
+ @args = args
60
+ @block = block
61
+ # Cater for optional second argument like in case of `eql?` or `respond_to?`
62
+ @arity = compiler.predicates[name].arity.abs
63
+ end
64
+
65
+ # Negate a predicate
66
+ #
67
+ # @example
68
+ # required(:name).value(:string) { !empty? }
69
+ #
70
+ # @return [Negation]
71
+ #
72
+ # @api public
73
+ def !
74
+ Negation.new(self)
75
+ end
76
+
77
+ # @api private
78
+ def ensure_valid
79
+ if arity - 1 != args.size
80
+ raise ::ArgumentError, "#{name} predicate arity is invalid"
81
+ end
82
+ end
83
+
84
+ # Compile predicate to a rule object
85
+ #
86
+ # @api private
87
+ def to_rule
88
+ compiler.visit(to_ast)
89
+ end
90
+
91
+ # Dump predicate to an AST
92
+ #
93
+ # @return [Array]
94
+ #
95
+ # @api private
96
+ def to_ast(*)
97
+ [:predicate, [name, compiler.predicates.arg_list(name, *args)]]
98
+ end
99
+ alias_method :ast, :to_ast
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dry
4
+ module Schema
5
+ # @api private
6
+ class PredicateInferrer < ::Dry::Types::PredicateInferrer
7
+ Compiler = ::Class.new(superclass::Compiler) do
8
+ # @api private
9
+ def visit_intersection(node)
10
+ left_node, right_node, = node
11
+ left = visit(left_node)
12
+ right = visit(right_node)
13
+
14
+ [left, right].flatten.compact
15
+ end
16
+ end
17
+
18
+ def initialize(registry = PredicateRegistry.new)
19
+ super
20
+
21
+ @compiler = Compiler.new(registry)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dry
4
+ module Schema
5
+ # A registry with predicate objects from `Dry::Logic::Predicates`
6
+ #
7
+ # @api private
8
+ class PredicateRegistry < ::Dry::Types::PredicateRegistry
9
+ # @api private
10
+ def arg_list(name, *values)
11
+ predicate = self[name]
12
+ # Cater for optional second argument like in case of `eql?` or `respond_to?`
13
+ arity = predicate.arity.abs
14
+
15
+ predicate
16
+ .parameters
17
+ .map(&:last)
18
+ .zip(values + ::Array.new(arity - values.size, Undefined))
19
+ end
20
+ end
21
+ end
22
+ end