mongory 0.4.0 → 0.6.0

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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +44 -0
  3. data/README.md +37 -7
  4. data/examples/benchmark-rails.rb +52 -0
  5. data/examples/benchmark.rb +79 -18
  6. data/lib/generators/mongory/matcher/matcher_generator.rb +1 -1
  7. data/lib/mongory/converters/abstract_converter.rb +22 -32
  8. data/lib/mongory/converters/condition_converter.rb +7 -4
  9. data/lib/mongory/converters/data_converter.rb +18 -7
  10. data/lib/mongory/converters/key_converter.rb +43 -19
  11. data/lib/mongory/converters/value_converter.rb +24 -19
  12. data/lib/mongory/matchers/abstract_matcher.rb +94 -32
  13. data/lib/mongory/matchers/abstract_multi_matcher.rb +16 -45
  14. data/lib/mongory/matchers/and_matcher.rb +38 -10
  15. data/lib/mongory/matchers/array_record_matcher.rb +54 -28
  16. data/lib/mongory/matchers/elem_match_matcher.rb +13 -9
  17. data/lib/mongory/matchers/eq_matcher.rb +12 -7
  18. data/lib/mongory/matchers/every_matcher.rb +20 -9
  19. data/lib/mongory/matchers/exists_matcher.rb +15 -14
  20. data/lib/mongory/matchers/field_matcher.rb +58 -38
  21. data/lib/mongory/matchers/gt_matcher.rb +15 -7
  22. data/lib/mongory/matchers/gte_matcher.rb +15 -7
  23. data/lib/mongory/matchers/hash_condition_matcher.rb +47 -25
  24. data/lib/mongory/matchers/in_matcher.rb +12 -10
  25. data/lib/mongory/matchers/literal_matcher.rb +42 -48
  26. data/lib/mongory/matchers/lt_matcher.rb +15 -7
  27. data/lib/mongory/matchers/lte_matcher.rb +15 -7
  28. data/lib/mongory/matchers/ne_matcher.rb +12 -7
  29. data/lib/mongory/matchers/nin_matcher.rb +12 -9
  30. data/lib/mongory/matchers/not_matcher.rb +9 -5
  31. data/lib/mongory/matchers/or_matcher.rb +42 -13
  32. data/lib/mongory/matchers/present_matcher.rb +14 -15
  33. data/lib/mongory/matchers/regex_matcher.rb +37 -22
  34. data/lib/mongory/matchers.rb +0 -1
  35. data/lib/mongory/query_builder.rb +88 -26
  36. data/lib/mongory/query_matcher.rb +39 -12
  37. data/lib/mongory/query_operator.rb +1 -1
  38. data/lib/mongory/utils/context.rb +41 -0
  39. data/lib/mongory/utils/debugger.rb +6 -4
  40. data/lib/mongory/utils.rb +1 -0
  41. data/lib/mongory/version.rb +1 -1
  42. data/lib/mongory.rb +3 -3
  43. data/mongory.gemspec +3 -3
  44. metadata +9 -9
  45. data/lib/mongory/matchers/README.md +0 -57
  46. data/lib/mongory/matchers/abstract_operator_matcher.rb +0 -46
@@ -2,18 +2,22 @@
2
2
 
3
3
  module Mongory
4
4
  module Matchers
5
- # FieldMatcher is responsible for extracting a value from a record
6
- # using a field (or index) and then delegating the match to LiteralMatcher logic.
5
+ # FieldMatcher handles field-level matching by extracting and comparing field values.
7
6
  #
8
- # It handles nested access in structures like Hashes or Arrays, and guards
9
- # against types that should not be dig into (e.g., String, Symbol, Proc).
7
+ # This matcher is responsible for:
8
+ # 1. Extracting field values from records using dot notation
9
+ # 2. Converting extracted values if needed
10
+ # 3. Delegating the actual comparison to a submatcher
10
11
  #
11
- # This matcher is typically used when the query refers to a specific field,
12
- # like `{ age: { :$gte => 18 } }` where `:age` is passed as the dig field.
12
+ # It supports:
13
+ # - Hash records with string/symbol keys
14
+ # - Array records with numeric indices
15
+ # - Objects that respond to `[]`
13
16
  #
