cuprum-collections 0.4.0 → 0.5.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 (118) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +73 -0
  3. data/README.md +5 -5
  4. data/lib/cuprum/collections/association.rb +9 -28
  5. data/lib/cuprum/collections/associations/belongs_to.rb +1 -8
  6. data/lib/cuprum/collections/associations/has_many.rb +1 -10
  7. data/lib/cuprum/collections/associations/has_one.rb +1 -10
  8. data/lib/cuprum/collections/basic/collection.rb +56 -49
  9. data/lib/cuprum/collections/basic/command.rb +22 -88
  10. data/lib/cuprum/collections/basic/commands/assign_one.rb +2 -6
  11. data/lib/cuprum/collections/basic/commands/build_one.rb +1 -4
  12. data/lib/cuprum/collections/basic/commands/destroy_one.rb +4 -8
  13. data/lib/cuprum/collections/basic/commands/find_many.rb +4 -24
  14. data/lib/cuprum/collections/basic/commands/find_matching.rb +5 -21
  15. data/lib/cuprum/collections/basic/commands/find_one.rb +3 -20
  16. data/lib/cuprum/collections/basic/commands/insert_one.rb +3 -6
  17. data/lib/cuprum/collections/basic/commands/update_one.rb +3 -6
  18. data/lib/cuprum/collections/basic/commands/validate_one.rb +13 -18
  19. data/lib/cuprum/collections/basic/query.rb +26 -40
  20. data/lib/cuprum/collections/basic/repository.rb +4 -3
  21. data/lib/cuprum/collections/basic/scopes/all_scope.rb +25 -0
  22. data/lib/cuprum/collections/basic/scopes/base.rb +32 -0
  23. data/lib/cuprum/collections/basic/scopes/builder.rb +39 -0
  24. data/lib/cuprum/collections/basic/scopes/conjunction_scope.rb +20 -0
  25. data/lib/cuprum/collections/basic/scopes/criteria_scope.rb +62 -0
  26. data/lib/cuprum/collections/basic/scopes/disjunction_scope.rb +20 -0
  27. data/lib/cuprum/collections/basic/scopes/none_scope.rb +33 -0
  28. data/lib/cuprum/collections/basic/scopes.rb +23 -0
  29. data/lib/cuprum/collections/basic.rb +1 -0
  30. data/lib/cuprum/collections/collection.rb +24 -82
  31. data/lib/cuprum/collections/collection_command.rb +116 -0
  32. data/lib/cuprum/collections/commands/abstract_find_many.rb +11 -21
  33. data/lib/cuprum/collections/commands/abstract_find_matching.rb +43 -24
  34. data/lib/cuprum/collections/commands/abstract_find_one.rb +7 -10
  35. data/lib/cuprum/collections/commands/associations/find_many.rb +3 -8
  36. data/lib/cuprum/collections/commands/associations/require_many.rb +5 -5
  37. data/lib/cuprum/collections/commands/create.rb +3 -3
  38. data/lib/cuprum/collections/commands/find_one_matching.rb +6 -6
  39. data/lib/cuprum/collections/commands/query_command.rb +19 -0
  40. data/lib/cuprum/collections/commands/update.rb +3 -3
  41. data/lib/cuprum/collections/commands/upsert.rb +10 -10
  42. data/lib/cuprum/collections/commands.rb +1 -0
  43. data/lib/cuprum/collections/constraints/ordering.rb +2 -2
  44. data/lib/cuprum/collections/errors/abstract_find_error.rb +25 -42
  45. data/lib/cuprum/collections/errors/extra_attributes.rb +3 -3
  46. data/lib/cuprum/collections/errors/failed_validation.rb +2 -2
  47. data/lib/cuprum/collections/errors/invalid_parameters.rb +2 -2
  48. data/lib/cuprum/collections/errors/invalid_query.rb +10 -16
  49. data/lib/cuprum/collections/errors/missing_default_contract.rb +1 -1
  50. data/lib/cuprum/collections/errors/unknown_operator.rb +1 -1
  51. data/lib/cuprum/collections/queries.rb +31 -0
  52. data/lib/cuprum/collections/query.rb +50 -62
  53. data/lib/cuprum/collections/relation.rb +5 -383
  54. data/lib/cuprum/collections/relations/cardinality.rb +66 -0
  55. data/lib/cuprum/collections/relations/options.rb +18 -0
  56. data/lib/cuprum/collections/relations/parameters.rb +217 -0
  57. data/lib/cuprum/collections/relations/primary_keys.rb +23 -0
  58. data/lib/cuprum/collections/relations/scope.rb +65 -0
  59. data/lib/cuprum/collections/relations.rb +14 -0
  60. data/lib/cuprum/collections/repository.rb +5 -5
  61. data/lib/cuprum/collections/resource.rb +10 -41
  62. data/lib/cuprum/collections/rspec/contracts/association_contracts.rb +80 -90
  63. data/lib/cuprum/collections/rspec/contracts/collection_contracts.rb +69 -111
  64. data/lib/cuprum/collections/rspec/contracts/command_contracts.rb +42 -1335
  65. data/lib/cuprum/collections/rspec/contracts/query_contracts.rb +352 -531
  66. data/lib/cuprum/collections/rspec/contracts/relation_contracts.rb +74 -191
  67. data/lib/cuprum/collections/rspec/contracts/repository_contracts.rb +13 -13
  68. data/lib/cuprum/collections/rspec/contracts/scope_contracts.rb +1029 -0
  69. data/lib/cuprum/collections/rspec/contracts/scopes/builder_contracts.rb +856 -0
  70. data/lib/cuprum/collections/rspec/contracts/scopes/composition_contracts.rb +1430 -0
  71. data/lib/cuprum/collections/rspec/contracts/scopes/criteria_contracts.rb +2217 -0
  72. data/lib/cuprum/collections/rspec/contracts/scopes/logical_contracts.rb +297 -0
  73. data/lib/cuprum/collections/rspec/contracts/scopes.rb +13 -0
  74. data/lib/cuprum/collections/rspec/contracts.rb +2 -0
  75. data/lib/cuprum/collections/rspec/deferred/association_examples.rb +2098 -0
  76. data/lib/cuprum/collections/rspec/deferred/collection_examples.rb +338 -0
  77. data/lib/cuprum/collections/rspec/deferred/command_examples.rb +160 -0
  78. data/lib/cuprum/collections/rspec/deferred/commands/assign_one_examples.rb +178 -0
  79. data/lib/cuprum/collections/rspec/deferred/commands/build_one_examples.rb +94 -0
  80. data/lib/cuprum/collections/rspec/deferred/commands/destroy_one_examples.rb +118 -0
  81. data/lib/cuprum/collections/rspec/deferred/commands/find_many_examples.rb +307 -0
  82. data/lib/cuprum/collections/rspec/deferred/commands/find_matching_examples.rb +143 -0
  83. data/lib/cuprum/collections/rspec/deferred/commands/find_one_examples.rb +116 -0
  84. data/lib/cuprum/collections/rspec/deferred/commands/insert_one_examples.rb +103 -0
  85. data/lib/cuprum/collections/rspec/deferred/commands/update_one_examples.rb +99 -0
  86. data/lib/cuprum/collections/rspec/deferred/commands/validate_one_examples.rb +117 -0
  87. data/lib/cuprum/collections/rspec/deferred/commands.rb +8 -0
  88. data/lib/cuprum/collections/rspec/deferred/relation_examples.rb +1437 -0
  89. data/lib/cuprum/collections/rspec/deferred/resource_examples.rb +26 -0
  90. data/lib/cuprum/collections/rspec/deferred.rb +8 -0
  91. data/lib/cuprum/collections/scope.rb +29 -0
  92. data/lib/cuprum/collections/scopes/all.rb +51 -0
  93. data/lib/cuprum/collections/scopes/all_scope.rb +18 -0
  94. data/lib/cuprum/collections/scopes/base.rb +79 -0
  95. data/lib/cuprum/collections/scopes/builder.rb +39 -0
  96. data/lib/cuprum/collections/scopes/building.rb +221 -0
  97. data/lib/cuprum/collections/scopes/composition.rb +162 -0
  98. data/lib/cuprum/collections/scopes/conjunction.rb +44 -0
  99. data/lib/cuprum/collections/scopes/conjunction_scope.rb +12 -0
  100. data/lib/cuprum/collections/scopes/container.rb +65 -0
  101. data/lib/cuprum/collections/scopes/criteria/parser.rb +241 -0
  102. data/lib/cuprum/collections/scopes/criteria.rb +206 -0
  103. data/lib/cuprum/collections/scopes/criteria_scope.rb +12 -0
  104. data/lib/cuprum/collections/scopes/disjunction.rb +45 -0
  105. data/lib/cuprum/collections/scopes/disjunction_scope.rb +12 -0
  106. data/lib/cuprum/collections/scopes/none.rb +62 -0
  107. data/lib/cuprum/collections/scopes/none_scope.rb +18 -0
  108. data/lib/cuprum/collections/scopes.rb +23 -0
  109. data/lib/cuprum/collections/version.rb +2 -2
  110. data/lib/cuprum/collections.rb +14 -9
  111. metadata +61 -15
  112. data/lib/cuprum/collections/basic/query_builder.rb +0 -69
  113. data/lib/cuprum/collections/command.rb +0 -26
  114. data/lib/cuprum/collections/queries/parse.rb +0 -22
  115. data/lib/cuprum/collections/queries/parse_block.rb +0 -206
  116. data/lib/cuprum/collections/queries/parse_strategy.rb +0 -91
  117. data/lib/cuprum/collections/query_builder.rb +0 -61
  118. data/lib/cuprum/collections/rspec/contracts/basic/command_contracts.rb +0 -484
