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,182 @@
1
+ require_relative 'parameter'
2
+ require_relative '../value/validator'
3
+ require_relative '../value/coder'
4
+ require_relative 'definition'
5
+ require_relative '../builder'
6
+
7
+ module ParamsReady
8
+ module Parameter
9
+ class ValueParameter < Parameter
10
+ def_delegators :@definition, :coder
11
+
12
+ def marshal(intent)
13
+ return nil if is_nil?
14
+
15
+ value = bare_value
16
+ return value unless intent.marshal?(name_for_formatter)
17
+
18
+ coder.format(value, intent)
19
+ end
20
+
21
+ protected
22
+
23
+ def update_self(value)
24
+ clone = dup
25
+ clone.set_value value
26
+
27
+ if frozen?
28
+ if clone == self
29
+ return false, self
30
+ else
31
+ [true, clone.freeze]
32
+ end
33
+ else
34
+ [true, clone]
35
+ end
36
+ end
37
+
38
+ def populate_with(value)
39
+ @value = value.dup
40
+ end
41
+ end
42
+
43
+ class ValueParameterBuilder < Builder
44
+ module ValueLike
45
+ def constrain(name_or_constraint, *args, strategy: :raise, **opts, &block)
46
+ validator = Value::Validator.instance(name_or_constraint, *args, strategy: strategy, **opts, &block)
47
+ @definition.add_constraint validator
48
+ end
49
+
50
+ def coerce(&block)
51
+ @definition.set_coerce(block)
52
+ end
53
+
54
+ def format(&block)
55
+ @definition.set_format(block)
56
+ end
57
+
58
+ def type_identifier(name)
59
+ @definition.set_type_identifier(name)
60
+ end
61
+ end
62
+
63
+ include ValueLike
64
+ extend Extensions::Registry
65
+
66
+ register :value
67
+
68
+ registry :coders, as: :coder, getter: true do |name, _|
69
+ builder_class = ValueParameterBuilder[name]
70
+ builder_class.register name
71
+ end
72
+
73
+ def self.instance(name, coder_or_name = nil, altn: nil)
74
+ coder = if coder_or_name.is_a? Symbol
75
+ self.coder(coder_or_name)
76
+ elsif coder_or_name.nil?
77
+ Value::GenericCoder.new(name)
78
+ else
79
+ coder_or_name
80
+ end
81
+ new ValueParameterDefinition.new(name, coder, altn: altn)
82
+ end
83
+
84
+ def self.[](type)
85
+ builder = Class.new(self)
86
+ capitalized = type.to_s.split('_').map(&:capitalize).join
87
+ qualified = "#{self.name}::#{capitalized}Builder".freeze
88
+
89
+ builder.define_singleton_method :name do
90
+ qualified
91
+ end
92
+
93
+ builder.define_singleton_method :instance do |name, altn: nil|
94
+ superclass.instance(name, type, altn: altn)
95
+ end
96
+
97
+ builder
98
+ end
99
+
100
+ register_coder :integer, Value::IntegerCoder
101
+ register_coder :decimal, Value::DecimalCoder
102
+ register_coder :string, Value::StringCoder
103
+ register_coder :symbol, Value::SymbolCoder
104
+ register_coder :boolean, Value::BooleanCoder
105
+ register_coder :date, Value::DateCoder
106
+ register_coder :datetime, Value::DateTimeCoder
107
+ end
108
+
109
+ class ValueParameterDefinition < Definition
110
+ extend Forwardable
111
+ def_delegators :@coder, :set_coerce, :set_format, :set_type_identifier
112
+
113
+ name_for_formatter :value
114
+
115
+ def name_for_formatter
116
+ coder_name = @coder.type_identifier
117
+ return coder_name unless coder_name.nil?
118
+
119
+ super
120
+ end
121
+
122
+ parameter_class ValueParameter
123
+
124
+ module ValueLike
125
+ def duplicate_value(value)
126
+ value.dup
127
+ end
128
+
129
+ def freeze_value(value)
130
+ value.freeze
131
+ end
132
+ end
133
+
134
+ include ValueLike
135
+
136
+ attr_reader :coder
137
+
138
+ collection :constraints, :constraint do |constraint|
139
+ raise ParamsReadyError, "Can't constrain after default has been set" if default_defined?
140
+ constraint
141
+ end
142
+
143
+ def initialize(name, coder, *args, constraints: [], **options)
144
+ @coder = coder
145
+ @constraints = constraints
146
+ super name, *args, **options
147
+ end
148
+
149
+ def try_canonicalize(input, context, validator = nil)
150
+ value = coder.try_coerce input, context
151
+ return value if Extensions::Undefined.value_indefinite?(value)
152
+
153
+ value, validator = validate value, validator
154
+ if validator.nil? || validator.ok?
155
+ [value.freeze, validator]
156
+ else
157
+ [nil, validator]
158
+ end
159
+ end
160
+
161
+ def ensure_canonical(value)
162
+ coerced = coder.try_coerce value, Format.instance(:backend)
163
+ if coder.strict_default? && value != coerced
164
+ raise ParamsReadyError, "input '#{value}' (#{value.class.name}) coerced to '#{coerced}' (#{coerced.class.name})"
165
+ end
166
+ validate coerced
167
+ coerced
168
+ end
169
+
170
+ def validate(value, validator = nil)
171
+ constraints.reduce([value, validator]) do |(value, validator), constraint|
172
+ constraint.validate value, validator
173
+ end
174
+ end
175
+
176
+ def finish
177
+ @coder.finish if @coder.respond_to?(:finish)
178
+ super
179
+ end
180
+ end
181
+ end
182
+ end
@@ -0,0 +1,14 @@
1
+ require_relative 'helpers/parameter_definer_class_methods'
2
+ require_relative 'helpers/parameter_storage_class_methods'
3
+ require_relative 'helpers/relation_builder_wrapper'
4
+
5
+ module ParamsReady
6
+ module ParameterDefiner
7
+ def self.included(base)
8
+ base.extend(Helpers::ParameterStorageClassMethods)
9
+ base.extend(Helpers::ParameterDefinerClassMethods)
10
+ end
11
+ end
12
+ end
13
+
14
+
@@ -0,0 +1,43 @@
1
+ require_relative 'result'
2
+ require_relative 'helpers/usage_rule'
3
+ require_relative 'helpers/options'
4
+ require_relative 'helpers/parameter_user_class_methods'
5
+ require_relative 'helpers/parameter_storage_class_methods'
6
+ require_relative 'parameter/state'
7
+
8
+ module ParamsReady
9
+ module ParameterUser
10
+ def self.included(base)
11
+ base.extend(Helpers::ParameterStorageClassMethods)
12
+ base.extend(Helpers::ParameterUserClassMethods)
13
+ end
14
+
15
+ protected
16
+
17
+ def parameter_definition(key)
18
+ self.class.parameter_definition key
19
+ end
20
+
21
+ def relation_definition(key)
22
+ self.class.relation_definition key
23
+ end
24
+
25
+ def populate_state_for(method, params, context = Format.instance(:frontend), validator = nil)
26
+ definition = create_state_for method
27
+ result, state = definition.from_input(params || {}, context: context, validator: validator || Result.new(:params_ready))
28
+ [result, state]
29
+ end
30
+
31
+ def create_state_for(method)
32
+ builder = Parameter::StateBuilder.instance
33
+ options = self.class.params_ready_storage
34
+ options.parameter_rules do |rule|
35
+ builder.add rule.parameter_definition if rule.valid_for(method)
36
+ end
37
+ options.relation_rules do |rule|
38
+ builder.relation rule.parameter_definition if rule.valid_for(method)
39
+ end
40
+ builder.build
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,68 @@
1
+ require_relative '../parameter/hash_parameter'
2
+ require_relative '../parameter/array_parameter'
3
+ require_relative 'grouping'
4
+ require_relative 'predicate'
5
+
6
+ module ParamsReady
7
+ module Query
8
+ class ArrayGrouping < Parameter::HashParameter
9
+ include Parameter::GroupingLike
10
+
11
+ def predicates
12
+ self[:array].to_a
13
+ end
14
+
15
+ def operator
16
+ self[:operator].unwrap
17
+ end
18
+
19
+ def context_for_predicates(restriction)
20
+ restriction.for_children(self)
21
+ end
22
+
23
+ def to_query(arel_table, context: Restriction.blanket_permission)
24
+ array = self[:array]
25
+
26
+ context = array.intent_for_children(context)
27
+ super arel_table, context: context
28
+ end
29
+ end
30
+
31
+ class ArrayGroupingBuilder < Builder
32
+ include Parameter::AbstractHashParameterBuilder::HashLike
33
+ PredicateRegistry.register_predicate :array_grouping_predicate, self
34
+
35
+ def prototype(type_name, name = :proto, *arr, **opts, &block)
36
+ prototype = PredicateRegistry.predicate(type_name).instance(name, *arr, **opts)
37
+ prototype.instance_eval(&block) unless block.nil?
38
+ @definition.set_prototype prototype.build
39
+ end
40
+
41
+ def self.instance(name, altn: nil)
42
+ new ArrayGroupingDefinition.new(name, altn: altn)
43
+ end
44
+
45
+ def operator(&block)
46
+ definition = Builder.define_grouping_operator(:operator, altn: :op, &block)
47
+ add definition
48
+ end
49
+ end
50
+
51
+ class ArrayGroupingDefinition < Parameter::HashParameterDefinition
52
+ def initialize(*args, **opts)
53
+ super
54
+ end
55
+
56
+ late_init :prototype, getter: false, obligatory: false do |prototype|
57
+ array = Builder.define_array(:array, altn: :a) do
58
+ prototype(prototype)
59
+ default []
60
+ end
61
+ add_child array
62
+ Extensions::Undefined
63
+ end
64
+
65
+ parameter_class ArrayGrouping
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,102 @@
1
+ require_relative 'predicate'
2
+ require_relative '../parameter/parameter'
3
+ require_relative '../parameter/definition'
4
+
5
+ module ParamsReady
6
+ module Query
7
+ class CustomPredicate < Parameter::AbstractParameter
8
+ include Predicate::DelegatingPredicate
9
+ include Predicate::HavingChildren
10
+
11
+ def initialize(definition)
12
+ super definition
13
+ @data = definition.type.create
14
+ end
15
+
16
+ def eligible_for_query?(arel_table, context)
17
+ return false unless context.permitted? self
18
+ eligibility_test = definition.eligibility_test
19
+ return true if eligibility_test.nil?
20
+
21
+ instance_exec(arel_table, context, &eligibility_test)
22
+ end
23
+
24
+ def to_query(arel_table, context: Restriction.blanket_permission)
25
+ return unless eligible_for_query?(arel_table, context)
26
+
27
+ to_query = definition.to_query
28
+ raise ParamsReadyError, "Method 'to_query' unimplemented in '#{name}'" if to_query.nil?
29
+ result = instance_exec(arel_table, context, &to_query)
30
+
31
+ case result
32
+ when Arel::Nodes::Node, nil
33
+ result
34
+ else
35
+ literal = Arel::Nodes::SqlLiteral.new(result)
36
+ grouping = Arel::Nodes::Grouping.new(literal)
37
+ grouping
38
+ end
39
+ end
40
+
41
+ def test(record)
42
+ test = definition.test
43
+ raise ParamsReadyError, "Method 'test' unimplemented in '#{name}'" if test.nil?
44
+ self.instance_exec(record, &test)
45
+ end
46
+ end
47
+
48
+ class CustomPredicateBuilder < Builder
49
+ PredicateRegistry.register_predicate :custom_predicate, self
50
+ include AbstractPredicateBuilder::HavingType
51
+
52
+ def initialize(name, altn: nil)
53
+ super CustomPredicateDefinition.new(name, altn: altn)
54
+ end
55
+
56
+ def type_builder_instance(type_name, name, *args, altn:, **opts, &block)
57
+ builder_class = Builder.builder(type_name)
58
+ builder_class.instance(name, *args, altn: altn, **opts)
59
+ end
60
+
61
+ def data_object_handles
62
+ [@definition.name, @definition.altn]
63
+ end
64
+
65
+ def to_query(&proc)
66
+ @definition.set_to_query(proc)
67
+ end
68
+
69
+ def eligible(&proc)
70
+ @definition.set_eligibility_test(proc)
71
+ end
72
+
73
+ def test(&proc)
74
+ @definition.set_test(proc)
75
+ end
76
+ end
77
+
78
+ class CustomPredicateDefinition < Parameter::AbstractDefinition
79
+ late_init :type, obligatory: true, freeze: false
80
+ freeze_variable :type
81
+ late_init :to_query, obligatory: false
82
+ late_init :eligibility_test, obligatory: false
83
+ late_init :test, obligatory: false
84
+
85
+ include Parameter::DelegatingDefinition[:type]
86
+
87
+ def initialize(*args, type: nil, to_query: nil, test: nil, **opts)
88
+ @type = type
89
+ @to_query = to_query
90
+ @test = test
91
+ super *args, **opts
92
+ end
93
+
94
+ def finish
95
+ @type.finish
96
+ super
97
+ end
98
+
99
+ parameter_class CustomPredicate
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,103 @@
1
+ require_relative 'predicate'
2
+ require_relative 'structured_grouping'
3
+ require_relative 'join_clause'
4
+
5
+ module ParamsReady
6
+ module Query
7
+ class ExistsPredicate < StructuredGrouping
8
+ include Predicate::HavingAssociations
9
+
10
+ def to_query(query_table, context: Restriction.blanket_permission)
11
+ query_table = definition.outer_table || query_table
12
+
13
+ subquery_table = self.definition.arel_table
14
+ raise ParamsReadyError, "Arel table for '#{name}' not set" if subquery_table.nil?
15
+
16
+ predicates = predicate_group(subquery_table, context: context)
17
+
18
+ join_clause = self.related query_table, subquery_table, context
19
+ subquery = GroupingOperator.instance(:and).connect(predicates, join_clause)
20
+ select = subquery_table.where(subquery)
21
+ query = select.take(1).project(Arel.star).exists
22
+ if definition.has_child?(:existence) && self[:existence].unwrap == :none
23
+ query.not
24
+ else
25
+ query
26
+ end
27
+ end
28
+
29
+ def related(query_table, subquery_table, context)
30
+ return nil if definition.related.nil?
31
+
32
+ grouping = definition.related.to_arel(query_table, subquery_table, context, self)
33
+ subquery_table.grouping(grouping)
34
+ end
35
+
36
+ def test(record)
37
+ return nil unless is_definite?
38
+
39
+ collection = dig(record, definition.path_to_collection)
40
+
41
+ result = if collection.nil?
42
+ false
43
+ else
44
+ collection.any? do |item|
45
+ super item
46
+ end
47
+ end
48
+
49
+ if definition.has_child?(:existence) && self[:existence].unwrap == :none
50
+ !result
51
+ else
52
+ result
53
+ end
54
+ end
55
+ end
56
+
57
+ class ExistsPredicateBuilder < Builder
58
+ PredicateRegistry.register_predicate :exists_predicate, self
59
+
60
+ include GroupingLike
61
+ include Parameter::AbstractHashParameterBuilder::HashLike
62
+ include HavingArelTable
63
+
64
+ def self.instance(name, altn: nil, coll: nil)
65
+ new ExistsPredicateDefinition.new(name, altn: altn, path_to_collection: Array(coll))
66
+ end
67
+
68
+ def related(on: nil, eq: nil, &block)
69
+ join_statement = JoinStatement.new(on: on, eq: eq, &block)
70
+ @definition.set_related(join_statement)
71
+ end
72
+
73
+ def outer_table(arel_table)
74
+ @definition.set_outer_table arel_table
75
+ end
76
+
77
+ def existence(&block)
78
+ definition = Builder.define_symbol(:existence, altn: :ex) do
79
+ constrain :enum, [:some, :none]
80
+ include &block
81
+ end
82
+ add definition
83
+ end
84
+ end
85
+
86
+ class ExistsPredicateDefinition < StructuredGroupingDefinition
87
+ late_init :outer_table, obligatory: false, freeze: false
88
+ late_init :arel_table, obligatory: false, freeze: false
89
+ late_init :related, obligatory: false, freeze: true
90
+
91
+ def initialize(*args, path_to_collection: nil, **opts)
92
+ @path_to_collection = path_to_collection
93
+ super *args, **opts
94
+ end
95
+
96
+ def path_to_collection
97
+ @path_to_collection || [@name]
98
+ end
99
+
100
+ parameter_class ExistsPredicate
101
+ end
102
+ end
103
+ end