14
- # @example
15
- # matcher = FieldMatcher.build(:age, { :$gte => 18 })
16
- # matcher.match?({ age: 20 }) #=> true
17
+ # @example Basic field matching
18
+ # matcher = FieldMatcher.build('age', 30)
19
+ # matcher.match?({ 'age' => 30 }) #=> true
20
+ # matcher.match?({ age: 30 }) #=> true
17
21
  #
18
22
  # @see LiteralMatcher
19
23
  class FieldMatcher < LiteralMatcher
@@ -29,16 +33,18 @@ module Mongory
29
33
  ::Symbol
30
34
  ].freeze
31
35
 
32
- # Initializes the matcher with a target field and condition.
36
+ # Initializes a new field matcher.
33
37
  #
34
- # @param field [Object] the field (or index) used to dig into the record
35
- # @param condition [Object] the condition to match against the extracted value
36
- def initialize(field, condition)
38
+ # @param field [String, Symbol] the field to match against
39
+ # @param condition [Object] the condition to match with
40
+ # @param context [Context] the query context
41
+ def initialize(field, condition, context: Context.new)
37
42
  @field = field
38
- super(condition)
43
+ super(condition, context: context)
39
44
  end
40
45
 
41
- # Performs field-based matching against the given record.
46
+ # Creates a raw Proc that performs the field matching operation.
47
+ # The Proc extracts the field value and delegates to the submatcher.
42
48
  #
43
49
  # This method first ensures the record is structurally eligible for field extraction—
44
50
  # it must be a Hash, Array, or respond to `[]`. If the structure does not allow for
@@ -54,9 +60,6 @@ module Mongory
54
60
  # Once the value is extracted, it is passed through the data converter
55
61
  # and matched against the condition via the superclass.
56
62
  #
57
- # @param record [Object] the input data structure to be matched
58
- # @return [Boolean] true if the extracted field value matches the condition; false otherwise
59
- #
60
63
  # @example Matching a Hash with a nil field value
61
64
  # matcher = Mongory::QueryMatcher.new(a: nil)
62
65
  # matcher.match?({ a: nil }) # => true
@@ -72,27 +75,44 @@ module Mongory
72
75
  # @example Hash with symbol key, matcher uses string key
73
76
  # matcher = Mongory::QueryMatcher.new('a' => 123)
74
77
  # matcher.match?({ a: 123 }) # => true
75
- def match(record)
76
- sub_record =
77
- case record
78
- when Hash
79
- record.fetch(@field) do
80
- record.fetch(@field.to_sym, KEY_NOT_FOUND)
81
- end
82
- when Array
83
- record.fetch(@field, KEY_NOT_FOUND)
84
- when KEY_NOT_FOUND, *CLASSES_NOT_ALLOW_TO_DIG
85
- return false
86
- else
87
- return false unless record.respond_to?(:[])
88
78
 
89
- record[@field]
90
- end
79
+ # Creates a raw Proc that performs the field-based matching operation.
80
+ # The Proc extracts the field value and delegates matching to the superclass.
81
+ #
82
+ # @return [Proc] A proc that performs field-based matching with context awareness
83
+ # @note The proc handles field extraction and delegates matching to the superclass
84
+ def raw_proc
85
+ super_proc = super
86
+ field = @field
87
+ need_convert = @context.need_convert
88
+ data_converter = Mongory.data_converter
89
+
90
+ Proc.new do |record|
91
+ sub_record =
92
+ case record
93
+ when Hash
94
+ record.fetch(field) do
95
+ record.fetch(field.to_sym, KEY_NOT_FOUND)
96
+ end
97
+ when Array
98
+ record.fetch(field, KEY_NOT_FOUND)
99
+ when KEY_NOT_FOUND, *CLASSES_NOT_ALLOW_TO_DIG
100
+ next false
101
+ else
102
+ next false unless record.respond_to?(:[])
91
103
 
92
- super(Mongory.data_converter.convert(sub_record))
104
+ record[field]
105
+ end
106
+
107
+ sub_record = data_converter.convert(sub_record) if need_convert
108
+ super_proc.call(sub_record)
109
+ end
93
110
  end
94
111
 
95
- # @return [String] a deduplication field used for matchers inside multi-match constructs
112
+ # Returns a unique key for this matcher, including the field name.
113
+ # Used for deduplication in multi-matchers.
114
+ #
115
+ # @return [String] a unique key for this matcher
96
116
  # @see AbstractMultiMatcher#matchers