@@ -100,22 +100,22 @@ module Cuprum::Collections::Commands
100
100
 
101
101
  def create_entity(attributes:)
102
102
  Cuprum::Collections::Commands::Create
103
- .new(collection: collection, contract: contract)
104
- .call(attributes: attributes)
103
+ .new(collection:, contract:)
104
+ .call(attributes:)
105
105
  end
106
106
 
107
107
  def filter_attributes(attributes:)
108
108
  tools
109
109
  .hash_tools
110
110
  .convert_keys_to_strings(attributes)
111
- .select { |key, _| attribute_names.include?(key) }
111
+ .slice(*attribute_names)
112
112
  end
113
113
 
114
114
  def find_entity(attributes:)
115
- filtered = filter_attributes(attributes: attributes)
115
+ filtered = filter_attributes(attributes:)
116
116
  result =
117
117
  Cuprum::Collections::Commands::FindOneMatching
118
- .new(collection: collection)
118
+ .new(collection:)
119
119
  .call(attributes: filtered)
120
120
 
121
121
  return if result.error.is_a?(Cuprum::Collections::Errors::NotFound)
@@ -140,12 +140,12 @@ module Cuprum::Collections::Commands
140
140
  end
141
141
 
142
142
  def process(attributes:)
143
- entity = step { find_entity(attributes: attributes) }
143
+ entity = step { find_entity(attributes:) }
144
144
 
