mongory 0.4.0 → 0.6.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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +51 -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 +9 -19
  9. data/lib/mongory/converters/converted.rb +81 -0
  10. data/lib/mongory/converters/data_converter.rb +18 -7
  11. data/lib/mongory/converters/key_converter.rb +43 -19
  12. data/lib/mongory/converters/value_converter.rb +24 -19
  13. data/lib/mongory/converters.rb +1 -0
  14. data/lib/mongory/matchers/abstract_matcher.rb +94 -32
  15. data/lib/mongory/matchers/abstract_multi_matcher.rb +16 -45
  16. data/lib/mongory/matchers/and_matcher.rb +38 -10
  17. data/lib/mongory/matchers/array_record_matcher.rb +54 -28
  18. data/lib/mongory/matchers/elem_match_matcher.rb +13 -9
  19. data/lib/mongory/matchers/eq_matcher.rb +12 -7
  20. data/lib/mongory/matchers/every_matcher.rb +20 -9
  21. data/lib/mongory/matchers/exists_matcher.rb +15 -14
  22. data/lib/mongory/matchers/field_matcher.rb +58 -38
  23. data/lib/mongory/matchers/gt_matcher.rb +15 -7
  24. data/lib/mongory/matchers/gte_matcher.rb +15 -7
  25. data/lib/mongory/matchers/hash_condition_matcher.rb +54 -26
  26. data/lib/mongory/matchers/in_matcher.rb +20 -13
  27. data/lib/mongory/matchers/literal_matcher.rb +42 -48
  28. data/lib/mongory/matchers/lt_matcher.rb +15 -7
  29. data/lib/mongory/matchers/lte_matcher.rb +15 -7
  30. data/lib/mongory/matchers/ne_matcher.rb +12 -7
  31. data/lib/mongory/matchers/nin_matcher.rb +20 -12
  32. data/lib/mongory/matchers/not_matcher.rb +9 -5
  33. data/lib/mongory/matchers/or_matcher.rb +42 -13
  34. data/lib/mongory/matchers/present_matcher.rb +14 -15
  35. data/lib/mongory/matchers/regex_matcher.rb +37 -22
  36. data/lib/mongory/matchers/size_matcher.rb +50 -0
  37. data/lib/mongory/matchers.rb +1 -1
  38. data/lib/mongory/query_builder.rb +89 -27
  39. data/lib/mongory/query_matcher.rb +40 -13
  40. data/lib/mongory/query_operator.rb +1 -1
  41. data/lib/mongory/utils/context.rb +41 -0
  42. data/lib/mongory/utils/debugger.rb +6 -4
  43. data/lib/mongory/utils.rb +1 -0
  44. data/lib/mongory/version.rb +1 -1
  45. data/lib/mongory.rb +3 -3
  46. data/mongory.gemspec +3 -3
  47. metadata +11 -9
  48. data/lib/mongory/matchers/README.md +0 -57
  49. data/lib/mongory/matchers/abstract_operator_matcher.rb +0 -46
@@ -10,12 +10,19 @@ module Mongory
10
10
  #
11
11
  # Internally it compiles all conditions and invokes `QueryMatcher`.
12
12
  #
13
- # @example
13
+ # @example Basic query
14
14
  # records.mongory
15
15
  # .where(:age.gte => 18)
16
16
  # .or({ :name => /J/ }, { :name.eq => 'Bob' })
17
17
  # .limit(2)
18
18
  # .to_a
19
+ #
20
+ # @example Complex query
21
+ # records.mongory
22
+ # .where(:status => 'active')
23
+ # .not(:age.lt => 18)
24
+ # .any_of({ :role => 'admin' }, { :role => 'moderator' })
25
+ # .pluck(:name, :email)
19
26
  class QueryBuilder
20
27
  include ::Enumerable
21
28
  include Utils
@@ -23,44 +30,68 @@ module Mongory
23
30
  # Initializes a new query builder with the given record set.
24
31
  #
25
32
  # @param records [Enumerable] the collection to query against
26
- def initialize(records)
33
+ def initialize(records, context: Utils::Context.new)
27
34
  @records = records
35
+ @context = context
28
36
  set_matcher
29
37
  end