97
117
  def uniq_key
98
118
  super + "field:#{@field}"
@@ -100,9 +120,9 @@ module Mongory
100
120
 
101
121
  private
102
122
 
103
- # Returns a single-line summary of the dig matcher including the field and condition.
123
+ # Returns a single-line summary of the field matcher including the field and condition.
104
124
  #
105
- # @return [String]
125
+ # @return [String] a formatted title for tree display
106
126
  def tree_title
107
127
  "Field: #{@field.inspect} to match: #{@condition.inspect}"
108
128
  end
@@ -111,7 +131,7 @@ module Mongory
111
131
  #
112
132
  # @param record [Object] the input record
113
133
  # @param result [Boolean] match result
114
- # @return [String] formatted debug string
134
+ # @return [String] formatted debug string with highlighted field
115
135
  def debug_display(record, result)
116
136
  "#{self.class.name.split('::').last} #{colored_result(result)}, " \
117
137
  "condition: #{@condition.inspect}, " \
@@ -6,7 +6,7 @@ module Mongory
6
6
  #
7
7
  # It returns true if the record is strictly greater than the condition.
8
8
  #
9
- # Inherits core logic from AbstractOperatorMatcher, including
9
+ # Inherits core logic from AbstractMatcher, including
10
10
  # error handling and optional preprocessing.
11
11
  #
12
12
  # @example
@@ -14,13 +14,21 @@ module Mongory
14
14
  # matcher.match?(15) #=> true
15
15
  # matcher.match?(10) #=> false
16
16
  #
17
- # @see AbstractOperatorMatcher
18
- class GtMatcher < AbstractOperatorMatcher
19
- # Returns the Ruby `>` operator symbol for comparison.
17
+ # @see AbstractMatcher
18
+ class GtMatcher < AbstractMatcher
19
+ # Creates a raw Proc that performs the greater-than comparison.
20
+ # The Proc uses the `>` operator to compare values.
20
21
  #
21
- # @return [Symbol] the greater-than operator
22
- def operator
23
- :>
22
+ # @return [Proc] A proc that performs greater-than comparison with error handling
23
+ # @note The proc includes error handling for invalid comparisons
24
+ def raw_proc
25
+ condition = @condition
26
+
27
+ Proc.new do |record|
28
+ record > condition
29
+ rescue StandardError
30
+ false
31
+ end
24
32
  end
25
33
  end
26
34
 
@@ -6,7 +6,7 @@ module Mongory
6
6
  #
7
7
  # It returns true if the record is greater than or equal to the condition value.
8
8
  #
9
- # Inherits comparison logic and error safety from AbstractOperatorMatcher.
9
+ # Inherits comparison logic and error safety from AbstractMatcher.
10
10
  #
11
11
  # @example
12
12
  # matcher = GteMatcher.build(10)
@@ -14,13 +14,21 @@ module Mongory
14
14
  # matcher.match?(11) #=> true
15
15
  # matcher.match?(9) #=> false
16
16
  #
17
- # @see AbstractOperatorMatcher
18
- class GteMatcher < AbstractOperatorMatcher
19
- # Returns the Ruby `>=` operator symbol for comparison.
17
+ # @see AbstractMatcher
18
+ class GteMatcher < AbstractMatcher
19
+ # Creates a raw Proc that performs the greater-than-or-equal comparison.
20
+ # The Proc uses the `>=` operator to compare values.
20
21
  #
21
- # @return [Symbol] the greater-than-or-equal operator
22
- def operator
23
- :>=
22
+ # @return [Proc] A proc that performs greater-than-or-equal comparison with error handling
23
+ # @note The proc includes error handling for invalid comparisons
24
+ def raw_proc
25
+ condition = @condition
26
+
27
+ Proc.new do |record|
28
+ record >= condition
29
+ rescue StandardError
30
+ false
31
+ end
24
32
  end
25
33
  end
26
34
 
@@ -9,46 +9,68 @@ module Mongory
9
9
  #
10
10
  # Each subcondition is matched independently using the `:all?` strategy, meaning
11
11
  # all subconditions must match for the entire HashConditionMatcher to succeed.
12
+ # For empty conditions, it returns true (using TRUE_PROC).
12
13
  #
