mongory 0.6.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c030da605cb94ba20f4be31a03e37a5b95277b1c2b93b48622080521752acbfa
4
- data.tar.gz: 3577f0da8e823b74fa25459bb0a66dcb2609ff1eef366ae0ce08402a22d8c049
3
+ metadata.gz: f2e272e3c249c11059ecf96ad8eb0659d5e0fb0157050e7d69b6e272e16f874a
4
+ data.tar.gz: 84316343470d975672edbed9e590661bf555af5aa6ceb862d4e850efac0f5266
5
5
  SHA512:
6
- metadata.gz: 723cdce8afcd41e7f69148934890ac3bc133e297bda01e412b69e491ece2fdfe1b8b27c87244c6417ce4905d18fb8d3082b4217c979b2f6ec1e65bcc71ec0091
7
- data.tar.gz: eaa426a3780cc094c0dfd0e5e168fbce4c94db61f504fefa1a44efed5d569bc76a6caf348f5170c46c92916b0ceb384105757b72cbb43e96fdc16208abf7715f
6
+ metadata.gz: f0709d9abd1a769c111cc27b73a25f1d79b2af931de122fad4538259b97eccd5a8bb26336fab68d8affbba830ffa9a8af3bf07323f17051f588c633938a43b75
7
+ data.tar.gz: 292d65f888d36af1d22a9e4d7973ee8995c055c37abc837b1fdca161f5d4b37ed285c253da7a6019172848cb89d35e76e5ce0fa7b88725272ecdc46718d23968
data/CHANGELOG.md CHANGED
@@ -1,4 +1,11 @@
1
1
  # Changelog
2
+ ## [0.6.1] - 2025-04-24
3
+
4
+ ### Major Changes
5
+ - Add size matcher
6
+ - $in and $nin supports range condition
7
+ - Query matcher inherits from Hash condition matcher to accept hash condition only
8
+ - Introduced Converted to mark converted data and prevent double convert
2
9
 
3
10
  ## [0.6.0] - 2025-04-23
4
11
 
@@ -21,28 +21,16 @@ module Mongory
21
21
  # @param condition [Hash] the flat condition hash to convert
22
22
  # @return [Hash] the transformed nested condition
23
23
  def convert(condition)
24
- result = {}
24
+ return condition if condition.is_a?(Converted)
25
+
26
+ result = Converted::Hash.new
25
27
  condition.each_pair do |k, v|
26
28
  converted_value = value_converter.convert(v)
27
29
  converted_pair = key_converter.convert(k, converted_value)
28
- result.merge!(converted_pair, &deep_merge_block)
30
+ result.deep_merge!(converted_pair)
29
31
  end
30
- result
31
- end
32
32
 
33
- # Provides a block that merges values for overlapping keys in a deep way.
34
- # When both values are hashes, recursively merges them.
35
- # Otherwise, uses the second value.
36
- #
37
- # @return [Proc] a block for deep merging hash values
38
- def deep_merge_block
39
- @deep_merge_block ||= Proc.new do |_, a, b|
40
- if a.is_a?(Hash) && b.is_a?(Hash)
41
- a.merge(b, &deep_merge_block)
42
- else
43
- b
44
- end
45
- end
33
+ result
46
34
  end
47
35
 
48
36
  # @note Singleton instance, not configurable after initialization
@@ -65,7 +53,6 @@ module Mongory
65
53
  #
66
54
  # @return [void]
67
55
  def freeze
68
- deep_merge_block
69
56
  super
70
57
  key_converter.freeze