30
38
 
31
39
  # Iterates through all records that match the current matcher.
40
+ # Uses the standard matcher implementation.
32
41
  #
33
- # @yieldparam record [Object]
34
- # @return [Enumerator]
42
+ # @yieldparam record [Object] each matching record
43
+ # @return [Enumerator] if no block given
44
+ # @return [void] if block given
35
45
  def each
36
46
  return to_enum(:each) unless block_given?
37
47
 
38
48
  @matcher.prepare_query
39
49
  @records.each do |record|
50
+ @context.current_record = record
40
51
  yield record if @matcher.match?(record)
41
52
  end
42
53
  end
43
54
 
55
+ # Iterates through all records that match the current matcher.
56
+ # Uses a compiled Proc for faster matching.
57
+ #
58
+ # @yieldparam record [Object] each matching record
59
+ # @return [Enumerator] if no block given
60
+ # @return [void] if block given
61
+ def fast
62
+ return to_enum(:fast) unless block_given?
63
+
64
+ @context.need_convert = false
65
+ @matcher.prepare_query
66
+ matcher_block = @matcher.to_proc
67
+ @records.each do |record|
68
+ yield record if matcher_block.call(record)
69
+ end
70
+ end
71
+
44
72
  # Adds a condition to filter records using the given condition.
73
+ # This is an alias for `and`.
45
74
  #
46
- # @param condition [Hash]
75
+ # @param condition [Hash] the condition to add
47
76
  # @return [QueryBuilder] a new builder instance
48
77
  def where(condition)
49
78
  self.and(condition)
50
79
  end
51
80
 
52
81
  # Adds a negated condition to the current query.
82
+ # Wraps the condition in a `$not` operator.
53
83
  #
54
- # @param condition [Hash]
55
- # @return [QueryBuilder]
84
+ # @param condition [Hash] the condition to negate
85
+ # @return [QueryBuilder] a new builder instance
56
86
  def not(condition)
57
87
  self.and('$not' => condition)
58
88
  end
59
89
 
60
90
  # Adds one or more conditions combined with `$and`.
91
+ # All conditions must match for a record to be included.
61
92
  #
62
- # @param conditions [Array<Hash>]
63
- # @return [QueryBuilder]
93
+ # @param conditions [Array<Hash>] the conditions to add
94
+ # @return [QueryBuilder] a new builder instance
64
95
  def and(*conditions)
65
96
  dup_instance_exec do
66
97
  add_conditions('$and', conditions)
@@ -68,9 +99,10 @@ module Mongory
68
99
  end
69
100
 
70
101
  # Adds one or more conditions combined with `$or`.
102
+ # Any condition can match for a record to be included.
71
103
  #
72
- # @param conditions [Array<Hash>]
73
- # @return [QueryBuilder]
104
+ # @param conditions [Array<Hash>] the conditions to add
105
+ # @return [QueryBuilder] a new builder instance
74
106
  def or(*conditions)
75
107
  operator = '$or'
76
108
  dup_instance_exec do
@@ -85,24 +117,34 @@ module Mongory
85
117
  # Adds a `$or` query combined inside an `$and` block.
86
118
  # This is a semantic alias for `.and('$or' => [...])`
87
119
  #
88
- # @param conditions [Array<Hash>]
89
- # @return [QueryBuilder]
120
+ # @param conditions [Array<Hash>] the conditions to add
121
+ # @return [QueryBuilder] a new builder instance
90
122
  def any_of(*conditions)
91
123
  self.and('$or' => conditions)
92
124
  end
93
125
 
126
+ # Adds an `$in` condition to the query.
127
+ # Matches records where the field value is in the given array.
128
+ #
129
+ # @param condition [Hash] the field and values to match
130
+ # @return [QueryBuilder] a new builder instance
94
131
  def in(condition)
95
132
  self.and(wrap_values_with_key(condition, '$in'))
96
133
  end
97
134
 
135
+ # Adds a `$nin` condition to the query.
136
+ # Matches records where the field value is not in the given array.
137
+ #
138
+ # @param condition [Hash] the field and values to exclude
139
+ # @return [QueryBuilder] a new builder instance
98
140
  def nin(condition)
