cuprum-collections 0.5.1 → 0.6.0.rc.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +47 -0
- data/lib/cuprum/collections/adaptable/collection.rb +18 -0
- data/lib/cuprum/collections/adaptable/command.rb +22 -0
- data/lib/cuprum/collections/adaptable/commands/abstract_assign_one.rb +27 -0
- data/lib/cuprum/collections/adaptable/commands/abstract_build_one.rb +25 -0
- data/lib/cuprum/collections/adaptable/commands/abstract_validate_one.rb +35 -0
- data/lib/cuprum/collections/adaptable/commands.rb +15 -0
- data/lib/cuprum/collections/adaptable/query.rb +64 -0
- data/lib/cuprum/collections/adaptable.rb +13 -0
- data/lib/cuprum/collections/adapter.rb +300 -0
- data/lib/cuprum/collections/adapters/data_adapter.rb +82 -0
- data/lib/cuprum/collections/adapters/entity_adapter.rb +76 -0
- data/lib/cuprum/collections/adapters/hash_adapter.rb +48 -0
- data/lib/cuprum/collections/adapters.rb +14 -0
- data/lib/cuprum/collections/basic/collection.rb +2 -20
- data/lib/cuprum/collections/basic/commands/destroy_one.rb +1 -1
- data/lib/cuprum/collections/basic/commands/find_many.rb +0 -31
- data/lib/cuprum/collections/basic/commands/find_matching.rb +0 -94
- data/lib/cuprum/collections/basic/commands/find_one.rb +0 -18
- data/lib/cuprum/collections/basic/commands/insert_one.rb +1 -1
- data/lib/cuprum/collections/basic/commands/update_one.rb +1 -1
- data/lib/cuprum/collections/basic/scopes/criteria_scope.rb +36 -21
- data/lib/cuprum/collections/basic.rb +6 -5
- data/lib/cuprum/collections/collection.rb +6 -0
- data/lib/cuprum/collections/collection_command.rb +1 -1
- data/lib/cuprum/collections/commands/abstract_find_many.rb +40 -3
- data/lib/cuprum/collections/commands/abstract_find_matching.rb +102 -0
- data/lib/cuprum/collections/commands/abstract_find_one.rb +23 -1
- data/lib/cuprum/collections/commands/associations/find_many.rb +1 -3
- data/lib/cuprum/collections/commands/associations/require_many.rb +1 -1
- data/lib/cuprum/collections/commands/find_one_matching.rb +10 -10
- data/lib/cuprum/collections/commands/query_command.rb +6 -4
- data/lib/cuprum/collections/commands/upsert.rb +0 -2
- data/lib/cuprum/collections/constraints/order/attributes_array.rb +5 -4
- data/lib/cuprum/collections/constraints/order/attributes_hash.rb +5 -4
- data/lib/cuprum/collections/constraints/order/sort_direction.rb +2 -2
- data/lib/cuprum/collections/constraints/ordering.rb +11 -9
- data/lib/cuprum/collections/constraints/query_hash.rb +2 -2
- data/lib/cuprum/collections/errors/abstract_find_error.rb +101 -23
- data/lib/cuprum/collections/errors/extra_attributes.rb +3 -3
- data/lib/cuprum/collections/errors/failed_validation.rb +3 -3
- data/lib/cuprum/collections/errors/missing_default_contract.rb +12 -4
- data/lib/cuprum/collections/queries.rb +4 -0
- data/lib/cuprum/collections/relation.rb +0 -2
- data/lib/cuprum/collections/relations/parameters.rb +120 -68
- data/lib/cuprum/collections/repository.rb +71 -6
- data/lib/cuprum/collections/rspec/contracts/query_contracts.rb +23 -4
- data/lib/cuprum/collections/rspec/contracts/repository_contracts.rb +18 -0
- data/lib/cuprum/collections/rspec/contracts/scope_contracts.rb +51 -0
- data/lib/cuprum/collections/rspec/contracts/scopes/builder_contracts.rb +10 -0
- data/lib/cuprum/collections/rspec/contracts/scopes/composition_contracts.rb +8 -0
- data/lib/cuprum/collections/rspec/contracts/scopes/criteria_contracts.rb +18 -366
- data/lib/cuprum/collections/rspec/contracts/scopes/logical_contracts.rb +30 -0
- data/lib/cuprum/collections/rspec/contracts/scopes.rb +2 -0
- data/lib/cuprum/collections/rspec/contracts.rb +2 -10
- data/lib/cuprum/collections/rspec/deferred/adapter_examples.rb +1077 -0
- data/lib/cuprum/collections/rspec/deferred/collection_examples.rb +27 -7
- data/lib/cuprum/collections/rspec/deferred/commands/assign_one_examples.rb +4 -4
- data/lib/cuprum/collections/rspec/deferred/commands/build_one_examples.rb +2 -2
- data/lib/cuprum/collections/rspec/deferred/commands/destroy_one_examples.rb +2 -2
- data/lib/cuprum/collections/rspec/deferred/commands/find_many_examples.rb +5 -5
- data/lib/cuprum/collections/rspec/deferred/commands/find_matching_examples.rb +45 -12
- data/lib/cuprum/collections/rspec/deferred/commands/find_one_examples.rb +2 -2
- data/lib/cuprum/collections/rspec/deferred/commands/insert_one_examples.rb +1 -1
- data/lib/cuprum/collections/rspec/deferred/commands/update_one_examples.rb +1 -1
- data/lib/cuprum/collections/rspec/deferred/query_examples.rb +930 -0
- data/lib/cuprum/collections/rspec/deferred/relation_examples.rb +48 -17
- data/lib/cuprum/collections/rspec/deferred/repository_examples.rb +961 -0
- data/lib/cuprum/collections/rspec/deferred/scope_examples.rb +598 -0
- data/lib/cuprum/collections/rspec/deferred/scopes/all_examples.rb +391 -0
- data/lib/cuprum/collections/rspec/deferred/scopes/builder_examples.rb +857 -0
- data/lib/cuprum/collections/rspec/deferred/scopes/composition_examples.rb +93 -0
- data/lib/cuprum/collections/rspec/deferred/scopes/conjunction_examples.rb +438 -0
- data/lib/cuprum/collections/rspec/deferred/scopes/criteria_examples.rb +1941 -0
- data/lib/cuprum/collections/rspec/deferred/scopes/disjunction_examples.rb +415 -0
- data/lib/cuprum/collections/rspec/deferred/scopes/none_examples.rb +385 -0
- data/lib/cuprum/collections/rspec/deferred/scopes/parser_examples.rb +740 -0
- data/lib/cuprum/collections/rspec/deferred/scopes.rb +8 -0
- data/lib/cuprum/collections/scope.rb +2 -2
- data/lib/cuprum/collections/scopes/container.rb +5 -4
- data/lib/cuprum/collections/scopes/criteria/parser.rb +24 -48
- data/lib/cuprum/collections/scopes/criteria.rb +7 -6
- data/lib/cuprum/collections/version.rb +4 -4
- data/lib/cuprum/collections.rb +5 -1
- metadata +47 -11
- data/lib/cuprum/collections/rspec/contracts/association_contracts.rb +0 -2127
- data/lib/cuprum/collections/rspec/contracts/basic.rb +0 -11
- data/lib/cuprum/collections/rspec/contracts/collection_contracts.rb +0 -387
- data/lib/cuprum/collections/rspec/contracts/command_contracts.rb +0 -169
- data/lib/cuprum/collections/rspec/contracts/relation_contracts.rb +0 -1264
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'cuprum/parameter_validation'
|
|
4
|
+
|
|
3
5
|
require 'cuprum/collections/commands'
|
|
4
6
|
require 'cuprum/collections/constraints/ordering'
|
|
5
7
|
require 'cuprum/collections/errors/invalid_query'
|
|
@@ -7,6 +9,102 @@ require 'cuprum/collections/errors/invalid_query'
|
|
|
7
9
|
module Cuprum::Collections::Commands
|
|
8
10
|
# Abstract implementation of the FindMatching command.
|
|
9
11
|
module AbstractFindMatching
|
|
12
|
+
include Cuprum::ParameterValidation
|
|
13
|
+
|
|
14
|
+
# @!method call(envelope: false, limit: nil, offset: nil, order: nil, scope: nil, where: nil, &block)
|
|
15
|
+
# Queries the collection for items matching the given conditions.
|
|
16
|
+
#
|
|
17
|
+
# @param envelope [Boolean] If true, wraps the result value in a Hash.
|
|
18
|
+
# @param limit [Integer] The maximum number of results to return.
|
|
19
|
+
# @param offset [Integer] The initial ordered items to skip.
|
|
20
|
+
# @param order [Array<String, Symbol>, Hash<{String, Symbol => Symbol}>]
|
|
21
|
+
# The sort order of the returned items. Should be either an array of
|
|
22
|
+
# attribute names or a hash of attribute names and directions.
|
|
23
|
+
# @param scope [Cuprum::Collections::Basic::Query, nil] Optional scope for
|
|
24
|
+
# the query. Items must match the scope as well as the :where filters.
|
|
25
|
+
# @param where [Object] Additional filters for selecting data. The command
|
|
26
|
+
# will only return data matching these filters.
|
|
27
|
+
# @yield The given block is passed to a QueryBuilder, which converts the
|
|
28
|
+
# block to query criteria and generates a new query using those
|
|
29
|
+
# criteria.
|
|
30
|
+
# @yieldreturn [Hash] The filters to apply to the query. The hash keys
|
|
31
|
+
# should be the names of attributes or columns, and the corresponding
|
|
32
|
+
# values should be either the literal value for that attribute or a
|
|
33
|
+
# method call for a valid operation defined for the query.
|
|
34
|
+
#
|
|
35
|
+
# @example Querying all items in the collection.
|
|
36
|
+
# command = FindMatching.new(collection_name: 'books', data: books)
|
|
37
|
+
# command.call
|
|
38
|
+
# #=> an enumerable iterating all items in the collection
|
|
39
|
+
#
|
|
40
|
+
# @example Querying all items matching some critera:
|
|
41
|
+
# command.call { { author: 'Nnedi Okorafor' } }
|
|
42
|
+
# #=> an enumerable iterating all items in the collection whose author
|
|
43
|
+
# is 'Nnedi Okorafor'
|
|
44
|
+
#
|
|
45
|
+
# @example Ordering query results
|
|
46
|
+
# command.call(order: :title) { { author: 'Nnedi Okorafor' } }
|
|
47
|
+
# #=> an enumerable iterating all items in the collection whose author
|
|
48
|
+
# # is 'Nnedi Okorafor', sorted by :title in ascending order
|
|
49
|
+
#
|
|
50
|
+
# @example Advanced filtering
|
|
51
|
+
# command.call do
|
|
52
|
+
# {
|
|
53
|
+
# category: eq('Science Fiction and Fantasy'),
|
|
54
|
+
# author: ne('J.R.R. Tolkien')
|
|
55
|
+
# }
|
|
56
|
+
# end
|
|
57
|
+
# #=> an enumerable iterating all items in the collection whose category
|
|
58
|
+
# # is 'Science Fiction and Fantasy', and whose author is not
|
|
59
|
+
# # 'J.R.R. Tolkien'.
|
|
60
|
+
#
|
|
61
|
+
# @example Advanced ordering
|
|
62
|
+
# order = { author: :asc, genre: :desc }
|
|
63
|
+
# command.call(order: order) { { author: 'Nnedi Okorafor' } }
|
|
64
|
+
# #=> an enumerable iterating all items in the collection whose author
|
|
65
|
+
# # is 'Nnedi Okorafor', sorted first by :author in ascending order
|
|
66
|
+
# # and within the same author by genre in descending order
|
|
67
|
+
#
|
|
68
|
+
# @example Filtering, ordering, and subsets
|
|
69
|
+
# command.call(offset: 50, limit: 10, order: :author) do
|
|
70
|
+
# { category: 'Science Fiction and Fantasy' }
|
|
71
|
+
# end
|
|
72
|
+
# #=> an enumerable iterating the 51st through 60th items in the
|
|
73
|
+
# # collection whose category is 'Science Fiction and Fantasy', sorted
|
|
74
|
+
# # by :author in ascending order.
|
|
75
|
+
#
|
|
76
|
+
# @example Wrapping the result in an envelope
|
|
77
|
+
# command =
|
|
78
|
+
# FindMatching.new(
|
|
79
|
+
# collection_name: 'books',
|
|
80
|
+
# data: books
|
|
81
|
+
# )
|
|
82
|
+
# command.call(envelope: true)
|
|
83
|
+
# #=> {
|
|
84
|
+
# 'books' => [] # an array containing the matching items
|
|
85
|
+
# }
|
|
86
|
+
#
|
|
87
|
+
# @overload call(limit: nil, offset: nil, order: nil, &block)
|
|
88
|
+
# When the :envelope option is false (default), the command returns an
|
|
89
|
+
# Enumerator which can be iterated to return the matching items.
|
|
90
|
+
#
|
|
91
|
+
# @return [Cuprum::Result<Enumerator>] the matching items in the
|
|
92
|
+
# specified order as an Enumerator.
|
|
93
|
+
#
|
|
94
|
+
# @overload call(limit: nil, offset: nil, order: nil, &block)
|
|
95
|
+
# When the :envelope option is true, the command immediately evaluates
|
|
96
|
+
# the query and wraps the resulting array in a Hash, using the name of
|
|
97
|
+
# the collection as the key.
|
|
98
|
+
#
|
|
99
|
+
# @return [Cuprum::Result<Hash{String, Array<Hash{String, Object}>}>] a
|
|
100
|
+
# hash with the collection name as key and the matching items as
|
|
101
|
+
# value.
|
|
102
|
+
validate :envelope, :boolean, optional: true
|
|
103
|
+
validate :limit, Integer, optional: true
|
|
104
|
+
validate :offset, Integer, optional: true
|
|
105
|
+
validate :order
|
|
106
|
+
validate :where
|
|
107
|
+
|
|
10
108
|
private
|
|
11
109
|
|
|
12
110
|
def apply_query(limit:, offset:, order:, scope:)
|
|
@@ -22,6 +120,8 @@ module Cuprum::Collections::Commands
|
|
|
22
120
|
def build_scope(value, &)
|
|
23
121
|
return Cuprum::Collections::Scope.build(&) if block_given?
|
|
24
122
|
|
|
123
|
+
return Cuprum::Collections::Scope.build(&value) if value.is_a?(Proc)
|
|
124
|
+
|
|
25
125
|
return value if value.is_a?(Cuprum::Collections::Scopes::Base)
|
|
26
126
|
|
|
27
127
|
Cuprum::Collections::Scope.build(value) if value
|
|
@@ -69,6 +169,8 @@ module Cuprum::Collections::Commands
|
|
|
69
169
|
def validate_where(value, as: 'where')
|
|
70
170
|
return if value.nil?
|
|
71
171
|
|
|
172
|
+
return if value.is_a?(Proc) && (-1..1).cover?(value.arity)
|
|
173
|
+
|
|
72
174
|
return if value.is_a?(Cuprum::Collections::Scopes::Base)
|
|
73
175
|
|
|
74
176
|
return if validate_attributes(value, as:).empty?
|
|
@@ -1,11 +1,33 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'cuprum/parameter_validation'
|
|
4
|
+
|
|
3
5
|
require 'cuprum/collections/commands'
|
|
4
6
|
require 'cuprum/collections/errors/not_found'
|
|
5
7
|
|
|
6
8
|
module Cuprum::Collections::Commands
|
|
7
9
|
# Abstract implementation of the FindOne command.
|
|
8
10
|
module AbstractFindOne
|
|
11
|
+
include Cuprum::ParameterValidation
|
|
12
|
+
|
|
13
|
+
# @!method call(primary_key:, envelope: false)
|
|
14
|
+
# Queries the collection for the item with the given primary key.
|
|
15
|
+
#
|
|
16
|
+
# The command will find and return the entity with the given primary key.
|
|
17
|
+
# If the entity is not found, the command will fail and return a NotFound
|
|
18
|
+
# error.
|
|
19
|
+
#
|
|
20
|
+
# When the :envelope option is true, the command wraps the item in a Hash,
|
|
21
|
+
# using the singular name of the collection as the key.
|
|
22
|
+
#
|
|
23
|
+
# @param envelope [Boolean] If true, wraps the result value in a Hash.
|
|
24
|
+
# @param primary_key [Object] The primary key of the requested item.
|
|
25
|
+
#
|
|
26
|
+
# @return [Cuprum::Result<Hash{String, Object}>] a result with the
|
|
27
|
+
# requested item.
|
|
28
|
+
validate :envelope, :boolean, optional: true
|
|
29
|
+
validate :primary_key
|
|
30
|
+
|
|
9
31
|
private
|
|
10
32
|
|
|
11
33
|
def apply_query(primary_key:)
|
|
@@ -20,7 +42,7 @@ module Cuprum::Collections::Commands
|
|
|
20
42
|
error = Cuprum::Collections::Errors::NotFound.new(
|
|
21
43
|
attribute_name: primary_key_name,
|
|
22
44
|
attribute_value: primary_key,
|
|
23
|
-
|
|
45
|
+
name:,
|
|
24
46
|
primary_key: true
|
|
25
47
|
)
|
|
26
48
|
Cuprum::Result.new(error:)
|
|
@@ -74,9 +74,7 @@ module Cuprum::Collections::Commands::Associations
|
|
|
74
74
|
private
|
|
75
75
|
|
|
76
76
|
def collection
|
|
77
|
-
repository.
|
|
78
|
-
qualified_name: association.qualified_name
|
|
79
|
-
)
|
|
77
|
+
repository.find(qualified_name: association.qualified_name)
|
|
80
78
|
end
|
|
81
79
|
|
|
82
80
|
def extract_keys(association, hsh)
|
|
@@ -24,7 +24,7 @@ module Cuprum::Collections::Commands::Associations
|
|
|
24
24
|
Cuprum::Collections::Errors::NotFound.new(
|
|
25
25
|
attribute_name: association.query_key_name,
|
|
26
26
|
attribute_value:,
|
|
27
|
-
|
|
27
|
+
name: association.name,
|
|
28
28
|
primary_key: association.primary_key_query?
|
|
29
29
|
)
|
|
30
30
|
end
|
|
@@ -91,25 +91,25 @@ module Cuprum::Collections::Commands
|
|
|
91
91
|
|
|
92
92
|
private
|
|
93
93
|
|
|
94
|
-
def error_params_for(attributes: nil, &
|
|
95
|
-
{
|
|
94
|
+
def error_params_for(attributes: nil, &)
|
|
95
|
+
{ name: collection.name }.merge(
|
|
96
96
|
if block_given?
|
|
97
|
-
{ query: collection.query.where(&
|
|
97
|
+
{ query: collection.query.where(&) }
|
|
98
98
|
else
|
|
99
99
|
{ attributes: }
|
|
100
100
|
end
|
|
101
101
|
)
|
|
102
102
|
end
|
|
103
103
|
|
|
104
|
-
def not_found_error(attributes: nil, &
|
|
104
|
+
def not_found_error(attributes: nil, &)
|
|
105
105
|
Cuprum::Collections::Errors::NotFound.new(
|
|
106
|
-
**error_params_for(attributes:, &
|
|
106
|
+
**error_params_for(attributes:, &)
|
|
107
107
|
)
|
|
108
108
|
end
|
|
109
109
|
|
|
110
|
-
def not_unique_error(attributes: nil, &
|
|
110
|
+
def not_unique_error(attributes: nil, &)
|
|
111
111
|
Cuprum::Collections::Errors::NotUnique.new(
|
|
112
|
-
**error_params_for(attributes:, &
|
|
112
|
+
**error_params_for(attributes:, &)
|
|
113
113
|
)
|
|
114
114
|
end
|
|
115
115
|
|
|
@@ -120,14 +120,14 @@ module Cuprum::Collections::Commands
|
|
|
120
120
|
require_one_entity(attributes:, entities:, &block)
|
|
121
121
|
end
|
|
122
122
|
|
|
123
|
-
def require_one_entity(attributes:, entities:, &
|
|
123
|
+
def require_one_entity(attributes:, entities:, &)
|
|
124
124
|
case entities.count
|
|
125
125
|
when 0
|
|
126
|
-
failure(not_found_error(attributes:, &
|
|
126
|
+
failure(not_found_error(attributes:, &))
|
|
127
127
|
when 1
|
|
128
128
|
entities.first
|
|
129
129
|
when 2
|
|
130
|
-
failure(not_unique_error(attributes:, &
|
|
130
|
+
failure(not_unique_error(attributes:, &))
|
|
131
131
|
end
|
|
132
132
|
end
|
|
133
133
|
end
|
|
@@ -5,10 +5,12 @@ require 'cuprum/collections/commands'
|
|
|
5
5
|
module Cuprum::Collections::Commands
|
|
6
6
|
# Shared functionality for defining commands that query the collection.
|
|
7
7
|
module QueryCommand
|
|
8
|
-
# @
|
|
9
|
-
#
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
# @overload initialize(query:, **options)
|
|
9
|
+
# @param query [#call] the query object used to access the collection
|
|
10
|
+
# data.
|
|
11
|
+
# @param options [Hash] additional options for the collection.
|
|
12
|
+
def initialize(query:, **)
|
|
13
|
+
super(**)
|
|
12
14
|
|
|
13
15
|
@query = query
|
|
14
16
|
end
|
|
@@ -14,12 +14,13 @@ module Cuprum::Collections::Constraints::Order
|
|
|
14
14
|
@instance ||= new
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
# @
|
|
18
|
-
#
|
|
19
|
-
|
|
17
|
+
# @overload initialize(**options)
|
|
18
|
+
# @param options [Hash<Symbol, Object>] Configuration options for the
|
|
19
|
+
# constraint. Defaults to an empty Hash.
|
|
20
|
+
def initialize(**)
|
|
20
21
|
super(
|
|
21
22
|
item_type: Cuprum::Collections::Constraints::AttributeName.instance,
|
|
22
|
-
**
|
|
23
|
+
**
|
|
23
24
|
)
|
|
24
25
|
end
|
|
25
26
|
end
|
|
@@ -13,14 +13,15 @@ module Cuprum::Collections::Constraints::Order
|
|
|
13
13
|
@instance ||= new
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
# @
|
|
17
|
-
#
|
|
18
|
-
|
|
16
|
+
# @overload initialize(**options)
|
|
17
|
+
# @param options [Hash<Symbol, Object>] Configuration options for the
|
|
18
|
+
# constraint. Defaults to an empty Hash.
|
|
19
|
+
def initialize(**)
|
|
19
20
|
super(
|
|
20
21
|
key_type: Cuprum::Collections::Constraints::AttributeName.instance,
|
|
21
22
|
value_type: Cuprum::Collections::Constraints::Order::SortDirection
|
|
22
23
|
.instance,
|
|
23
|
-
**
|
|
24
|
+
**
|
|
24
25
|
)
|
|
25
26
|
end
|
|
26
27
|
end
|
|
@@ -41,15 +41,16 @@ module Cuprum::Collections::Constraints
|
|
|
41
41
|
@instance ||= new
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
-
# @
|
|
45
|
-
#
|
|
46
|
-
|
|
44
|
+
# @overload initialize(optional: nil, required: nil, **options)
|
|
45
|
+
# @param options [Hash<Symbol, Object>] Configuration options for the
|
|
46
|
+
# constraint. Defaults to an empty Hash.
|
|
47
|
+
def initialize(optional: nil, required: nil, **)
|
|
47
48
|
super(
|
|
48
49
|
*ordering_constraints,
|
|
49
50
|
**resolve_required_option(
|
|
50
51
|
optional:,
|
|
51
52
|
required:,
|
|
52
|
-
**
|
|
53
|
+
**
|
|
53
54
|
)
|
|
54
55
|
)
|
|
55
56
|
end
|
|
@@ -91,13 +92,14 @@ module Cuprum::Collections::Constraints
|
|
|
91
92
|
(errors || Stannum::Errors.new).add(negated_type)
|
|
92
93
|
end
|
|
93
94
|
|
|
94
|
-
#
|
|
95
|
+
# @overload with_options(**options)
|
|
96
|
+
# Creates a copy of the constraint and updates the copy's options.
|
|
95
97
|
#
|
|
96
|
-
#
|
|
98
|
+
# @param options [Hash] The options to update.
|
|
97
99
|
#
|
|
98
|
-
#
|
|
99
|
-
def with_options(**
|
|
100
|
-
super(**resolve_required_option(**
|
|
100
|
+
# @return [Stannum::Constraints::Base] the copied constraint.
|
|
101
|
+
def with_options(**)
|
|
102
|
+
super(**resolve_required_option(**))
|
|
101
103
|
end
|
|
102
104
|
|
|
103
105
|
private
|
|
@@ -8,11 +8,11 @@ require 'cuprum/collections/constraints/attribute_name'
|
|
|
8
8
|
module Cuprum::Collections::Constraints
|
|
9
9
|
# Asserts that the object is a Hash with valid attribute name keys.
|
|
10
10
|
class QueryHash < Stannum::Constraints::Types::HashType
|
|
11
|
-
def initialize(**
|
|
11
|
+
def initialize(**)
|
|
12
12
|
super(
|
|
13
13
|
allow_empty: true,
|
|
14
14
|
key_type: attribute_name_constraint,
|
|
15
|
-
**
|
|
15
|
+
**
|
|
16
16
|
)
|
|
17
17
|
end
|
|
18
18
|
|
|
@@ -1,44 +1,110 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'cuprum/collections/errors'
|
|
4
|
+
require 'cuprum/collections/relations/parameters'
|
|
4
5
|
|
|
5
6
|
module Cuprum::Collections::Errors
|
|
6
7
|
# Abstract base class for failed query errors.
|
|
7
8
|
class AbstractFindError < Cuprum::Error # rubocop:disable Metrics/ClassLength
|
|
8
|
-
|
|
9
|
+
COLLECTION_KEYWORDS = %i[
|
|
10
|
+
collection
|
|
11
|
+
collection_name
|
|
12
|
+
entity_class
|
|
13
|
+
name
|
|
14
|
+
qualified_name
|
|
15
|
+
].freeze
|
|
16
|
+
private_constant :COLLECTION_KEYWORDS
|
|
17
|
+
|
|
18
|
+
QUERYING_KEYWORDS = %i[
|
|
9
19
|
attribute_name
|
|
10
20
|
attribute_value
|
|
11
21
|
attributes
|
|
12
22
|
primary_key
|
|
13
23
|
query
|
|
14
24
|
].freeze
|
|
15
|
-
private_constant :
|
|
25
|
+
private_constant :QUERYING_KEYWORDS
|
|
26
|
+
|
|
27
|
+
VALID_PARAMETERS = %i[entity_class name qualified_name].freeze
|
|
28
|
+
private_constant :VALID_PARAMETERS
|
|
29
|
+
|
|
30
|
+
class << self
|
|
31
|
+
# Resolves the details about the queried collection.
|
|
32
|
+
#
|
|
33
|
+
# @param params [Hash] the parameters to resolve.
|
|
34
|
+
#
|
|
35
|
+
# @return [Hash] the collection details.
|
|
36
|
+
def resolve_collection(**params) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
37
|
+
if params[:collection].is_a?(Cuprum::Collections::Collection)
|
|
38
|
+
return collection_details(params[:collection])
|
|
39
|
+
elsif collection_name?(params[:collection])
|
|
40
|
+
return { 'name' => params[:collection].to_s }
|
|
41
|
+
elsif collection_name?(params[:collection_name])
|
|
42
|
+
# @deprecated 0.6.0
|
|
43
|
+
tools.core_tools.deprecate(
|
|
44
|
+
':collection_name parameter is deprecated',
|
|
45
|
+
message: 'Use the :name parameter instead.'
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
return { 'name' => params[:collection_name].to_s }
|
|
49
|
+
elsif VALID_PARAMETERS.any? { |key| params.key?(key) }
|
|
50
|
+
return Cuprum::Collections::Relations::Parameters
|
|
51
|
+
.resolve_parameters(params)
|
|
52
|
+
.slice(*VALID_PARAMETERS)
|
|
53
|
+
.to_h { |key, value| [key.to_s, value.to_s] }
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
raise ArgumentError, "collection, name or entity class can't be blank"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
def collection_details(collection)
|
|
62
|
+
entity_class = collection.entity_class
|
|
63
|
+
entity_class = entity_class.name if entity_class.is_a?(Class)
|
|
64
|
+
|
|
65
|
+
{
|
|
66
|
+
'entity_class' => entity_class,
|
|
67
|
+
'name' => collection.name,
|
|
68
|
+
'qualified_name' => collection.qualified_name
|
|
69
|
+
}
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def collection_name?(value)
|
|
73
|
+
return false unless value.is_a?(String) || value.is_a?(Symbol)
|
|
74
|
+
|
|
75
|
+
!value.to_s.empty?
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def tools
|
|
79
|
+
SleepingKingStudios::Tools::Toolbelt.instance
|
|
80
|
+
end
|
|
81
|
+
end
|
|
16
82
|
|
|
17
|
-
# @overload initialize(attribute_name:, attribute_value:,
|
|
18
|
-
# @param attribute_name [String]
|
|
19
|
-
# @param attribute_value [Object]
|
|
20
|
-
# @param
|
|
21
|
-
# @param primary_key [true, false]
|
|
83
|
+
# @overload initialize(attribute_name:, attribute_value:, name:, primary_key: false)
|
|
84
|
+
# @param attribute_name [String] the name of the queried attribute.
|
|
85
|
+
# @param attribute_value [Object] the value of the queried attribute.
|
|
86
|
+
# @param name [String] the name of the collection.
|
|
87
|
+
# @param primary_key [true, false] indicates that the queried attribute is
|
|
22
88
|
# the primary key for the collection.
|
|
23
89
|
#
|
|
24
|
-
# @overload initialize(attributes:,
|
|
25
|
-
# @param attributes [Hash<String=>Object>]
|
|
26
|
-
# @param
|
|
90
|
+
# @overload initialize(attributes:, name:)
|
|
91
|
+
# @param attributes [Hash<String=>Object>] the queried attributes.
|
|
92
|
+
# @param name [String] the name of the collection.
|
|
27
93
|
#
|
|
28
|
-
# @overload initialize(query:,
|
|
29
|
-
# @param
|
|
94
|
+
# @overload initialize(query:, name:)
|
|
95
|
+
# @param name [String] the name of the collection.
|
|
30
96
|
# @param query [Cuprum::Collections::Query] The performed query.
|
|
31
|
-
def initialize(
|
|
32
|
-
@
|
|
97
|
+
def initialize(**params) # rubocop:disable Metrics/MethodLength
|
|
98
|
+
@collection = self.class.resolve_collection(**params)
|
|
99
|
+
@collection_name = @collection['name']
|
|
33
100
|
@primary_key = false
|
|
34
101
|
|
|
35
|
-
resolve_options(**
|
|
102
|
+
resolve_options(**params.except(*COLLECTION_KEYWORDS))
|
|
36
103
|
|
|
37
104
|
super(
|
|
38
105
|
attribute_name:,
|
|
39
106
|
attribute_value:,
|
|
40
107
|
attributes:,
|
|
41
|
-
collection_name:,
|
|
42
108
|
message: generate_message,
|
|
43
109
|
scope:
|
|
44
110
|
)
|
|
@@ -53,12 +119,24 @@ module Cuprum::Collections::Errors
|
|
|
53
119
|
# @return [Hash<String=>Object>] The queried attributes.
|
|
54
120
|
attr_reader :attributes
|
|
55
121
|
|
|
56
|
-
# @return [
|
|
57
|
-
attr_reader :
|
|
122
|
+
# @return [Hash] the resolved collection details.
|
|
123
|
+
attr_reader :collection
|
|
58
124
|
|
|
59
125
|
# @return [Cuprum::Collections::Scopes::Base] the query scope, if any.
|
|
60
126
|
attr_reader :scope
|
|
61
127
|
|
|
128
|
+
# @return [String] the name of the collection.
|
|
129
|
+
#
|
|
130
|
+
# @deprecated 0.6.0
|
|
131
|
+
def collection_name
|
|
132
|
+
tools.core_tools.deprecate(
|
|
133
|
+
'#collection_name is deprecated',
|
|
134
|
+
message: 'Use the #collection method instead.'
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
collection['name']
|
|
138
|
+
end
|
|
139
|
+
|
|
62
140
|
# @return [Array<Array>] the details of the query, in scope format.
|
|
63
141
|
def details # rubocop:disable Metrics/MethodLength
|
|
64
142
|
if attribute_name
|
|
@@ -86,13 +164,13 @@ module Cuprum::Collections::Errors
|
|
|
86
164
|
|
|
87
165
|
def as_json_data
|
|
88
166
|
{
|
|
89
|
-
'
|
|
90
|
-
'details'
|
|
167
|
+
'collection' => collection,
|
|
168
|
+
'details' => details
|
|
91
169
|
}.merge(find_data)
|
|
92
170
|
end
|
|
93
171
|
|
|
94
172
|
def entity_name
|
|
95
|
-
titleize(tools.str.singularize(
|
|
173
|
+
titleize(tools.str.singularize(collection['name']))
|
|
96
174
|
end
|
|
97
175
|
|
|
98
176
|
def find_data # rubocop:disable Metrics/MethodLength
|
|
@@ -161,7 +239,7 @@ module Cuprum::Collections::Errors
|
|
|
161
239
|
resolve_query_options(**options)
|
|
162
240
|
else
|
|
163
241
|
raise ArgumentError,
|
|
164
|
-
'missing keywords :attribute_name, :attribute_value
|
|
242
|
+
'missing keywords :attribute_name, :attribute_value, :attributes, ' \
|
|
165
243
|
'or :query'
|
|
166
244
|
end
|
|
167
245
|
end
|
|
@@ -177,7 +255,7 @@ module Cuprum::Collections::Errors
|
|
|
177
255
|
def validate_keywords(extra_keywords:) # rubocop:disable Metrics/MethodLength
|
|
178
256
|
return if extra_keywords.empty?
|
|
179
257
|
|
|
180
|
-
ambiguous_keywords = extra_keywords &
|
|
258
|
+
ambiguous_keywords = extra_keywords & QUERYING_KEYWORDS
|
|
181
259
|
|
|
182
260
|
if ambiguous_keywords.empty?
|
|
183
261
|
raise ArgumentError,
|
|
@@ -15,7 +15,7 @@ module Cuprum::Collections::Errors
|
|
|
15
15
|
# that were assigned to the entity.
|
|
16
16
|
# @param valid_attributes [Array<String>] The names of valid attributes for
|
|
17
17
|
# the entity.
|
|
18
|
-
def initialize(
|
|
18
|
+
def initialize(extra_attributes:, valid_attributes:, entity_class: nil)
|
|
19
19
|
@entity_class = entity_class
|
|
20
20
|
@extra_attributes = extra_attributes
|
|
21
21
|
@valid_attributes = valid_attributes
|
|
@@ -42,14 +42,14 @@ module Cuprum::Collections::Errors
|
|
|
42
42
|
|
|
43
43
|
def as_json_data
|
|
44
44
|
{
|
|
45
|
-
'entity_class' => entity_class
|
|
45
|
+
'entity_class' => entity_class&.name,
|
|
46
46
|
'extra_attributes' => extra_attributes,
|
|
47
47
|
'valid_attributes' => valid_attributes
|
|
48
48
|
}
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
def default_message
|
|
52
|
-
"invalid attributes for #{entity_class
|
|
52
|
+
"invalid attributes for #{entity_class&.name || 'an entity'}: " \
|
|
53
53
|
"#{extra_attributes.join(', ')}"
|
|
54
54
|
end
|
|
55
55
|
end
|
|
@@ -13,7 +13,7 @@ module Cuprum::Collections::Errors
|
|
|
13
13
|
# @param entity_class [Class] The class of the assigned entity.
|
|
14
14
|
# @param errors [Stannum::Errors] The errors generated when validating the
|
|
15
15
|
# entity.
|
|
16
|
-
def initialize(
|
|
16
|
+
def initialize(errors:, entity_class: nil)
|
|
17
17
|
@entity_class = entity_class
|
|
18
18
|
@errors = errors
|
|
19
19
|
|
|
@@ -34,13 +34,13 @@ module Cuprum::Collections::Errors
|
|
|
34
34
|
|
|
35
35
|
def as_json_data
|
|
36
36
|
{
|
|
37
|
-
'entity_class' => entity_class
|
|
37
|
+
'entity_class' => entity_class&.name,
|
|
38
38
|
'errors' => format_errors
|
|
39
39
|
}
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
def default_message
|
|
43
|
-
"#{entity_class
|
|
43
|
+
"#{entity_class&.name || 'an entity'} failed validation"
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
def format_errors
|
|
@@ -11,7 +11,7 @@ module Cuprum::Collections::Errors
|
|
|
11
11
|
TYPE = 'cuprum.collections.errors.missing_default_contract'
|
|
12
12
|
|
|
13
13
|
# @param entity_class [Class] The class of the assigned entity.
|
|
14
|
-
def initialize(entity_class:)
|
|
14
|
+
def initialize(entity_class: nil)
|
|
15
15
|
@entity_class = entity_class
|
|
16
16
|
|
|
17
17
|
super(
|
|
@@ -26,12 +26,20 @@ module Cuprum::Collections::Errors
|
|
|
26
26
|
private
|
|
27
27
|
|
|
28
28
|
def as_json_data
|
|
29
|
-
{ 'entity_class' => entity_class
|
|
29
|
+
{ 'entity_class' => entity_class&.name }
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
def default_message
|
|
33
|
-
"attempted to validate
|
|
34
|
-
"#{
|
|
33
|
+
"attempted to validate #{entity_name}, but " \
|
|
34
|
+
"#{entity_class_name} does not define a default contract"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def entity_class_name
|
|
38
|
+
entity_class&.name || 'the entity class'
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def entity_name
|
|
42
|
+
entity_class ? "a #{entity_class.name}" : 'an entity'
|
|
35
43
|
end
|
|
36
44
|
end
|
|
37
45
|
end
|