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,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
@@ -0,0 +1,109 @@
1
+ require_relative 'error'
2
+
3
+ module ParamsReady
4
+ class AbstractReporter
5
+ attr_reader :name
6
+
7
+ def initialize(name)
8
+ @name = name.to_s.freeze
9
+ end
10
+
11
+ def error!(err)
12
+ report_error(nil, err)
13
+ end
14
+
15
+ def full_path(path)
16
+ return [name] if path.nil? || path.empty?
17
+ [name, *path]
18
+ end
19
+
20
+ def for_child(name)
21
+ Reporter.new name, self
22
+ end
23
+ end
24
+
25
+ class Result < AbstractReporter
26
+ class Error < ParamsReadyError; end
27
+
28
+ def initialize(name)
29
+ super
30
+ @errors = []
31
+ @children = {}
32
+ end
33
+
34
+ def full_scope(scope)
35
+ return name if scope.empty?
36
+
37
+ "#{scope}.#{name}"
38
+ end
39
+
40
+ def errors(scope = '')
41
+ scope = full_scope(scope)
42
+ proper = @errors.empty? ? {} : { scope => @errors }
43
+ @children.values.reduce(proper) do |result, child|
44
+ result.merge(child.errors(scope))
45
+ end
46
+ end
47
+
48
+ def report_error(path, err)
49
+ raise ParamsReadyError, "Is not Error: #{err}" unless err.is_a? StandardError
50
+
51
+ name, *path = path
52
+ if name.nil?
53
+ @errors << err
54
+ else
55
+ @children[name] ||= Result.new(name)
56
+ @children[name].report_error(path, err)
57
+ end
58
+ end
59
+
60
+ def ok?
61
+ return false unless @errors.empty?
62
+
63
+ @children.values.all? do |child|
64
+ child.ok?
65
+ end
66
+ end
67
+
68
+ def child_ok?(path)
69
+ name, *path = path
70
+ return ok? if name.nil?
71
+ return true unless @children.key? name
72
+
73
+ @children[name].child_ok?(path)
74
+ end
75
+
76
+ def error
77
+ return nil if ok?
78
+
79
+ Result::Error.new(error_messages(' -- '))
80
+ end
81
+
82
+ def error_messages(separator = "\n")
83
+ errors.flat_map do |scope, errors|
84
+ ["errors for #{scope}"] + errors.map { |err| err.message }
85
+ end.join(separator)
86
+ end
87
+ end
88
+
89
+ class Reporter < AbstractReporter
90
+ attr_reader :name
91
+
92
+ def initialize(name, parent)
93
+ super name
94
+ @parent = parent
95
+ end
96
+
97
+ def ok?
98
+ child_ok?(nil)
99
+ end
100
+
101
+ def child_ok?(path)
102
+ @parent.child_ok?(full_path(path))
103
+ end
104
+
105
+ def report_error(path, err)
106
+ @parent.report_error(full_path(path), err)
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,210 @@
1
+ require 'date'
2
+ require_relative '../error'
3
+ require_relative '../extensions/late_init'
4
+ require_relative '../extensions/finalizer'
5
+ require_relative '../extensions/class_reader_writer'
6
+
7
+ module ParamsReady
8
+ module Value
9
+ module Coercion
10
+ def try_coerce(input, context)
11
+ coerce input, context
12
+ rescue => _error
13
+ raise CoercionError.new(input, value_class_name)
14
+ end
15
+
16
+ def strict_default?
17
+ true
18
+ end
19
+ end
20
+
21
+ class AbstractCoder
22
+ extend Extensions::ClassReaderWriter
23
+
24
+ def self.value_class_name
25
+ last = self.name.split("::").last
26
+ last.remove('Coder')
27
+ end
28
+
29
+ class_reader_writer :type_identifier
30
+ type_identifier :value
31
+ end
32
+
33
+ class Coder < AbstractCoder
34
+ extend Coercion
35
+
36
+ def self.instance(**opts)
37
+ raise ParamsReadyError, "No options expected, got: #{opts}" unless opts.empty?
38
+
39
+ self
40
+ end
41
+
42
+ class Instantiable < AbstractCoder
43
+ include Coercion
44
+
45
+ def self.instance(**opts)
46
+ new **opts
47
+ end
48
+
49
+ def type_identifier
50
+ self.class.type_identifier
51
+ end
52
+
53
+ def strict_default?
54
+ self.class.strict_default?
55
+ end
56
+
57
+ def value_class_name
58
+ self.class.value_class_name
59
+ end
60
+ end
61
+ end
62
+
63
+ class GenericCoder
64
+ extend Extensions::LateInit
65
+ extend Extensions::Finalizer
66
+ include Extensions::Finalizer::InstanceMethods
67
+ include Coercion
68
+
69
+ def initialize(name)
70
+ @name = name
71
+ @coerce = nil
72
+ @format = nil
73
+ @type_identifier = nil
74
+ end
75
+
76
+ late_init(:coerce, getter: false)
77
+ late_init(:format, getter: false)
78
+ late_init(:type_identifier, obligatory: false)
79
+
80
+ def value_class_name
81
+ @name
82
+ end
83
+
84
+ def coerce(input, context)
85
+ @coerce[input, context]
86
+ end
87
+
88
+ def format(value, format)
89
+ @format[value, format]
90
+ end
91
+
92
+ def finish
93
+ super
94
+ freeze
95
+ end
96
+ end
97
+
98
+ class IntegerCoder < Coder
99
+ type_identifier :number
100
+
101
+ def self.coerce(input, _)
102
+ return nil if input.nil? || input == ''
103
+ Integer(input)
104
+ end
105
+
106
+ def self.format(value, format)
107
+ value.to_s
108
+ end
109
+ end
110
+
111
+ class DecimalCoder < Coder
112
+ type_identifier :number
113
+
114
+ def self.coerce(input, _)
115
+ return nil if input.nil? || input == ''
116
+ BigDecimal(input)
117
+ end
118
+
119
+ def self.format(value, format)
120
+ value.to_s('F')
121
+ end
122
+ end
123
+
124
+ class BooleanCoder < Coder
125
+ type_identifier :boolean
126
+
127
+ def self.coerce(input, _)
128
+ return nil if input.nil? || input == ''
129
+ return input if input.is_a?(TrueClass) || input.is_a?(FalseClass)
130
+ str = input.to_s
131
+ case str
132
+ when 'true', 'TRUE', 't', 'T', '1'
133
+ true
134
+ when 'false', 'FALSE', 'f', 'F', '0'
135
+ false
136
+ else
137
+ raise
138
+ end
139
+ end
140
+
141
+ def self.format(value, format)
142
+ value.to_s
143
+ end
144
+ end
145
+
146
+ class StringCoder < Coder
147
+ def self.coerce(input, _)
148
+ input.to_s
149
+ end
150
+
151
+ def self.format(value, _)
152
+ value
153
+ end
154
+ end
155
+
156
+ class SymbolCoder < Coder
157
+ type_identifier :symbol
158
+
159
+ def self.coerce(input, _)
160
+ input.to_sym
161
+ end
162
+
163
+ def self.format(value, format)
164
+ value.to_s
165
+ end
166
+ end
167
+
168
+ class DateCoder < Coder
169
+ type_identifier :date
170
+
171
+ def self.coerce(input, _)
172
+ return nil if input.nil? || input == ''
173
+ if input.is_a?(Numeric)
174
+ Time.at(input).to_date
175
+ elsif input.is_a?(String)
176
+ Date.parse(input)
177
+ elsif input.respond_to?(:to_date)
178
+ input.to_date
179
+ else
180
+ raise ParamsReadyError, "Unimplemented for type #{input.class.name}"
181
+ end
182
+ end
183
+
184
+ def self.format(value, format)
185
+ value.to_s
186
+ end
187
+ end
188
+
189
+ class DateTimeCoder < Coder
190
+ type_identifier :date
191
+
192
+ def self.coerce(input, _)
193
+ return nil if input.nil? || input == ''
194
+ if input.is_a?(Numeric)
195
+ Time.at(input).to_datetime
196
+ elsif input.is_a?(String)
197
+ DateTime.parse(input)
198
+ elsif input.respond_to?(:to_datetime)
199
+ input.to_datetime
200
+ else
201
+ raise ParamsReadyError, "Unimplemented for type #{input.class.name}"
202
+ end
203
+ end
204
+
205
+ def self.format(value, format)
206
+ value.to_s
207
+ end
208
+ end
209
+ end
210
+ end