13
14
  # This matcher plays a central role in dispatching symbolic query conditions
14
15
  # to the appropriate field or operator matcher.
15
16
  #
16
- # @example
17
+ # @example Basic field matching
17
18
  # matcher = HashConditionMatcher.build({ age: { :$gt => 30 }, active: true })
18
19
  # matcher.match?(record) #=> true only if all subconditions match
19
20
  #
21
+ # @example Empty conditions
22
+ # matcher = HashConditionMatcher.build({})
23
+ # matcher.match?(record) #=> true (uses TRUE_PROC)
24
+ #
20
25
  # @see AbstractMultiMatcher
21
26
  class HashConditionMatcher < AbstractMultiMatcher
22
27
  enable_unwrap!
23
- # Constructs the appropriate submatcher for a key-value pair.
24
- # If the key is a registered operator, dispatches to the corresponding matcher.
25
- # Otherwise, assumes the key is a field path and uses FieldMatcher.
26
28
 
27
- # @see FieldMatcher
28
- # @see Matchers.lookup
29
- # @param key [String] the condition key (either an operator or field name)
30
- # @param value [Object] the condition value
31
- # @return [AbstractMatcher] a matcher instance
32
- def build_sub_matcher(key, value)
33
- case key
34
- when *Matchers.operators
35
- # If the key is a recognized operator, use the corresponding matcher
36
- # to handle the value.
37
- # This allows for nested conditions like { :$and => [{ age: { :$gt => 30 } }] }
38
- # or { :$or => [{ name: 'John' }, { age: { :$lt => 25 } }] }
39
- # The operator matcher is built using the value.
40
- Matchers.lookup(key).build(value)
41
- else
42
- FieldMatcher.build(key, value)
29
+ # Creates a raw Proc that performs the hash condition matching operation.
30
+ # The Proc combines all submatcher Procs and returns true only if all match.
31
+ # For empty conditions, returns TRUE_PROC.
32
+ #
33
+ # @return [Proc] a Proc that performs the hash condition matching operation
34
+ def raw_proc
35
+ return TRUE_PROC if matchers.empty?
36
+
37
+ combine_procs(*matchers.map(&:to_proc))
38
+ end
39
+
40
+ # Recursively combines multiple matcher procs with AND logic.
41
+ # This method optimizes the combination of multiple matchers by building
42
+ # a balanced tree of AND operations.
43
+ #
44
+ # @param left [Proc] The left matcher proc to combine
45
+ # @param rest [Array<Proc>] The remaining matcher procs to combine
46
+ # @return [Proc] A new proc that combines all matchers with AND logic
47
+ # @example
48
+ # combine_procs(proc1, proc2, proc3)
49
+ # #=> proc { |record| proc1.call(record) && proc2.call(record) && proc3.call(record) }
50
+ def combine_procs(left, *rest)
51
+ return left if rest.empty?
52
+
53
+ right = combine_procs(*rest)
54
+ Proc.new do |record|
55
+ left.call(record) && right.call(record)
43
56
  end
44
57
  end
45
58
 
46
- # Specifies the matching strategy for all subconditions.
47
- # Uses `:all?`, meaning all conditions must be satisfied.
59
+ # Returns the list of matchers for each key-value pair in the condition.
48
60
  #
49
- # @return [Symbol] the combining operator method
50
- def operator
51
- :all?
61
+ # For each pair:
62
+ # - If the key is a registered operator, uses the corresponding matcher
63
+ # - Otherwise, wraps the value in a FieldMatcher for field path matching
64
+ #
65
+ # @return [Array<AbstractMatcher>] List of matchers for each condition
66
+ define_instance_cache_method(:matchers) do
67
+ @condition.map do |key, value|
68
+ if (matcher_class = Matchers.lookup(key))
69
+ matcher_class.build(value, context: @context)
70
+ else
71
+ FieldMatcher.build(key, value, context: @context)
72
+ end
73
+ end
52
74
  end
53
75
  end
54
76
  end
@@ -24,17 +24,19 @@ module Mongory
24
24
  #
25
25
  # @see AbstractMatcher
26
26
  class InMatcher < AbstractMatcher
27
- # Matches if any element of the record appears in the condition array.
28
- # Converts record to an array before intersecting.
27
+ # Creates a raw Proc that performs the in-matching operation.
28
+ # The Proc checks if any element of the record is in the condition array.
29
29
  #