145
145
  if entity
146
- update_entity(attributes: attributes, entity: entity)
146
+ update_entity(attributes:, entity:)
147
147
  else
148
- create_entity(attributes: attributes)
148
+ create_entity(attributes:)
149
149
  end
150
150
  end
151
151
 
@@ -155,8 +155,8 @@ module Cuprum::Collections::Commands
155
155
 
156
156
  def update_entity(attributes:, entity:)
157
157
  Cuprum::Collections::Commands::Update
158
- .new(collection: collection, contract: contract)
159
- .call(attributes: attributes, entity: entity)
158
+ .new(collection:, contract:)
159
+ .call(attributes:, entity:)
160
160
  end
161
161
  end
162
162
  end
@@ -8,6 +8,7 @@ module Cuprum::Collections
8
8
  autoload :Associations, 'cuprum/collections/commands/associations'
9
9
  autoload :Create, 'cuprum/collections/commands/create'
10
10
  autoload :FindOneMatching, 'cuprum/collections/commands/find_one_matching'
11
+ autoload :QueryCommand, 'cuprum/collections/commands/query_command'
11
12
  autoload :Update, 'cuprum/collections/commands/update'
12
13
  autoload :Upsert, 'cuprum/collections/commands/upsert'
13
14
  end
@@ -47,8 +47,8 @@ module Cuprum::Collections::Constraints
47
47
  super(
48
48
  *ordering_constraints,
49
49
  **resolve_required_option(
50
- optional: optional,
51
- required: required,
50
+ optional:,
51
+ required:,
52
52
  **options
53
53
  )
54
54
  )
