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.
- checksums.yaml +7 -0
- data/lib/arel/cte_name.rb +20 -0
- data/lib/params_ready/builder.rb +161 -0
- data/lib/params_ready/error.rb +31 -0
- data/lib/params_ready/extensions/class_reader_writer.rb +33 -0
- data/lib/params_ready/extensions/collection.rb +43 -0
- data/lib/params_ready/extensions/delegation.rb +25 -0
- data/lib/params_ready/extensions/finalizer.rb +26 -0
- data/lib/params_ready/extensions/freezer.rb +49 -0
- data/lib/params_ready/extensions/hash.rb +46 -0
- data/lib/params_ready/extensions/late_init.rb +38 -0
- data/lib/params_ready/extensions/registry.rb +44 -0
- data/lib/params_ready/extensions/undefined.rb +23 -0
- data/lib/params_ready/format.rb +132 -0
- data/lib/params_ready/helpers/arel_builder.rb +68 -0
- data/lib/params_ready/helpers/callable.rb +14 -0
- data/lib/params_ready/helpers/conditional_block.rb +31 -0
- data/lib/params_ready/helpers/find_in_hash.rb +22 -0
- data/lib/params_ready/helpers/interface_definer.rb +48 -0
- data/lib/params_ready/helpers/key_map.rb +176 -0
- data/lib/params_ready/helpers/memo.rb +41 -0
- data/lib/params_ready/helpers/options.rb +107 -0
- data/lib/params_ready/helpers/parameter_definer_class_methods.rb +39 -0
- data/lib/params_ready/helpers/parameter_storage_class_methods.rb +63 -0
- data/lib/params_ready/helpers/parameter_user_class_methods.rb +35 -0
- data/lib/params_ready/helpers/relation_builder_wrapper.rb +35 -0
- data/lib/params_ready/helpers/rule.rb +76 -0
- data/lib/params_ready/helpers/storage.rb +30 -0
- data/lib/params_ready/helpers/usage_rule.rb +36 -0
- data/lib/params_ready/input_context.rb +31 -0
- data/lib/params_ready/intent.rb +70 -0
- data/lib/params_ready/marshaller/array_marshallers.rb +132 -0
- data/lib/params_ready/marshaller/builder_module.rb +9 -0
- data/lib/params_ready/marshaller/collection.rb +165 -0
- data/lib/params_ready/marshaller/definition_module.rb +63 -0
- data/lib/params_ready/marshaller/enum_set_marshallers.rb +96 -0
- data/lib/params_ready/marshaller/parameter_module.rb +11 -0
- data/lib/params_ready/marshaller/polymorph_marshallers.rb +67 -0
- data/lib/params_ready/marshaller/struct_marshallers.rb +100 -0
- data/lib/params_ready/marshaller/tuple_marshallers.rb +103 -0
- data/lib/params_ready/ordering/column.rb +60 -0
- data/lib/params_ready/ordering/ordering.rb +276 -0
- data/lib/params_ready/output_parameters.rb +138 -0
- data/lib/params_ready/pagination/abstract_pagination.rb +18 -0
- data/lib/params_ready/pagination/cursor.rb +171 -0
- data/lib/params_ready/pagination/direction.rb +148 -0
- data/lib/params_ready/pagination/keyset_pagination.rb +254 -0
- data/lib/params_ready/pagination/keysets.rb +70 -0
- data/lib/params_ready/pagination/nulls.rb +31 -0
- data/lib/params_ready/pagination/offset_pagination.rb +130 -0
- data/lib/params_ready/pagination/tendency.rb +28 -0
- data/lib/params_ready/parameter/abstract_struct_parameter.rb +204 -0
- data/lib/params_ready/parameter/array_parameter.rb +197 -0
- data/lib/params_ready/parameter/definition.rb +272 -0
- data/lib/params_ready/parameter/enum_set_parameter.rb +102 -0
- data/lib/params_ready/parameter/parameter.rb +475 -0
- data/lib/params_ready/parameter/polymorph_parameter.rb +172 -0
- data/lib/params_ready/parameter/state.rb +132 -0
- data/lib/params_ready/parameter/struct_parameter.rb +64 -0
- data/lib/params_ready/parameter/tuple_parameter.rb +152 -0
- data/lib/params_ready/parameter/value_parameter.rb +186 -0
- data/lib/params_ready/parameter_definer.rb +14 -0
- data/lib/params_ready/parameter_user.rb +35 -0
- data/lib/params_ready/query/array_grouping.rb +68 -0
- data/lib/params_ready/query/custom_predicate.rb +102 -0
- data/lib/params_ready/query/exists_predicate.rb +103 -0
- data/lib/params_ready/query/fixed_operator_predicate.rb +77 -0
- data/lib/params_ready/query/grouping.rb +177 -0
- data/lib/params_ready/query/join_clause.rb +87 -0
- data/lib/params_ready/query/nullness_predicate.rb +71 -0
- data/lib/params_ready/query/polymorph_predicate.rb +77 -0
- data/lib/params_ready/query/predicate.rb +203 -0
- data/lib/params_ready/query/predicate_operator.rb +132 -0
- data/lib/params_ready/query/relation.rb +337 -0
- data/lib/params_ready/query/structured_grouping.rb +58 -0
- data/lib/params_ready/query/variable_operator_predicate.rb +125 -0
- data/lib/params_ready/query_context.rb +21 -0
- data/lib/params_ready/restriction.rb +252 -0
- data/lib/params_ready/result.rb +109 -0
- data/lib/params_ready/value/coder.rb +210 -0
- data/lib/params_ready/value/constraint.rb +198 -0
- data/lib/params_ready/value/custom.rb +56 -0
- data/lib/params_ready/value/validator.rb +81 -0
- data/lib/params_ready/version.rb +7 -0
- data/lib/params_ready.rb +28 -0
- metadata +227 -0
@@ -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,35 @@
|
|
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(key, params, context = Format.instance(:frontend), validator = nil)
|
26
|
+
definition = create_state_for key
|
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(key)
|
32
|
+
self.class.params_ready_option.create_state_for(key)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require_relative '../parameter/struct_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::StructParameter
|
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::AbstractStructParameterBuilder::StructLike
|
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::StructParameterDefinition
|
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::AbstractStructParameterBuilder::StructLike
|
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
|
@@ -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(joined_table, base_table, context, parameter)
|
23
|
+
join_statement = @statement.to_arel(base_table, @arel_table, context, parameter)
|
24
|
+
joined_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
|