99
141
  self.and(wrap_values_with_key(condition, '$nin'))
100
142
  end
101
143
 
102
144
  # Limits the number of records returned by the query.
103
145
  #
104
- # @param count [Integer]
105
- # @return [QueryBuilder]
146
+ # @param count [Integer] the maximum number of records to return
147
+ # @return [QueryBuilder] a new builder instance
106
148
  def limit(count)
107
149
  dup_instance_exec do
108
150
  @records = take(count)
@@ -111,9 +153,10 @@ module Mongory
111
153
 
112
154
  # Extracts selected fields from matching records.
113
155
  #
114
- # @param field [Symbol, String]
115
- # @param fields [Array<Symbol, String>]
116
- # @return [Array<Object>, Array<Array<Object>>]
156
+ # @param field [Symbol, String] the first field to extract
157
+ # @param fields [Array<Symbol, String>] additional fields to extract
158
+ # @return [Array<Object>] array of single field values if one field given
159
+ # @return [Array<Array<Object>>] array of field value arrays if multiple fields given
117
160
  def pluck(field, *fields)
118
161
  if fields.empty?
119
162
  map { |record| record[field] }
@@ -126,11 +169,24 @@ module Mongory
126
169
  # Returns the raw parsed condition for this query.
127
170
  #
128
171
  # @return [Hash] the raw compiled condition
129
- def condition
172
+ def raw_condition
130
173
  @matcher.condition
131
174
  end
132
175
 
133
- alias_method :selector, :condition
176
+ # Creates a new query builder with additional context configuration.
177
+ #
178
+ # @param addon_context [Hash] Additional context configuration to merge
179
+ # @return [QueryBuilder] A new query builder instance with merged context
180
+ # @note Creates a new query builder with the current matcher's condition and merged context
181
+ # @example
182
+ # query.with_context(need_convert: false) #=> Returns a new query builder with conversion disabled
183
+ def with_context(addon_context = {})
184
+ dup_instance_exec do
185
+ @context = @context.dup
186
+ @context.config.merge!(addon_context)
187
+ set_matcher(@matcher.condition)
188
+ end
189
+ end
134
190
 
135
191
  # Prints the internal matcher tree structure for the current query.
136
192
  # Will output a human-readable visual tree of matchers.
@@ -148,8 +204,7 @@ module Mongory
148
204
  # @private
149
205
  # Duplicates the query and executes the block in context.
150
206
  #
151
- # @yieldparam dup [QueryBuilder]
152
- # @return [QueryBuilder]
207
+ # @return [QueryBuilder] the modified duplicate
153
208
  def dup_instance_exec(&block)
154
209
  dup.tap do |obj|
155
210
  obj.instance_exec(&block)
@@ -160,24 +215,31 @@ module Mongory
160
215
  # Builds the internal matcher tree from a condition hash.
161
216
  # Used to eagerly parse conditions to improve inspect/debug visibility.
162
217
  #
163
- # @param condition [Hash]
218
+ # @param condition [Hash] the condition to build the matcher from
164
219
  # @return [void]
165
220
  def set_matcher(condition = {})
166
- @matcher = QueryMatcher.new(condition)
221
+ @matcher = QueryMatcher.new(condition, context: @context)
167
222
  end
168
223
 
169
224
  # @private
170
225
  # Merges additional conditions into the matcher.
171
226
  #
172
- # @param key [String, Symbol]
173
- # @param conditions [Array<Hash>]
227
+ # @param key [String, Symbol] the operator key (e.g. '$and', '$or')
228
+ # @param conditions [Array<Hash>] the conditions to add
229
+ # @return [void]
174
230
  def add_conditions(key, conditions)
175
- condition_dup = @matcher.condition.dup
231
+ condition_dup = {}.merge!(@matcher.condition)
176
232
  condition_dup[key] ||= []
177
233
  condition_dup[key] += conditions
178
234
  set_matcher(condition_dup)
179
235
  end
180
236
 
237
+ # @private
238
+ # Wraps values in a condition hash with a given operator key.
239
+ #
240
+ # @param condition [Hash] the condition to transform
241
+ # @param key [String] the operator key to wrap with
242
+ # @return [Hash] the transformed condition
181
243
  def wrap_values_with_key(condition, key)