@@ -35,12 +35,12 @@ module Cuprum::Collections::Errors
35
35
  resolve_options(**options)
36
36
 
37
37
  super(
38
- attribute_name: attribute_name,
39
- attribute_value: attribute_value,
40
- attributes: attributes,
41
- collection_name: collection_name,
38
+ attribute_name:,
39
+ attribute_value:,
40
+ attributes:,
41
+ collection_name:,
42
42
  message: generate_message,
43
- query: query&.criteria
43
+ scope:
44
44
  )
45
45
  end
46
46
 
@@ -56,18 +56,23 @@ module Cuprum::Collections::Errors
56
56
  # @return [String] the name of the collection.
57
57
  attr_reader :collection_name
58
58
 
59
- # @return [Cuprum::Collections::Query] the performed query, if any.
60
- attr_reader :query
59
+ # @return [Cuprum::Collections::Scopes::Base] the query scope, if any.
60
+ attr_reader :scope
61
61
 
62
- # @return [Array<Array>] the details of the query, in query criteria format.
63
- def details
62
+ # @return [Array<Array>] the details of the query, in scope format.
63
+ def details # rubocop:disable Metrics/MethodLength
64
64
  if attribute_name
65
- [[attribute_name, :equal, attribute_value]]
65
+ criteria = [[attribute_name, :equal, attribute_value]]
66
+
67
+ { 'type' => :criteria, 'criteria' => criteria }
66
68
  elsif attributes
67
- attributes
68
- .map { |attr_name, attr_value| [attr_name, :equal, attr_value] }
69
- elsif query
70
- query.criteria
69
+ criteria =
70
+ attributes
71
+ .map { |attr_name, attr_value| [attr_name, :equal, attr_value] }
72
+
73
+ { 'type' => :criteria, 'criteria' => criteria }
74
+ elsif scope
75
+ scope
71
76
  end
72
77
  end
73
78
 
@@ -112,10 +117,10 @@ module Cuprum::Collections::Errors
112
117
  if attribute_name
113
118
  "#{core_message} with #{attribute_name.inspect} " \
114
119
  "#{attribute_value.inspect}" \
115
- "#{primary_key? ? ' (primary key)' : ''}"
120
+ "#{' (primary key)' if primary_key?}"
116
121
  elsif attributes
117
122
  "#{core_message} with attributes #{attributes.inspect}"
118
- elsif query
123
+ elsif scope
119
124
  "#{core_message} matching the query"
120
125
  end
121
126
  end
@@ -142,15 +147,13 @@ module Cuprum::Collections::Errors
142
147
 
143
148
  def resolve_query_options(**options)
144
149
  options = options.dup
145
- @query = options.delete(:query)
150
+ @scope = options.delete(:query)&.scope
146
151
 
147
152
  validate_keywords(extra_keywords: options.keys)
148
153
  end
149
154
 
150
155
  def resolve_options(**options) # rubocop:disable Metrics/MethodLength
151
- if options[:primary_key_name] && options[:primary_key_values]
152
- resolve_primary_key_options(**options)
153
- elsif options[:attribute_name] && options.key?(:attribute_value)
156
+ if options[:attribute_name] && options.key?(:attribute_value)
154
157
  resolve_attribute_options(**options)
155
158
  elsif options[:attributes]
156
159
  resolve_attributes_options(**options)
@@ -163,26 +166,6 @@ module Cuprum::Collections::Errors
163
166
  end
164
167
  end
165
168
 
166
- def resolve_primary_key_options(**options) # rubocop:disable Metrics/MethodLength
167
- values = Array(options[:primary_key_values])
168
-
169
- unless values.size == 1
170
- raise ArgumentError,
171
- 'deprecated mode does not support empty or multiple attribute values'
172
- end
173
-
174
- SleepingKingStudios::Tools::CoreTools
175
- .instance
176
- .deprecate(
177
- 'NotFound.new(primary_key_name:, primary_key_values:)',
178
- message: 'use NotFound.new(attribute_name:, attribute_value:)'
179
- )
180
-
181
- @attribute_name = options[:primary_key_name]
182
- @attribute_value = values.first
183
- @primary_key = true
184
- end
185
-
186
169
  def titleize(string)
