params_ready_rails5 0.0.7

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 (86) hide show
  1. checksums.yaml +7 -0
  2. data/lib/arel/cte_name.rb +20 -0
  3. data/lib/params_ready/builder.rb +161 -0
  4. data/lib/params_ready/error.rb +31 -0
  5. data/lib/params_ready/extensions/class_reader_writer.rb +33 -0
  6. data/lib/params_ready/extensions/collection.rb +43 -0
  7. data/lib/params_ready/extensions/delegation.rb +25 -0
  8. data/lib/params_ready/extensions/finalizer.rb +26 -0
  9. data/lib/params_ready/extensions/freezer.rb +49 -0
  10. data/lib/params_ready/extensions/hash.rb +46 -0
  11. data/lib/params_ready/extensions/late_init.rb +38 -0
  12. data/lib/params_ready/extensions/registry.rb +44 -0
  13. data/lib/params_ready/extensions/undefined.rb +23 -0
  14. data/lib/params_ready/format.rb +132 -0
  15. data/lib/params_ready/helpers/arel_builder.rb +68 -0
  16. data/lib/params_ready/helpers/callable.rb +14 -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/interface_definer.rb +48 -0
  20. data/lib/params_ready/helpers/key_map.rb +176 -0
  21. data/lib/params_ready/helpers/memo.rb +41 -0
  22. data/lib/params_ready/helpers/options.rb +107 -0
  23. data/lib/params_ready/helpers/parameter_definer_class_methods.rb +39 -0
  24. data/lib/params_ready/helpers/parameter_storage_class_methods.rb +63 -0
  25. data/lib/params_ready/helpers/parameter_user_class_methods.rb +35 -0
  26. data/lib/params_ready/helpers/relation_builder_wrapper.rb +35 -0
  27. data/lib/params_ready/helpers/rule.rb +76 -0
  28. data/lib/params_ready/helpers/storage.rb +30 -0
  29. data/lib/params_ready/helpers/usage_rule.rb +36 -0
  30. data/lib/params_ready/input_context.rb +31 -0
  31. data/lib/params_ready/intent.rb +70 -0
  32. data/lib/params_ready/marshaller/array_marshallers.rb +132 -0
  33. data/lib/params_ready/marshaller/builder_module.rb +9 -0
  34. data/lib/params_ready/marshaller/collection.rb +165 -0
  35. data/lib/params_ready/marshaller/definition_module.rb +63 -0
  36. data/lib/params_ready/marshaller/enum_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/struct_marshallers.rb +100 -0
  40. data/lib/params_ready/marshaller/tuple_marshallers.rb +103 -0
  41. data/lib/params_ready/ordering/column.rb +60 -0
  42. data/lib/params_ready/ordering/ordering.rb +276 -0
  43. data/lib/params_ready/output_parameters.rb +138 -0
  44. data/lib/params_ready/pagination/abstract_pagination.rb +18 -0
  45. data/lib/params_ready/pagination/cursor.rb +171 -0
  46. data/lib/params_ready/pagination/direction.rb +148 -0
  47. data/lib/params_ready/pagination/keyset_pagination.rb +254 -0
  48. data/lib/params_ready/pagination/keysets.rb +70 -0
  49. data/lib/params_ready/pagination/nulls.rb +31 -0
  50. data/lib/params_ready/pagination/offset_pagination.rb +130 -0
  51. data/lib/params_ready/pagination/tendency.rb +28 -0
  52. data/lib/params_ready/parameter/abstract_struct_parameter.rb +204 -0
  53. data/lib/params_ready/parameter/array_parameter.rb +197 -0
  54. data/lib/params_ready/parameter/definition.rb +272 -0
  55. data/lib/params_ready/parameter/enum_set_parameter.rb +102 -0
  56. data/lib/params_ready/parameter/parameter.rb +475 -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/struct_parameter.rb +64 -0
  60. data/lib/params_ready/parameter/tuple_parameter.rb +152 -0
  61. data/lib/params_ready/parameter/value_parameter.rb +186 -0
  62. data/lib/params_ready/parameter_definer.rb +14 -0
  63. data/lib/params_ready/parameter_user.rb +35 -0
  64. data/lib/params_ready/query/array_grouping.rb +68 -0
  65. data/lib/params_ready/query/custom_predicate.rb +102 -0
  66. data/lib/params_ready/query/exists_predicate.rb +103 -0
  67. data/lib/params_ready/query/fixed_operator_predicate.rb +77 -0
  68. data/lib/params_ready/query/grouping.rb +177 -0
  69. data/lib/params_ready/query/join_clause.rb +87 -0
  70. data/lib/params_ready/query/nullness_predicate.rb +71 -0
  71. data/lib/params_ready/query/polymorph_predicate.rb +77 -0
  72. data/lib/params_ready/query/predicate.rb +203 -0
  73. data/lib/params_ready/query/predicate_operator.rb +132 -0
  74. data/lib/params_ready/query/relation.rb +337 -0
  75. data/lib/params_ready/query/structured_grouping.rb +58 -0
  76. data/lib/params_ready/query/variable_operator_predicate.rb +125 -0
  77. data/lib/params_ready/query_context.rb +21 -0
  78. data/lib/params_ready/restriction.rb +252 -0
  79. data/lib/params_ready/result.rb +109 -0
  80. data/lib/params_ready/value/coder.rb +210 -0
  81. data/lib/params_ready/value/constraint.rb +198 -0
  82. data/lib/params_ready/value/custom.rb +56 -0
  83. data/lib/params_ready/value/validator.rb +81 -0
  84. data/lib/params_ready/version.rb +7 -0
  85. data/lib/params_ready.rb +28 -0
  86. metadata +227 -0