71
58
  value_converter.freeze
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mongory
4
+ module Converters
5
+ # Converted is a module that provides conversion structures marked as
6
+ # Converted. It is used to convert various types of data into a
7
+ # standardized form for MongoDB queries. The module includes
8
+ # classes for converting hashes and arrays into nested structures.
9
+ # It is used internally by the ConditionConverter and ValueConverter
10
+ # to handle the conversion of complex data types into a format
11
+ # suitable for MongoDB queries.
12
+ module Converted
13
+ def instance_convert(other)
14
+ return other if other.is_a?(Converted)
15
+
16
+ case other
17
+ when Hash
18
+ Converted::Hash.new(other)
19
+ when Array
20
+ Converted::Array.new(other)
21
+ else
22
+ other
23
+ end
24
+ end
25
+
26
+ # Converts a flat condition hash into a nested structure.
27
+ # Applies value conversion to each element.
28
+ # This is used for conditions that are hashes of values.
29
+ # It is used internally by the ConditionConverter and ValueConverter
30
+ # to handle the conversion of complex data types into a format
31
+ # suitable for MongoDB queries.
32
+ class Hash < ::Hash
33
+ include Converted
34
+
35
+ def initialize(other = {})
36
+ super()
37
+ other.each_pair do |k, v|
38
+ self[k] = instance_convert(v)
39
+ end
40
+ end
41
+
42
+ def deep_merge(other)
43
+ dup.deep_merge!(Hash.new(other))
44
+ end
45
+
46
+ def deep_merge!(other)
47
+ _deep_merge!(self, Hash.new(other))
48
+ end
49
+
50
+ private
51
+
52
+ def _deep_merge!(left, right)
53
+ left.merge!(right) do |_, a, b|
54
+ if a.is_a?(::Hash) && b.is_a?(::Hash)
55
+ _deep_merge!(a.dup, b)
56
+ else
57
+ b
58
+ end
59
+ end
60
+ end
61
+ end
62
+
63
+ # Converts a flat condition array into a nested structure.
64
+ # Applies value conversion to each element.
65
+ # This is used for conditions that are arrays of values.
66
+ # It is used internally by the ConditionConverter and ValueConverter
67
+ # to handle the conversion of complex data types into a format
68
+ # suitable for MongoDB queries.
69
+ class Array < ::Array
70
+ include Converted
71
+
72
+ def initialize(other)
73
+ super()
74
+ other.each do |v|
75
+ self << instance_convert(v)
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -26,10 +26,10 @@ module Mongory
26
26
  # @return [Object] the converted value
27
27
  def convert(target)
28
28
  case target
29
- when String, Integer, Regexp
29
+ when String, Integer, Regexp, Converted
30
30
  target
31
31
  when Array
32
- target.map { |x| convert(x) }
32
+ Converted::Array.new(target.map { |x| convert(x) })
33
33
  when Hash
34
34
  condition_converter.convert(target)
35
35
  else
@@ -5,3 +5,4 @@ require_relative 'converters/condition_converter'
5
5
  require_relative 'converters/data_converter'
6
6
  require_relative 'converters/key_converter'
7
7
  require_relative 'converters/value_converter'
8
+ require_relative 'converters/converted'
@@ -72,6 +72,12 @@ module Mongory
72
72
  end
73
73
  end
74
74
  end
75
+
76
+ def check_validity!
77
+ return super if @condition.is_a?(Hash)
78
+
79
+ raise TypeError, 'condition needs a Hash.'
80
+ end
75
81
  end
76
82
  end
77
83
  end
@@ -33,6 +33,8 @@ module Mongory
33
33
 
34
34
  Proc.new do |record|
35
35
  if record.is_a?(Array)
36
+ return false if condition.is_a?(Range)
37
+
36
38
  is_present?(condition & record)
37
39
  else
38
40
  condition.include?(record)
@@ -40,12 +42,15 @@ module Mongory
40
42
  end
41
43
  end
42
44
 
43
- # Ensures the condition is an array.
45
+ # Ensures the condition is an array or range.
44
46
  #
45
- # @raise [TypeError] if condition is not an array
47
+ # @raise [TypeError] if condition is not an array nor a range
46
48
  # @return [void]
47
49
  def check_validity!
48
- raise TypeError, '$in needs an array' unless @condition.is_a?(Array)
50
+ return if @condition.is_a?(Array)
51
+ return if @condition.is_a?(Range)
52
+
53
+ raise TypeError, '$in needs an array or range'
49
54
  end
50
55
  end
51
56
 
@@ -33,6 +33,8 @@ module Mongory
33
33
 
34
34
  Proc.new do |record|
35
35
  if record.is_a?(Array)
36
+ return true if condition.is_a?(Range)
37
+
36
38
  is_blank?(condition & record)
37
39
  else
38
40
  !condition.include?(record)
@@ -40,12 +42,15 @@ module Mongory
40
42
  end
41
43
  end
42
44
 
43
- # Ensures the condition is a valid array.
45
+ # Ensures the condition is a valid array or range.
44
46
  #
45
- # @raise [TypeError] if the condition is not an array
47
+ # @raise [TypeError] if the condition is not an array nor a range
46
48
  # @return [void]
47
49
  def check_validity!
48
- raise TypeError, '$nin needs an array' unless @condition.is_a?(Array)
50
+ return if @condition.is_a?(Array)
51
+ return if @condition.is_a?(Range)
52
+
53
+ raise TypeError, '$nin needs an array'
49
54
  end