187
170
  tools.str.underscore(string).split('_').map(&:capitalize).join(' ')
188
171
  end
@@ -198,11 +181,11 @@ module Cuprum::Collections::Errors
198
181
 
199
182
  if ambiguous_keywords.empty?
200
183
  raise ArgumentError,
201
- "unknown keyword#{extra_keywords.size == 1 ? '' : 's'} " \
184
+ "unknown keyword#{'s' unless extra_keywords.size == 1} " \
202
185
  "#{extra_keywords.map(&:inspect).join(', ')}"
203
186
  else
204
187
  raise ArgumentError,
205
- "ambiguous keyword#{extra_keywords.size == 1 ? '' : 's'} " \
188
+ "ambiguous keyword#{'s' unless extra_keywords.size == 1} " \
206
189
  "#{ambiguous_keywords.map(&:inspect).join(', ')}"
207
190
  end
208
191
  end
@@ -21,10 +21,10 @@ module Cuprum::Collections::Errors
21
21
  @valid_attributes = valid_attributes
22
22
 
23
23
  super(
24
- entity_class: entity_class,
25
- extra_attributes: extra_attributes,
24
+ entity_class:,
25
+ extra_attributes:,
26
26
  message: default_message,
27
- valid_attributes: valid_attributes
27
+ valid_attributes:
28
28
  )
29
29
  end
30
30
 
@@ -18,8 +18,8 @@ module Cuprum::Collections::Errors
18
18
  @errors = errors
19
19
 
20
20
  super(
21
- entity_class: entity_class,
22
- errors: errors,
21
+ entity_class:,
22
+ errors:,
23
23
  message: default_message
24
24
  )
25
25
  end
@@ -18,8 +18,8 @@ module Cuprum::Collections::Errors
18
18
  @errors = errors
19
19
 
20
20
  super(
21
- command: command,
22
- errors: errors,
21
+ command:,
22
+ errors:,
23
23
  message: "invalid parameters for command #{command.class}"
24
24
  )
25
25
  end
@@ -10,36 +10,30 @@ module Cuprum::Collections::Errors
10
10
  # Short string used to identify the type of error.
11
11
  TYPE = 'cuprum.collections.errors.invalid_query'
12
12
 
13
- # @param errors [Stannum::Errors] The errors returned by the query builder.
14
- # @param strategy [Symbol] The strategy used to construct the query.
15
- def initialize(errors:, strategy:, message: nil)
16
- @errors = errors
17
- @strategy = strategy
13
+ # @param query [Object] the given filter parameters, if any.
14
+ # @param message [String] the message to display.
15
+ def initialize(query:, message: nil)
16
+ @query = query
18
17
 
19
18
  super(
20
- errors: errors,
21
- message: message || default_message,
22
- strategy: strategy
19
+ message: message || default_message,
20
+ query:
23
21
  )
24
22
  end
25
23
 
26
- # @return [Stannum::Errors] the errors returned by the query builder.
27
- attr_reader :errors
28
-
29
- # @return [Symbol] the strategy used to construct the query.
30
- attr_reader :strategy
24
+ # @return [Object] the given filter parameters, if any.
25
+ attr_reader :query
31
26
 
32
27
  private
33
28
 
34
29
  def as_json_data
35
30
  {
36
- 'errors' => errors.to_a,
37
- 'strategy' => strategy
31
+ 'query' => (query.respond_to?(:as_json) ? query.as_json : query.inspect)
38
32
  }
39
33
  end
40
34
 
41
35
  def default_message
42
- "unable to parse query with strategy #{strategy.inspect}"
36
+ 'unable to parse query'
43
37
  end
44
38
  end
45
39
  end
@@ -15,7 +15,7 @@ module Cuprum::Collections::Errors
15
15
  @entity_class = entity_class
16
16
 
17
17
  super(
18
- entity_class: entity_class,
18
+ entity_class:,
19
19
  message: default_message
20
20
  )
