params_ready 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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