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.
Files changed (83) hide show
  1. checksums.yaml +7 -0
  2. data/lib/arel/cte_name.rb +20 -0
  3. data/lib/params_ready.rb +36 -0
  4. data/lib/params_ready/builder.rb +140 -0
  5. data/lib/params_ready/error.rb +31 -0
  6. data/lib/params_ready/extensions/class_reader_writer.rb +33 -0
  7. data/lib/params_ready/extensions/collection.rb +43 -0
  8. data/lib/params_ready/extensions/delegation.rb +25 -0
  9. data/lib/params_ready/extensions/finalizer.rb +26 -0
  10. data/lib/params_ready/extensions/freezer.rb +49 -0
  11. data/lib/params_ready/extensions/hash.rb +46 -0
  12. data/lib/params_ready/extensions/late_init.rb +38 -0
  13. data/lib/params_ready/extensions/registry.rb +44 -0
  14. data/lib/params_ready/extensions/undefined.rb +15 -0
  15. data/lib/params_ready/format.rb +130 -0
  16. data/lib/params_ready/helpers/arel_builder.rb +68 -0
  17. data/lib/params_ready/helpers/conditional_block.rb +31 -0
  18. data/lib/params_ready/helpers/find_in_hash.rb +22 -0
  19. data/lib/params_ready/helpers/key_map.rb +176 -0
  20. data/lib/params_ready/helpers/memo.rb +42 -0
  21. data/lib/params_ready/helpers/options.rb +39 -0
  22. data/lib/params_ready/helpers/parameter_definer_class_methods.rb +39 -0
  23. data/lib/params_ready/helpers/parameter_storage_class_methods.rb +36 -0
  24. data/lib/params_ready/helpers/parameter_user_class_methods.rb +31 -0
  25. data/lib/params_ready/helpers/relation_builder_wrapper.rb +35 -0
  26. data/lib/params_ready/helpers/rule.rb +57 -0
  27. data/lib/params_ready/helpers/storage.rb +30 -0
  28. data/lib/params_ready/helpers/usage_rule.rb +18 -0
  29. data/lib/params_ready/input_context.rb +31 -0
  30. data/lib/params_ready/intent.rb +70 -0
  31. data/lib/params_ready/marshaller/array_marshallers.rb +132 -0
  32. data/lib/params_ready/marshaller/builder_module.rb +9 -0
  33. data/lib/params_ready/marshaller/collection.rb +165 -0
  34. data/lib/params_ready/marshaller/definition_module.rb +63 -0
  35. data/lib/params_ready/marshaller/hash_marshallers.rb +100 -0
  36. data/lib/params_ready/marshaller/hash_set_marshallers.rb +96 -0
  37. data/lib/params_ready/marshaller/parameter_module.rb +11 -0
  38. data/lib/params_ready/marshaller/polymorph_marshallers.rb +67 -0
  39. data/lib/params_ready/marshaller/tuple_marshallers.rb +103 -0
  40. data/lib/params_ready/ordering/column.rb +60 -0
  41. data/lib/params_ready/ordering/ordering.rb +276 -0
  42. data/lib/params_ready/output_parameters.rb +127 -0
  43. data/lib/params_ready/pagination/abstract_pagination.rb +18 -0
  44. data/lib/params_ready/pagination/cursor.rb +171 -0
  45. data/lib/params_ready/pagination/direction.rb +148 -0
  46. data/lib/params_ready/pagination/keyset_pagination.rb +254 -0
  47. data/lib/params_ready/pagination/keysets.rb +70 -0
  48. data/lib/params_ready/pagination/nulls.rb +31 -0
  49. data/lib/params_ready/pagination/offset_pagination.rb +130 -0
  50. data/lib/params_ready/pagination/tendency.rb +28 -0
  51. data/lib/params_ready/parameter/abstract_hash_parameter.rb +204 -0
  52. data/lib/params_ready/parameter/array_parameter.rb +197 -0
  53. data/lib/params_ready/parameter/definition.rb +264 -0
  54. data/lib/params_ready/parameter/hash_parameter.rb +63 -0
  55. data/lib/params_ready/parameter/hash_set_parameter.rb +101 -0
  56. data/lib/params_ready/parameter/parameter.rb +456 -0
  57. data/lib/params_ready/parameter/polymorph_parameter.rb +172 -0
  58. data/lib/params_ready/parameter/state.rb +132 -0
  59. data/lib/params_ready/parameter/tuple_parameter.rb +152 -0
  60. data/lib/params_ready/parameter/value_parameter.rb +182 -0
  61. data/lib/params_ready/parameter_definer.rb +14 -0
  62. data/lib/params_ready/parameter_user.rb +43 -0
  63. data/lib/params_ready/query/array_grouping.rb +68 -0
  64. data/lib/params_ready/query/custom_predicate.rb +102 -0
  65. data/lib/params_ready/query/exists_predicate.rb +103 -0
  66. data/lib/params_ready/query/fixed_operator_predicate.rb +77 -0
  67. data/lib/params_ready/query/grouping.rb +177 -0
  68. data/lib/params_ready/query/join_clause.rb +87 -0
  69. data/lib/params_ready/query/nullness_predicate.rb +71 -0
  70. data/lib/params_ready/query/polymorph_predicate.rb +77 -0
  71. data/lib/params_ready/query/predicate.rb +203 -0
  72. data/lib/params_ready/query/predicate_operator.rb +132 -0
  73. data/lib/params_ready/query/relation.rb +337 -0
  74. data/lib/params_ready/query/structured_grouping.rb +58 -0
  75. data/lib/params_ready/query/variable_operator_predicate.rb +125 -0
  76. data/lib/params_ready/query_context.rb +21 -0
  77. data/lib/params_ready/restriction.rb +252 -0
  78. data/lib/params_ready/result.rb +109 -0
  79. data/lib/params_ready/value/coder.rb +181 -0
  80. data/lib/params_ready/value/constraint.rb +198 -0
  81. data/lib/params_ready/value/custom.rb +56 -0
  82. data/lib/params_ready/value/validator.rb +68 -0
  83. 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