params_ready_rails5 0.0.7

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