params_ready 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/arel/cte_name.rb +20 -0
- data/lib/params_ready.rb +36 -0
- data/lib/params_ready/builder.rb +140 -0
- data/lib/params_ready/error.rb +31 -0
- data/lib/params_ready/extensions/class_reader_writer.rb +33 -0
- data/lib/params_ready/extensions/collection.rb +43 -0
- data/lib/params_ready/extensions/delegation.rb +25 -0
- data/lib/params_ready/extensions/finalizer.rb +26 -0
- data/lib/params_ready/extensions/freezer.rb +49 -0
- data/lib/params_ready/extensions/hash.rb +46 -0
- data/lib/params_ready/extensions/late_init.rb +38 -0
- data/lib/params_ready/extensions/registry.rb +44 -0
- data/lib/params_ready/extensions/undefined.rb +15 -0
- data/lib/params_ready/format.rb +130 -0
- data/lib/params_ready/helpers/arel_builder.rb +68 -0
- data/lib/params_ready/helpers/conditional_block.rb +31 -0
- data/lib/params_ready/helpers/find_in_hash.rb +22 -0
- data/lib/params_ready/helpers/key_map.rb +176 -0
- data/lib/params_ready/helpers/memo.rb +42 -0
- data/lib/params_ready/helpers/options.rb +39 -0
- data/lib/params_ready/helpers/parameter_definer_class_methods.rb +39 -0
- data/lib/params_ready/helpers/parameter_storage_class_methods.rb +36 -0
- data/lib/params_ready/helpers/parameter_user_class_methods.rb +31 -0
- data/lib/params_ready/helpers/relation_builder_wrapper.rb +35 -0
- data/lib/params_ready/helpers/rule.rb +57 -0
- data/lib/params_ready/helpers/storage.rb +30 -0
- data/lib/params_ready/helpers/usage_rule.rb +18 -0
- data/lib/params_ready/input_context.rb +31 -0
- data/lib/params_ready/intent.rb +70 -0
- data/lib/params_ready/marshaller/array_marshallers.rb +132 -0
- data/lib/params_ready/marshaller/builder_module.rb +9 -0
- data/lib/params_ready/marshaller/collection.rb +165 -0
- data/lib/params_ready/marshaller/definition_module.rb +63 -0
- data/lib/params_ready/marshaller/hash_marshallers.rb +100 -0
- data/lib/params_ready/marshaller/hash_set_marshallers.rb +96 -0
- data/lib/params_ready/marshaller/parameter_module.rb +11 -0
- data/lib/params_ready/marshaller/polymorph_marshallers.rb +67 -0
- data/lib/params_ready/marshaller/tuple_marshallers.rb +103 -0
- data/lib/params_ready/ordering/column.rb +60 -0
- data/lib/params_ready/ordering/ordering.rb +276 -0
- data/lib/params_ready/output_parameters.rb +127 -0
- data/lib/params_ready/pagination/abstract_pagination.rb +18 -0
- data/lib/params_ready/pagination/cursor.rb +171 -0
- data/lib/params_ready/pagination/direction.rb +148 -0
- data/lib/params_ready/pagination/keyset_pagination.rb +254 -0
- data/lib/params_ready/pagination/keysets.rb +70 -0
- data/lib/params_ready/pagination/nulls.rb +31 -0
- data/lib/params_ready/pagination/offset_pagination.rb +130 -0
- data/lib/params_ready/pagination/tendency.rb +28 -0
- data/lib/params_ready/parameter/abstract_hash_parameter.rb +204 -0
- data/lib/params_ready/parameter/array_parameter.rb +197 -0
- data/lib/params_ready/parameter/definition.rb +264 -0
- data/lib/params_ready/parameter/hash_parameter.rb +63 -0
- data/lib/params_ready/parameter/hash_set_parameter.rb +101 -0
- data/lib/params_ready/parameter/parameter.rb +456 -0
- data/lib/params_ready/parameter/polymorph_parameter.rb +172 -0
- data/lib/params_ready/parameter/state.rb +132 -0
- data/lib/params_ready/parameter/tuple_parameter.rb +152 -0
- data/lib/params_ready/parameter/value_parameter.rb +182 -0
- data/lib/params_ready/parameter_definer.rb +14 -0
- data/lib/params_ready/parameter_user.rb +43 -0
- data/lib/params_ready/query/array_grouping.rb +68 -0
- data/lib/params_ready/query/custom_predicate.rb +102 -0
- data/lib/params_ready/query/exists_predicate.rb +103 -0
- data/lib/params_ready/query/fixed_operator_predicate.rb +77 -0
- data/lib/params_ready/query/grouping.rb +177 -0
- data/lib/params_ready/query/join_clause.rb +87 -0
- data/lib/params_ready/query/nullness_predicate.rb +71 -0
- data/lib/params_ready/query/polymorph_predicate.rb +77 -0
- data/lib/params_ready/query/predicate.rb +203 -0
- data/lib/params_ready/query/predicate_operator.rb +132 -0
- data/lib/params_ready/query/relation.rb +337 -0
- data/lib/params_ready/query/structured_grouping.rb +58 -0
- data/lib/params_ready/query/variable_operator_predicate.rb +125 -0
- data/lib/params_ready/query_context.rb +21 -0
- data/lib/params_ready/restriction.rb +252 -0
- data/lib/params_ready/result.rb +109 -0
- data/lib/params_ready/value/coder.rb +181 -0
- data/lib/params_ready/value/constraint.rb +198 -0
- data/lib/params_ready/value/custom.rb +56 -0
- data/lib/params_ready/value/validator.rb +68 -0
- metadata +181 -0
@@ -0,0 +1,58 @@
|
|
1
|
+
require_relative '../parameter/hash_parameter'
|
2
|
+
require_relative '../parameter/value_parameter'
|
3
|
+
require_relative 'fixed_operator_predicate'
|
4
|
+
require_relative 'nullness_predicate'
|
5
|
+
require_relative 'grouping'
|
6
|
+
|
7
|
+
module ParamsReady
|
8
|
+
module Query
|
9
|
+
class StructuredGrouping < Parameter::HashParameter
|
10
|
+
include Parameter::GroupingLike
|
11
|
+
def predicates
|
12
|
+
return [] if is_nil?
|
13
|
+
|
14
|
+
definition.predicates.keys.map do |name|
|
15
|
+
parameter = child(name)
|
16
|
+
next nil unless parameter.is_definite?
|
17
|
+
parameter
|
18
|
+
end.compact
|
19
|
+
end
|
20
|
+
|
21
|
+
def operator
|
22
|
+
self[:operator].unwrap
|
23
|
+
end
|
24
|
+
|
25
|
+
def context_for_predicates(restriction)
|
26
|
+
intent_for_children(restriction)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class StructuredGroupingBuilder < Builder
|
31
|
+
include GroupingLike
|
32
|
+
include Parameter::AbstractHashParameterBuilder::HashLike
|
33
|
+
PredicateRegistry.register_predicate :structured_grouping_predicate, self
|
34
|
+
|
35
|
+
def self.instance(name, altn: nil)
|
36
|
+
new StructuredGroupingDefinition.new(name, altn: altn)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class StructuredGroupingDefinition < Parameter::HashParameterDefinition
|
41
|
+
attr_reader :arel_table, :predicates
|
42
|
+
|
43
|
+
def initialize(*args, **opts)
|
44
|
+
@predicates = {}
|
45
|
+
super *args, **opts
|
46
|
+
end
|
47
|
+
|
48
|
+
def add_predicate(predicate)
|
49
|
+
raise ParamsReadyError, "Predicate name taken: '#{predicate.name}" if predicates.key? predicate.name
|
50
|
+
predicates[predicate.name] = predicate
|
51
|
+
end
|
52
|
+
|
53
|
+
parameter_class StructuredGrouping
|
54
|
+
|
55
|
+
freeze_variables :predicates
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require_relative 'predicate'
|
2
|
+
require_relative '../parameter/hash_parameter'
|
3
|
+
require_relative '../parameter/value_parameter'
|
4
|
+
require_relative '../value/validator'
|
5
|
+
require_relative 'predicate_operator'
|
6
|
+
|
7
|
+
module ParamsReady
|
8
|
+
module Query
|
9
|
+
class VariableOperatorPredicate < Parameter::AbstractParameter
|
10
|
+
include Predicate::DelegatingPredicate
|
11
|
+
include Predicate::HavingAttribute
|
12
|
+
|
13
|
+
def initialize(definition)
|
14
|
+
super definition
|
15
|
+
@data = definition.hash_parameter.create
|
16
|
+
end
|
17
|
+
|
18
|
+
def build_query(select_expression, context: nil)
|
19
|
+
operator.to_query(select_expression, value)
|
20
|
+
end
|
21
|
+
|
22
|
+
def perform_test(record, attribute_name)
|
23
|
+
operator.test(record, attribute_name, value)
|
24
|
+
end
|
25
|
+
|
26
|
+
def value
|
27
|
+
@data[:value].unwrap
|
28
|
+
end
|
29
|
+
|
30
|
+
def operator
|
31
|
+
@data[:operator].unwrap
|
32
|
+
end
|
33
|
+
|
34
|
+
def inspect_content
|
35
|
+
op, val = if is_definite?
|
36
|
+
op = @data[:operator].unwrap_or(nil)&.name || '?'
|
37
|
+
val = @data[:value].unwrap_or('?')
|
38
|
+
else
|
39
|
+
%w[? ?]
|
40
|
+
end
|
41
|
+
"#{definition.attribute_name} #{op} #{val}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class OperatorCoder < Value::Coder
|
46
|
+
def self.coerce(value, context)
|
47
|
+
return value if value.class == Class && value < PredicateOperator
|
48
|
+
identifier = value.to_sym
|
49
|
+
PredicateRegistry.operator(identifier, context)
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.format(value, intent)
|
53
|
+
intent.hash_key(value)
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.strict_default?
|
57
|
+
false
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
Parameter::ValueParameterBuilder.register_coder :predicate_operator, OperatorCoder
|
62
|
+
|
63
|
+
class VariableOperatorPredicateBuilder < AbstractPredicateBuilder
|
64
|
+
PredicateRegistry.register_predicate :variable_operator_predicate, self
|
65
|
+
include HavingType
|
66
|
+
include HavingAttribute
|
67
|
+
include DelegatingBuilder[:hash_parameter_builder]
|
68
|
+
|
69
|
+
def self.instance(name, altn: nil, attr: nil)
|
70
|
+
new VariableOperatorPredicateDefinition.new name, altn: altn, attribute_name: attr
|
71
|
+
end
|
72
|
+
|
73
|
+
def data_object_handles
|
74
|
+
[:value, :val]
|
75
|
+
end
|
76
|
+
|
77
|
+
def operators(*arr, &block)
|
78
|
+
@definition.set_operators(arr, &block)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
class VariableOperatorPredicateDefinition < AbstractPredicateDefinition
|
83
|
+
include HavingAttribute
|
84
|
+
|
85
|
+
attr_reader :hash_parameter_builder
|
86
|
+
attr_reader :hash_parameter
|
87
|
+
|
88
|
+
def initialize(*args, attribute_name: nil, **opts)
|
89
|
+
super *args, **opts
|
90
|
+
@attribute_name = attribute_name
|
91
|
+
@hash_parameter_builder = Builder.builder(:hash).instance(name, altn: altn)
|
92
|
+
@operator_parameter_builder = Builder.builder(:predicate_operator).instance(:operator, altn: :op)
|
93
|
+
end
|
94
|
+
|
95
|
+
def set_type(type)
|
96
|
+
@type = type
|
97
|
+
@hash_parameter_builder.add @type.finish
|
98
|
+
end
|
99
|
+
|
100
|
+
def set_operators(array, &block)
|
101
|
+
context = Format.instance(:backend)
|
102
|
+
|
103
|
+
operators = array.map do |name|
|
104
|
+
PredicateRegistry.operator(name, context)
|
105
|
+
end
|
106
|
+
@operator_parameter_builder.include do
|
107
|
+
constrain :enum, operators
|
108
|
+
end
|
109
|
+
@operator_parameter_builder.include(&block) unless block.nil?
|
110
|
+
@hash_parameter_builder.add @operator_parameter_builder.build
|
111
|
+
end
|
112
|
+
|
113
|
+
def finish
|
114
|
+
@hash_parameter = @hash_parameter_builder.build
|
115
|
+
@hash_parameter_builder = nil
|
116
|
+
@operator_parameter_builder = nil
|
117
|
+
@type = nil
|
118
|
+
|
119
|
+
super
|
120
|
+
end
|
121
|
+
|
122
|
+
parameter_class VariableOperatorPredicate
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require_relative 'restriction'
|
3
|
+
|
4
|
+
module ParamsReady
|
5
|
+
class QueryContext
|
6
|
+
include Restriction::Wrapper
|
7
|
+
extend Forwardable
|
8
|
+
attr_reader :data
|
9
|
+
def_delegator :data, :[]
|
10
|
+
|
11
|
+
def initialize(restriction, data = {})
|
12
|
+
@data = data.freeze
|
13
|
+
raise ParamsReadyError, "Restriction expected, got: #{restriction.inspect}" unless restriction.is_a? Restriction
|
14
|
+
@restriction = restriction.freeze
|
15
|
+
end
|
16
|
+
|
17
|
+
def clone(restriction:)
|
18
|
+
QueryContext.new restriction, data
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,252 @@
|
|
1
|
+
module ParamsReady
|
2
|
+
class Restriction
|
3
|
+
module Wrapper
|
4
|
+
extend Forwardable
|
5
|
+
attr_reader :restriction
|
6
|
+
def_delegators :restriction, :name_permitted?, :permitted?, :permissions_for
|
7
|
+
|
8
|
+
def delegate(*args)
|
9
|
+
return self if @restriction.everything?
|
10
|
+
|
11
|
+
new_restriction = @restriction.delegate(*args)
|
12
|
+
clone(restriction: new_restriction)
|
13
|
+
end
|
14
|
+
|
15
|
+
def for_children(parameter)
|
16
|
+
return self if @restriction.everything?
|
17
|
+
|
18
|
+
new_restriction = @restriction.for_children parameter
|
19
|
+
clone(restriction: new_restriction)
|
20
|
+
end
|
21
|
+
|
22
|
+
def permit_all
|
23
|
+
return self if @restriction.everything?
|
24
|
+
|
25
|
+
clone(restriction: Restriction.blanket_permission)
|
26
|
+
end
|
27
|
+
|
28
|
+
def permit(*list)
|
29
|
+
restriction = Restriction.permit(*list)
|
30
|
+
return self if @restriction.everything? && restriction.everything?
|
31
|
+
|
32
|
+
clone(restriction: restriction)
|
33
|
+
end
|
34
|
+
|
35
|
+
def prohibit(*list)
|
36
|
+
restriction = Restriction.prohibit(*list)
|
37
|
+
return self if @restriction.everything? && restriction.everything?
|
38
|
+
|
39
|
+
clone(restriction: restriction)
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_restriction
|
43
|
+
@restriction
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class Everything
|
48
|
+
def self.key?(_)
|
49
|
+
true
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.[](_)
|
53
|
+
Everything
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.dup
|
57
|
+
self
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class Nothing
|
62
|
+
def self.key?(_)
|
63
|
+
false
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.[](_)
|
67
|
+
Nothing
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.dup
|
71
|
+
self
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.blanket_permission
|
76
|
+
@blanket_permission ||= Allowlist.new
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.permit(*args)
|
80
|
+
Allowlist.instance(*args)
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.prohibit(*args)
|
84
|
+
Denylist.instance(*args)
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.from_array(arr)
|
88
|
+
arr.each_with_object({}) do |element, restriction|
|
89
|
+
case element
|
90
|
+
when String, Symbol
|
91
|
+
restriction[element.to_sym] = Everything
|
92
|
+
when Hash
|
93
|
+
element.each do |key, value|
|
94
|
+
restriction[key.to_sym] = value
|
95
|
+
end
|
96
|
+
else
|
97
|
+
raise TypeError.new("Unexpected as restriction item: #{element}")
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def self.instance(*list)
|
103
|
+
return blanket_permission if list.length == 1 && list[0] == default
|
104
|
+
|
105
|
+
restriction_list = if list.length == 1 && list[0].is_a?(Regexp)
|
106
|
+
list[0]
|
107
|
+
else
|
108
|
+
from_array(list)
|
109
|
+
end
|
110
|
+
new restriction_list
|
111
|
+
end
|
112
|
+
|
113
|
+
attr_reader :restriction
|
114
|
+
|
115
|
+
def initialize(restriction = self.class.default)
|
116
|
+
@restriction = if restriction.is_a? self.class
|
117
|
+
restriction.restriction
|
118
|
+
else
|
119
|
+
restriction.freeze
|
120
|
+
end
|
121
|
+
freeze
|
122
|
+
end
|
123
|
+
|
124
|
+
def hash
|
125
|
+
@restriction.hash
|
126
|
+
end
|
127
|
+
|
128
|
+
def everything?
|
129
|
+
@restriction == self.class.default
|
130
|
+
end
|
131
|
+
|
132
|
+
def name_listed?(name)
|
133
|
+
if @restriction.is_a? Regexp
|
134
|
+
name =~ @restriction
|
135
|
+
else
|
136
|
+
@restriction.key?(name)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def permitted?(parameter)
|
141
|
+
name = parameter.name
|
142
|
+
return false unless name_permitted?(name)
|
143
|
+
return true unless parameter.respond_to? :permission_depends_on
|
144
|
+
|
145
|
+
children = parameter.permission_depends_on
|
146
|
+
intent = parameter.intent_for_children(self)
|
147
|
+
children.all? do |child|
|
148
|
+
intent.permitted?(child)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def delegate(parent, delegate_name, *others)
|
153
|
+
return self if everything?
|
154
|
+
|
155
|
+
list = restriction_list_for(parent)
|
156
|
+
|
157
|
+
self.class.instance({ delegate_name => list }, *others)
|
158
|
+
end
|
159
|
+
|
160
|
+
def for_children(parameter)
|
161
|
+
return self if everything?
|
162
|
+
|
163
|
+
list = restriction_list_for(parameter)
|
164
|
+
if list.is_a? Restriction
|
165
|
+
list
|
166
|
+
else
|
167
|
+
self.class.instance(*list)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def restriction_list_for(parameter)
|
172
|
+
name = parameter.name
|
173
|
+
raise ParamsReadyError, "Parameter '#{name}' not permitted" unless name_permitted? name
|
174
|
+
restriction_list_for_name(name)
|
175
|
+
end
|
176
|
+
|
177
|
+
def to_restriction
|
178
|
+
self
|
179
|
+
end
|
180
|
+
|
181
|
+
def permit_all
|
182
|
+
self.class.permit_all
|
183
|
+
end
|
184
|
+
|
185
|
+
def self.permit_all
|
186
|
+
new default
|
187
|
+
end
|
188
|
+
|
189
|
+
class Allowlist < Restriction
|
190
|
+
def self.default
|
191
|
+
Everything
|
192
|
+
end
|
193
|
+
|
194
|
+
def name_permitted?(name)
|
195
|
+
name_listed?(name)
|
196
|
+
end
|
197
|
+
|
198
|
+
def permit(*args)
|
199
|
+
self.class.permit(*args)
|
200
|
+
end
|
201
|
+
|
202
|
+
def ==(other)
|
203
|
+
return false unless other.is_a? self.class
|
204
|
+
return true if object_id == other.object_id
|
205
|
+
|
206
|
+
restriction == other.restriction
|
207
|
+
end
|
208
|
+
|
209
|
+
protected
|
210
|
+
|
211
|
+
def restriction_list_for_name(name)
|
212
|
+
if @restriction.is_a? Regexp
|
213
|
+
self.class.default
|
214
|
+
else
|
215
|
+
@restriction[name]
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
class Denylist < Restriction
|
221
|
+
def self.default
|
222
|
+
Nothing
|
223
|
+
end
|
224
|
+
|
225
|
+
def name_permitted?(name)
|
226
|
+
return true unless name_listed?(name)
|
227
|
+
return false unless @restriction.is_a?(Hash)
|
228
|
+
return true if @restriction[name].is_a?(Array)
|
229
|
+
return true if @restriction[name].is_a?(Symbol)
|
230
|
+
return true if @restriction[name] == self.class.default
|
231
|
+
|
232
|
+
false
|
233
|
+
end
|
234
|
+
|
235
|
+
def prohibit(*args)
|
236
|
+
self.class.prohibit(*args)
|
237
|
+
end
|
238
|
+
|
239
|
+
protected
|
240
|
+
|
241
|
+
def restriction_list_for_name(name)
|
242
|
+
if @restriction.is_a? Regexp
|
243
|
+
self.class.default
|
244
|
+
elsif @restriction[name].nil?
|
245
|
+
self.class.default
|
246
|
+
else
|
247
|
+
@restriction[name]
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|