30
- # @param record [Object] the record value to test
31
- # @return [Boolean] whether any values intersect
32
- def match(record)
33
- record = normalize(record)
34
- if record.is_a?(Array)
35
- is_present?(@condition & record)
36
- else
37
- @condition.include?(record)
30
+ # @return [Proc] a Proc that performs the in-matching operation
31
+ def raw_proc
32
+ condition = @condition
33
+
34
+ Proc.new do |record|
35
+ if record.is_a?(Array)
36
+ is_present?(condition & record)
37
+ else
38
+ condition.include?(record)
39
+ end
38
40
  end
39
41
  end
40
42
 
@@ -2,55 +2,49 @@
2
2
 
3
3
  module Mongory
4
4
  module Matchers
5
- # LiteralMatcher is responsible for handling raw literal values in query conditions.
5
+ # LiteralMatcher handles direct value comparison with special array handling.
6
6
  #
7
- # This matcher dispatches logic based on the type of the literal value,
8
- # such as nil, Array, Regexp, Hash, etc., and delegates to the appropriate specialized matcher.
7
+ # This matcher is used when a condition is a literal value (not an operator).
8
+ # It handles both direct equality comparison and array-record scenarios.
9
9
  #
10
- # It is used when the query condition is a direct literal and not an operator or nested query.
10
+ # For array records:
11
+ # - Uses ArrayRecordMatcher to check if any element matches
12
+ # For non-array records:
13
+ # - Uses appropriate matcher based on condition type (Hash, Regexp, nil, etc.)
11
14
  #
12
- # @example Supported usages
13
- # { name: "Alice" } # String literal
14
- # { age: 18 } # Numeric literal
15
- # { active: true } # Boolean literal
16
- # { tags: [1, 2, 3] } # Array literal → ArrayRecordMatcher
17
- # { email: /@gmail\\.com/i } # Regexp literal → RegexMatcher
18
- # { info: nil } # nil literal → nil_matcher (matches null or missing)
15
+ # @example Basic equality matching
16
+ # matcher = LiteralMatcher.build(42)
17
+ # matcher.match?(42) #=> true
18
+ # matcher.match?([42, 43]) #=> true (array contains 42)
19
19
  #
20
- # @note This matcher is commonly dispatched from HashConditionMatcher or FieldMatcher
21
- # when the condition is a simple literal value, not an operator hash.
20
+ # @example Regexp matching
21
+ # matcher = LiteralMatcher.build(/foo/)
22
+ # matcher.match?("foo") #=> true
23
+ # matcher.match?(["foobar"]) #=> true
22
24
  #
23
- # === Supported literal types:
24
- # - String
25
- # - Integer / Float
26
- # - Symbol
27
- # - TrueClass / FalseClass
28
- # - NilClass → delegates to nil_matcher
29
- # - Regexp → delegates to RegexMatcher
30
- # - Array → delegates to ArrayRecordMatcher
31
- # - Hash → delegates to HashConditionMatcher (if treated as sub-query)
32
- # - Other unrecognized values → fallback to equality match (==)
25
+ # @example Hash condition matching
26
+ # matcher = LiteralMatcher.build({ '$gt' => 10 })
27
+ # matcher.match?(15) #=> true
28
+ # matcher.match?([5, 15]) #=> true
33
29
  #
34
- # === Excluded types (handled by other matchers):
35
- # - Operator hashes like `{ "$gt" => 5 }` → handled by OperatorMatcher
36
- # - Nested paths like `"a.b.c"` → handled by FieldMatcher
37
- # - Query combinators like `$or`, `$and`, `$not` → handled by corresponding matchers
38
- #
39
- # @see Mongory::Matchers::RegexMatcher
40
- # @see Mongory::Matchers::OrMatcher
41
- # @see Mongory::Matchers::ArrayRecordMatcher
42
- # @see Mongory::Matchers::HashConditionMatcher
30
+ # @see AbstractMatcher
31
+ # @see ArrayRecordMatcher
43
32
  class LiteralMatcher < AbstractMatcher
44
- # Matches the given record against the condition.
33
+ # Creates a raw Proc that performs the literal matching operation.
34
+ # The Proc handles both array and non-array records appropriately.
45
35
  #
