cuprum-collections 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|