21
21
  end
@@ -15,7 +15,7 @@ module Cuprum::Collections::Errors
15
15
 
16
16
  super(
17
17
  message: generate_message,
18
- operator: operator
18
+ operator:
19
19
  )
20
20
  end
21
21
 
@@ -19,6 +19,37 @@ module Cuprum::Collections
19
19
  ONE_OF: :one_of
20
20
  ).freeze
21
21
 
22
+ # Exception raised when trying to invert an operator with no inverse.
23
+ class UninvertibleOperatorException < StandardError; end
24
+
25
+ # Exception raised when an invalid operator is referenced.
26
+ class UnknownOperatorException < StandardError
27
+ # @param msg [String] the exception message.
28
+ # @param name [String] the name of the invalid operator.
29
+ def initialize(msg = nil, name = nil)
30
+ super(msg)
31
+
32
+ @name = name
33
+ end
34
+
35
+ # @return [String] the name of the invalid operator.
36
+ def name
37
+ @name || cause&.name
38
+ end
39
+ end
40
+
41
+ # Defines the negated operator corresponding to each operator.
42
+ INVERTIBLE_OPERATORS = {
43
+ Operators::EQUAL => Operators::NOT_EQUAL,
44
+ Operators::GREATER_THAN => Operators::LESS_THAN_OR_EQUAL_TO,
45
+ Operators::GREATER_THAN_OR_EQUAL_TO => Operators::LESS_THAN,
46
+ Operators::LESS_THAN => Operators::GREATER_THAN_OR_EQUAL_TO, # rubocop:disable Layout/LineLength
47
+ Operators::LESS_THAN_OR_EQUAL_TO => Operators::GREATER_THAN,
48
+ Operators::NOT_EQUAL => Operators::EQUAL,
49
+ Operators::NOT_ONE_OF => Operators::ONE_OF,
50
+ Operators::ONE_OF => Operators::NOT_ONE_OF
51
+ }.freeze
52
+
22
53
  # Enumerates the valid operators as a Set for performant lookup.
23
54
  VALID_OPERATORS = Set.new(Operators.values).freeze
24
55
  end
@@ -2,38 +2,36 @@
2
2
 
3
3
  require 'cuprum/collections'
4
4
  require 'cuprum/collections/queries/ordering'
5
+ require 'cuprum/collections/scopes/all_scope'
5
6
 
6
7
  module Cuprum::Collections
7
8
  # Abstract base class for collection Query implementations.
8
9
  class Query
10
+ include Enumerable
11
+
9
12
  UNDEFINED = Object.new.freeze
10
13
  private_constant :UNDEFINED
11
14
 
12
- def initialize
13
- @criteria = []
15
+ # @param scope [Cuprum::Collections::Scopes::Base] the base scope for the
16
+ # query. Defaults to nil.
17
+ def initialize(scope: nil)
18
+ @limit = nil
19
+ @offset = nil
20
+ @order = {}
21
+ @scope = scope ? default_scope.and(scope) : default_scope
14
22
  end
15
23
 
16
- # Returns a normalized representation of the query criteria.
17
- #
18
- # The query criteria define which data from the collection matches the
19
- # query. Specifically, an item in the collection matches the query if and
20
- # only if it matches each criterion. If the query has no criteria, then it
21
- # will match all items in the collection.
22
- #
23
- # Each criterion is represented as an Array with three elements:
24
- # - The name of the property or column to select by.
25
- # - The operation to filter, such as :eq (an equality operation).
26
- # - The expected value.
24
+ # Returns the current scope for the query.
27
25
  #
28
- # For example, a query that selects all items whose :series property is
29
- # equal to 'The Lord of the Rings' would have the following criterion:
30
- # `[:series, :eq, 'The Lord of the Rings']`.
26
+ # Composition methods should not be called on the scope directly, as they
27
+ # will not change the scope object bound to the query. Call the
28
+ # corresponding methods on the query itself, i.e. call query.where() instead
29
+ # of query.scope.where().
31
30
  #
32
- # @return [Array<Array>] the query criteria.
33
- #
34
- # @see #where
35
- def criteria
36
- @criteria.dup
31
+ # @return [Cuprum::Collections::Scopes::Base] the current scope for the
32
+ # query.
33
+ def scope
34
+ @scope ||= default_scope
37
35
  end