50
55
  end
51
56
 
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mongory
4
+ module Matchers
5
+ # Matcher for the `$size` operator.
6
+ #
7
+ # This matcher expects the input to be an array, and delegates the comparison
8
+ # to a literal matcher using the array's size as the value.
9
+ #
10
+ # For example, the condition `{ tags: { '$size' => 3 } }` will match any
11
+ # document where `tags` is an array of length 3.
12
+ #
13
+ # ### Supported compound usages:
14
+ #
15
+ # ```ruby
16
+ # Mongory.where(tags: { '$size' => 3 }) # exactly 3 elements
17
+ # Mongory.where(tags: { '$size' => { '$gt' => 1 } }) # more than 1
18
+ # Mongory.where(comments: { '$size' => { '$gt' => 1, '$lte' => 5 } }) # more than 1, up to 5 elements
19
+ # Mongory.where(tags: { '$size' => { '$in' => [1, 2, 3] } }) # 1, 2, or 3 elements
20
+ # ```
21
+ #
22
+ # @see LiteralMatcher
23
+ #
24
+ # @note Ruby's Symbol class already defines a `#size` method,
25
+ # that will return the size of the symbol object.
26
+ # So, this is the only operator that cannot be used with
27
+ # the symbol snippet syntax (e.g. `:tags.size`).
28
+ #
29
+ # Use string key syntax instead: `:"tags.$size" => ...`
30
+ class SizeMatcher < LiteralMatcher
31
+ # Creates a raw Proc that performs the size matching operation.
32
+ #
33
+ # The returned Proc checks if the input is an Array. If so, it calculates
34
+ # the array's size and passes it to the wrapped literal matcher Proc.
35
+ #
36
+ # @return [Proc] A proc that performs size-based matching
37
+ def raw_proc
38
+ super_proc = super
39
+
40
+ Proc.new do |record|
41
+ next false unless record.is_a?(Array)
42
+
43
+ super_proc.call(record.size)
44
+ end
45
+ end
46
+ end
47
+
48
+ register(:size, '$size', SizeMatcher)
49
+ end
50
+ end
@@ -173,3 +173,4 @@ require_relative 'matchers/not_matcher'
173
173
  require_relative 'matchers/or_matcher'
174
174
  require_relative 'matchers/present_matcher'
175
175
  require_relative 'matchers/regex_matcher'
176
+ require_relative 'matchers/size_matcher'
@@ -228,7 +228,7 @@ module Mongory
228
228
  # @param conditions [Array<Hash>] the conditions to add
229
229
  # @return [void]
230
230
  def add_conditions(key, conditions)
231
- condition_dup = @matcher.condition.dup
231
+ condition_dup = {}.merge!(@matcher.condition)
232
232
  condition_dup[key] ||= []
233
233
  condition_dup[key] += conditions
234
234
  set_matcher(condition_dup)
@@ -28,7 +28,7 @@ module Mongory
28
28
  #
29
29
  # @see Matchers::LiteralMatcher
30
30
  # @see Converters::ConditionConverter
31
- class QueryMatcher < Matchers::LiteralMatcher
31
+ class QueryMatcher < Matchers::HashConditionMatcher
32
32
  # Initializes a new query matcher with the given condition.
33
33
  # The condition is converted using Mongory.condition_converter
34
34
  # before being passed to the parent matcher.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mongory
4
- VERSION = '0.6.0'
4
+ VERSION = '0.6.1'
5
5
  end
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.6.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-23 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:
@@ -37,6 +37,7 @@ files:
37
37
  - lib/mongory/converters.rb
38
38
  - lib/mongory/converters/abstract_converter.rb
39
39
  - lib/mongory/converters/condition_converter.rb
40
+ - lib/mongory/converters/converted.rb
40
41
  - lib/mongory/converters/data_converter.rb
41
42
  - lib/mongory/converters/key_converter.rb
42
43
  - lib/mongory/converters/value_converter.rb
@@ -63,6 +64,7 @@ files:
63
64
  - lib/mongory/matchers/or_matcher.rb
64
65
  - lib/mongory/matchers/present_matcher.rb
65
66
  - lib/mongory/matchers/regex_matcher.rb
67
+ - lib/mongory/matchers/size_matcher.rb
66
68
  - lib/mongory/mongoid.rb
67
69
  - lib/mongory/query_builder.rb
68
70
  - lib/mongory/query_matcher.rb