182
244
  condition.transform_values do |sub_condition|
183
245
  { key => sub_condition }
@@ -11,31 +11,58 @@ module Mongory
11
11
  # Typically used internally by `QueryBuilder`.
12
12
  #
13
13
  # Conversion via Mongory.data_converter is applied to the record
14
+ # before matching to ensure consistent data types.
14
15
  #
15
- # @example
16
- # matcher = QueryMatcher.build({ :age.gte => 18 })
17
- # matcher.match?(record)
16
+ # @example Basic matching
17
+ # matcher = QueryMatcher.new({ :age.gte => 18 })
18
+ # matcher.match?(record) # => true/false
19
+ #
20
+ # @example Complex condition
21
+ # matcher = QueryMatcher.new({
22
+ # :age.gte => 18,
23
+ # :$or => [
24
+ # { :name => /J/ },
25
+ # { :name.eq => 'Bob' }
26
+ # ]
27
+ # })
18
28
  #
19
29
  # @see Matchers::LiteralMatcher
20
30
  # @see Converters::ConditionConverter
21
- class QueryMatcher < Matchers::LiteralMatcher
31
+ class QueryMatcher < Matchers::HashConditionMatcher
32
+ # Initializes a new query matcher with the given condition.
33
+ # The condition is converted using Mongory.condition_converter
34
+ # before being passed to the parent matcher.
35
+ #
22
36
  # @param condition [Hash<Symbol, Object>] a query condition using operator-style symbol keys,
23
37
  # e.g. { :age.gt => 18 }, which will be parsed by `Mongory.condition_converter`.
24
- def initialize(condition)
25
- super(Mongory.condition_converter.convert(condition))
38
+ # @param context [Context] The query context containing configuration and current record
39
+ # @option context [Hash] :config The query configuration
40
+ # @option context [Object] :current_record The current record being processed
41
+ # @option context [Boolean] :need_convert Whether the record needs to be converted
42
+ def initialize(condition, context: Utils::Context.new)
43
+ super(Mongory.condition_converter.convert(condition), context: context)
26
44
  end
27
45
 
28
- # Matches the given record against the condition.
46
+ # Returns a Proc that can be used for fast matching.
47
+ # The Proc converts the record using Mongory.data_converter
48
+ # and delegates to the superclass's raw_proc.
29
49
  #
30
- # @param record [Object] the raw input record (e.g., Hash or model object) to be matched.
31
- # It will be converted internally using `Mongory.data_converter`.
32
- # @return [Boolean] whether the record satisfies the condition
33
- def match(record)
34
- super(Mongory.data_converter.convert(record))
50
+ # @return [Proc] A proc that performs query matching with context awareness
51
+ # @note The proc includes error handling and context-based record conversion
52
+ def raw_proc
53
+ super_proc = super
54
+ need_convert = @context.need_convert
55
+ data_converter = Mongory.data_converter
56
+
57
+ Proc.new do |record|
58
+ record = data_converter.convert(record) if need_convert
59
+ super_proc.call(record)
60
+ rescue StandardError
61
+ false
62
+ end
35
63
  end
36
64
 
37
65
  # Renders the full matcher tree for the current query.
38
- #
39
66
  # This method is intended to be the public entry point for rendering
40
67
  # the matcher tree. It does not accept any arguments and internally
41
68
  # handles rendering via the configured pretty-print logic.
@@ -22,7 +22,7 @@ module Mongory
22
22
  # @param other [Object] the value to match against
23
23
  # @return [Hash] converted query condition
24
24
  def __expr_part__(other, *)
25
- Converters::KeyConverter.instance.convert(@name, @operator => other)
25
+ { @name => { @operator => other } }
26
26
  end
27
27
  end
28
28
  end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mongory
