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,102 @@
1
+ require 'set'
2
+ require_relative 'struct_parameter'
3
+ require_relative 'value_parameter'
4
+ require_relative '../intent'
5
+ require_relative '../marshaller/enum_set_marshallers'
6
+ require_relative '../marshaller/parameter_module'
7
+
8
+ module ParamsReady
9
+ module Parameter
10
+ class EnumSetParameter < AbstractStructParameter
11
+ include Marshaller::ParameterModule
12
+
13
+ def self.intent_for_set(intent)
14
+ Intent.new(
15
+ intent.format.update(
16
+ omit: [],
17
+ remap: false
18
+ ),
19
+ intent.restriction
20
+ )
21
+ end
22
+
23
+ def member?(key)
24
+ raise ParamsReadyError, "Key not defined: '#{key}'" unless definition.has_child? key
25
+
26
+ if is_definite?
27
+ bare_value[key].unwrap == true
28
+ else
29
+ false
30
+ end
31
+ end
32
+ end
33
+
34
+ class EnumSetParameterBuilder < Builder
35
+ include Marshaller::BuilderModule
36
+
37
+ register :enum_set
38
+ register_deprecated :hash_set, use: :enum_set
39
+
40
+ def self.instance(name, altn: nil, type: :boolean)
41
+ new EnumSetParameterDefinition.new(name, altn: altn, type: type)
42
+ end
43
+
44
+ def add(input, *args, val: nil, **opts, &block)
45
+ type = @definition.type
46
+ definition = self.class.resolve(type, input, *args, **opts, &block)
47
+ @definition.add_child definition, value: val
48
+ end
49
+
50
+
51
+ def self.resolve(type, input, *args, **opts, &block)
52
+ if input.is_a? AbstractDefinition
53
+ input
54
+ else
55
+ define_registered_parameter(type, input, *args, **opts, &block)
56
+ end
57
+ end
58
+ end
59
+
60
+ class EnumSetParameterDefinition < AbstractStructParameterDefinition
61
+ attr_reader :type, :values
62
+ freeze_variable :values
63
+ name_for_formatter :enum_set
64
+ parameter_class EnumSetParameter
65
+ include Marshaller::DefinitionModule[Marshaller::EnumSetMarshallers.collection]
66
+
67
+ def initialize(*args, type: :boolean, **opts)
68
+ @type = type
69
+ @values = {}
70
+ super *args, **opts
71
+ end
72
+
73
+ def ensure_canonical(set)
74
+ raise ParamsReadyError, "Unexpected default type: #{set.class.name}" unless set.is_a?(Set)
75
+
76
+ context = Format.instance(:backend)
77
+ value, _validator = try_canonicalize set, context, nil, freeze: true
78
+ return value if value.length == set.length
79
+
80
+ extra_keys = set.reject do |key|
81
+ value.key?(key) || value.key?(key.to_s)
82
+ end.map do |key|
83
+ "'#{key.to_s}'"
84
+ end.join(", ")
85
+ raise ParamsReadyError, "extra elements found -- #{extra_keys}" if extra_keys.length > 0
86
+
87
+ value
88
+ end
89
+
90
+ def add_child(child, value:)
91
+ value = value.nil? ? child.name : value
92
+
93
+ if @values.key(value).nil?
94
+ @values[child.name] = value
95
+ else
96
+ raise ParamsReadyError, "Value '#{value}' already taken by '#{@values.key(value)}'"
97
+ end
98
+ super child
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,475 @@
1
+ require 'forwardable'
2
+ require_relative '../extensions/freezer'
3
+ require_relative '../error'
4
+ require_relative '../helpers/memo'
5
+ require_relative '../helpers/find_in_hash'
6
+
7
+ module ParamsReady
8
+ module Parameter
9
+ module FromHash
10
+ def set_from_hash(hash, context: nil, validator: Result.new(name))
11
+ if no_input?(context)
12
+ populate(context, validator)
13
+ else
14
+ _, input = find_in_hash hash, context
15
+ set_from_input(input, context, validator)
16
+ end
17
+ end
18
+ end
19
+
20
+ module ComplexParameter
21
+ def update_child(value, path)
22
+ child, child_name, child_path = child_for_update(path)
23
+ changed, updated = child.update_if_applicable(value, child_path)
24
+
25
+ if frozen? && !changed
26
+ [false, self]
27
+ else
28
+ clone = updated_clone(child_name, updated)
29
+ [true, clone]
30
+ end
31
+ end
32
+ end
33
+
34
+ module DelegatingParameter
35
+ include ComplexParameter
36
+ include FromHash
37
+
38
+ def self.included(recipient)
39
+ recipient.freeze_variable :data
40
+ end
41
+
42
+ def method_missing(name, *args)
43
+ if @data.respond_to?(name)
44
+ @data.send name, *args
45
+ else
46
+ super
47
+ end
48
+ end
49
+
50
+ def respond_to_missing?(name, include_private = false)
51
+ if @data.respond_to?(name, include_private)
52
+ true
53
+ else
54
+ super
55
+ end
56
+ end
57
+
58
+ def set_value(input, context = Format.instance(:backend), validator = nil)
59
+ if self.match?(input)
60
+ super input.unwrap, context, validator
61
+ else
62
+ super
63
+ end
64
+ end
65
+
66
+ def hash
67
+ [definition, data].hash
68
+ end
69
+
70
+ def ==(other)
71
+ return false unless self.match?(other)
72
+ data == other.data
73
+ end
74
+
75
+ alias_method :eql?, :==
76
+
77
+ protected
78
+
79
+ def child_for_update(path)
80
+ [@data, nil, *path]
81
+ end
82
+
83
+ def updated_clone(_child_name, updated)
84
+ clone = definition.create
85
+ clone.instance_variable_set :@data, updated
86
+ clone.freeze if frozen?
87
+ clone
88
+ end
89
+
90
+ def set_from_input(input, context, validator)
91
+ if self.match?(input)
92
+ super input.unwrap, context, validator
93
+ else
94
+ super
95
+ end
96
+ end
97
+
98
+ def populate_other(other)
99
+ data.populate_other(other.data)
100
+ end
101
+ end
102
+
103
+ class AbstractParameter
104
+ attr_reader :definition
105
+ extend Forwardable
106
+ extend Extensions::Freezer
107
+ include Extensions::Freezer::InstanceMethods
108
+ include FromHash
109
+
110
+ def_delegators :@definition, :name, :altn, :name_for_formatter
111
+
112
+ def self.intent_for_children(method, &block)
113
+ case method
114
+ when :restriction
115
+ raise ParamsReadyError, "Block unexpected for '#{method}' method" unless block.nil?
116
+ define_method :intent_for_children do |intent|
117
+ intent.for_children(self)
118
+ end
119
+ when :delegate
120
+ define_method :intent_for_children do |intent|
121
+ delegate_name, *others = self.instance_eval(&block)
122
+ intent.delegate(self, delegate_name, *others)
123
+ end
124
+ when :pass
125
+ raise ParamsReadyError, "Block unexpected for '#{method}' method" unless block.nil?
126
+ define_method :intent_for_children do |intent|
127
+ intent
128
+ end
129
+ else
130
+ raise ParamsReadyError, "Unimplemented permission method: '#{method}'"
131
+ end
132
+ end
133
+
134
+ intent_for_children :pass
135
+
136
+ def initialize(definition, **options)
137
+ raise ParamsReadyError, "Unexpected options: #{options}" unless options.empty?
138
+ @definition = definition
139
+ end
140
+
141
+ def update_in(value, path)
142
+ _, updated = update_if_applicable(value, path)
143
+ updated
144
+ end
145
+
146
+ def update_if_applicable(value, path)
147
+ if path.empty?
148
+ update_self(value)
149
+ elsif respond_to? :update_child
150
+ update_child(value, path)
151
+ else
152
+ raise ParamsReadyError, "Expected path to be terminated in '#{name}'"
153
+ end
154
+ end
155
+
156
+ def populate(context, validator)
157
+ return if definition.populator.nil?
158
+
159
+ definition.populator.call(context, self)
160
+ validator
161
+ rescue => error
162
+ populator_error = PopulatorError.new(error)
163
+ if validator.nil?
164
+ raise populator_error
165
+ else
166
+ validator.error! populator_error
167
+ end
168
+ validator
169
+ end
170
+
171
+ def match?(other)
172
+ return false unless other.instance_of?(self.class)
173
+ definition == other.definition
174
+ end
175
+
176
+ def ==(other)
177
+ return false unless self.match?(other)
178
+
179
+ bare_value == other.bare_value
180
+ rescue
181
+ false
182
+ end
183
+
184
+ def to_hash(format = Format.instance(:backend), restriction: nil, data: nil)
185
+ restriction ||= Restriction.blanket_permission
186
+ intent = Intent.new(format, restriction, data: data)
187
+ to_hash_if_eligible(intent) || {}
188
+ end
189
+
190
+ def inspect
191
+ preserve = Format.instance(:inspect).preserve?(self)
192
+ content = preserve ? inspect_content : '[FILTERED]'
193
+ "#{self.class.name.split("::").last} #{self.name}: { #{content} }"
194
+ end
195
+
196
+ def dup
197
+ clone = definition.create
198
+ populate_other clone
199
+ clone
200
+ end
201
+
202
+ protected
203
+
204
+ def update_self(value)
205
+ clone = definition.create
206
+ clone.set_value value
207
+ clone.freeze if frozen?
208
+ [true, clone]
209
+ end
210
+ end
211
+
212
+ class Parameter < AbstractParameter
213
+ def_delegators :@definition,
214
+ :default, :optional?, :default_defined?, :constraints, :no_output?, :no_input?
215
+
216
+ def initialize(definition)
217
+ @value = Extensions::Undefined
218
+ super definition
219
+ end
220
+
221
+ def set_value(input, context = Format.instance(:backend), validator = nil)
222
+ if Extensions::Undefined.value_indefinite?(input)
223
+ handle_indefinite_input(input, validator)
224
+ elsif self.match? input
225
+ @value = input.bare_value
226
+ else
227
+ begin
228
+ value, validator = definition.try_canonicalize(input, context, validator)
229
+ if validator.nil? || validator.ok?
230
+ if Extensions::Undefined.value_indefinite?(value)
231
+ handle_indefinite_input(value, validator)
232
+ else
233
+ @value = value
234
+ end
235
+ end
236
+ rescue StandardError => e
237
+ if validator.nil?
238
+ raise e
239
+ else
240
+ validator.error! e
241
+ end
242
+ end
243
+ end
244
+ validator
245
+ end
246
+
247
+ def definite_default?
248
+ default_defined? && !default.nil?
249
+ end
250
+
251
+ def nil_default?
252
+ default_defined? && default.nil?
253
+ end
254
+
255
+ def is_definite?
256
+ return true if @value != Extensions::Undefined && !@value.nil?
257
+ return false if optional? && @value.nil?
258
+
259
+ definite_default?
260
+ end
261
+
262
+ def is_default?
263
+ return false unless default_defined?
264
+
265
+ @value == Extensions::Undefined || @value == default
266
+ end
267
+
268
+ def is_nil?
269
+ return false if is_definite?
270
+ return true if optional?
271
+ return true if nil_default?
272
+
273
+ false
274
+ end
275
+
276
+ def is_undefined?
277
+ @value == Extensions::Undefined && allows_undefined?
278
+ end
279
+
280
+ def allows_undefined?
281
+ return true if optional?
282
+
283
+ !default_defined?
284
+ end
285
+
286
+ def eligible_for_output?(intent)
287
+ intent.preserve?(self)
288
+ end
289
+
290
+ def hash_key(format)
291
+ format.hash_key(self)
292
+ end
293
+
294
+ def set_from_input(input, context, validator)
295
+ preprocessed = definition.preprocess(input, context, validator)
296
+ set_value preprocessed, context, validator
297
+ definition.postprocess(self, context, validator)
298
+ validator
299
+ end
300
+
301
+ def to_hash_if_eligible(intent = Intent.instance(:backend))
302
+ return nil unless eligible_for_output? intent
303
+
304
+ formatted = format_self_permitted(intent)
305
+ wrap_output(formatted, intent)
306
+ end
307
+
308
+ def format_self_permitted(intent)
309
+ intent = intent_for_children(intent)
310
+ format(intent)
311
+ end
312
+
313
+ def format(intent)
314
+ value = memo(intent)
315
+ return value if value != Extensions::Undefined
316
+
317
+ value = marshal(intent)
318
+ memo!(value, intent)
319
+ value
320
+ end
321
+
322
+ def memo(intent)
323
+ return Extensions::Undefined if @memo.nil?
324
+
325
+ @memo.cached_value(intent)
326
+ end
327
+
328
+ def memo!(value, intent)
329
+ return if @memo.nil? || !frozen?
330
+
331
+ @memo.cache_value(value, intent)
332
+ end
333
+
334
+ def wrap_output(output, intent)
335
+ name_or_path = hash_key(intent)
336
+ if name_or_path.is_a? Array
337
+ *path, name = name_or_path
338
+ result = {}
339
+ Helpers::KeyMap::Mapping::Path.store(name, output, result, path)
340
+ result
341
+ else
342
+ { name_or_path => output }
343
+ end
344
+ end
345
+
346
+ def unwrap
347
+ format(Intent.instance(:backend))
348
+ end
349
+
350
+ def unwrap_or(*args, &block)
351
+ ensure_default_present!(*args, &block)
352
+
353
+ if is_definite?
354
+ begin
355
+ unwrap
356
+ rescue StandardError => _
357
+ supply_default(*args, &block)
358
+ end
359
+ else
360
+ supply_default(*args, &block)
361
+ end
362
+ end
363
+
364
+ def find_in_hash(hash, context)
365
+ Helpers::FindInHash.find_in_hash hash, hash_key(context)
366
+ end
367
+
368
+ def populate_other(other)
369
+ raise ParamsReadyError, "Not a matching param: #{other.class.name}" unless match? other
370
+ return other unless is_definite?
371
+
372
+ value = bare_value
373
+ other.populate_with(value)
374
+ other
375
+ end
376
+
377
+ def inspect_content
378
+ @value.inspect
379
+ end
380
+
381
+ freeze_variable :value
382
+
383
+ def freeze
384
+ if definition.memoize? and !frozen?
385
+ @memo = Helpers::Memo.new(definition.memoize)
386
+ end
387
+ init_for_read true
388
+ super
389
+ end
390
+
391
+ def hash
392
+ [definition, @value].hash
393
+ end
394
+
395
+ alias_method :eql?, :==
396
+
397
+ protected
398
+
399
+ def handle_indefinite_input(input, validator)
400
+ value_missing validator
401
+ if default_defined?
402
+ # if value_missing doesn't crash,
403
+ # and the parameter is optional
404
+ # it's safe to set to nil or Extensions::Undefined
405
+ @value = Extensions::Undefined
406
+ elsif optional?
407
+ # Clear possible previous state,
408
+ # will be set to default on read
409
+ @value = input
410
+ else
411
+ raise ParamsReadyError, "Unexpected state for '#{name}' in #handle_indefinite_input" if validator.ok?
412
+ end
413
+ end
414
+
415
+ def value_missing(validator = nil)
416
+ if !nil_allowed?
417
+ e = ValueMissingError.new self.name
418
+ if validator
419
+ validator.error!(e)
420
+ else
421
+ raise e
422
+ end
423
+ end
424
+ validator
425
+ end
426
+
427
+ def nil_allowed?
428
+ optional? || default_defined?
429
+ end
430
+
431
+ def bare_value
432
+ init_for_read
433
+ return @value if is_definite?
434
+
435
+ value_missing
436
+ nil
437
+ end
438
+
439
+ def ensure_default_present!(*args, &block)
440
+ raise ParamsReadyError, 'Single default value expected' if args.length > 1
441
+ raise ParamsReadyError, 'Supply either default or a block' if args.length == 0 && block.nil?
442
+ warn 'WARNING: block supersedes default value' if args.length > 0 && block
443
+ end
444
+
445
+ def supply_default(*args, &block)
446
+ if block.nil?
447
+ args[0]
448
+ else
449
+ block.call
450
+ end
451
+ end
452
+
453
+ def init_for_read(to_be_frozen = false)
454
+ return unless @value == Extensions::Undefined
455
+ return unless default_defined?
456
+
457
+ @value = definition.fetch_default(duplicate: !to_be_frozen)
458
+ end
459
+
460
+ def init_for_write
461
+ return if is_definite?
462
+
463
+ if default_defined? && !default.nil?
464
+ @value = definition.fetch_default
465
+ else
466
+ init_value
467
+ end
468
+ end
469
+
470
+ def init_value
471
+ # NOOP
472
+ end
473
+ end
474
+ end
475
+ end