46
- # @param record [Object] the record to be matched
47
- # @return [Boolean] whether the record satisfies the condition
48
- def match(record)
49
- case record
50
- when Array
51
- array_record_matcher.match?(record)
52
- else
53
- dispatched_matcher.match?(record)
36
+ # @return [Proc] a Proc that performs the literal matching operation
37
+ def raw_proc
38
+ array_record_proc = nil
39
+ dispatched_proc = dispatched_matcher.to_proc
40
+
41
+ Proc.new do |record|
42
+ if record.is_a?(Array)
43
+ array_record_proc ||= array_record_matcher.to_proc
44
+ array_record_proc.call(record)
45
+ else
46
+ dispatched_proc.call(record)
47
+ end
54
48
  end
55
49
  end
56
50
 
@@ -77,16 +71,16 @@ module Mongory
77
71
  define_matcher(:dispatched) do
78
72
  case @condition
79
73
  when Hash
80
- HashConditionMatcher.build(@condition)
74
+ HashConditionMatcher.build(@condition, context: @context)
81
75
  when Regexp
82
- RegexMatcher.build(@condition)
76
+ RegexMatcher.build(@condition, context: @context)
83
77
  when nil
84
78
  OrMatcher.build([
85
79
  { '$exists' => false },
86
80
  { '$eq' => nil }
87
- ])
81
+ ], context: @context)
88
82
  else
89
- EqMatcher.build(@condition)
83
+ EqMatcher.build(@condition, context: @context)
90
84
  end
91
85
  end
92
86
 
@@ -96,7 +90,7 @@ module Mongory
96
90
  # @return [ArrayRecordMatcher] the matcher used to match array-type records
97
91
  # @!method array_record_matcher
98
92
  define_matcher(:array_record) do
99
- ArrayRecordMatcher.build(@condition)
93
+ ArrayRecordMatcher.build(@condition, context: @context)
100
94
  end
101
95
 
102
96
  # Validates the nested condition matcher, if applicable.
@@ -109,8 +103,8 @@ module Mongory
109
103
  # Outputs the matcher tree by selecting either collection or condition matcher.
110
104
  # Delegates `render_tree` to whichever submatcher was active.
111
105
  #
112
- # @param prefix [String]
113
- # @param is_last [Boolean]
106
+ # @param prefix [String] the prefix string for tree rendering
107
+ # @param is_last [Boolean] whether this is the last node in the tree
114
108
  # @return [void]
115
109
  def render_tree(prefix = '', is_last: true)
116
110
  super
@@ -6,7 +6,7 @@ module Mongory
6
6
  #
7
7
  # It returns true if the record is strictly less than the condition value.
8
8
  #
9
- # This matcher inherits from AbstractOperatorMatcher and uses the `<` operator.
9
+ # This matcher inherits from AbstractMatcher and uses the `<` operator.
10
10
  #
11
11
  # @example
12
12
  # matcher = LtMatcher.build(10)
@@ -14,13 +14,21 @@ module Mongory
14
14
  # matcher.match?(10) #=> false
15
15
  # matcher.match?(11) #=> false
16
16
  #
17
- # @see AbstractOperatorMatcher
18
- class LtMatcher < AbstractOperatorMatcher
19
- # Returns the Ruby `<` operator symbol for comparison.
17
+ # @see AbstractMatcher
18
+ class LtMatcher < AbstractMatcher
19
+ # Creates a raw Proc that performs the less-than comparison.
20
+ # The Proc uses the `<` operator to compare values.
20
21
  #
21
- # @return [Symbol] the less-than operator
22
- def operator
23
- :<
22
+ # @return [Proc] A proc that performs less-than comparison with error handling
23
+ # @note The proc includes error handling for invalid comparisons
24
+ def raw_proc
25
+ condition = @condition
26
+
27
+ Proc.new do |record|
28
+ record < condition
29
+ rescue StandardError
30
+ false
31
+ end
24
32
  end
25
33
  end
26
34
 
@@ -6,7 +6,7 @@ module Mongory
6
6
  #
7
7
  # It returns true if the record is less than or equal to the condition value.
8
8
  #
9
- # This matcher inherits from AbstractOperatorMatcher and uses the `<=` operator.
9
+ # This matcher inherits from AbstractMatcher and uses the `<=` operator.
10
10
  #