4
+ module Utils
5
+ # Context is a utility class that provides a stable but mutatable
6
+ # shared context for the Mongory query builder. It holds the configuration
7
+ # and the current record being matcher tree processed.
8
+ #
9
+ # @example
10
+ # context = Mongory::Utils::Context.new(config)
11
+ # context.current_record = record
12
+ # context.config = new_config
13
+ #
14
+ # @attr [Config] config The configuration object for the context
15
+ # @attr [Record] current_record The current record being processed in the matcher tree
16
+ # @attr [Boolean] need_convert Whether the record needs to be converted before matching
17
+ class Context
18
+ attr_accessor :config, :current_record, :need_convert
19
+
20
+ # Initializes a new Context instance with the given configuration.
21
+ #
22
+ # @param config [Config] The configuration object for the context.
23
+ # @return [Context] A new Context instance.
24
+ def initialize(config = {})
25
+ @config = config
26
+ @current_record = nil
27
+ @need_convert = true
28
+ end
29
+
30
+ # Creates a duplicate of the context with its own configuration.
31
+ #
32
+ # @return [Context] A new context instance with duplicated configuration
33
+ # @note The new context shares the same configuration object but has its own state
34
+ def dup
35
+ new_context = super
36
+ new_context.config = @config.dup
37
+ new_context
38
+ end
39
+ end
40
+ end
41
+ end
@@ -32,16 +32,18 @@ module Mongory
32
32
  @trace_entries = []
33
33
  end
34
34
 
35
- # Enables debug mode by aliasing `match?` to `debug_match`.
35
+ # Enables debug mode by aliasing `to_proc` to `debug_proc`.
36
36
  # @return [void]
37
+ # @note Changes the behavior of to_proc to use debug_proc instead of cached_proc
37
38
  def enable
38
- Matchers::AbstractMatcher.alias_method :match?, :debug_match
39
+ Matchers::AbstractMatcher.alias_method :to_proc, :debug_proc
39
40
  end
40
41
 
41
- # Disables debug mode by restoring `match?` to `regular_match`.
42
+ # Disables debug mode by restoring `to_proc` to `cached_proc`.
42
43
  # @return [void]
44
+ # @note Restores the original behavior of to_proc
43
45
  def disable
44
- Matchers::AbstractMatcher.alias_method :match?, :regular_match
46
+ Matchers::AbstractMatcher.alias_method :to_proc, :cached_proc
45
47
  end
46
48
 
47
49
  # Wraps a matcher evaluation block with indentation control.
data/lib/mongory/utils.rb CHANGED
@@ -3,6 +3,7 @@
3
3
  require 'date'
4
4
  require_relative 'utils/singleton_builder'
5
5
  require_relative 'utils/debugger'
6
+ require_relative 'utils/context'
6
7
 
7
8
  module Mongory
8
9
  # Utility helpers shared across Mongory internals.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mongory
4
- VERSION = '0.4.0'
4
+ VERSION = '0.6.1'
5
5
  end
data/lib/mongory.rb CHANGED
@@ -58,21 +58,21 @@ module Mongory
58
58
  #
59
59
  # @return [Converters::DataConverter]
60
60
  def self.data_converter
61
- Converters::DataConverter.instance
61
+ @data_converter ||= Converters::DataConverter.instance
62
62
  end
63
63
 
64
64
  # Returns the condition converter instance.
65
65
  #
66
66
  # @return [Converters::ConditionConverter]
67
67
  def self.condition_converter
68
- Converters::ConditionConverter.instance
68
+ @condition_converter ||= Converters::ConditionConverter.instance
69
69
  end
70
70
 
71
71
  # Returns the debugger instance.
72
72
  #
73
73
  # @return [Utils::Debugger]
74
74
  def self.debugger
75
- Utils::Debugger.instance
75
+ @debugger ||= Utils::Debugger.instance
76
76
  end
77
77
 
78
78
  # Builds a new query over the given record set.
data/mongory.gemspec CHANGED
@@ -10,15 +10,15 @@ Gem::Specification.new do |spec|
10
10
 
11
11
  spec.summary = 'MongoDB-like in-memory query DSL for Ruby'
12
12
  spec.description = 'A Mongo-like in-memory query DSL for Ruby'
13
- spec.homepage = 'https://koten0224.github.io/mongory-rb/'
13
+ spec.homepage = 'https://mongoryhq.github.io/mongory-rb/'
14
14
  spec.license = 'MIT'
15
15
  spec.required_ruby_version = '>= 2.6.0'
16
16
 
17
17
  spec.metadata['allowed_push_host'] = 'https://rubygems.org'
