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,77 @@
1
+ require 'forwardable'
2
+
3
+ require_relative '../parameter/value_parameter'
4
+ require_relative '../parameter/array_parameter'
5
+ require_relative 'predicate'
6
+ require_relative 'predicate_operator'
7
+
8
+ module ParamsReady
9
+ module Query
10
+ class FixedOperatorPredicate < Parameter::AbstractParameter
11
+ include Predicate::DelegatingPredicate
12
+ include Predicate::HavingAttribute
13
+
14
+ def initialize(definition, **options)
15
+ super definition
16
+ @data = definition.type.create
17
+ end
18
+
19
+ def build_query(select_expression, context: nil)
20
+ definition.operator.to_query(select_expression, @data.unwrap)
21
+ end
22
+
23
+ def perform_test(record, attribute_name)
24
+ definition.operator.test(record, attribute_name, @data.unwrap)
25
+ end
26
+
27
+ def inspect_content
28
+ op = definition.operator.name
29
+ "#{definition.attribute_name} #{op} #{@data.inspect}"
30
+ end
31
+ end
32
+
33
+ class FixedOperatorPredicateBuilder < AbstractPredicateBuilder
34
+ PredicateRegistry.register_predicate :fixed_operator_predicate, self
35
+ include HavingType
36
+ include HavingValue
37
+ include HavingAttribute
38
+
39
+ def self.instance(name, altn: nil, attr: nil)
40
+ new FixedOperatorPredicateDefinition.new name, altn: altn, attribute_name: attr
41
+ end
42
+
43
+ def data_object_handles
44
+ [@definition.name, @definition.altn]
45
+ end
46
+
47
+ def operator(name)
48
+ operator = PredicateRegistry.operator name, Format.instance(:backend)
49
+ @definition.set_operator operator
50
+ end
51
+ end
52
+
53
+ class FixedOperatorPredicateDefinition < AbstractPredicateDefinition
54
+ extend Forwardable
55
+ include HavingAttribute
56
+ include Parameter::DelegatingDefinition[:type]
57
+
58
+ late_init :operator, obligatory: true
59
+ late_init :type, obligatory: true, freeze: false
60
+
61
+ def initialize(*args, attribute_name: nil, type: nil, operator: nil, **opts)
62
+ @attribute_name = attribute_name
63
+ @type = type
64
+ @operator = operator
65
+ @associations = []
66
+ super *args, **opts
67
+ end
68
+
69
+ def finish
70
+ @type.finish
71
+ super
72
+ end
73
+
74
+ parameter_class FixedOperatorPredicate
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,177 @@
1
+ require_relative 'join_clause'
2
+ require_relative '../parameter/parameter'
3
+ require_relative '../parameter/value_parameter'
4
+
5
+ module ParamsReady
6
+ class Builder
7
+ module GroupingLike
8
+ def predicate_builder(name)
9
+ symbol = name.to_sym
10
+ return nil unless Query::PredicateRegistry.has_predicate?(symbol)
11
+
12
+ Query::PredicateRegistry.predicate(symbol)
13
+ end
14
+
15
+ def method_missing(name, *args, **opts, &proc)
16
+ builder_class = predicate_builder(name)
17
+ if builder_class
18
+ builder = builder_class.instance *args, **opts
19
+ build_predicate builder, &proc
20
+ else
21
+ super
22
+ end
23
+ end
24
+
25
+ def respond_to_missing?(name, include_private = false)
26
+ return true unless predicate_builder(name).nil?
27
+
28
+ super
29
+ end
30
+
31
+ def build_predicate(builder, &proc)
32
+ builder.instance_eval(&proc)
33
+ definition = builder.build
34
+ add_predicate definition
35
+ end
36
+
37
+ def add_predicate(name_or_definition, *args, **opts, &block)
38
+ if name_or_definition.is_a? Parameter::AbstractDefinition
39
+ @definition.add_predicate name_or_definition
40
+ add name_or_definition
41
+ else
42
+ builder = predicate_builder(name_or_definition).instance *args, **opts
43
+ build_predicate builder, &block
44
+ end
45
+ end
46
+
47
+ def operator(&block)
48
+ definition = Builder.define_grouping_operator(:operator, altn: :op, &block)
49
+ add definition
50
+ end
51
+ end
52
+ end
53
+
54
+ module Parameter
55
+ module GroupingLike
56
+ def predicate_group(arel_table, context: Restriction.blanket_permission)
57
+ subqueries = predicates.reduce(nil) do |acc, predicate|
58
+ query = predicate.to_query_if_eligible(arel_table, context: context)
59
+ # This duplicates the operator logic
60
+ # but we want operator to be optional
61
+ # for single predicate groupings
62
+ next query if acc.nil?
63
+
64
+ operator.connect(acc, query)
65
+ end
66
+ return nil if subqueries.nil?
67
+ arel_table.grouping(subqueries)
68
+ end
69
+
70
+ def eligible_for_query?(_table, context)
71
+ return false unless context.permitted? self
72
+
73
+ is_definite?
74
+ end
75
+
76
+ def to_query_if_eligible(arel_table, context:)
77
+ return nil unless eligible_for_query?(arel_table, context)
78
+
79
+ context = context_for_predicates(context)
80
+ to_query(arel_table, context: context)
81
+ end
82
+
83
+ def to_query(arel_table, context: Restriction.blanket_permission)
84
+ self.predicate_group(arel_table, context: context)
85
+ end
86
+
87
+ def test(record)
88
+ return nil unless is_definite?
89
+
90
+ predicates = self.predicates
91
+ return nil if predicates.empty?
92
+
93
+ operator.test(record, predicates)
94
+ end
95
+ end
96
+ end
97
+
98
+ module Query
99
+ class GroupingOperatorCoder < Value::SymbolCoder
100
+ def self.coerce(value, _)
101
+ return value if value.is_a? GroupingOperator
102
+
103
+ symbol = super
104
+ GroupingOperator.instance(symbol)
105
+ end
106
+
107
+ def self.format(value, _)
108
+ value.type.to_s
109
+ end
110
+
111
+ def self.strict_default?
112
+ false
113
+ end
114
+ end
115
+
116
+ Parameter::ValueParameterBuilder.register_coder :grouping_operator, GroupingOperatorCoder
117
+
118
+ class GroupingOperator
119
+ attr_reader :type
120
+
121
+ def self.instance(type)
122
+ raise ParamsReadyError, "Unimplemented operator: #{type}" unless @instances.key? type
123
+ @instances[type]
124
+ end
125
+
126
+ def arel_method
127
+ case type
128
+ when :and, :or then type
129
+ else
130
+ raise ParamsReadyError, "Unimplemented operator: #{type}"
131
+ end
132
+ end
133
+
134
+ def test_method
135
+ case type
136
+ when :and then :all?
137
+ when :or then :any?
138
+ else
139
+ raise ParamsReadyError, "Unimplemented operator: #{type}"
140
+ end
141
+ end
142
+
143
+ def connect(a, b)
144
+ return b if a.nil?
145
+ return a if b.nil?
146
+ a.send arel_method, b
147
+ end
148
+
149
+ def ==(other)
150
+ return false unless other.class <= GroupingOperator
151
+ type == other.type
152
+ end
153
+
154
+ def test(record, predicates)
155
+ definite = predicates.map do |predicate|
156
+ predicate.test(record)
157
+ end.compact
158
+
159
+ return nil if definite.empty?
160
+
161
+ definite.send(test_method)
162
+ end
163
+
164
+ protected
165
+
166
+ def initialize(type)
167
+ @type = type
168
+ end
169
+
170
+ @instances = {}
171
+ @instances[:and] = GroupingOperator.new(:and)
172
+ @instances[:or] = GroupingOperator.new(:or)
173
+
174
+ private_class_method :new
175
+ end
176
+ end
177
+ end
@@ -0,0 +1,87 @@
1
+ require_relative '../helpers/arel_builder'
2
+
3
+ module ParamsReady
4
+ module Query
5
+ class Join
6
+ attr_reader :arel_table, :statement, :type
7
+
8
+ def initialize(table, type, &block)
9
+ @arel_table = table
10
+ @type = arel_type(type)
11
+ @statement = JoinStatement.new(&block)
12
+ end
13
+
14
+ def arel_type(type)
15
+ case type
16
+ when :inner then Arel::Nodes::InnerJoin
17
+ when :outer then Arel::Nodes::OuterJoin
18
+ else raise ParamsReadyError, "Unimplemented join type '#{type}'"
19
+ end
20
+ end
21
+
22
+ def to_arel(base_table, context, parameter)
23
+ join_statement = @statement.to_arel(base_table, @arel_table, context, parameter)
24
+ base_table.join(@arel_table, @type).on(join_statement)
25
+ end
26
+ end
27
+
28
+ class JoinStatement
29
+ def initialize(on: nil, eq: nil, &block)
30
+ @conditions = []
31
+ if on
32
+ condition = on(on)
33
+ if eq
34
+ condition.eq(eq)
35
+ end
36
+ else
37
+ raise ParamsReadyError('Parameter :eq unexpected') unless eq.nil?
38
+ end
39
+
40
+ instance_eval(&block) unless block.nil?
41
+ raise ParamsReadyError, "Join clause is empty" if @conditions.empty?
42
+ end
43
+
44
+ def on(expression, arel_table: nil)
45
+ condition = JoinCondition.new(expression, arel_table: arel_table)
46
+ @conditions << condition
47
+ condition
48
+ end
49
+
50
+ def to_arel(base_table, join_table, context, parameter)
51
+ @conditions.reduce(nil) do |result, condition|
52
+ arel = condition.to_arel(base_table, join_table, context, parameter)
53
+ next arel if result.nil?
54
+
55
+ result.and(arel)
56
+ end
57
+ end
58
+ end
59
+
60
+ class JoinCondition
61
+ def initialize(expression, arel_table: nil)
62
+ @on = Helpers::ArelBuilder.instance(expression, arel_table: arel_table)
63
+ @to = nil
64
+ @op = nil
65
+ end
66
+
67
+ def eq(expression, arel_table: nil)
68
+ raise ParamsReadyError, "Operator already set" unless @op.nil?
69
+ @op = :eq
70
+ @to = Helpers::ArelBuilder.instance(expression, arel_table: arel_table)
71
+ end
72
+
73
+ def to_arel(base_table, join_table, context, parameter)
74
+ if @to.nil?
75
+ grouping = @on.to_arel(:none, context, parameter)
76
+ return grouping if grouping.is_a? Arel::Nodes::Node
77
+
78
+ Arel::Nodes::Grouping.new(grouping)
79
+ else
80
+ lhs = @on.to_arel(base_table, context, parameter)
81
+ rhs = @to.to_arel(join_table, context, parameter)
82
+ lhs.send(@op, rhs)
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,71 @@
1
+ require_relative 'predicate'
2
+ require_relative '../parameter/value_parameter'
3
+
4
+ module ParamsReady
5
+ module Query
6
+ class NullnessPredicate < Parameter::AbstractParameter
7
+ include Predicate::DelegatingPredicate
8
+ include Predicate::HavingAttribute
9
+
10
+ def initialize(definition)
11
+ super definition
12
+ @data = definition.value_parameter.create
13
+ end
14
+
15
+ def build_query(select_expression, context: nil)
16
+ query = select_expression.eq(nil)
17
+ if !unwrap
18
+ query.not
19
+ else
20
+ query
21
+ end
22
+ end
23
+
24
+ def perform_test(record, attribute_name)
25
+ if unwrap
26
+ return true if record.nil?
27
+ record.send(attribute_name).nil?
28
+ else
29
+ return false if record.nil?
30
+ !record.send(attribute_name).nil?
31
+ end
32
+ end
33
+
34
+ def inspect_content
35
+ "#{definition.attribute_name} is_null? #{@data.inspect}"
36
+ end
37
+ end
38
+
39
+ class NullnessPredicateBuilder < AbstractPredicateBuilder
40
+ include HavingAttribute
41
+ PredicateRegistry.register_predicate :nullness_predicate, self
42
+
43
+ include HavingValue
44
+
45
+ def self.instance(name, altn: nil, attr: nil)
46
+ new NullnessPredicateDefinition.new name, altn: altn, attribute_name: attr
47
+ end
48
+ end
49
+
50
+ class NullnessPredicateDefinition < AbstractPredicateDefinition
51
+ include HavingAttribute
52
+ include Parameter::DelegatingDefinition[:value_parameter]
53
+
54
+ attr_reader :value_parameter
55
+ freeze_variables :value_parameter
56
+
57
+ def initialize(*args, attribute_name: nil, **opts)
58
+ super *args, **opts
59
+ @attribute_name = attribute_name
60
+ @value_parameter = Builder.builder(:boolean).instance(self.name, altn: self.altn).fetch
61
+ end
62
+
63
+ def finish
64
+ @value_parameter.finish
65
+ super
66
+ end
67
+
68
+ parameter_class NullnessPredicate
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,77 @@
1
+ require 'forwardable'
2
+ require_relative 'predicate'
3
+ require_relative '../parameter/polymorph_parameter'
4
+
5
+ module ParamsReady
6
+ module Query
7
+ class PolymorphPredicate < Parameter::AbstractParameter
8
+ include Predicate::DelegatingPredicate
9
+ include Predicate::HavingChildren
10
+
11
+ def_delegators :@data,
12
+ :intent_for_children,
13
+ :permission_depends_on
14
+
15
+ def initialize(definition, **_)
16
+ super definition
17
+ @data = definition.polymorph_parameter.create
18
+ end
19
+
20
+ def to_query(arel_table, context: Restriction.blanket_permission)
21
+ data[data.type].to_query_if_eligible(arel_table, context: context)
22
+ end
23
+
24
+ def test(record)
25
+ return nil unless is_definite?
26
+
27
+ data[data.type].test(record)
28
+ end
29
+ end
30
+
31
+ class PolymorphPredicateBuilder < AbstractPredicateBuilder
32
+ PredicateRegistry.register_predicate :polymorph_predicate, self
33
+ include HavingValue
34
+
35
+ def self.instance(name, altn: nil)
36
+ new PolymorphPredicateDefinition.new name, altn: altn
37
+ end
38
+
39
+ def type(type_name, *args, **opts, &block)
40
+ builder = PredicateRegistry.predicate(type_name).instance(*args, **opts)
41
+ builder.instance_eval(&block) unless block.nil?
42
+ type = builder.build
43
+ @definition.add_type type
44
+ end
45
+
46
+ def identifier(identifier)
47
+ @definition.set_identifier(identifier)
48
+ end
49
+ end
50
+
51
+ class PolymorphPredicateDefinition < AbstractPredicateDefinition
52
+ extend Forwardable
53
+
54
+ attr_reader :polymorph_parameter, :name, :altn
55
+ freeze_variable :polymorph_parameter
56
+ def_delegators :@polymorph_parameter,
57
+ :add_type,
58
+ :set_optional,
59
+ :set_default,
60
+ :set_identifier,
61
+ :set_marshaller
62
+
63
+ def initialize(*args, **opts)
64
+ super
65
+ @polymorph_parameter = Parameter::PolymorphParameterDefinition.new(name, altn: altn)
66
+ @optional = false
67
+ end
68
+
69
+ def finish
70
+ @polymorph_parameter.finish
71
+ super
72
+ end
73
+
74
+ parameter_class PolymorphPredicate
75
+ end
76
+ end
77
+ end