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,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,181 @@
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
+ class Coder
10
+ extend Extensions::ClassReaderWriter
11
+
12
+ class_reader_writer :type_identifier
13
+ type_identifier :value
14
+
15
+ def self.value_class_name
16
+ last = self.name.split("::").last
17
+ last.remove('Coder')
18
+ end
19
+
20
+ def self.try_coerce(input, context)
21
+ coerce input, context
22
+ rescue => _error
23
+ raise CoercionError.new(input, value_class_name)
24
+ end
25
+
26
+ def self.strict_default?
27
+ true
28
+ end
29
+ end
30
+
31
+ class GenericCoder
32
+ extend Extensions::LateInit
33
+ extend Extensions::Finalizer
34
+ include Extensions::Finalizer::InstanceMethods
35
+
36
+ def initialize(name)
37
+ @name = name
38
+ @coerce = nil
39
+ @format = nil
40
+ @type_identifier = nil
41
+ end
42
+
43
+ def strict_default?; true; end
44
+
45
+ late_init(:coerce, getter: false)
46
+ late_init(:format, getter: false)
47
+ late_init(:type_identifier, obligatory: false)
48
+
49
+ def value_class_name
50
+ @name
51
+ end
52
+
53
+ def try_coerce(input, context)
54
+ @coerce[input, context]
55
+ rescue => _error
56
+ raise CoercionError.new(input, @name)
57
+ end
58
+
59
+ def format(value, format)
60
+ @format[value, format]
61
+ end
62
+
63
+ def finish
64
+ super
65
+ freeze
66
+ end
67
+ end
68
+
69
+ class IntegerCoder < Coder
70
+ type_identifier :number
71
+
72
+ def self.coerce(input, _)
73
+ return nil if input.nil? || input == ''
74
+ Integer(input)
75
+ end
76
+
77
+ def self.format(value, format)
78
+ value.to_s
79
+ end
80
+ end
81
+
82
+ class DecimalCoder < Coder
83
+ type_identifier :number
84
+
85
+ def self.coerce(input, _)
86
+ return nil if input.nil? || input == ''
87
+ BigDecimal(input)
88
+ end
89
+
90
+ def self.format(value, format)
91
+ value.to_s('F')
92
+ end
93
+ end
94
+
95
+ class BooleanCoder < Coder
96
+ type_identifier :boolean
97
+
98
+ def self.coerce(input, _)
99
+ return nil if input.nil? || input == ''
100
+ return input if input.is_a?(TrueClass) || input.is_a?(FalseClass)
101
+ str = input.to_s
102
+ case str
103
+ when 'true', 'TRUE', 't', 'T', '1'
104
+ true
105
+ when 'false', 'FALSE', 'f', 'F', '0'
106
+ false
107
+ else
108
+ raise
109
+ end
110
+ end
111
+
112
+ def self.format(value, format)
113
+ value.to_s
114
+ end
115
+ end
116
+
117
+ class StringCoder < Coder
118
+ def self.coerce(input, _)
119
+ input.to_s
120
+ end
121
+
122
+ def self.format(value, _)
123
+ value
124
+ end
125
+ end
126
+
127
+ class SymbolCoder < Coder
128
+ type_identifier :symbol
129
+
130
+ def self.coerce(input, _)
131
+ input.to_sym
132
+ end
133
+
134
+ def self.format(value, format)
135
+ value.to_s
136
+ end
137
+ end
138
+
139
+ class DateCoder < Coder
140
+ type_identifier :date
141
+
142
+ def self.coerce(input, _)
143
+ return nil if input.nil? || input == ''
144
+ if input.is_a?(Numeric)
145
+ Time.at(input).to_date
146
+ elsif input.is_a?(String)
147
+ Date.parse(input)
148
+ elsif input.respond_to?(:to_date)
149
+ input.to_date
150
+ else
151
+ raise ParamsReadyError, "Unimplemented for type #{input.class.name}"
152
+ end
153
+ end
154
+
155
+ def self.format(value, format)
156
+ value.to_s
157
+ end
158
+ end
159
+
160
+ class DateTimeCoder < Coder
161
+ type_identifier :date
162
+
163
+ def self.coerce(input, _)
164
+ return nil if input.nil? || input == ''
165
+ if input.is_a?(Numeric)
166
+ Time.at(input).to_datetime
167
+ elsif input.is_a?(String)
168
+ DateTime.parse(input)
169
+ elsif input.respond_to?(:to_datetime)
170
+ input.to_datetime
171
+ else
172
+ raise ParamsReadyError, "Unimplemented for type #{input.class.name}"
173
+ end
174
+ end
175
+
176
+ def self.format(value, format)
177
+ value.to_s
178
+ end
179
+ end
180
+ end
181
+ end
@@ -0,0 +1,198 @@
1
+ require 'set'
2
+ require_relative '../extensions/undefined'
3
+ require_relative '../extensions/registry'
4
+ require_relative '../error'
5
+
6
+ module ParamsReady
7
+ module Value
8
+
9
+ class Constraint
10
+ class Error < ParamsReadyError; end
11
+
12
+ extend Extensions::Registry
13
+ registry :constraint_types, as: :constraint_type, getter: true
14
+
15
+ def self.register(name)
16
+ Constraint.register_constraint_type(name, self)
17
+ end
18
+
19
+ attr_reader :condition
20
+
21
+ def initialize(cond)
22
+ @condition = cond.freeze
23
+ freeze
24
+ end
25
+
26
+ def clamp?; false; end
27
+
28
+ def self.build(cond, *args, **opts, &block)
29
+ if block.nil?
30
+ new cond, *args, **opts
31
+ else
32
+ new cond, block, *args, **opts
33
+ end
34
+ end
35
+
36
+ def self.instance(cond, *args, **opts)
37
+ case cond
38
+ when Range
39
+ RangeConstraint.new(cond, *args, **opts)
40
+ when Array, Set
41
+ EnumConstraint.new(cond, *args, **opts)
42
+ else
43
+ raise ParamsReadyError, "Unknown constraint type: " + cond.class.name
44
+ end
45
+ end
46
+
47
+ def valid?(input)
48
+ raise ParamsReadyError, 'This is an abstract class'
49
+ end
50
+
51
+ def error_message
52
+ "didn't pass validation"
53
+ end
54
+ end
55
+
56
+ class RangeConstraint < Constraint
57
+ register :range
58
+
59
+ def initialize(cond, *args, **opts)
60
+ raise ParamsReadyError, "Expected Range, got: " + cond.class.name unless cond.is_a?(Range)
61
+ super cond, *args, **opts
62
+ end
63
+
64
+ def valid?(input)
65
+ @condition.include?(input)
66
+ end
67
+
68
+ def error_message
69
+ 'not in range'
70
+ end
71
+
72
+ def clamp(value)
73
+ if value < @condition.min
74
+ @condition.min
75
+ elsif value > @condition.max
76
+ @condition.max
77
+ else
78
+ value
79
+ end
80
+ end
81
+
82
+ def clamp?
83
+ return false if @condition.min.nil? || @condition.max.nil?
84
+
85
+ true
86
+ end
87
+ end
88
+
89
+ class EnumConstraint < Constraint
90
+ register :enum
91
+
92
+ def initialize(cond, *args, **opts)
93
+ raise ParamsReadyError, "Expected Set or Array, got: " + cond.class.name unless
94
+ cond.is_a?(Set) ||
95
+ cond.is_a?(Array)
96
+ super cond, *args, **opts
97
+ end
98
+
99
+ def valid?(input)
100
+ if input.is_a?(String)
101
+ @condition.include?(input) || @condition.include?(input.to_sym)
102
+ else
103
+ @condition.include?(input)
104
+ end
105
+ end
106
+
107
+ def error_message
108
+ 'not in enum'
109
+ end
110
+ end
111
+
112
+ class OperatorConstraint < Constraint
113
+ register :operator
114
+
115
+ OPERATORS = [:=~, :<, :<=, :==, :>=, :>].to_set.freeze
116
+
117
+ def initialize(operator, value, *args, **opts)
118
+ unless OPERATORS.member? operator
119
+ raise ParamsReadyError, "Unsupported operator: #{operator}"
120
+ end
121
+ cond = Condition.instance(operator, value)
122
+ super(cond, *args, **opts)
123
+ end
124
+
125
+ def clamp(value)
126
+ return value if valid?(value)
127
+
128
+ @condition.clamp(value)
129
+ end
130
+
131
+ def clamp?
132
+ @condition.clamp?
133
+ end
134
+
135
+ def valid?(input)
136
+ @condition.true?(input)
137
+ end
138
+
139
+ def error_message
140
+ @condition.error_message
141
+ end
142
+
143
+ module ClampingCondition
144
+ CLAMPING_OPERATORS = %i(<= == >=).to_set.freeze
145
+ def clamp(_)
146
+ case @operator
147
+ when :<=, :>=, :==
148
+ get_value
149
+ else
150
+ raise "Unexpected operator: #{@operator}"
151
+ end
152
+ end
153
+
154
+ def clamp?
155
+ CLAMPING_OPERATORS.member? @operator
156
+ end
157
+ end
158
+
159
+ class Condition
160
+ include ClampingCondition
161
+
162
+ def initialize(operator, value)
163
+ @operator = operator
164
+ @value = value
165
+ end
166
+
167
+ def true?(input)
168
+ input.send(@operator, get_value)
169
+ end
170
+
171
+ def error_message
172
+ "not #{@operator} #{get_value}"
173
+ end
174
+
175
+ def self.instance(operator, value)
176
+ case value
177
+ when Method, Proc
178
+ DynamicCondition.new operator, value
179
+ else
180
+ StaticCondition.new operator, value
181
+ end
182
+ end
183
+ end
184
+
185
+ class StaticCondition < Condition
186
+ def get_value
187
+ @value
188
+ end
189
+ end
190
+
191
+ class DynamicCondition < Condition
192
+ def get_value
193
+ @value.call
194
+ end
195
+ end
196
+ end
197
+ end
198
+ end