18
18
  spec.metadata['rubygems_mfa_required'] = 'true'
19
19
  spec.metadata['homepage_uri'] = spec.homepage
20
- spec.metadata['source_code_uri'] = 'https://github.com/koten0224/mongory-rb'
21
- spec.metadata['changelog_uri'] = 'https://github.com/koten0224/mongory-rb/blob/main/CHANGELOG.md'
20
+ spec.metadata['source_code_uri'] = 'https://github.com/mongoryhq/mongory-rb'
21
+ spec.metadata['changelog_uri'] = 'https://github.com/mongoryhq/mongory-rb/blob/main/CHANGELOG.md'
22
22
 
23
23
  # Specify which files should be added to the gem when it is released.
24
24
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mongory
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - koten0224
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-04-20 00:00:00.000000000 Z
11
+ date: 2025-04-24 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A Mongo-like in-memory query DSL for Ruby
14
14
  email:
@@ -26,6 +26,7 @@ files:
26
26
  - README.md
27
27
  - Rakefile
28
28
  - examples/README.md
29
+ - examples/benchmark-rails.rb
29
30
  - examples/benchmark.rb
30
31
  - lib/generators/mongory/install/install_generator.rb
31
32
  - lib/generators/mongory/install/templates/initializer.rb.erb
@@ -36,14 +37,13 @@ files:
36
37
  - lib/mongory/converters.rb
37
38
  - lib/mongory/converters/abstract_converter.rb
38
39
  - lib/mongory/converters/condition_converter.rb
40
+ - lib/mongory/converters/converted.rb
39
41
  - lib/mongory/converters/data_converter.rb
40
42
  - lib/mongory/converters/key_converter.rb
41
43
  - lib/mongory/converters/value_converter.rb
42
44
  - lib/mongory/matchers.rb
43
- - lib/mongory/matchers/README.md
44
45
  - lib/mongory/matchers/abstract_matcher.rb
45
46
  - lib/mongory/matchers/abstract_multi_matcher.rb
46
- - lib/mongory/matchers/abstract_operator_matcher.rb
47
47
  - lib/mongory/matchers/and_matcher.rb
48
48
  - lib/mongory/matchers/array_record_matcher.rb
49
49
  - lib/mongory/matchers/elem_match_matcher.rb
@@ -64,27 +64,29 @@ files:
64
64
  - lib/mongory/matchers/or_matcher.rb
65
65
  - lib/mongory/matchers/present_matcher.rb
66
66
  - lib/mongory/matchers/regex_matcher.rb
67
+ - lib/mongory/matchers/size_matcher.rb
67
68
  - lib/mongory/mongoid.rb
68
69
  - lib/mongory/query_builder.rb
69
70
  - lib/mongory/query_matcher.rb
70
71
  - lib/mongory/query_operator.rb
71
72
  - lib/mongory/rails.rb
72
73
  - lib/mongory/utils.rb
74
+ - lib/mongory/utils/context.rb
73
75
  - lib/mongory/utils/debugger.rb
74
76
  - lib/mongory/utils/rails_patch.rb
75
77
  - lib/mongory/utils/singleton_builder.rb
76
78
  - lib/mongory/version.rb
77
79
  - mongory.gemspec
78
80
  - sig/mongory.rbs
79
- homepage: https://koten0224.github.io/mongory-rb/
81
+ homepage: https://mongoryhq.github.io/mongory-rb/
80
82
  licenses:
81
83
  - MIT
82
84
  metadata:
83
85
  allowed_push_host: https://rubygems.org
84
86
  rubygems_mfa_required: 'true'
85
- homepage_uri: https://koten0224.github.io/mongory-rb/
86
- source_code_uri: https://github.com/koten0224/mongory-rb
87
- changelog_uri: https://github.com/koten0224/mongory-rb/blob/main/CHANGELOG.md
87
+ homepage_uri: https://mongoryhq.github.io/mongory-rb/
88
+ source_code_uri: https://github.com/mongoryhq/mongory-rb
89
+ changelog_uri: https://github.com/mongoryhq/mongory-rb/blob/main/CHANGELOG.md
88
90
  post_install_message:
89
91
  rdoc_options: []