11
11
  # @example
12
12
  # matcher = LteMatcher.build(10)
@@ -14,13 +14,21 @@ module Mongory
14
14
  # matcher.match?(10) #=> true
15
15
  # matcher.match?(11) #=> false
16
16
  #
17
- # @see AbstractOperatorMatcher
18
- class LteMatcher < AbstractOperatorMatcher
19
- # Returns the Ruby `<=` operator symbol for comparison.
17
+ # @see AbstractMatcher
18
+ class LteMatcher < AbstractMatcher
19
+ # Creates a raw Proc that performs the less-than-or-equal comparison.
20
+ # The Proc uses the `<=` operator to compare values.
20
21
  #
21
- # @return [Symbol] the less-than-or-equal operator
22
- def operator
23
- :<=
22
+ # @return [Proc] A proc that performs less-than-or-equal comparison with error handling
23
+ # @note The proc includes error handling for invalid comparisons
24
+ def raw_proc
25
+ condition = @condition
26
+
27
+ Proc.new do |record|
28
+ record <= condition
29
+ rescue StandardError
30
+ false
31
+ end
24
32
  end
25
33
  end
26
34
 
@@ -6,7 +6,7 @@ module Mongory
6
6
  #
7
7
  # It returns true if the record is *not equal* to the condition.
8
8
  #
9
- # This matcher inherits its logic from AbstractOperatorMatcher
9
+ # This matcher inherits its logic from AbstractMatcher
10
10
  # and uses Ruby's `!=` operator for comparison.
11
11
  #
12
12
  # @example
@@ -14,13 +14,18 @@ module Mongory
14
14
  # matcher.match?(41) #=> true
15
15
  # matcher.match?(42) #=> false
16
16
  #
17
- # @see AbstractOperatorMatcher
18
- class NeMatcher < AbstractOperatorMatcher
19
- # Returns the Ruby `!=` operator symbol for comparison.
17
+ # @see AbstractMatcher
18
+ class NeMatcher < AbstractMatcher
19
+ # Creates a raw Proc that performs the not-equal comparison.
20
+ # The Proc uses the `!=` operator to compare values.
20
21
  #
21
- # @return [Symbol] the not-equal operator
22
- def operator
23
- :!=
22
+ # @return [Proc] A proc that performs not-equal comparison
23
+ def raw_proc
24
+ condition = @condition
25
+
26
+ Proc.new do |record|
27
+ record != condition
28
+ end
24
29
  end
25
30
  end
26
31
 
@@ -24,16 +24,19 @@ module Mongory
24
24
  #
25
25
  # @see AbstractMatcher
26
26
  class NinMatcher < AbstractMatcher
27
- # Matches true if the record has no elements in common with the condition array.
27
+ # Creates a raw Proc that performs the not-in matching operation.
28
+ # The Proc checks if the record has no elements in common with the condition array.
28
29
  #
29
- # @param record [Object] the value to be tested
30
- # @return [Boolean] whether the record is disjoint from the condition array
31
- def match(record)
32
- record = normalize(record)
33
- if record.is_a?(Array)
34
- is_blank?(@condition & record)
35
- else
36
- !@condition.include?(record)
30
+ # @return [Proc] A proc that performs not-in matching
31
+ def raw_proc
32
+ condition = @condition
33
+
34
+ Proc.new do |record|
35
+ if record.is_a?(Array)
36
+ is_blank?(condition & record)
37
+ else
38
+ !condition.include?(record)
39
+ end
37
40
  end
38
41
  end
39
42
 
@@ -18,12 +18,16 @@ module Mongory
18
18
  #
19
19
  # @see LiteralMatcher
20
20
  class NotMatcher < LiteralMatcher
21
- # Inverts the result of LiteralMatcher#match.
21
+ # Creates a raw Proc that performs the not-matching operation.
22
+ # The Proc inverts the result of the wrapped matcher.
22
23
  #
23
- # @param record [Object] the value to test
24
- # @return [Boolean] whether the negated condition is satisfied
25
- def match(record)
26
- !super(record)
24
+ # @return [Proc] A proc that performs not-matching
25
+ def raw_proc
26
+ super_proc = super
27
+
28
+ Proc.new do |record|
29
+ !super_proc.call(record)
30
+ end
27
31
  end
28
32
  end
29
33