matchers 0.1.0.pre.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 (127) hide show
  1. checksums.yaml +7 -0
  2. data/lib/matcher/assertions.rb +19 -0
  3. data/lib/matcher/autoload.rb +5 -0
  4. data/lib/matcher/base.rb +183 -0
  5. data/lib/matcher/compatibility.rb +34 -0
  6. data/lib/matcher/debug.rb +62 -0
  7. data/lib/matcher/dsl/builder.rb +99 -0
  8. data/lib/matcher/dsl/chain.rb +84 -0
  9. data/lib/matcher/dsl/expression_dsl.rb +306 -0
  10. data/lib/matcher/dsl/matcher_dsl.rb +5 -0
  11. data/lib/matcher/dsl/optional.rb +82 -0
  12. data/lib/matcher/dsl/optional_chain.rb +24 -0
  13. data/lib/matcher/dsl/others.rb +28 -0
  14. data/lib/matcher/errors/and_error.rb +88 -0
  15. data/lib/matcher/errors/boolean_collector.rb +51 -0
  16. data/lib/matcher/errors/element_error.rb +24 -0
  17. data/lib/matcher/errors/empty_error.rb +23 -0
  18. data/lib/matcher/errors/error.rb +39 -0
  19. data/lib/matcher/errors/error_collector.rb +100 -0
  20. data/lib/matcher/errors/nested_error.rb +98 -0
  21. data/lib/matcher/errors/or_error.rb +88 -0
  22. data/lib/matcher/expression_cache.rb +57 -0
  23. data/lib/matcher/expression_labeler.rb +96 -0
  24. data/lib/matcher/expressions/array_expression.rb +45 -0
  25. data/lib/matcher/expressions/block.rb +189 -0
  26. data/lib/matcher/expressions/call.rb +307 -0
  27. data/lib/matcher/expressions/call_error.rb +45 -0
  28. data/lib/matcher/expressions/constant.rb +53 -0
  29. data/lib/matcher/expressions/expression.rb +237 -0
  30. data/lib/matcher/expressions/expression_walker.rb +77 -0
  31. data/lib/matcher/expressions/hash_expression.rb +59 -0
  32. data/lib/matcher/expressions/proc_expression.rb +96 -0
  33. data/lib/matcher/expressions/range_expression.rb +65 -0
  34. data/lib/matcher/expressions/recorder.rb +136 -0
  35. data/lib/matcher/expressions/rescue_last_error_expression.rb +49 -0
  36. data/lib/matcher/expressions/set_expression.rb +45 -0
  37. data/lib/matcher/expressions/string_expression.rb +53 -0
  38. data/lib/matcher/expressions/symbol_proc.rb +53 -0
  39. data/lib/matcher/expressions/variable.rb +87 -0
  40. data/lib/matcher/hash_stack.rb +52 -0
  41. data/lib/matcher/list.rb +102 -0
  42. data/lib/matcher/markers.rb +7 -0
  43. data/lib/matcher/matcher_cache.rb +18 -0
  44. data/lib/matcher/matchers/all_matcher.rb +60 -0
  45. data/lib/matcher/matchers/always_matcher.rb +34 -0
  46. data/lib/matcher/matchers/any_matcher.rb +70 -0
  47. data/lib/matcher/matchers/array_matcher.rb +72 -0
  48. data/lib/matcher/matchers/block_matcher.rb +61 -0
  49. data/lib/matcher/matchers/boolean_matcher.rb +37 -0
  50. data/lib/matcher/matchers/dig_matcher.rb +149 -0
  51. data/lib/matcher/matchers/each_matcher.rb +85 -0
  52. data/lib/matcher/matchers/each_pair_matcher.rb +119 -0
  53. data/lib/matcher/matchers/equal_matcher.rb +198 -0
  54. data/lib/matcher/matchers/equal_set_matcher.rb +112 -0
  55. data/lib/matcher/matchers/expression_matcher.rb +69 -0
  56. data/lib/matcher/matchers/filter_matcher.rb +115 -0
  57. data/lib/matcher/matchers/hash_matcher.rb +315 -0
  58. data/lib/matcher/matchers/imply_matcher.rb +83 -0
  59. data/lib/matcher/matchers/imply_some_matcher.rb +116 -0
  60. data/lib/matcher/matchers/index_by_matcher.rb +177 -0
  61. data/lib/matcher/matchers/inline_matcher.rb +101 -0
  62. data/lib/matcher/matchers/keys_matcher.rb +131 -0
  63. data/lib/matcher/matchers/kind_of_matcher.rb +35 -0
  64. data/lib/matcher/matchers/lazy_all_matcher.rb +69 -0
  65. data/lib/matcher/matchers/lazy_any_matcher.rb +69 -0
  66. data/lib/matcher/matchers/let_matcher.rb +73 -0
  67. data/lib/matcher/matchers/map_matcher.rb +148 -0
  68. data/lib/matcher/matchers/negated_array_matcher.rb +38 -0
  69. data/lib/matcher/matchers/negated_each_matcher.rb +36 -0
  70. data/lib/matcher/matchers/negated_each_pair_matcher.rb +38 -0
  71. data/lib/matcher/matchers/negated_imply_some_matcher.rb +46 -0
  72. data/lib/matcher/matchers/negated_matcher.rb +25 -0
  73. data/lib/matcher/matchers/negated_project_matcher.rb +31 -0
  74. data/lib/matcher/matchers/never_matcher.rb +35 -0
  75. data/lib/matcher/matchers/one_matcher.rb +68 -0
  76. data/lib/matcher/matchers/optional_matcher.rb +38 -0
  77. data/lib/matcher/matchers/parse_float_matcher.rb +86 -0
  78. data/lib/matcher/matchers/parse_integer_matcher.rb +101 -0
  79. data/lib/matcher/matchers/parse_iso8601_helper.rb +41 -0
  80. data/lib/matcher/matchers/parse_iso8601_matcher.rb +52 -0
  81. data/lib/matcher/matchers/parse_json_helper.rb +43 -0
  82. data/lib/matcher/matchers/parse_json_matcher.rb +59 -0
  83. data/lib/matcher/matchers/project_matcher.rb +72 -0
  84. data/lib/matcher/matchers/raises_matcher.rb +131 -0
  85. data/lib/matcher/matchers/range_matcher.rb +50 -0
  86. data/lib/matcher/matchers/reference_matcher.rb +213 -0
  87. data/lib/matcher/matchers/reference_matcher_collection.rb +57 -0
  88. data/lib/matcher/matchers/regexp_matcher.rb +86 -0
  89. data/lib/matcher/messages/expected_phrasing.rb +355 -0
  90. data/lib/matcher/messages/message.rb +104 -0
  91. data/lib/matcher/messages/message_builder.rb +35 -0
  92. data/lib/matcher/messages/message_rules.rb +240 -0
  93. data/lib/matcher/messages/namespaced_message_builder.rb +19 -0
  94. data/lib/matcher/messages/phrasing.rb +59 -0
  95. data/lib/matcher/messages/standard_message_builder.rb +105 -0
  96. data/lib/matcher/patterns/ast_mapping.rb +42 -0
  97. data/lib/matcher/patterns/capture_hole.rb +33 -0
  98. data/lib/matcher/patterns/constant_hole.rb +14 -0
  99. data/lib/matcher/patterns/hole.rb +30 -0
  100. data/lib/matcher/patterns/method_hole.rb +62 -0
  101. data/lib/matcher/patterns/pattern.rb +104 -0
  102. data/lib/matcher/patterns/pattern_building.rb +39 -0
  103. data/lib/matcher/patterns/pattern_capture.rb +11 -0
  104. data/lib/matcher/patterns/pattern_match.rb +29 -0
  105. data/lib/matcher/patterns/variable_hole.rb +14 -0
  106. data/lib/matcher/reporter.rb +103 -0
  107. data/lib/matcher/rules/message_factory.rb +26 -0
  108. data/lib/matcher/rules/message_rule.rb +18 -0
  109. data/lib/matcher/rules/message_rule_context.rb +26 -0
  110. data/lib/matcher/rules/rule_builder.rb +29 -0
  111. data/lib/matcher/rules/rule_set.rb +57 -0
  112. data/lib/matcher/rules/transform_builder.rb +24 -0
  113. data/lib/matcher/rules/transform_mapping.rb +5 -0
  114. data/lib/matcher/rules/transform_rule.rb +21 -0
  115. data/lib/matcher/state.rb +40 -0
  116. data/lib/matcher/testing/error_builder.rb +62 -0
  117. data/lib/matcher/testing/error_checker.rb +514 -0
  118. data/lib/matcher/testing/error_testing.rb +37 -0
  119. data/lib/matcher/testing/pattern_testing.rb +11 -0
  120. data/lib/matcher/testing/pattern_testing_scope.rb +34 -0
  121. data/lib/matcher/testing.rb +107 -0
  122. data/lib/matcher/undefined.rb +10 -0
  123. data/lib/matcher/utils/mapping_utils.rb +61 -0
  124. data/lib/matcher/utils.rb +72 -0
  125. data/lib/matcher/version.rb +5 -0
  126. data/lib/matcher.rb +346 -0
  127. metadata +174 -0
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Matcher
4
+ module MappingUtils
5
+ def unary_call?(expression)
6
+ expression.unary? && expression.receiver == Variable.actual
7
+ end
8
+
9
+ def index_call?(expression)
10
+ expression.binary? &&
11
+ expression.receiver == Variable.actual &&
12
+ expression.args[0].is_a?(Constant)
13
+ end
14
+
15
+ def operand_of(expression)
16
+ expression.args[0].value
17
+ end
18
+
19
+ def index_call_to(operand)
20
+ Call.new(Variable.actual, :[], [Constant.new(operand)])
21
+ end
22
+
23
+ def map_errors(error, &)
24
+ case error
25
+ when EmptyError
26
+ error
27
+ when AndError, OrError
28
+ children = error.children.map { map_errors(_1, &) }
29
+ error.class.new(children)
30
+ when NestedError
31
+ yield(error) || error
32
+ when ElementError
33
+ NestedError.new(mapped_base, error)
34
+ else
35
+ raise "Unexpected error: #{error.inspect}"
36
+ end
37
+ end
38
+
39
+ def map_base(method, expression)
40
+ with_index = expression.variables.include?(:index)
41
+
42
+ block = if unary_call?(expression)
43
+ SymbolProc.new(expression.method)
44
+ else
45
+ element = expression.free_symbol(:e)
46
+ parameters = [[:opt, element]]
47
+ parameters << %i[opt index] if with_index
48
+ substituted = expression.substitute(actual: element, original: :actual)
49
+
50
+ Block.new(parameters, substituted)
51
+ end
52
+
53
+ if with_index
54
+ enum_for = Call.new(Variable.actual, method)
55
+ Call.new(enum_for, :with_index, [], {}, block)
56
+ else
57
+ Call.new(Variable.actual, method, [], {}, block)
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Matcher
4
+ module Utils
5
+ def self.to_string(obj)
6
+ case obj
7
+ when Hash
8
+ return "{}" if obj.empty?
9
+
10
+ body = obj.map do |k, v|
11
+ if k.is_a?(Symbol)
12
+ "#{k}: #{to_string(v)}"
13
+ else
14
+ "#{k.inspect} => #{to_string(v)}"
15
+ end
16
+ end
17
+
18
+ "{ #{body.join(', ')} }"
19
+ when Array
20
+ "[#{obj.map { |v| to_string(v) }.join(', ')}]"
21
+ else
22
+ obj.inspect
23
+ end
24
+ end
25
+
26
+ def self.call_block(block, values, parameters: block.parameters)
27
+ args = []
28
+ kwargs = {}
29
+
30
+ parameters.each do |type, name|
31
+ case type
32
+ when :req, :opt, :rest
33
+ args << values[:actual] if args.length == 0
34
+ when :keyreq
35
+ kwargs[name] = values[name]
36
+ when :key
37
+ value = values[name]
38
+ kwargs[name] = value if !value.nil? || values.key?(name)
39
+ when :keyrest
40
+ kwargs.merge!(values.except(*kwargs.keys))
41
+ end
42
+ end
43
+
44
+ block.call(*args, **kwargs)
45
+ end
46
+
47
+ def self.inspect_block_params(block)
48
+ arg_names = []
49
+ kwarg_names = []
50
+
51
+ block.parameters.each do |type, name|
52
+ case type
53
+ when :req, :opt, :rest
54
+ arg_names << name
55
+ when :keyreq, :key, :keyrest
56
+ kwarg_names << name
57
+ end
58
+ end
59
+
60
+ arg_parts = arg_names.all? { _1.match?(/^_[1-9]$/) } ? [] : arg_names
61
+ kwarg_parts = kwarg_names.map { "#{_1}:" }
62
+
63
+ (arg_parts + kwarg_parts).join(", ")
64
+ end
65
+
66
+ def self.block_location(block)
67
+ file, line = block.source_location
68
+
69
+ "#{File.basename(file)}:#{line}"
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Matcher
4
+ VERSION = "0.1.0.pre.1"
5
+ end
data/lib/matcher.rb ADDED
@@ -0,0 +1,346 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "matcher/version"
4
+
5
+ require "singleton"
6
+ require "forwardable"
7
+
8
+ require_relative "matcher/autoload"
9
+ require_relative "matcher/markers"
10
+
11
+ require_relative "matcher/dsl/expression_dsl"
12
+ require_relative "matcher/dsl/matcher_dsl"
13
+ require_relative "matcher/base"
14
+
15
+ require_relative "matcher/dsl/builder"
16
+ require_relative "matcher/dsl/optional"
17
+ require_relative "matcher/dsl/optional_chain"
18
+ require_relative "matcher/dsl/others"
19
+ require_relative "matcher/dsl/chain"
20
+
21
+ require_relative "matcher/patterns/pattern_building"
22
+ require_relative "matcher/utils/mapping_utils"
23
+
24
+ require_relative "matcher/assertions"
25
+ require_relative "matcher/compatibility"
26
+ require_relative "matcher/debug"
27
+ require_relative "matcher/expression_labeler"
28
+ require_relative "matcher/expression_cache"
29
+ require_relative "matcher/hash_stack"
30
+ require_relative "matcher/list"
31
+ require_relative "matcher/matcher_cache"
32
+ require_relative "matcher/state"
33
+ require_relative "matcher/undefined"
34
+ require_relative "matcher/utils"
35
+
36
+ require_relative "matcher/errors/error"
37
+ require_relative "matcher/errors/and_error"
38
+ require_relative "matcher/errors/element_error"
39
+ require_relative "matcher/errors/empty_error"
40
+ require_relative "matcher/errors/error_collector"
41
+ require_relative "matcher/errors/nested_error"
42
+ require_relative "matcher/errors/or_error"
43
+ require_relative "matcher/errors/boolean_collector"
44
+
45
+ require_relative "matcher/expressions/expression"
46
+ require_relative "matcher/expressions/array_expression"
47
+ require_relative "matcher/expressions/block"
48
+ require_relative "matcher/expressions/call"
49
+ require_relative "matcher/expressions/call_error"
50
+ require_relative "matcher/expressions/constant"
51
+ require_relative "matcher/expressions/expression_walker"
52
+ require_relative "matcher/expressions/hash_expression"
53
+ require_relative "matcher/expressions/proc_expression"
54
+ require_relative "matcher/expressions/range_expression"
55
+ require_relative "matcher/expressions/recorder"
56
+ require_relative "matcher/expressions/rescue_last_error_expression"
57
+ require_relative "matcher/expressions/set_expression"
58
+ require_relative "matcher/expressions/string_expression"
59
+ require_relative "matcher/expressions/symbol_proc"
60
+ require_relative "matcher/expressions/variable"
61
+
62
+ require_relative "matcher/matchers/all_matcher"
63
+ require_relative "matcher/matchers/always_matcher"
64
+ require_relative "matcher/matchers/any_matcher"
65
+ require_relative "matcher/matchers/array_matcher"
66
+ require_relative "matcher/matchers/block_matcher"
67
+ require_relative "matcher/matchers/boolean_matcher"
68
+ require_relative "matcher/matchers/dig_matcher"
69
+ require_relative "matcher/matchers/each_matcher"
70
+ require_relative "matcher/matchers/each_pair_matcher"
71
+ require_relative "matcher/matchers/equal_matcher"
72
+ require_relative "matcher/matchers/equal_set_matcher"
73
+ require_relative "matcher/matchers/expression_matcher"
74
+ require_relative "matcher/matchers/filter_matcher"
75
+ require_relative "matcher/matchers/hash_matcher"
76
+ require_relative "matcher/matchers/index_by_matcher"
77
+ require_relative "matcher/matchers/inline_matcher"
78
+ require_relative "matcher/matchers/imply_matcher"
79
+ require_relative "matcher/matchers/imply_some_matcher"
80
+ require_relative "matcher/matchers/keys_matcher"
81
+ require_relative "matcher/matchers/kind_of_matcher"
82
+ require_relative "matcher/matchers/lazy_all_matcher"
83
+ require_relative "matcher/matchers/lazy_any_matcher"
84
+ require_relative "matcher/matchers/let_matcher"
85
+ require_relative "matcher/matchers/map_matcher"
86
+ require_relative "matcher/matchers/negated_array_matcher"
87
+ require_relative "matcher/matchers/negated_each_matcher"
88
+ require_relative "matcher/matchers/negated_each_pair_matcher"
89
+ require_relative "matcher/matchers/negated_imply_some_matcher"
90
+ require_relative "matcher/matchers/negated_matcher"
91
+ require_relative "matcher/matchers/negated_project_matcher"
92
+ require_relative "matcher/matchers/never_matcher"
93
+ require_relative "matcher/matchers/one_matcher"
94
+ require_relative "matcher/matchers/optional_matcher"
95
+ require_relative "matcher/matchers/parse_float_matcher"
96
+ require_relative "matcher/matchers/parse_integer_matcher"
97
+ require_relative "matcher/matchers/parse_iso8601_helper"
98
+ require_relative "matcher/matchers/parse_json_helper"
99
+ require_relative "matcher/matchers/project_matcher"
100
+ require_relative "matcher/matchers/raises_matcher"
101
+ require_relative "matcher/matchers/range_matcher"
102
+ require_relative "matcher/matchers/reference_matcher"
103
+ require_relative "matcher/matchers/reference_matcher_collection"
104
+ require_relative "matcher/matchers/regexp_matcher"
105
+
106
+ require_relative "matcher/messages/phrasing"
107
+ require_relative "matcher/messages/message_builder"
108
+ require_relative "matcher/messages/expected_phrasing"
109
+ require_relative "matcher/messages/message"
110
+ require_relative "matcher/messages/namespaced_message_builder"
111
+ require_relative "matcher/messages/standard_message_builder"
112
+
113
+ require_relative "matcher/patterns/hole"
114
+ require_relative "matcher/patterns/ast_mapping"
115
+ require_relative "matcher/patterns/capture_hole"
116
+ require_relative "matcher/patterns/constant_hole"
117
+ require_relative "matcher/patterns/method_hole"
118
+ require_relative "matcher/patterns/pattern"
119
+ require_relative "matcher/patterns/pattern_capture"
120
+ require_relative "matcher/patterns/pattern_match"
121
+ require_relative "matcher/patterns/variable_hole"
122
+
123
+ require_relative "matcher/rules/message_factory"
124
+ require_relative "matcher/rules/message_rule"
125
+ require_relative "matcher/rules/message_rule_context"
126
+ require_relative "matcher/rules/rule_builder"
127
+ require_relative "matcher/rules/rule_set"
128
+ require_relative "matcher/rules/transform_builder"
129
+ require_relative "matcher/rules/transform_mapping"
130
+ require_relative "matcher/rules/transform_rule"
131
+
132
+ require_relative "matcher/testing/error_builder"
133
+ require_relative "matcher/testing/error_checker"
134
+ require_relative "matcher/testing/error_testing"
135
+ require_relative "matcher/testing/pattern_testing"
136
+ require_relative "matcher/testing/pattern_testing_scope"
137
+
138
+ module Matcher
139
+ UNDEFINED = Undefined.instance
140
+
141
+ def self.undefined?(object)
142
+ # Note that for an ExpressionRecorder object == UNDEFINED won't work.
143
+
144
+ # rubocop:disable Style/YodaCondition
145
+ UNDEFINED == object
146
+ # rubocop:enable Style/YodaCondition
147
+ end
148
+
149
+ ##
150
+ # Builds a matcher from a block
151
+ #
152
+ # Inside the block all matcher and expression building helpers are available.
153
+ # The return value of the block is converted to a matcher via +Matcher.of+.
154
+ # @example
155
+ # m = Matcher.build do
156
+ # { name: String, count: 1..10 }
157
+ # end
158
+ # m.match?({ name: 'test', count: 5 }) # => true
159
+ # @return [Base]
160
+ # @see Matcher.of
161
+ # @see MatcherDsl
162
+ # @see ExpressionDsl
163
+ def self.build(&block)
164
+ with_build_session do |build_session|
165
+ builder = Builder.new(block.binding.receiver, build_session:)
166
+ object = builder.instance_exec(&block)
167
+ builder.refs.finalize
168
+
169
+ matcher = if builder.refs? && builder.refs.last_object_id == object.__id__
170
+ builder.refs.last_matcher
171
+ else
172
+ builder.matcher_of(object)
173
+ end
174
+
175
+ if (assigns = builder.assigns)
176
+ LetMatcher.new(assigns, matcher)
177
+ else
178
+ matcher
179
+ end
180
+ end
181
+ end
182
+
183
+ def self.max_reference_depth
184
+ @max_reference_depth ||= 100
185
+ end
186
+
187
+ def self.max_reference_depth=(value)
188
+ @max_reference_depth = value
189
+ end
190
+
191
+ ##
192
+ # Converts an object into a matcher
193
+ #
194
+ # The conversion depends on the type of the value:
195
+ #
196
+ # +Module+ or +Class+: match kind with {KindOfMatcher}
197
+ # Matcher.of(String).match?("Hello World!")
198
+ # +Range+: match between with {RangeMatcher}
199
+ # Matcher.of(1..10).match?(5)
200
+ # +Regexp+: match pattern with {RegexpMatcher}
201
+ # Matcher.of(/Hello/).match?("Hello World!")
202
+ # +Array+: match all elements with {ArrayMatcher}
203
+ # Matcher.of([1, String]).match?([1, "Hello"])
204
+ # +Hash+: match all entries with {HashMatcher}
205
+ # Matcher.of({ a: 1, b: 0..10 }).match?({ a: 1, b: 5 })
206
+ # +Expression+ or +Recorder+: match where evaluated expression is truthy
207
+ # (see {ExpressionMatcher})
208
+ # even = Matcher::Expression.build { _.even? }
209
+ # Matcher.of(even).match?(4)
210
+ # other objects: match equal value with {EqualMatcher}
211
+ # Matcher.of(1).match?(1)
212
+ # @param object the value to convert
213
+ # @return [Base]
214
+ def self.of(object, matcher_cache: nil, expression_cache: nil)
215
+ object = Expression.try_recorder(object)
216
+
217
+ case object
218
+ when NoMatcher
219
+ raise ArgumentError, "Cannot use #{object.class} as matcher"
220
+ when Module
221
+ KindOfMatcher.cache(object, matcher_cache)
222
+ when OptionalChain
223
+ object.fallback
224
+ when Base
225
+ object
226
+ when Expression
227
+ ExpressionMatcher.cache(object, matcher_cache, expression_cache)
228
+ when Proc
229
+ BlockMatcher.new(object)
230
+ when Range
231
+ RangeMatcher.cache(object, matcher_cache)
232
+ when Regexp
233
+ RegexpMatcher.cache(object, matcher_cache)
234
+ when Hash
235
+ hash = object.to_h do |k, v|
236
+ case k
237
+ when -> { Recorder.recorder?(_1) }
238
+ k = Recorder.to_expression(k)
239
+ when Optional
240
+ k = Optional.cache(k.value, matcher_cache)
241
+ when Base
242
+ raise "Cannot use matcher as key for hash matcher"
243
+ when NoKey
244
+ raise "Cannot use #{k.class} as key for hash matcher"
245
+ when Others
246
+ # keep k
247
+ else
248
+ k = Expression.expression_or_value(k)
249
+ end
250
+
251
+ [k, of(v, matcher_cache:, expression_cache:)]
252
+ end
253
+
254
+ HashMatcher.new(hash)
255
+ when Array
256
+ ArrayMatcher.new(object.map { of(_1, matcher_cache:, expression_cache:) })
257
+ when Optional
258
+ matcher = of(object.value, matcher_cache:, expression_cache:)
259
+
260
+ OptionalMatcher.cache(matcher, matcher_cache)
261
+ else
262
+ EqualMatcher.cache(object, matcher_cache)
263
+ end
264
+ end
265
+
266
+ def self.cache(object)
267
+ build_session = Matcher.build_session
268
+ matcher_cache = MatcherCache.current(build_session)
269
+ expression_cache = ExpressionCache.current(build_session)
270
+
271
+ of(object, matcher_cache:, expression_cache:)
272
+ end
273
+
274
+ def self.parenthesize(matcher)
275
+ matcher_to_s = matcher.to_s
276
+
277
+ matcher_to_s = "(#{matcher_to_s})" if
278
+ case matcher
279
+ when ExpressionMatcher
280
+ !matcher.negated &&
281
+ matcher.expression.precedence > Call::OPERATOR_PRECEDENCE[:^]
282
+ when EqualMatcher, KindOfMatcher, RangeMatcher, RegexpMatcher,
283
+ ArrayMatcher, HashMatcher
284
+
285
+ false
286
+ else
287
+ matcher_to_s !~ /\A(~?\w+(\(.*\)|\[.*\])?|-> \{.*})\z/
288
+ end
289
+
290
+ matcher_to_s
291
+ end
292
+
293
+ def self.settings
294
+ Thread.current[:matcher_settings_stack] || {}
295
+ end
296
+
297
+ def self.with_settings(**settings)
298
+ stack = (Thread.current[:matcher_settings_stack] ||= HashStack.new)
299
+ stack.push(settings)
300
+
301
+ begin
302
+ yield
303
+ ensure
304
+ stack.pop(settings)
305
+ Thread.current[:matcher_settings_stack] = nil if stack.empty?
306
+ end
307
+ end
308
+
309
+ def self.session
310
+ Thread.current[:matcher_session]
311
+ end
312
+
313
+ def self.with_session(initial = {})
314
+ return yield if Thread.current[:matcher_session]
315
+
316
+ begin
317
+ Thread.current[:matcher_session] = initial
318
+
319
+ yield
320
+ ensure
321
+ Thread.current[:matcher_session] = nil
322
+ end
323
+ end
324
+
325
+ def self.build_session
326
+ Thread.current[:matcher_build_session]
327
+ end
328
+
329
+ def self.with_build_session(initial = {})
330
+ build_session = Thread.current[:matcher_build_session]
331
+
332
+ return yield build_session if build_session
333
+
334
+ begin
335
+ Thread.current[:matcher_build_session] = initial
336
+
337
+ yield initial
338
+ ensure
339
+ Thread.current[:matcher_build_session] = nil
340
+ end
341
+ end
342
+
343
+ Debug.init
344
+ end
345
+
346
+ require_relative "matcher/messages/message_rules"
metadata ADDED
@@ -0,0 +1,174 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: matchers
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0.pre.1
5
+ platform: ruby
6
+ authors:
7
+ - Rico Jasper
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies: []
12
+ description: 'A DSL for building matchers that validate nested data structures. Ruby
13
+ literals like classes, ranges, regexps, arrays, and hashes are automatically converted
14
+ into matchers. Mismatches produce error trees with paths pointing to each failing
15
+ element.
16
+
17
+ '
18
+ email:
19
+ - jasper.rico@gmail.com
20
+ executables: []
21
+ extensions: []
22
+ extra_rdoc_files: []
23
+ files:
24
+ - lib/matcher.rb
25
+ - lib/matcher/assertions.rb
26
+ - lib/matcher/autoload.rb
27
+ - lib/matcher/base.rb
28
+ - lib/matcher/compatibility.rb
29
+ - lib/matcher/debug.rb
30
+ - lib/matcher/dsl/builder.rb
31
+ - lib/matcher/dsl/chain.rb
32
+ - lib/matcher/dsl/expression_dsl.rb
33
+ - lib/matcher/dsl/matcher_dsl.rb
34
+ - lib/matcher/dsl/optional.rb
35
+ - lib/matcher/dsl/optional_chain.rb
36
+ - lib/matcher/dsl/others.rb
37
+ - lib/matcher/errors/and_error.rb
38
+ - lib/matcher/errors/boolean_collector.rb
39
+ - lib/matcher/errors/element_error.rb
40
+ - lib/matcher/errors/empty_error.rb
41
+ - lib/matcher/errors/error.rb
42
+ - lib/matcher/errors/error_collector.rb
43
+ - lib/matcher/errors/nested_error.rb
44
+ - lib/matcher/errors/or_error.rb
45
+ - lib/matcher/expression_cache.rb
46
+ - lib/matcher/expression_labeler.rb
47
+ - lib/matcher/expressions/array_expression.rb
48
+ - lib/matcher/expressions/block.rb
49
+ - lib/matcher/expressions/call.rb
50
+ - lib/matcher/expressions/call_error.rb
51
+ - lib/matcher/expressions/constant.rb
52
+ - lib/matcher/expressions/expression.rb
53
+ - lib/matcher/expressions/expression_walker.rb
54
+ - lib/matcher/expressions/hash_expression.rb
55
+ - lib/matcher/expressions/proc_expression.rb
56
+ - lib/matcher/expressions/range_expression.rb
57
+ - lib/matcher/expressions/recorder.rb
58
+ - lib/matcher/expressions/rescue_last_error_expression.rb
59
+ - lib/matcher/expressions/set_expression.rb
60
+ - lib/matcher/expressions/string_expression.rb
61
+ - lib/matcher/expressions/symbol_proc.rb
62
+ - lib/matcher/expressions/variable.rb
63
+ - lib/matcher/hash_stack.rb
64
+ - lib/matcher/list.rb
65
+ - lib/matcher/markers.rb
66
+ - lib/matcher/matcher_cache.rb
67
+ - lib/matcher/matchers/all_matcher.rb
68
+ - lib/matcher/matchers/always_matcher.rb
69
+ - lib/matcher/matchers/any_matcher.rb
70
+ - lib/matcher/matchers/array_matcher.rb
71
+ - lib/matcher/matchers/block_matcher.rb
72
+ - lib/matcher/matchers/boolean_matcher.rb
73
+ - lib/matcher/matchers/dig_matcher.rb
74
+ - lib/matcher/matchers/each_matcher.rb
75
+ - lib/matcher/matchers/each_pair_matcher.rb
76
+ - lib/matcher/matchers/equal_matcher.rb
77
+ - lib/matcher/matchers/equal_set_matcher.rb
78
+ - lib/matcher/matchers/expression_matcher.rb
79
+ - lib/matcher/matchers/filter_matcher.rb
80
+ - lib/matcher/matchers/hash_matcher.rb
81
+ - lib/matcher/matchers/imply_matcher.rb
82
+ - lib/matcher/matchers/imply_some_matcher.rb
83
+ - lib/matcher/matchers/index_by_matcher.rb
84
+ - lib/matcher/matchers/inline_matcher.rb
85
+ - lib/matcher/matchers/keys_matcher.rb
86
+ - lib/matcher/matchers/kind_of_matcher.rb
87
+ - lib/matcher/matchers/lazy_all_matcher.rb
88
+ - lib/matcher/matchers/lazy_any_matcher.rb
89
+ - lib/matcher/matchers/let_matcher.rb
90
+ - lib/matcher/matchers/map_matcher.rb
91
+ - lib/matcher/matchers/negated_array_matcher.rb
92
+ - lib/matcher/matchers/negated_each_matcher.rb
93
+ - lib/matcher/matchers/negated_each_pair_matcher.rb
94
+ - lib/matcher/matchers/negated_imply_some_matcher.rb
95
+ - lib/matcher/matchers/negated_matcher.rb
96
+ - lib/matcher/matchers/negated_project_matcher.rb
97
+ - lib/matcher/matchers/never_matcher.rb
98
+ - lib/matcher/matchers/one_matcher.rb
99
+ - lib/matcher/matchers/optional_matcher.rb
100
+ - lib/matcher/matchers/parse_float_matcher.rb
101
+ - lib/matcher/matchers/parse_integer_matcher.rb
102
+ - lib/matcher/matchers/parse_iso8601_helper.rb
103
+ - lib/matcher/matchers/parse_iso8601_matcher.rb
104
+ - lib/matcher/matchers/parse_json_helper.rb
105
+ - lib/matcher/matchers/parse_json_matcher.rb
106
+ - lib/matcher/matchers/project_matcher.rb
107
+ - lib/matcher/matchers/raises_matcher.rb
108
+ - lib/matcher/matchers/range_matcher.rb
109
+ - lib/matcher/matchers/reference_matcher.rb
110
+ - lib/matcher/matchers/reference_matcher_collection.rb
111
+ - lib/matcher/matchers/regexp_matcher.rb
112
+ - lib/matcher/messages/expected_phrasing.rb
113
+ - lib/matcher/messages/message.rb
114
+ - lib/matcher/messages/message_builder.rb
115
+ - lib/matcher/messages/message_rules.rb
116
+ - lib/matcher/messages/namespaced_message_builder.rb
117
+ - lib/matcher/messages/phrasing.rb
118
+ - lib/matcher/messages/standard_message_builder.rb
119
+ - lib/matcher/patterns/ast_mapping.rb
120
+ - lib/matcher/patterns/capture_hole.rb
121
+ - lib/matcher/patterns/constant_hole.rb
122
+ - lib/matcher/patterns/hole.rb
123
+ - lib/matcher/patterns/method_hole.rb
124
+ - lib/matcher/patterns/pattern.rb
125
+ - lib/matcher/patterns/pattern_building.rb
126
+ - lib/matcher/patterns/pattern_capture.rb
127
+ - lib/matcher/patterns/pattern_match.rb
128
+ - lib/matcher/patterns/variable_hole.rb
129
+ - lib/matcher/reporter.rb
130
+ - lib/matcher/rules/message_factory.rb
131
+ - lib/matcher/rules/message_rule.rb
132
+ - lib/matcher/rules/message_rule_context.rb
133
+ - lib/matcher/rules/rule_builder.rb
134
+ - lib/matcher/rules/rule_set.rb
135
+ - lib/matcher/rules/transform_builder.rb
136
+ - lib/matcher/rules/transform_mapping.rb
137
+ - lib/matcher/rules/transform_rule.rb
138
+ - lib/matcher/state.rb
139
+ - lib/matcher/testing.rb
140
+ - lib/matcher/testing/error_builder.rb
141
+ - lib/matcher/testing/error_checker.rb
142
+ - lib/matcher/testing/error_testing.rb
143
+ - lib/matcher/testing/pattern_testing.rb
144
+ - lib/matcher/testing/pattern_testing_scope.rb
145
+ - lib/matcher/undefined.rb
146
+ - lib/matcher/utils.rb
147
+ - lib/matcher/utils/mapping_utils.rb
148
+ - lib/matcher/version.rb
149
+ homepage: https://github.com/rjasper/ruby-matchers
150
+ licenses:
151
+ - MIT
152
+ metadata:
153
+ homepage_uri: https://github.com/rjasper/ruby-matchers
154
+ source_code_uri: https://github.com/rjasper/ruby-matchers/tree/pre
155
+ changelog_uri: https://github.com/rjasper/ruby-matchers/releases
156
+ rubygems_mfa_required: 'true'
157
+ rdoc_options: []
158
+ require_paths:
159
+ - lib
160
+ required_ruby_version: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - ">="
163
+ - !ruby/object:Gem::Version
164
+ version: 3.2.0
165
+ required_rubygems_version: !ruby/object:Gem::Requirement
166
+ requirements:
167
+ - - ">="
168
+ - !ruby/object:Gem::Version
169
+ version: '0'
170
+ requirements: []
171
+ rubygems_version: 3.6.9
172
+ specification_version: 4
173
+ summary: Composable data structure matchers with error reporting
174
+ test_files: []