38
36
 
39
37
  # Sets or returns the maximum number of items returned by the query.
@@ -151,49 +149,35 @@ module Cuprum::Collections
151
149
  dup.reset!
152
150
  end
153
151
 
154
- # Returns a copy of the query with the specified filters.
152
+ # @overload where(hash)
153
+ # Returns a copy of the query with the specified filters.
155
154
  #
156
- # The given parameters are used to construct query criteria, which define
157
- # which data from the collection matches the query. Specifically, an item in
158
- # the collection matches the query if and only if it matches each criterion.
159
- # If the query has no criteria, then it will match all items in the
160
- # collection.
155
+ # If the query already has a scope, then the filters will be merged with
156
+ # the existing scope. Any items in the collection must match both the
157
+ # previous scope and the new criteria to be returned by the query.
161
158
  #
162
- # When #where is called on a query that already defines criteria, then the
163
- # new criteria are appended to the old. Any items in the collection must
164
- # match both the old and the new criteria to be returned by the query.
159
+ # @param hash [Hash{String=>Object}] the filters to apply to the query.
165
160
  #
166
- # @example Filtering Data By Equality
167
- # # The query will only return items whose author is 'J.R.R. Tolkien'.
168
- # query = query.where { { author: 'J.R.R. Tolkien' } }
161
+ # @example Filtering Data By Equality
162
+ # # The query will only return items whose author is 'J.R.R. Tolkien'.
163
+ # query = query.where { { author: 'J.R.R. Tolkien' } }
169
164
  #
170
- # @example Filtering Data By Operator
171
- # # The query will only return items whose author is 'J.R.R. Tolkien',
172
- # # and whose series is not 'The Lord of the Rings'.
173
- # query = query.where do
174
- # {
175
- # author: eq('J.R.R. Tolkien'),
176
- # series: ne('The Lord of the Rings')
177
- # }
178
- # end
165
+ # @see #scope
179
166
  #
180
167
  # @overload where(&block)
181
- # @yield The given block is passed to a QueryBuilder, which converts the
182
- # block to query criteria and generates a new query using those
183
- # criteria.
168
+ # Returns a copy of the query with the specified filters.
184
169
  #
185
- # @yieldreturn [Hash] The filters to apply to the query. The hash keys
186
- # should be the names of attributes or columns, and the corresponding
187
- # values should be either the literal value for that attribute or a
188
- # method call for a valid operation defined for the query.
170
+ # If the query already has a scope, then the filters will be merged with
171
+ # the existing scope. Any items in the collection must match both the
172
+ # previous scope and the new criteria to be returned by the query.
189
173
  #
190
- # @see #criteria
191
- def where(filter = nil, strategy: nil, &block)
192
- filter ||= block
193
-
194
- return dup if filter.nil? && strategy.nil?
195
-
196
- query_builder.call(strategy: strategy, where: filter)
174
+ # @yieldreturn [Hash{String=>Object}] the filters to apply to the query.
175
+ # The hash keys should be the names of attributes or columns, and the
176
+ # corresponding values should be either the literal value for that
177
+ # attribute or a method call for a valid operation defined for the
178
+ # query.
179
+ def where(...)
180
+ dup.with_scope(scope.where(...)).reset!
197
181
  end
198
182
 
199
183
  protected
@@ -204,12 +188,6 @@ module Cuprum::Collections
204
188
  # :nocov:
205
189
  end
206
190
 
207
- def with_criteria(criteria)
208
- @criteria += criteria
209
-
210
- self
211
- end
212
-
213
191
  def with_limit(count)
214
192
  @limit = count
215
193
 
@@ -228,8 +206,18 @@ module Cuprum::Collections
228
206
  self
229
207
  end
230
208
 
209
+ def with_scope(scope)
210
+ @scope = scope
211
+
212
+ self
213
+ end
214
+
231
215
  private
232
216
 
217
+ def default_scope
218
+ Cuprum::Collections::Scopes::AllScope.new
219
+ end
220
+
233
221
  def validate_limit(count)
234
222
  return if count.is_a?(Integer) && !count.negative?
235
223