cuprum-collections 0.1.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 +7 -0
- data/CHANGELOG.md +59 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/DEVELOPMENT.md +25 -0
- data/LICENSE +22 -0
- data/README.md +950 -0
- data/lib/cuprum/collections/base.rb +11 -0
- data/lib/cuprum/collections/basic/collection.rb +135 -0
- data/lib/cuprum/collections/basic/command.rb +112 -0
- data/lib/cuprum/collections/basic/commands/assign_one.rb +54 -0
- data/lib/cuprum/collections/basic/commands/build_one.rb +45 -0
- data/lib/cuprum/collections/basic/commands/destroy_one.rb +48 -0
- data/lib/cuprum/collections/basic/commands/find_many.rb +65 -0
- data/lib/cuprum/collections/basic/commands/find_matching.rb +126 -0
- data/lib/cuprum/collections/basic/commands/find_one.rb +49 -0
- data/lib/cuprum/collections/basic/commands/insert_one.rb +50 -0
- data/lib/cuprum/collections/basic/commands/update_one.rb +52 -0
- data/lib/cuprum/collections/basic/commands/validate_one.rb +69 -0
- data/lib/cuprum/collections/basic/commands.rb +18 -0
- data/lib/cuprum/collections/basic/query.rb +160 -0
- data/lib/cuprum/collections/basic/query_builder.rb +69 -0
- data/lib/cuprum/collections/basic/rspec/command_contract.rb +392 -0
- data/lib/cuprum/collections/basic/rspec.rb +8 -0
- data/lib/cuprum/collections/basic.rb +22 -0
- data/lib/cuprum/collections/command.rb +26 -0
- data/lib/cuprum/collections/commands/abstract_find_many.rb +77 -0
- data/lib/cuprum/collections/commands/abstract_find_matching.rb +64 -0
- data/lib/cuprum/collections/commands/abstract_find_one.rb +44 -0
- data/lib/cuprum/collections/commands.rb +8 -0
- data/lib/cuprum/collections/constraints/attribute_name.rb +22 -0
- data/lib/cuprum/collections/constraints/order/attributes_array.rb +26 -0
- data/lib/cuprum/collections/constraints/order/attributes_hash.rb +27 -0
- data/lib/cuprum/collections/constraints/order/complex_ordering.rb +46 -0
- data/lib/cuprum/collections/constraints/order/sort_direction.rb +32 -0
- data/lib/cuprum/collections/constraints/order.rb +8 -0
- data/lib/cuprum/collections/constraints/ordering.rb +114 -0
- data/lib/cuprum/collections/constraints/query_hash.rb +25 -0
- data/lib/cuprum/collections/constraints.rb +8 -0
- data/lib/cuprum/collections/errors/already_exists.rb +86 -0
- data/lib/cuprum/collections/errors/extra_attributes.rb +66 -0
- data/lib/cuprum/collections/errors/failed_validation.rb +66 -0
- data/lib/cuprum/collections/errors/invalid_parameters.rb +50 -0
- data/lib/cuprum/collections/errors/invalid_query.rb +55 -0
- data/lib/cuprum/collections/errors/missing_default_contract.rb +49 -0
- data/lib/cuprum/collections/errors/not_found.rb +81 -0
- data/lib/cuprum/collections/errors/unknown_operator.rb +71 -0
- data/lib/cuprum/collections/errors.rb +8 -0
- data/lib/cuprum/collections/queries/ordering.rb +74 -0
- data/lib/cuprum/collections/queries/parse.rb +22 -0
- data/lib/cuprum/collections/queries/parse_block.rb +206 -0
- data/lib/cuprum/collections/queries/parse_strategy.rb +91 -0
- data/lib/cuprum/collections/queries.rb +25 -0
- data/lib/cuprum/collections/query.rb +247 -0
- data/lib/cuprum/collections/query_builder.rb +61 -0
- data/lib/cuprum/collections/rspec/assign_one_command_contract.rb +168 -0
- data/lib/cuprum/collections/rspec/build_one_command_contract.rb +93 -0
- data/lib/cuprum/collections/rspec/collection_contract.rb +153 -0
- data/lib/cuprum/collections/rspec/destroy_one_command_contract.rb +106 -0
- data/lib/cuprum/collections/rspec/find_many_command_contract.rb +327 -0
- data/lib/cuprum/collections/rspec/find_matching_command_contract.rb +194 -0
- data/lib/cuprum/collections/rspec/find_one_command_contract.rb +154 -0
- data/lib/cuprum/collections/rspec/fixtures.rb +89 -0
- data/lib/cuprum/collections/rspec/insert_one_command_contract.rb +83 -0
- data/lib/cuprum/collections/rspec/query_builder_contract.rb +92 -0
- data/lib/cuprum/collections/rspec/query_contract.rb +650 -0
- data/lib/cuprum/collections/rspec/querying_contract.rb +298 -0
- data/lib/cuprum/collections/rspec/update_one_command_contract.rb +79 -0
- data/lib/cuprum/collections/rspec/validate_one_command_contract.rb +96 -0
- data/lib/cuprum/collections/rspec.rb +8 -0
- data/lib/cuprum/collections/version.rb +59 -0
- data/lib/cuprum/collections.rb +26 -0
- metadata +219 -0
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cuprum/error'
|
4
|
+
require 'sleeping_king_studios/tools/toolbelt'
|
5
|
+
|
6
|
+
require 'cuprum/collections/errors'
|
7
|
+
|
8
|
+
module Cuprum::Collections::Errors
|
9
|
+
# Returned when a find command does not find the requested items.
|
10
|
+
class NotFound < Cuprum::Error
|
11
|
+
# Short string used to identify the type of error.
|
12
|
+
TYPE = 'cuprum.collections.errors.not_found'
|
13
|
+
|
14
|
+
# @param collection_name [String, Symbol] The name of the collection.
|
15
|
+
# @param primary_key_name [String, Symbol] The name of the primary key
|
16
|
+
# attribute.
|
17
|
+
# @param primary_key_values [Object, Array] The expected values of the
|
18
|
+
# primary key attribute.
|
19
|
+
def initialize(collection_name:, primary_key_name:, primary_key_values:)
|
20
|
+
@collection_name = collection_name
|
21
|
+
@primary_key_name = primary_key_name
|
22
|
+
@primary_key_values = Array(primary_key_values)
|
23
|
+
|
24
|
+
super(message: default_message)
|
25
|
+
end
|
26
|
+
|
27
|
+
# @return [String, Symbol] the name of the collection.
|
28
|
+
attr_reader :collection_name
|
29
|
+
|
30
|
+
# @return [String, Symbol] the name of the primary key attribute.
|
31
|
+
attr_reader :primary_key_name
|
32
|
+
|
33
|
+
# @return [Array] The expected values of the primary key attribute.
|
34
|
+
attr_reader :primary_key_values
|
35
|
+
|
36
|
+
# @return [Hash] a serializable hash representation of the error.
|
37
|
+
def as_json
|
38
|
+
{
|
39
|
+
'data' => {
|
40
|
+
'collection_name' => collection_name,
|
41
|
+
'primary_key_name' => primary_key_name,
|
42
|
+
'primary_key_values' => primary_key_values
|
43
|
+
},
|
44
|
+
'message' => message,
|
45
|
+
'type' => type
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
# @return [String] short string used to identify the type of error.
|
50
|
+
def type
|
51
|
+
TYPE
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def default_message
|
57
|
+
primary_keys = primary_key_values.map(&:inspect).join(', ')
|
58
|
+
|
59
|
+
"#{entity_name} not found with #{primary_key_name} #{primary_keys}"
|
60
|
+
end
|
61
|
+
|
62
|
+
def entity_name
|
63
|
+
entity_name = collection_name
|
64
|
+
entity_name = tools.str.singularize(entity_name) if singular?
|
65
|
+
|
66
|
+
titleize(entity_name)
|
67
|
+
end
|
68
|
+
|
69
|
+
def singular?
|
70
|
+
primary_key_values.size == 1
|
71
|
+
end
|
72
|
+
|
73
|
+
def titleize(string)
|
74
|
+
tools.str.underscore(string).split('_').map(&:capitalize).join(' ')
|
75
|
+
end
|
76
|
+
|
77
|
+
def tools
|
78
|
+
SleepingKingStudios::Tools::Toolbelt.instance
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cuprum/collections/errors'
|
4
|
+
require 'cuprum/collections/queries'
|
5
|
+
|
6
|
+
module Cuprum::Collections::Errors
|
7
|
+
# An error returned when a query attempts to filter by an unknown operator.
|
8
|
+
class UnknownOperator < Cuprum::Error
|
9
|
+
# Short string used to identify the type of error.
|
10
|
+
TYPE = 'cuprum.collections.errors.unknown_operator'
|
11
|
+
|
12
|
+
# @param operator [String, Symbol] The unknown operator.
|
13
|
+
def initialize(operator:)
|
14
|
+
@operator = operator
|
15
|
+
|
16
|
+
super(
|
17
|
+
message: generate_message,
|
18
|
+
operator: operator
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
# @return [String, Symbol] the unknown operator.
|
23
|
+
attr_reader :operator
|
24
|
+
|
25
|
+
# @return [Hash] a serializable hash representation of the error.
|
26
|
+
def as_json
|
27
|
+
{
|
28
|
+
'data' => {
|
29
|
+
'corrections' => corrections,
|
30
|
+
'operator' => operator
|
31
|
+
},
|
32
|
+
'message' => message,
|
33
|
+
'type' => type
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
# @return [Array<String>] Suggested possible values for the operator.
|
38
|
+
def corrections
|
39
|
+
@corrections ||=
|
40
|
+
DidYouMean::SpellChecker
|
41
|
+
.new(dictionary: Cuprum::Collections::Queries::VALID_OPERATORS.to_a)
|
42
|
+
.correct(operator)
|
43
|
+
end
|
44
|
+
|
45
|
+
# @return [String] short string used to identify the type of error.
|
46
|
+
def type
|
47
|
+
TYPE
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def generate_message
|
53
|
+
message = "unknown operator #{operator.inspect}"
|
54
|
+
|
55
|
+
return message if corrections.empty?
|
56
|
+
|
57
|
+
"#{message} - did you mean #{suggestion}?"
|
58
|
+
end
|
59
|
+
|
60
|
+
def suggestion
|
61
|
+
tools.ary.humanize_list(
|
62
|
+
corrections.map(&:inspect),
|
63
|
+
last_separator: ', or '
|
64
|
+
)
|
65
|
+
end
|
66
|
+
|
67
|
+
def tools
|
68
|
+
SleepingKingStudios::Tools::Toolbelt.instance
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cuprum/collections/constraints/ordering'
|
4
|
+
require 'cuprum/collections/queries'
|
5
|
+
|
6
|
+
module Cuprum::Collections::Queries
|
7
|
+
# Functionality around validating and normalizing query sort orderings.
|
8
|
+
module Ordering
|
9
|
+
# Exception class for handling invalid order keywords.
|
10
|
+
class InvalidOrderError < ArgumentError; end
|
11
|
+
|
12
|
+
ORDER_HASH_VALUES = {
|
13
|
+
asc: :asc,
|
14
|
+
ascending: :asc,
|
15
|
+
desc: :desc,
|
16
|
+
descending: :desc
|
17
|
+
}.freeze
|
18
|
+
private_constant :ORDER_HASH_VALUES
|
19
|
+
|
20
|
+
class << self
|
21
|
+
# @overload normalize(*attributes, ordering_hash = nil)
|
22
|
+
# Converts the given sort order into a hash with standard values.
|
23
|
+
#
|
24
|
+
# @param attributes [Array<String, Symbol>] The attribute names to sort
|
25
|
+
# by, in ascending direction, and in order of importance.
|
26
|
+
# @param ordering_hash [Hash] An optional ordering hash, with keys that
|
27
|
+
# are valid attribute names and values that are valid sort directions.
|
28
|
+
#
|
29
|
+
# @return [Hash] the normalized sort ordering.
|
30
|
+
#
|
31
|
+
# @raise InvalidOrderError if any of the attributes are invalid
|
32
|
+
# attribute names.
|
33
|
+
# @raise InvalidOrderError if any of the hash keys are invalid attribute
|
34
|
+
# names, or any of the hash values are invalid sort directions.
|
35
|
+
def normalize(*attributes)
|
36
|
+
validate_ordering!(attributes)
|
37
|
+
|
38
|
+
qualified = attributes.last.is_a?(Hash) ? attributes.pop : {}
|
39
|
+
qualified = normalize_order_hash(qualified)
|
40
|
+
|
41
|
+
attributes
|
42
|
+
.each
|
43
|
+
.with_object({}) { |attribute, hsh| hsh[attribute.intern] = :asc }
|
44
|
+
.merge(qualified)
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def normalize_order_hash(hsh)
|
50
|
+
hsh.each.with_object({}) do |(key, value), normalized|
|
51
|
+
normalized[key.intern] = normalize_order_hash_value(value)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def normalize_order_hash_value(value)
|
56
|
+
value = value.downcase if value.is_a?(String)
|
57
|
+
|
58
|
+
ORDER_HASH_VALUES.fetch(value.is_a?(String) ? value.intern : value)
|
59
|
+
end
|
60
|
+
|
61
|
+
def ordering_constraint
|
62
|
+
Cuprum::Collections::Constraints::Ordering.instance
|
63
|
+
end
|
64
|
+
|
65
|
+
def validate_ordering!(attributes)
|
66
|
+
return if ordering_constraint.matches?(attributes)
|
67
|
+
|
68
|
+
raise InvalidOrderError,
|
69
|
+
'order must be a list of attribute names and/or a hash of attribute' \
|
70
|
+
' names with values :asc or :desc'
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cuprum/collections/queries'
|
4
|
+
require 'cuprum/collections/queries/parse_strategy'
|
5
|
+
|
6
|
+
module Cuprum::Collections::Queries
|
7
|
+
# Command to parse parameters passed to Query#where into criteria.
|
8
|
+
class Parse < Cuprum::Command
|
9
|
+
private
|
10
|
+
|
11
|
+
def process(where:, strategy: nil)
|
12
|
+
command = step do
|
13
|
+
Cuprum::Collections::Queries::ParseStrategy.new.call(
|
14
|
+
strategy: strategy,
|
15
|
+
where: where
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
command.call(where: where)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,206 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
|
5
|
+
require 'cuprum/errors/uncaught_exception'
|
6
|
+
require 'stannum/contracts/parameters_contract'
|
7
|
+
|
8
|
+
require 'cuprum/collections/command'
|
9
|
+
require 'cuprum/collections/constraints/query_hash'
|
10
|
+
require 'cuprum/collections/errors/invalid_query'
|
11
|
+
require 'cuprum/collections/errors/unknown_operator'
|
12
|
+
require 'cuprum/collections/queries'
|
13
|
+
|
14
|
+
module Cuprum::Collections::Queries
|
15
|
+
# Command for parsing a Query#where block into criteria.
|
16
|
+
#
|
17
|
+
# @example An Empty Query
|
18
|
+
# command = Cuprum::Collections::Queries::ParseBlock.new
|
19
|
+
# result = command.call { {} }
|
20
|
+
# result.value #=> []
|
21
|
+
#
|
22
|
+
# @example A Value Query
|
23
|
+
# command = Cuprum::Collections::Queries::ParseBlock.new
|
24
|
+
# result = command.call do
|
25
|
+
# {
|
26
|
+
# author: 'Nnedi Okorafor',
|
27
|
+
# series: 'Binti',
|
28
|
+
# genre: 'Africanfuturism'
|
29
|
+
# }
|
30
|
+
# end
|
31
|
+
# result.value #=>
|
32
|
+
# # [
|
33
|
+
# # ['author', :eq, 'Nnedi Okorafor'],
|
34
|
+
# # ['series', :eq, 'Binti'],
|
35
|
+
# # ['genre', :eq, 'Africanfuturism']
|
36
|
+
# # ]
|
37
|
+
#
|
38
|
+
# @example A Query With Operators
|
39
|
+
# command = Cuprum::Collections::Queries::ParseBlock.new
|
40
|
+
# result = command.call do
|
41
|
+
# {
|
42
|
+
# author: equal('Nnedi Okorafor'),
|
43
|
+
# series: not_equal('Binti')
|
44
|
+
# }
|
45
|
+
# end
|
46
|
+
# result.value #=>
|
47
|
+
# # [
|
48
|
+
# # ['author', :eq, 'Nnedi Okorafor'],
|
49
|
+
# # ['series', :ne, 'Binti']
|
50
|
+
# # ]
|
51
|
+
class ParseBlock < Cuprum::Collections::Command
|
52
|
+
# Evaluation context for query blocks.
|
53
|
+
class Builder < BasicObject
|
54
|
+
# Generates an equality criterion.
|
55
|
+
#
|
56
|
+
# @return [Array] the equality criterion.
|
57
|
+
def equals(value)
|
58
|
+
[nil, Operators::EQUAL, value]
|
59
|
+
end
|
60
|
+
alias equal equals
|
61
|
+
alias eq equals
|
62
|
+
|
63
|
+
# Generates a greater than comparison criterion.
|
64
|
+
#
|
65
|
+
# @return [Array] the greater than criterion.
|
66
|
+
def greater_than(value)
|
67
|
+
[nil, Operators::GREATER_THAN, value]
|
68
|
+
end
|
69
|
+
alias gt greater_than
|
70
|
+
|
71
|
+
# Generates a greater than or equal to comparison criterion.
|
72
|
+
#
|
73
|
+
# @return [Array] the greater than or equal to criterion.
|
74
|
+
def greater_than_or_equal_to(value)
|
75
|
+
[nil, Operators::GREATER_THAN_OR_EQUAL_TO, value]
|
76
|
+
end
|
77
|
+
alias gte greater_than_or_equal_to
|
78
|
+
|
79
|
+
# Generates a less than comparison criterion.
|
80
|
+
#
|
81
|
+
# @return [Array] the less than criterion.
|
82
|
+
def less_than(value)
|
83
|
+
[nil, Operators::LESS_THAN, value]
|
84
|
+
end
|
85
|
+
alias lt less_than
|
86
|
+
|
87
|
+
# Generates a less than or equal to comparison criterion.
|
88
|
+
#
|
89
|
+
# @return [Array] the less than or equal to criterion.
|
90
|
+
def less_than_or_equal_to(value)
|
91
|
+
[nil, Operators::LESS_THAN_OR_EQUAL_TO, value]
|
92
|
+
end
|
93
|
+
alias lte less_than_or_equal_to
|
94
|
+
|
95
|
+
# Generates a negated equality criterion.
|
96
|
+
#
|
97
|
+
# @return [Array] the negated equality criterion.
|
98
|
+
def not_equal(value)
|
99
|
+
[nil, Operators::NOT_EQUAL, value]
|
100
|
+
end
|
101
|
+
alias ne not_equal
|
102
|
+
|
103
|
+
# Generates a negated inclusion criterion.
|
104
|
+
#
|
105
|
+
# @return [Array] the negated inclusion criterion.
|
106
|
+
def not_one_of(value)
|
107
|
+
[nil, Operators::NOT_ONE_OF, value]
|
108
|
+
end
|
109
|
+
|
110
|
+
# Generates an inclusion criterion.
|
111
|
+
#
|
112
|
+
# @return [Array] the inclusion criterion.
|
113
|
+
def one_of(value)
|
114
|
+
[nil, Operators::ONE_OF, value]
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
class << self
|
119
|
+
extend Forwardable
|
120
|
+
|
121
|
+
def_delegators :validation_contract,
|
122
|
+
:errors_for,
|
123
|
+
:match,
|
124
|
+
:matches?
|
125
|
+
|
126
|
+
private
|
127
|
+
|
128
|
+
def validation_contract
|
129
|
+
self::MethodValidations.contracts.fetch(:call)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
validate_parameters :call do
|
134
|
+
keyword :where, Proc
|
135
|
+
end
|
136
|
+
|
137
|
+
private
|
138
|
+
|
139
|
+
def call_block(&block)
|
140
|
+
handle_unknown_operator { Builder.new.instance_exec(&block) }
|
141
|
+
rescue StandardError => exception
|
142
|
+
error = Cuprum::Errors::UncaughtException.new(
|
143
|
+
exception: exception,
|
144
|
+
message: 'uncaught exception when parsing query block'
|
145
|
+
)
|
146
|
+
|
147
|
+
failure(error)
|
148
|
+
end
|
149
|
+
|
150
|
+
def generate_criteria(hsh)
|
151
|
+
hsh.map do |key, value|
|
152
|
+
unless partial_criterion?(value)
|
153
|
+
next [key.to_s, Cuprum::Collections::Queries::Operators::EQUAL, value]
|
154
|
+
end
|
155
|
+
|
156
|
+
value.tap { |ary| ary[0] = key.to_s }
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def handle_unknown_operator
|
161
|
+
yield
|
162
|
+
rescue NoMethodError => exception
|
163
|
+
error = Cuprum::Collections::Errors::UnknownOperator.new(
|
164
|
+
operator: exception.name
|
165
|
+
)
|
166
|
+
|
167
|
+
failure(error)
|
168
|
+
end
|
169
|
+
|
170
|
+
def invalid_query_error(errors:, message: nil)
|
171
|
+
Cuprum::Collections::Errors::InvalidQuery.new(
|
172
|
+
errors: errors,
|
173
|
+
message: message,
|
174
|
+
strategy: :block
|
175
|
+
)
|
176
|
+
end
|
177
|
+
|
178
|
+
def partial_criterion?(obj)
|
179
|
+
return false unless obj.is_a?(Array) && obj.size == 3
|
180
|
+
|
181
|
+
attribute, operator, _value = obj
|
182
|
+
|
183
|
+
return false unless attribute.nil?
|
184
|
+
|
185
|
+
Cuprum::Collections::Queries::VALID_OPERATORS.include?(operator)
|
186
|
+
end
|
187
|
+
|
188
|
+
def process(where:)
|
189
|
+
hsh = step { call_block(&where) }
|
190
|
+
|
191
|
+
step { validate_hash(hsh) }
|
192
|
+
|
193
|
+
generate_criteria(hsh)
|
194
|
+
end
|
195
|
+
|
196
|
+
def validate_hash(obj)
|
197
|
+
constraint = Cuprum::Collections::Constraints::QueryHash.new
|
198
|
+
match, errors = constraint.match(obj)
|
199
|
+
|
200
|
+
return if match
|
201
|
+
|
202
|
+
message = 'query block returned invalid value'
|
203
|
+
failure(invalid_query_error(errors: errors, message: message))
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cuprum/collections/queries'
|
4
|
+
require 'cuprum/collections/queries/parse_block'
|
5
|
+
require 'cuprum/collections/errors/invalid_query'
|
6
|
+
|
7
|
+
module Cuprum::Collections::Queries
|
8
|
+
# Command to select the parsing strategy for parsing Query#where parameters.
|
9
|
+
class ParseStrategy < Cuprum::Command
|
10
|
+
STRATEGIES = {
|
11
|
+
block: Cuprum::Collections::Queries::ParseBlock
|
12
|
+
}.freeze
|
13
|
+
private_constant :STRATEGIES
|
14
|
+
|
15
|
+
# The :type of the error generated for an unknown parsing strategy.
|
16
|
+
UNKNOWN_STRATEGY_ERROR =
|
17
|
+
'cuprum.collections.errors.queries.unknown_strategy'
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def find_and_validate_strategy(strategy:, where:)
|
22
|
+
command_class = step { find_strategy_by_key(strategy: strategy) }
|
23
|
+
parameters = {
|
24
|
+
arguments: [],
|
25
|
+
block: nil,
|
26
|
+
keywords: { where: where }
|
27
|
+
}
|
28
|
+
|
29
|
+
return command_class if command_class.matches?(parameters)
|
30
|
+
|
31
|
+
errors = command_class.errors_for(parameters)
|
32
|
+
|
33
|
+
failure(invalid_parameters_error(errors: errors, strategy: strategy))
|
34
|
+
end
|
35
|
+
|
36
|
+
def find_strategy(strategy:, where:)
|
37
|
+
if strategy
|
38
|
+
return find_and_validate_strategy(strategy: strategy, where: where)
|
39
|
+
end
|
40
|
+
|
41
|
+
command_class = find_strategy_by_parameters(where: where)
|
42
|
+
|
43
|
+
return command_class if command_class
|
44
|
+
|
45
|
+
failure(unknown_strategy_error(strategy: strategy))
|
46
|
+
end
|
47
|
+
|
48
|
+
def find_strategy_by_key(strategy:)
|
49
|
+
STRATEGIES.fetch(strategy) do
|
50
|
+
failure(unknown_strategy_error(strategy: strategy))
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def find_strategy_by_parameters(where:)
|
55
|
+
STRATEGIES
|
56
|
+
.values
|
57
|
+
.find do |command_class|
|
58
|
+
command_class.matches?(
|
59
|
+
arguments: [],
|
60
|
+
block: nil,
|
61
|
+
keywords: { where: where }
|
62
|
+
)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def invalid_parameters_error(errors:, strategy:)
|
67
|
+
Cuprum::Collections::Errors::InvalidQuery.new(
|
68
|
+
errors: errors,
|
69
|
+
strategy: strategy
|
70
|
+
)
|
71
|
+
end
|
72
|
+
|
73
|
+
def process(strategy: nil, where: nil)
|
74
|
+
command_class = step do
|
75
|
+
find_strategy(strategy: strategy, where: where)
|
76
|
+
end
|
77
|
+
|
78
|
+
command_class.new
|
79
|
+
end
|
80
|
+
|
81
|
+
def unknown_strategy_error(strategy:)
|
82
|
+
errors = Stannum::Errors.new
|
83
|
+
errors[:strategy].add(UNKNOWN_STRATEGY_ERROR, strategy: strategy)
|
84
|
+
|
85
|
+
Cuprum::Collections::Errors::InvalidQuery.new(
|
86
|
+
errors: errors,
|
87
|
+
strategy: strategy
|
88
|
+
)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'sleeping_king_studios/tools/toolbox/constant_map'
|
4
|
+
|
5
|
+
require 'cuprum/collections'
|
6
|
+
|
7
|
+
module Cuprum::Collections
|
8
|
+
# Namespace for internal functionality for implementing collection queries.
|
9
|
+
module Queries
|
10
|
+
# Defines the supported operators for a Query.
|
11
|
+
Operators = SleepingKingStudios::Tools::Toolbox::ConstantMap.new(
|
12
|
+
EQUAL: :equal,
|
13
|
+
GREATER_THAN: :greater_than,
|
14
|
+
GREATER_THAN_OR_EQUAL_TO: :greater_than_or_equal_to,
|
15
|
+
LESS_THAN: :less_than,
|
16
|
+
LESS_THAN_OR_EQUAL_TO: :less_than_or_equal_to,
|
17
|
+
NOT_EQUAL: :not_equal,
|
18
|
+
NOT_ONE_OF: :not_one_of,
|
19
|
+
ONE_OF: :one_of
|
20
|
+
).freeze
|
21
|
+
|
22
|
+
# Enumerates the valid operators as a Set for performant lookup.
|
23
|
+
VALID_OPERATORS = Set.new(Operators.values).freeze
|
24
|
+
end
|
25
|
+
end
|