90
92
  require_paths:
@@ -100,7 +102,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
100
102
  - !ruby/object:Gem::Version
101
103
  version: '0'
102
104
  requirements: []
103
- rubygems_version: 3.1.6
105
+ rubygems_version: 3.0.8
104
106
  signing_key:
105
107
  specification_version: 4
106
108
  summary: MongoDB-like in-memory query DSL for Ruby
@@ -1,57 +0,0 @@
1
- # Matcher classes inheritance diagram
2
-
3
- ```mermaid
4
- ---
5
- config:
6
- theme: neutral
7
- look: classic
8
- ---
9
-
10
- graph TD
11
- %% style blocks
12
- classDef abstract fill:#ddd,stroke:#333,stroke-width:1px,color:#000;
13
- classDef main fill:#cce5ff,stroke:#339,stroke-width:1px;
14
- classDef multi fill:#d4edda,stroke:#282,stroke-width:1px;
15
- classDef operator fill:#fff3cd,stroke:#aa8800,stroke-width:1px;
16
- classDef leaf fill:#f8f9fa,stroke:#999,stroke-width:1px;
17
-
18
- %% Abstract base classes
19
- A[AbstractMatcher]
20
- C[AbstractMultiMatcher]
21
- D[AbstractOperatorMatcher]
22
-
23
- subgraph MultipleConditions
24
- F[HashConditionMatcher]
25
- I[AndMatcher]
26
- J[OrMatcher]
27
- W[ArrayRecordMatcher]
28
- end
29
-
30
- subgraph SimpleCompare
31
- E[EqMatcher]
32
- K[RegexMatcher]
33
- L[PresentMatcher]
34
- M[ExistsMatcher]
35
- O[NeMatcher]
36
- Q[GtMatcher]
37
- R[GteMatcher]
38
- S[LtMatcher]
39
- T[LteMatcher]
40
- end
41
-
42
- A --> B[LiteralMatcher]
43
- A --> U[InMatcher]
44
- A --> V[NinMatcher]
45
- A --> C --> MultipleConditions
46
- A --> D --> SimpleCompare
47
- B --> G[FieldMatcher]
48
- B --> H[ElemMatchMatcher]
49
- B --> N[NotMatcher]
50
-
51
- %% Apply classes
52
- class A,C,D abstract;
53
- class B main;
54
- class F,I,J,W multi;
55
- class E,K,L,M,O,Q,R,S,T operator;
56
- class G,U,V,H,N leaf;
57
- ```
@@ -1,46 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Mongory
4
- module Matchers
5
- # AbstractOperatorMatcher is a base class for matchers that apply a binary
6
- # operator (e.g., `==`, `<`, `>` etc.) between the record and the condition.
7
- #
8
- # This class assumes that the match logic consists of:
9
- # preprocess(record).send(operator, condition)
10
- # and provides a fallback behavior for invalid comparisons.
11
- #
12
- # Subclasses must implement `#operator` and may override `#preprocess`
13
- # to normalize or cast the record before comparison.
14
- #
15
- # @abstract
16
- # @see AbstractMatcher
17
- class AbstractOperatorMatcher < AbstractMatcher
18
- # A list of Boolean values used for type guarding in some subclasses.
19
- BOOLEAN_VALUES = [true, false].freeze
20
-
21
- # Applies the binary operator to the preprocessed record and condition.
22
- # If an error is raised (e.g., undefined comparison), the match fails.
23
- #
24
- # @param record [Object] the input record to test
25
- # @return [Boolean] the result of record <operator> condition
26
- def match(record)
27
- preprocess(record).send(operator, @condition)
28
- end
29
-
30
- # Hook for subclasses to transform the record before comparison.
31
- # Default behavior normalizes KEY_NOT_FOUND to nil.
32
- #
33
- # @param record [Object] the raw record value
34
- # @return [Object] the transformed value
35
- def preprocess(record)
36
- normalize(record)
37
- end
38
-
39
- # Returns the Ruby operator symbol to be used in comparison.
40
- # Must be implemented by subclasses (e.g., :==, :<, :>=)
41
- #
42
- # @return [Symbol] the comparison operator
43
- def operator; end
44
- end
45
- end
46
- end