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.
- checksums.yaml +7 -0
- data/lib/matcher/assertions.rb +19 -0
- data/lib/matcher/autoload.rb +5 -0
- data/lib/matcher/base.rb +183 -0
- data/lib/matcher/compatibility.rb +34 -0
- data/lib/matcher/debug.rb +62 -0
- data/lib/matcher/dsl/builder.rb +99 -0
- data/lib/matcher/dsl/chain.rb +84 -0
- data/lib/matcher/dsl/expression_dsl.rb +306 -0
- data/lib/matcher/dsl/matcher_dsl.rb +5 -0
- data/lib/matcher/dsl/optional.rb +82 -0
- data/lib/matcher/dsl/optional_chain.rb +24 -0
- data/lib/matcher/dsl/others.rb +28 -0
- data/lib/matcher/errors/and_error.rb +88 -0
- data/lib/matcher/errors/boolean_collector.rb +51 -0
- data/lib/matcher/errors/element_error.rb +24 -0
- data/lib/matcher/errors/empty_error.rb +23 -0
- data/lib/matcher/errors/error.rb +39 -0
- data/lib/matcher/errors/error_collector.rb +100 -0
- data/lib/matcher/errors/nested_error.rb +98 -0
- data/lib/matcher/errors/or_error.rb +88 -0
- data/lib/matcher/expression_cache.rb +57 -0
- data/lib/matcher/expression_labeler.rb +96 -0
- data/lib/matcher/expressions/array_expression.rb +45 -0
- data/lib/matcher/expressions/block.rb +189 -0
- data/lib/matcher/expressions/call.rb +307 -0
- data/lib/matcher/expressions/call_error.rb +45 -0
- data/lib/matcher/expressions/constant.rb +53 -0
- data/lib/matcher/expressions/expression.rb +237 -0
- data/lib/matcher/expressions/expression_walker.rb +77 -0
- data/lib/matcher/expressions/hash_expression.rb +59 -0
- data/lib/matcher/expressions/proc_expression.rb +96 -0
- data/lib/matcher/expressions/range_expression.rb +65 -0
- data/lib/matcher/expressions/recorder.rb +136 -0
- data/lib/matcher/expressions/rescue_last_error_expression.rb +49 -0
- data/lib/matcher/expressions/set_expression.rb +45 -0
- data/lib/matcher/expressions/string_expression.rb +53 -0
- data/lib/matcher/expressions/symbol_proc.rb +53 -0
- data/lib/matcher/expressions/variable.rb +87 -0
- data/lib/matcher/hash_stack.rb +52 -0
- data/lib/matcher/list.rb +102 -0
- data/lib/matcher/markers.rb +7 -0
- data/lib/matcher/matcher_cache.rb +18 -0
- data/lib/matcher/matchers/all_matcher.rb +60 -0
- data/lib/matcher/matchers/always_matcher.rb +34 -0
- data/lib/matcher/matchers/any_matcher.rb +70 -0
- data/lib/matcher/matchers/array_matcher.rb +72 -0
- data/lib/matcher/matchers/block_matcher.rb +61 -0
- data/lib/matcher/matchers/boolean_matcher.rb +37 -0
- data/lib/matcher/matchers/dig_matcher.rb +149 -0
- data/lib/matcher/matchers/each_matcher.rb +85 -0
- data/lib/matcher/matchers/each_pair_matcher.rb +119 -0
- data/lib/matcher/matchers/equal_matcher.rb +198 -0
- data/lib/matcher/matchers/equal_set_matcher.rb +112 -0
- data/lib/matcher/matchers/expression_matcher.rb +69 -0
- data/lib/matcher/matchers/filter_matcher.rb +115 -0
- data/lib/matcher/matchers/hash_matcher.rb +315 -0
- data/lib/matcher/matchers/imply_matcher.rb +83 -0
- data/lib/matcher/matchers/imply_some_matcher.rb +116 -0
- data/lib/matcher/matchers/index_by_matcher.rb +177 -0
- data/lib/matcher/matchers/inline_matcher.rb +101 -0
- data/lib/matcher/matchers/keys_matcher.rb +131 -0
- data/lib/matcher/matchers/kind_of_matcher.rb +35 -0
- data/lib/matcher/matchers/lazy_all_matcher.rb +69 -0
- data/lib/matcher/matchers/lazy_any_matcher.rb +69 -0
- data/lib/matcher/matchers/let_matcher.rb +73 -0
- data/lib/matcher/matchers/map_matcher.rb +148 -0
- data/lib/matcher/matchers/negated_array_matcher.rb +38 -0
- data/lib/matcher/matchers/negated_each_matcher.rb +36 -0
- data/lib/matcher/matchers/negated_each_pair_matcher.rb +38 -0
- data/lib/matcher/matchers/negated_imply_some_matcher.rb +46 -0
- data/lib/matcher/matchers/negated_matcher.rb +25 -0
- data/lib/matcher/matchers/negated_project_matcher.rb +31 -0
- data/lib/matcher/matchers/never_matcher.rb +35 -0
- data/lib/matcher/matchers/one_matcher.rb +68 -0
- data/lib/matcher/matchers/optional_matcher.rb +38 -0
- data/lib/matcher/matchers/parse_float_matcher.rb +86 -0
- data/lib/matcher/matchers/parse_integer_matcher.rb +101 -0
- data/lib/matcher/matchers/parse_iso8601_helper.rb +41 -0
- data/lib/matcher/matchers/parse_iso8601_matcher.rb +52 -0
- data/lib/matcher/matchers/parse_json_helper.rb +43 -0
- data/lib/matcher/matchers/parse_json_matcher.rb +59 -0
- data/lib/matcher/matchers/project_matcher.rb +72 -0
- data/lib/matcher/matchers/raises_matcher.rb +131 -0
- data/lib/matcher/matchers/range_matcher.rb +50 -0
- data/lib/matcher/matchers/reference_matcher.rb +213 -0
- data/lib/matcher/matchers/reference_matcher_collection.rb +57 -0
- data/lib/matcher/matchers/regexp_matcher.rb +86 -0
- data/lib/matcher/messages/expected_phrasing.rb +355 -0
- data/lib/matcher/messages/message.rb +104 -0
- data/lib/matcher/messages/message_builder.rb +35 -0
- data/lib/matcher/messages/message_rules.rb +240 -0
- data/lib/matcher/messages/namespaced_message_builder.rb +19 -0
- data/lib/matcher/messages/phrasing.rb +59 -0
- data/lib/matcher/messages/standard_message_builder.rb +105 -0
- data/lib/matcher/patterns/ast_mapping.rb +42 -0
- data/lib/matcher/patterns/capture_hole.rb +33 -0
- data/lib/matcher/patterns/constant_hole.rb +14 -0
- data/lib/matcher/patterns/hole.rb +30 -0
- data/lib/matcher/patterns/method_hole.rb +62 -0
- data/lib/matcher/patterns/pattern.rb +104 -0
- data/lib/matcher/patterns/pattern_building.rb +39 -0
- data/lib/matcher/patterns/pattern_capture.rb +11 -0
- data/lib/matcher/patterns/pattern_match.rb +29 -0
- data/lib/matcher/patterns/variable_hole.rb +14 -0
- data/lib/matcher/reporter.rb +103 -0
- data/lib/matcher/rules/message_factory.rb +26 -0
- data/lib/matcher/rules/message_rule.rb +18 -0
- data/lib/matcher/rules/message_rule_context.rb +26 -0
- data/lib/matcher/rules/rule_builder.rb +29 -0
- data/lib/matcher/rules/rule_set.rb +57 -0
- data/lib/matcher/rules/transform_builder.rb +24 -0
- data/lib/matcher/rules/transform_mapping.rb +5 -0
- data/lib/matcher/rules/transform_rule.rb +21 -0
- data/lib/matcher/state.rb +40 -0
- data/lib/matcher/testing/error_builder.rb +62 -0
- data/lib/matcher/testing/error_checker.rb +514 -0
- data/lib/matcher/testing/error_testing.rb +37 -0
- data/lib/matcher/testing/pattern_testing.rb +11 -0
- data/lib/matcher/testing/pattern_testing_scope.rb +34 -0
- data/lib/matcher/testing.rb +107 -0
- data/lib/matcher/undefined.rb +10 -0
- data/lib/matcher/utils/mapping_utils.rb +61 -0
- data/lib/matcher/utils.rb +72 -0
- data/lib/matcher/version.rb +5 -0
- data/lib/matcher.rb +346 -0
- 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
|
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: []
|