@@ -0,0 +1,337 @@
1
+ require 'forwardable'
2
+ require_relative 'structured_grouping'
3
+ require_relative 'join_clause'
4
+ require_relative '../pagination/offset_pagination'
5
+ require_relative '../pagination/keyset_pagination'
6
+ require_relative '../pagination/direction'
7
+ require_relative '../ordering/ordering'
8
+
9
+ module ParamsReady
10
+ module Query
11
+ class Relation < StructuredGrouping
12
+ module PageAccessors
13
+ def page_accessor(name, delegate = nil)
14
+ delegate ||= "#{name}_page"
15
+
16
+ define_method name do |*args|
17
+ send(delegate, *args)&.for_frontend
18
+ end
19
+ ruby2_keywords name
20
+ end
21
+
22
+ def self.extended(mod)
23
+ mod.page_accessor :current
24
+ mod.page_accessor :first
25
+ mod.page_accessor :last
26
+ mod.page_accessor :previous
27
+ mod.page_accessor :next
28
+ mod.page_accessor :before
29
+ mod.page_accessor :after
30
+ mod.page_accessor :limit_at, :limited_at
31
+ mod.page_accessor :toggle, :toggled_order
32
+ mod.page_accessor :reorder, :reordered
33
+ end
34
+ end
35
+
36
+ extend PageAccessors
37
+ extend Forwardable
38
+
39
+ def_delegators :pagination, :offset, :limit, :num_pages, :page_no, :has_previous?, :has_next?, :has_page?
40
+
41
+ def child_is_definite?(name)
42
+ return false unless definition.has_child?(name)
43
+ return false if self[name].nil?
44
+ return false unless self[name].is_definite?
45
+
46
+ true
47
+ end
48
+
49
+ def pagination
50
+ self[:pagination]
51
+ end
52
+
53
+ def ordering
54
+ self[:ordering]
55
+ end
56
+
57
+ def ordering_or_nil
58
+ return unless child_is_definite?(:ordering)
59
+
60
+ self[:ordering]
61
+ end
62
+
63
+ def new_offset(delta)
64
+ pagination.new_offset(delta)
65
+ end
66
+
67
+ def page(delta, count: nil)
68
+ return nil unless pagination.can_yield_page?(delta, count: count)
69
+ return self if delta == 0
70
+
71
+ new_offset = pagination.new_offset(delta)
72
+
73
+ update_in(new_offset, [:pagination, 0])
74
+ end
75
+
76
+ def current_page(count: nil)
77
+ page(0, count: count)
78
+ end
79
+
80
+ def first_page
81
+ value = pagination.first_page_value
82
+ update_in(value, [:pagination])
83
+ end
84
+
85
+ def last_page(count:)
86
+ value = pagination.last_page_value(count: count)
87
+ return if value.nil?
88
+ update_in(value, [:pagination])
89
+ end
90
+
91
+ def previous_page(delta = 1)
92
+ value = pagination.previous_page_value(delta)
93
+ return if value.nil?
94
+ update_in(value, [:pagination])
95
+ end
96
+
97
+ def next_page(delta = 1, count: nil)
98
+ value = pagination.next_page_value(delta, count: count)
99
+ return if value.nil?
100
+ page(delta, count: count)
101
+ end
102
+
103
+ def before_page(keyset)
104
+ tuple = { direction: :bfr, limit: limit, keyset: keyset }
105
+ update_in(tuple, [:pagination])
106
+ end
107
+
108
+ def after_page(keyset)
109
+ tuple = { direction: :aft, limit: limit, keyset: keyset }
110
+ update_in(tuple, [:pagination])
111
+ end
112
+
113
+ def limited_at(limit)
114
+ update_in(limit, [:pagination, pagination.limit_key])
115
+ end
116
+
117
+ def toggled_order(column)
118
+ new_order = ordering.toggled_order_value(column)
119
+ toggled = update_in(new_order, [:ordering])
120
+ toggled.update_in(0, [:pagination, 0])
121
+ end
122
+
123
+ def reordered(column, direction)
124
+ new_order = ordering.reordered_value(column, direction)
125
+ reordered = update_in(new_order, [:ordering])
126
+ reordered.update_in(0, [:pagination, 0])
127
+ end
128
+
129
+ def model_class(default_model_class)
130
+ default_model_class || definition.model_class
131
+ end
132
+
133
+ def arel_table(default_model_class)
134
+ model_class(default_model_class).arel_table
135
+ end
136
+
137
+ def perform_count(scope: nil, context: Restriction.blanket_permission)
138
+ scope ||= definition.model_class if definition.model_class_defined?
139
+ group = predicate_group(scope.arel_table, context: context)
140
+ relation = scope.where(group)
141
+ relation = perform_joins(relation, context)
142
+ relation.count
143
+ end
144
+
145
+ def keysets(limit, direction, keyset, scope: nil, context: Restriction.blanket_permission, &block)
146
+ model_class = scope || definition.model_class
147
+ group = predicate_group(model_class.arel_table, context: context)
148
+ relation = model_class.where(group)
149
+ relation = perform_joins(relation, context)
150
+
151
+ sql_literal = pagination.keysets_for_relation(relation, limit, direction, keyset, ordering, context, &block)
152
+
153
+ array = model_class.connection.execute(sql_literal.to_s).to_a
154
+ Pagination::Direction.instance(direction).keysets(keyset, array, &block)
155
+ end
156
+
157
+ def build_relation(scope: nil, include: [], context: Restriction.blanket_permission, paginate: true)
158
+ model_class = scope || definition.model_class
159
+ group = predicate_group(model_class.arel_table, context: context)
160
+ relation = model_class.where(group)
161
+ relation = relation.includes(*include) unless include.empty?
162
+ relation = perform_joins(relation, context)
163
+
164
+ order_and_paginate_relation(relation, context, paginate)
165
+ end
166
+
167
+ def perform_joins(relation, context)
168
+ return relation if definition.joins.empty?
169
+
170
+ sql = joined_tables(relation.arel_table, context).join_sources.map(&:to_sql).join(' ')
171
+ relation.joins(sql)
172
+ end
173
+
174
+ def to_count(model_class: nil, context: Restriction.blanket_permission)
175
+ model_class = model_class || definition.model_class
176
+
177
+ arel_table = joined_tables(model_class.arel_table, context)
178
+
179
+ group = self.predicate_group(model_class.arel_table, context: context)
180
+
181
+ query = if group.nil?
182
+ arel_table
183
+ else
184
+ arel_table.where(group)
185
+ end
186
+ query.project(arel_table[:id].count)
187
+ end
188
+
189
+ def build_select(model_class: nil, context: Restriction.blanket_permission, select_list: Arel.star, paginate: true)
190
+ arel_table, query = build_query(model_class, context)
191
+ query = order_and_paginate_query(query, arel_table, context, paginate)
192
+ query.project(select_list)
193
+ end
194
+
195
+ def build_keyset_query(limit, direction, keyset, model_class: nil, context: Restriction.blanket_permission)
196
+ arel_table, query = build_query(model_class, context)
197
+ pagination.select_keysets(query, limit, direction, keyset, ordering, arel_table, context)
198
+ end
199
+
200
+ def build_query(model_class, context)
201
+ arel_table = arel_table(model_class)
202
+
203
+ group = self.predicate_group(arel_table, context: context)
204
+ joined = joined_tables(arel_table, context)
205
+
206
+ query = if group.nil?
207
+ joined
208
+ else
209
+ joined.where(group)
210
+ end
211
+
212
+ [arel_table, query]
213
+ end
214
+
215
+ def order_if_applicable(arel_table, context)
216
+ if child_is_definite?(:ordering) && (context.permitted?(ordering) || ordering.required?)
217
+ ordering = self.ordering.to_arel(arel_table, context: context)
218
+ yield ordering if ordering.length > 0
219
+ end
220
+ end
221
+
222
+ def paginate_if_applicable(paginate)
223
+ if paginate && child_is_definite?(:pagination)
224
+ pagination = self.pagination
225
+ yield pagination
226
+ end
227
+ end
228
+
229
+ def order_and_paginate_relation(relation, context, paginate)
230
+ paginate_if_applicable(paginate) do |pagination|
231
+ relation = pagination.paginate_relation(relation, ordering_or_nil, context)
232
+ end
233
+
234
+ order_if_applicable(relation.arel_table, context) do |ordering|
235
+ relation = relation.order(ordering)
236
+ end
237
+ relation
238
+ end
239
+
240
+ def order_and_paginate_query(query, arel_table, context, paginate)
241
+ paginate_if_applicable(paginate) do |pagination|
242
+ query = pagination.paginate_query(query, ordering_or_nil, arel_table, context)
243
+ end
244
+
245
+ order_if_applicable(arel_table, context) do |ordering|
246
+ query = query.order(*ordering)
247
+ end
248
+ query
249
+ end
250
+
251
+ def joined_tables(base_table, context)
252
+ definition.joins.reduce(base_table) do |joined_table, join|
253
+ join.to_arel(joined_table, base_table, context, self)
254
+ end
255
+ end
256
+ end
257
+
258
+ class RelationParameterBuilder < Builder
259
+ include GroupingLike
260
+ include Parameter::AbstractStructParameterBuilder::StructLike
261
+ include HavingModel
262
+
263
+ def self.instance(name, altn: nil)
264
+ new RelationDefinition.new(name, altn: altn)
265
+ end
266
+
267
+ register :relation
268
+ DEFAULT_LIMIT = 10
269
+
270
+ def paginate(limit = DEFAULT_LIMIT, max_limit = nil, method: :offset, &block)
271
+ case method
272
+ when :offset
273
+ raise ParamsReadyError, 'Block not expected' unless block.nil?
274
+ add Pagination::OffsetPaginationDefinition.new(0, limit, max_limit).finish
275
+ when :keyset
276
+ ordering_builder = @definition.init_ordering_builder(empty: true)
277
+ rcpb = Pagination::KeysetPaginationBuilder.new ordering_builder, limit, max_limit
278
+ add rcpb.build(&block)
279
+ else
280
+ raise "Unimplemented pagination method '#{method}'"
281
+ end
282
+ end
283
+
284
+ def order(&proc)
285
+ ordering_builder = @definition.init_ordering_builder(empty: false)
286
+ ordering_builder.instance_eval(&proc) unless proc.nil?
287
+ ordering = ordering_builder.build
288
+ add ordering
289
+ end
290
+
291
+ def join_table(arel_table, type, &block)
292
+ join = Join.new arel_table, type, &block
293
+ join.freeze
294
+ @definition.add_join(join)
295
+ end
296
+ end
297
+
298
+ class RelationDefinition < StructuredGroupingDefinition
299
+ late_init :model_class, obligatory: false, freeze: false, getter: false
300
+ collection :joins, :join
301
+
302
+ def model_class
303
+ raise ParamsReadyError, "Model class not set for #{name}" if @model_class.nil?
304
+ @model_class
305
+ end
306
+
307
+ def init_ordering_builder(empty:)
308
+ raise ParamsReadyError, 'Ordering already defined' if empty == true && !@ordering_builder.nil?
309
+ @ordering_builder ||= Ordering::OrderingParameterBuilder.instance
310
+ end
311
+
312
+ def arel_table
313
+ model_class.arel_table
314
+ end
315
+
316
+ def model_class_defined?
317
+ !@model_class.nil?
318
+ end
319
+
320
+ attr_reader :joins
321
+
322
+ def initialize(*args, **opts)
323
+ @joins = []
324
+ @ordering_builder = nil
325
+ super
326
+ end
327
+
328
+ def finish
329
+ raise ParamsReadyError, 'Ordering must be explicitly declared' if @ordering_builder&.open?
330
+ @ordering_builder = nil
331
+ super
332
+ end
333
+
334
+ parameter_class Relation
335
+ end
336
+ end
337
+ end
@@ -0,0 +1,58 @@
1
+ require_relative '../parameter/struct_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::StructParameter
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::AbstractStructParameterBuilder::StructLike
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::StructParameterDefinition
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/struct_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.struct_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
+ @data[:operator].unwrap_or(nil)&.name || '?'
37
+ @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[:struct_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 :struct_parameter_builder
86
+ attr_reader :struct_parameter
87
+
88
+ def initialize(*args, attribute_name: nil, **opts)
89
+ super *args, **opts
90
+ @attribute_name = attribute_name
91
+ @struct_parameter_builder = Builder.builder(:struct).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
+ @struct_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
+ @struct_parameter_builder.add @operator_parameter_builder.build
111
+ end
112
+
113
+ def finish
114
+ @struct_parameter = @struct_parameter_builder.build
115
+ @struct_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