matchers 0.1.0.pre.test

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.

Potentially problematic release.


This version of matchers might be problematic. Click here for more details.

Files changed (123) hide show
  1. checksums.yaml +7 -0
  2. data/lib/matcher/assertions.rb +21 -0
  3. data/lib/matcher/base.rb +189 -0
  4. data/lib/matcher/builder.rb +74 -0
  5. data/lib/matcher/chain.rb +60 -0
  6. data/lib/matcher/debug.rb +48 -0
  7. data/lib/matcher/errors/and_error.rb +86 -0
  8. data/lib/matcher/errors/boolean_collector.rb +51 -0
  9. data/lib/matcher/errors/element_error.rb +24 -0
  10. data/lib/matcher/errors/empty_error.rb +23 -0
  11. data/lib/matcher/errors/error.rb +39 -0
  12. data/lib/matcher/errors/error_collector.rb +99 -0
  13. data/lib/matcher/errors/nested_error.rb +96 -0
  14. data/lib/matcher/errors/or_error.rb +86 -0
  15. data/lib/matcher/expression_cache.rb +57 -0
  16. data/lib/matcher/expression_labeler.rb +91 -0
  17. data/lib/matcher/expressions/array_expression.rb +45 -0
  18. data/lib/matcher/expressions/block.rb +153 -0
  19. data/lib/matcher/expressions/call.rb +338 -0
  20. data/lib/matcher/expressions/call_error.rb +45 -0
  21. data/lib/matcher/expressions/constant.rb +53 -0
  22. data/lib/matcher/expressions/expression.rb +147 -0
  23. data/lib/matcher/expressions/expression_building.rb +258 -0
  24. data/lib/matcher/expressions/expression_walker.rb +73 -0
  25. data/lib/matcher/expressions/hash_expression.rb +59 -0
  26. data/lib/matcher/expressions/proc_expression.rb +92 -0
  27. data/lib/matcher/expressions/range_expression.rb +58 -0
  28. data/lib/matcher/expressions/recorder.rb +86 -0
  29. data/lib/matcher/expressions/rescue_last_error_expression.rb +44 -0
  30. data/lib/matcher/expressions/set_expression.rb +45 -0
  31. data/lib/matcher/expressions/string_expression.rb +53 -0
  32. data/lib/matcher/expressions/symbol_proc.rb +53 -0
  33. data/lib/matcher/expressions/variable.rb +85 -0
  34. data/lib/matcher/hash_stack.rb +53 -0
  35. data/lib/matcher/list.rb +102 -0
  36. data/lib/matcher/markers/optional.rb +80 -0
  37. data/lib/matcher/markers/others.rb +28 -0
  38. data/lib/matcher/matcher_cache.rb +18 -0
  39. data/lib/matcher/matchers/all_matcher.rb +60 -0
  40. data/lib/matcher/matchers/always_matcher.rb +28 -0
  41. data/lib/matcher/matchers/any_matcher.rb +70 -0
  42. data/lib/matcher/matchers/array_matcher.rb +35 -0
  43. data/lib/matcher/matchers/block_matcher.rb +59 -0
  44. data/lib/matcher/matchers/boolean_matcher.rb +35 -0
  45. data/lib/matcher/matchers/dig_matcher.rb +146 -0
  46. data/lib/matcher/matchers/each_matcher.rb +52 -0
  47. data/lib/matcher/matchers/each_pair_matcher.rb +119 -0
  48. data/lib/matcher/matchers/equal_matcher.rb +197 -0
  49. data/lib/matcher/matchers/equal_set_matcher.rb +99 -0
  50. data/lib/matcher/matchers/expression_matcher.rb +73 -0
  51. data/lib/matcher/matchers/filter_matcher.rb +111 -0
  52. data/lib/matcher/matchers/hash_matcher.rb +223 -0
  53. data/lib/matcher/matchers/imply_matcher.rb +81 -0
  54. data/lib/matcher/matchers/imply_some_matcher.rb +112 -0
  55. data/lib/matcher/matchers/index_by_matcher.rb +175 -0
  56. data/lib/matcher/matchers/inline_matcher.rb +99 -0
  57. data/lib/matcher/matchers/keys_matcher.rb +121 -0
  58. data/lib/matcher/matchers/kind_of_matcher.rb +35 -0
  59. data/lib/matcher/matchers/lazy_all_matcher.rb +68 -0
  60. data/lib/matcher/matchers/lazy_any_matcher.rb +68 -0
  61. data/lib/matcher/matchers/let_matcher.rb +73 -0
  62. data/lib/matcher/matchers/map_matcher.rb +129 -0
  63. data/lib/matcher/matchers/matcher_building.rb +5 -0
  64. data/lib/matcher/matchers/negated_array_matcher.rb +38 -0
  65. data/lib/matcher/matchers/negated_each_matcher.rb +36 -0
  66. data/lib/matcher/matchers/negated_each_pair_matcher.rb +38 -0
  67. data/lib/matcher/matchers/negated_imply_some_matcher.rb +46 -0
  68. data/lib/matcher/matchers/negated_matcher.rb +23 -0
  69. data/lib/matcher/matchers/negated_project_matcher.rb +31 -0
  70. data/lib/matcher/matchers/never_matcher.rb +29 -0
  71. data/lib/matcher/matchers/one_matcher.rb +70 -0
  72. data/lib/matcher/matchers/optional_matcher.rb +38 -0
  73. data/lib/matcher/matchers/parse_float_matcher.rb +86 -0
  74. data/lib/matcher/matchers/parse_integer_matcher.rb +98 -0
  75. data/lib/matcher/matchers/parse_iso8601_matcher.rb +92 -0
  76. data/lib/matcher/matchers/parse_json_matcher.rb +95 -0
  77. data/lib/matcher/matchers/project_matcher.rb +68 -0
  78. data/lib/matcher/matchers/raises_matcher.rb +124 -0
  79. data/lib/matcher/matchers/range_matcher.rb +47 -0
  80. data/lib/matcher/matchers/reference_matcher.rb +111 -0
  81. data/lib/matcher/matchers/reference_matcher_collection.rb +57 -0
  82. data/lib/matcher/matchers/regexp_matcher.rb +84 -0
  83. data/lib/matcher/messages/expected_phrasing.rb +342 -0
  84. data/lib/matcher/messages/message.rb +102 -0
  85. data/lib/matcher/messages/message_builder.rb +35 -0
  86. data/lib/matcher/messages/message_rules.rb +223 -0
  87. data/lib/matcher/messages/namespaced_message_builder.rb +19 -0
  88. data/lib/matcher/messages/phrasing.rb +57 -0
  89. data/lib/matcher/messages/standard_message_builder.rb +105 -0
  90. data/lib/matcher/once_before.rb +18 -0
  91. data/lib/matcher/optional_chain.rb +24 -0
  92. data/lib/matcher/patterns/ast_mapping.rb +42 -0
  93. data/lib/matcher/patterns/capture_hole.rb +33 -0
  94. data/lib/matcher/patterns/constant_hole.rb +14 -0
  95. data/lib/matcher/patterns/hole.rb +30 -0
  96. data/lib/matcher/patterns/method_hole.rb +58 -0
  97. data/lib/matcher/patterns/pattern.rb +92 -0
  98. data/lib/matcher/patterns/pattern_building.rb +39 -0
  99. data/lib/matcher/patterns/pattern_capture.rb +11 -0
  100. data/lib/matcher/patterns/pattern_match.rb +29 -0
  101. data/lib/matcher/patterns/variable_hole.rb +14 -0
  102. data/lib/matcher/reporter.rb +98 -0
  103. data/lib/matcher/rules/message_factory.rb +25 -0
  104. data/lib/matcher/rules/message_rule.rb +18 -0
  105. data/lib/matcher/rules/message_rule_context.rb +24 -0
  106. data/lib/matcher/rules/rule_builder.rb +29 -0
  107. data/lib/matcher/rules/rule_set.rb +57 -0
  108. data/lib/matcher/rules/transform_builder.rb +24 -0
  109. data/lib/matcher/rules/transform_mapping.rb +5 -0
  110. data/lib/matcher/rules/transform_rule.rb +21 -0
  111. data/lib/matcher/state.rb +40 -0
  112. data/lib/matcher/testing/error_builder.rb +62 -0
  113. data/lib/matcher/testing/error_checker.rb +496 -0
  114. data/lib/matcher/testing/error_testing.rb +37 -0
  115. data/lib/matcher/testing/pattern_testing.rb +11 -0
  116. data/lib/matcher/testing/pattern_testing_scope.rb +34 -0
  117. data/lib/matcher/testing.rb +102 -0
  118. data/lib/matcher/undefined.rb +10 -0
  119. data/lib/matcher/utils/mapping_utils.rb +61 -0
  120. data/lib/matcher/utils.rb +72 -0
  121. data/lib/matcher/version.rb +5 -0
  122. data/lib/matcher.rb +337 -0
  123. metadata +167 -0
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Matcher
4
+ class MessageFactory
5
+ def initialize(value_paths, expressions, block)
6
+ @value_paths = value_paths
7
+ @expressions = expressions
8
+ @negate = false
9
+ @block = block
10
+ end
11
+
12
+ def negate!
13
+ @negate = !@negate
14
+ end
15
+
16
+ def create(context, value_tree)
17
+ # Using reduce instead of dig so it will fail if an unexpected leaf is encountered.
18
+ values = @value_paths.transform_values { _1.reduce(value_tree, :[]) }
19
+ message = context.instance_exec(values, @expressions, &@block)
20
+ message.negate! if @negate
21
+
22
+ message
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Matcher
4
+ class MessageRule
5
+ def initialize(patterns, block)
6
+ @patterns = patterns
7
+ @block = block
8
+ end
9
+
10
+ attr_reader :patterns
11
+
12
+ def apply(match)
13
+ expressions = @block.arity >= 2 ? match.expressions : nil
14
+
15
+ MessageFactory.new(match.value_paths, expressions, @block)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Matcher
4
+ class MessageRuleContext
5
+ extend Forwardable
6
+
7
+ def initialize(matcher, state)
8
+ @matcher = matcher
9
+ @state = state
10
+ end
11
+
12
+ def standard_message
13
+ StandardMessageBuilder.new(!@matcher.negated, @state.actual)
14
+ end
15
+
16
+ def expression_message
17
+ NamespacedMessageBuilder.new(!@matcher.negated, @state.actual, :expression)
18
+ end
19
+
20
+ def given
21
+ @matcher.expression.given_for(@state.values)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Matcher
4
+ class RuleBuilder
5
+ include PatternBuilding
6
+
7
+ def initialize(rules = [], build_session: Matcher.build_session)
8
+ ExpressionBuilding.init(self, build_session)
9
+
10
+ @rules = rules
11
+ end
12
+
13
+ attr_reader :rules
14
+
15
+ def transform(*patterns, negate: false, &block)
16
+ patterns.map! { pattern_of(_1) }
17
+ @rules << TransformRule.new(patterns, negate, block)
18
+
19
+ nil
20
+ end
21
+
22
+ def message(*patterns, &block)
23
+ patterns.map! { pattern_of(_1) }
24
+ @rules << MessageRule.new(patterns, block)
25
+
26
+ nil
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Matcher
4
+ class RuleSet
5
+ def initialize(rules = [], &)
6
+ @rules = rules
7
+
8
+ configure(&) if block_given?
9
+ end
10
+
11
+ def configure(&)
12
+ Matcher.with_build_session do |build_session|
13
+ builder = RuleBuilder.new(@rules, build_session:)
14
+ builder.instance_exec(&)
15
+ end
16
+
17
+ self
18
+ end
19
+
20
+ def apply(expression)
21
+ cur = expression
22
+ mapping = AstMapping.new
23
+ result = nil
24
+ negate = false
25
+
26
+ while (rule, match = find_rule(cur, mapping))
27
+ result = rule.apply(match)
28
+
29
+ if rule.is_a?(MessageRule)
30
+ result.negate! if negate
31
+
32
+ return result
33
+ end
34
+
35
+ cur = result.expression
36
+ mapping = result.mapping
37
+ negate = !negate if rule.negate?
38
+ end
39
+
40
+ result
41
+ end
42
+
43
+ private
44
+
45
+ def find_rule(expression, mapping)
46
+ @rules.each do |rule|
47
+ rule.patterns.each do |pattern|
48
+ match = pattern.match(expression, mapping)
49
+
50
+ return [rule, match] if match
51
+ end
52
+ end
53
+
54
+ nil
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Matcher
4
+ class TransformBuilder
5
+ include Singleton
6
+
7
+ def call(match, receiver, method, *args, **kwargs)
8
+ expression = Call.new(
9
+ receiver.expression,
10
+ method,
11
+ args.map(&:expression),
12
+ kwargs.transform_values(&:expression),
13
+ )
14
+
15
+ mapping = TransformMapping.new
16
+ mapping.path = match.mapping.path
17
+ mapping.receiver = receiver.mapping
18
+ mapping.args = args.map(&:mapping)
19
+ mapping.kwargs = kwargs.transform_values(&:mapping)
20
+
21
+ PatternCapture.new(expression, mapping)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Matcher
4
+ TransformMapping = Struct.new(:path, :receiver, :args, :kwargs)
5
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Matcher
4
+ class TransformRule
5
+ def initialize(patterns, negate, block)
6
+ @patterns = patterns
7
+ @negate = negate
8
+ @block = block
9
+ end
10
+
11
+ attr_reader :patterns
12
+
13
+ def negate?
14
+ @negate
15
+ end
16
+
17
+ def apply(match)
18
+ TransformBuilder.instance.instance_exec(match, &@block)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Matcher
4
+ class State
5
+ def initialize(values, boolean: false)
6
+ @values = values
7
+ @boolean = boolean
8
+ end
9
+
10
+ attr_reader :values
11
+
12
+ def boolean?
13
+ @boolean
14
+ end
15
+
16
+ def actual
17
+ @values[:actual]
18
+ end
19
+
20
+ def errors
21
+ @error_collector ||= new_collector
22
+ end
23
+
24
+ def new_collector
25
+ @boolean ? BooleanCollector.new : ErrorCollector.new
26
+ end
27
+
28
+ def result
29
+ @error_collector&.error || EmptyError.instance
30
+ end
31
+
32
+ def report(actual = self.actual)
33
+ StandardMessageBuilder.new(false, actual)
34
+ end
35
+
36
+ def expected(actual = self.actual)
37
+ StandardMessageBuilder.new(true, actual)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Matcher
4
+ class ErrorBuilder
5
+ def self.build(use_or: false, &)
6
+ errors = build_errors(&)
7
+
8
+ if use_or
9
+ OrError.from(errors)
10
+ else
11
+ AndError.from(errors)
12
+ end
13
+ end
14
+
15
+ def self.build_errors(&)
16
+ builder = ErrorBuilder.new
17
+ builder.instance_exec(&) if block_given?
18
+ builder.errors
19
+ end
20
+
21
+ attr_reader :errors
22
+
23
+ def initialize
24
+ @errors = []
25
+ end
26
+
27
+ def _or(*path, &)
28
+ errors = ErrorBuilder.build_errors(&)
29
+ error = OrError.from(errors)
30
+
31
+ @errors << nest(path, error)
32
+ end
33
+
34
+ def _and(*path, &)
35
+ errors = ErrorBuilder.build_errors(&)
36
+ error = AndError.from(errors)
37
+
38
+ @errors << nest(path, error)
39
+ end
40
+
41
+ def error(path_or_message, message = nil)
42
+ if message.nil?
43
+ @errors << ElementError.new(path_or_message)
44
+ else
45
+ path = Array(path_or_message)
46
+ element = ElementError.new(message)
47
+
48
+ @errors << nest(path, element)
49
+ end
50
+ end
51
+
52
+ def msg(actual)
53
+ StandardMessageBuilder.new(false, actual)
54
+ end
55
+
56
+ private
57
+
58
+ def nest(path, error)
59
+ path.reverse_each.reduce(error) { NestedError.from(_2, _1) }
60
+ end
61
+ end
62
+ end