active_record_extended_telescope 2.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.
- checksums.yaml +7 -0
- data/README.md +870 -0
- data/lib/active_record_extended.rb +10 -0
- data/lib/active_record_extended/active_record.rb +25 -0
- data/lib/active_record_extended/active_record/relation_patch.rb +50 -0
- data/lib/active_record_extended/arel.rb +7 -0
- data/lib/active_record_extended/arel/aggregate_function_name.rb +40 -0
- data/lib/active_record_extended/arel/nodes.rb +49 -0
- data/lib/active_record_extended/arel/predications.rb +50 -0
- data/lib/active_record_extended/arel/sql_literal.rb +16 -0
- data/lib/active_record_extended/arel/visitors/postgresql_decorator.rb +122 -0
- data/lib/active_record_extended/patch/5_1/where_clause.rb +11 -0
- data/lib/active_record_extended/patch/5_2/where_clause.rb +11 -0
- data/lib/active_record_extended/predicate_builder/array_handler_decorator.rb +20 -0
- data/lib/active_record_extended/query_methods/any_of.rb +93 -0
- data/lib/active_record_extended/query_methods/either.rb +62 -0
- data/lib/active_record_extended/query_methods/inet.rb +88 -0
- data/lib/active_record_extended/query_methods/json.rb +329 -0
- data/lib/active_record_extended/query_methods/select.rb +118 -0
- data/lib/active_record_extended/query_methods/unionize.rb +249 -0
- data/lib/active_record_extended/query_methods/where_chain.rb +132 -0
- data/lib/active_record_extended/query_methods/window.rb +93 -0
- data/lib/active_record_extended/query_methods/with_cte.rb +150 -0
- data/lib/active_record_extended/utilities/order_by.rb +77 -0
- data/lib/active_record_extended/utilities/support.rb +178 -0
- data/lib/active_record_extended/version.rb +5 -0
- data/lib/active_record_extended_telescope.rb +4 -0
- data/spec/active_record_extended_spec.rb +7 -0
- data/spec/query_methods/any_of_spec.rb +131 -0
- data/spec/query_methods/array_query_spec.rb +64 -0
- data/spec/query_methods/either_spec.rb +59 -0
- data/spec/query_methods/hash_query_spec.rb +45 -0
- data/spec/query_methods/inet_query_spec.rb +112 -0
- data/spec/query_methods/json_spec.rb +157 -0
- data/spec/query_methods/select_spec.rb +115 -0
- data/spec/query_methods/unionize_spec.rb +165 -0
- data/spec/query_methods/window_spec.rb +51 -0
- data/spec/query_methods/with_cte_spec.rb +50 -0
- data/spec/spec_helper.rb +28 -0
- data/spec/sql_inspections/any_of_sql_spec.rb +41 -0
- data/spec/sql_inspections/arel/aggregate_function_name_spec.rb +41 -0
- data/spec/sql_inspections/arel/array_spec.rb +63 -0
- data/spec/sql_inspections/arel/inet_spec.rb +66 -0
- data/spec/sql_inspections/contains_sql_queries_spec.rb +47 -0
- data/spec/sql_inspections/either_sql_spec.rb +55 -0
- data/spec/sql_inspections/json_sql_spec.rb +82 -0
- data/spec/sql_inspections/unionize_sql_spec.rb +124 -0
- data/spec/sql_inspections/window_sql_spec.rb +98 -0
- data/spec/sql_inspections/with_cte_sql_spec.rb +95 -0
- data/spec/support/database_cleaner.rb +15 -0
- data/spec/support/models.rb +68 -0
- metadata +245 -0
@@ -0,0 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_record_extended/version"
|
4
|
+
require "active_record_extended/utilities/support"
|
5
|
+
require "active_record_extended/utilities/order_by"
|
6
|
+
require "active_record_extended/active_record"
|
7
|
+
require "active_record_extended/arel"
|
8
|
+
|
9
|
+
module ActiveRecordExtended
|
10
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_record"
|
4
|
+
require "active_record/relation"
|
5
|
+
require "active_record/relation/merger"
|
6
|
+
require "active_record/relation/query_methods"
|
7
|
+
|
8
|
+
require "active_record_extended/predicate_builder/array_handler_decorator"
|
9
|
+
|
10
|
+
require "active_record_extended/active_record/relation_patch"
|
11
|
+
|
12
|
+
require "active_record_extended/query_methods/where_chain"
|
13
|
+
require "active_record_extended/query_methods/with_cte"
|
14
|
+
require "active_record_extended/query_methods/unionize"
|
15
|
+
require "active_record_extended/query_methods/any_of"
|
16
|
+
require "active_record_extended/query_methods/either"
|
17
|
+
require "active_record_extended/query_methods/inet"
|
18
|
+
require "active_record_extended/query_methods/json"
|
19
|
+
require "active_record_extended/query_methods/select"
|
20
|
+
|
21
|
+
if Gem::Requirement.new("~> 5.1.0").satisfied_by?(ActiveRecord.gem_version)
|
22
|
+
require "active_record_extended/patch/5_1/where_clause"
|
23
|
+
else
|
24
|
+
require "active_record_extended/patch/5_2/where_clause"
|
25
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_record_extended/query_methods/window"
|
4
|
+
require "active_record_extended/query_methods/unionize"
|
5
|
+
require "active_record_extended/query_methods/json"
|
6
|
+
|
7
|
+
module ActiveRecordExtended
|
8
|
+
module RelationPatch
|
9
|
+
module QueryDelegation
|
10
|
+
delegate :with, :define_window, :select_window, :foster_select, to: :all
|
11
|
+
delegate(*::ActiveRecordExtended::QueryMethods::Unionize::UNIONIZE_METHODS, to: :all)
|
12
|
+
delegate(*::ActiveRecordExtended::QueryMethods::Json::JSON_QUERY_METHODS, to: :all)
|
13
|
+
end
|
14
|
+
|
15
|
+
module Merger
|
16
|
+
def normal_values
|
17
|
+
super + [:union, :define_window]
|
18
|
+
end
|
19
|
+
|
20
|
+
def merge
|
21
|
+
merge_ctes!
|
22
|
+
super
|
23
|
+
end
|
24
|
+
|
25
|
+
def merge_ctes!
|
26
|
+
return unless other.with_values?
|
27
|
+
|
28
|
+
if other.recursive_value? && !relation.recursive_value?
|
29
|
+
relation.with!(:chain).recursive(other.cte)
|
30
|
+
else
|
31
|
+
relation.with!(other.cte)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
module ArelBuildPatch
|
37
|
+
def build_arel(*aliases)
|
38
|
+
super.tap do |arel|
|
39
|
+
build_windows(arel) if window_values?
|
40
|
+
build_unions(arel) if union_values?
|
41
|
+
build_with(arel) if with_values?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
ActiveRecord::Relation.prepend(ActiveRecordExtended::RelationPatch::ArelBuildPatch)
|
49
|
+
ActiveRecord::Relation::Merger.prepend(ActiveRecordExtended::RelationPatch::Merger)
|
50
|
+
ActiveRecord::Querying.prepend(ActiveRecordExtended::RelationPatch::QueryDelegation)
|
@@ -0,0 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_record_extended/arel/nodes"
|
4
|
+
require "active_record_extended/arel/sql_literal"
|
5
|
+
require "active_record_extended/arel/aggregate_function_name"
|
6
|
+
require "active_record_extended/arel/predications"
|
7
|
+
require "active_record_extended/arel/visitors/postgresql_decorator"
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Arel
|
4
|
+
module Nodes
|
5
|
+
class AggregateFunctionName < ::Arel::Nodes::Node
|
6
|
+
include Arel::Predications
|
7
|
+
include Arel::WindowPredications
|
8
|
+
attr_accessor :name, :expressions, :distinct, :alias, :orderings
|
9
|
+
|
10
|
+
def initialize(name, expr, distinct = false)
|
11
|
+
super()
|
12
|
+
@name = name.to_s.upcase
|
13
|
+
@expressions = expr
|
14
|
+
@distinct = distinct
|
15
|
+
end
|
16
|
+
|
17
|
+
def order_by(expr)
|
18
|
+
@orderings = expr
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def as(aliaz)
|
23
|
+
self.alias = SqlLiteral.new(aliaz)
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
def hash
|
28
|
+
[@name, @expressions, @distinct, @alias, @orderings].hash
|
29
|
+
end
|
30
|
+
|
31
|
+
def eql?(other)
|
32
|
+
self.class == other.class &&
|
33
|
+
expressions == other.expressions &&
|
34
|
+
orderings == other.orderings &&
|
35
|
+
distinct == other.distinct
|
36
|
+
end
|
37
|
+
alias == eql?
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "arel/nodes/binary"
|
4
|
+
require "arel/nodes/function"
|
5
|
+
|
6
|
+
module Arel
|
7
|
+
module Nodes
|
8
|
+
if Gem::Requirement.new("< 6.1").satisfied_by?(ActiveRecord.gem_version)
|
9
|
+
["Contains", "Overlaps"].each { |binary_node_name| const_set(binary_node_name, Class.new(::Arel::Nodes::Binary)) }
|
10
|
+
end
|
11
|
+
|
12
|
+
[
|
13
|
+
"ContainsHStore",
|
14
|
+
"ContainsArray",
|
15
|
+
"ContainedInArray"
|
16
|
+
].each { |binary_node_name| const_set(binary_node_name, Class.new(::Arel::Nodes::Binary)) }
|
17
|
+
|
18
|
+
[
|
19
|
+
"RowToJson",
|
20
|
+
"JsonBuildObject",
|
21
|
+
"JsonbBuildObject",
|
22
|
+
"ToJson",
|
23
|
+
"ToJsonb",
|
24
|
+
"Array",
|
25
|
+
"ArrayAgg"
|
26
|
+
].each do |function_node_name|
|
27
|
+
func_klass = Class.new(::Arel::Nodes::Function) do
|
28
|
+
def initialize(*args)
|
29
|
+
super
|
30
|
+
return if @expressions.is_a?(::Array)
|
31
|
+
|
32
|
+
@expressions = @expressions.is_a?(::Arel::Nodes::Node) ? [@expressions] : [::Arel.sql(@expressions)]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
const_set(function_node_name, func_klass)
|
37
|
+
end
|
38
|
+
|
39
|
+
module Inet
|
40
|
+
[
|
41
|
+
"Contains",
|
42
|
+
"ContainsEquals",
|
43
|
+
"ContainedWithin",
|
44
|
+
"ContainedWithinEquals",
|
45
|
+
"ContainsOrContainedWithin"
|
46
|
+
].each { |binary_node_name| const_set(binary_node_name, Class.new(::Arel::Nodes::Binary)) }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "arel/predications"
|
4
|
+
|
5
|
+
module Arel
|
6
|
+
module Predications
|
7
|
+
def any(other)
|
8
|
+
any_tags_function = Arel::Nodes::NamedFunction.new("ANY", [self])
|
9
|
+
Arel::Nodes::Equality.new(Nodes.build_quoted(other, self), any_tags_function)
|
10
|
+
end
|
11
|
+
|
12
|
+
def all(other)
|
13
|
+
all_tags_function = Arel::Nodes::NamedFunction.new("ALL", [self])
|
14
|
+
Arel::Nodes::Equality.new(Nodes.build_quoted(other, self), all_tags_function)
|
15
|
+
end
|
16
|
+
|
17
|
+
def overlaps(other)
|
18
|
+
Nodes::Overlaps.new(self, Nodes.build_quoted(other, self))
|
19
|
+
end
|
20
|
+
alias overlap overlaps
|
21
|
+
|
22
|
+
def contains(other)
|
23
|
+
Nodes::Contains.new self, Nodes.build_quoted(other, self)
|
24
|
+
end
|
25
|
+
|
26
|
+
def contained_in_array(other)
|
27
|
+
Nodes::ContainedInArray.new self, Nodes.build_quoted(other, self)
|
28
|
+
end
|
29
|
+
|
30
|
+
def inet_contains(other)
|
31
|
+
Nodes::Inet::Contains.new self, Nodes.build_quoted(other, self)
|
32
|
+
end
|
33
|
+
|
34
|
+
def inet_contains_or_is_contained_within(other)
|
35
|
+
Nodes::Inet::ContainsOrContainedWithin.new self, Nodes.build_quoted(other, self)
|
36
|
+
end
|
37
|
+
|
38
|
+
def inet_contained_within(other)
|
39
|
+
Nodes::Inet::ContainedWithin.new self, Nodes.build_quoted(other, self)
|
40
|
+
end
|
41
|
+
|
42
|
+
def inet_contained_within_or_equals(other)
|
43
|
+
Nodes::Inet::ContainedWithinEquals.new self, Nodes.build_quoted(other, self)
|
44
|
+
end
|
45
|
+
|
46
|
+
def inet_contains_or_equals(other)
|
47
|
+
Nodes::Inet::ContainsEquals.new self, Nodes.build_quoted(other, self)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "arel/nodes/sql_literal"
|
4
|
+
|
5
|
+
# CTE alias fix for Rails 6.1
|
6
|
+
module Arel
|
7
|
+
module Nodes
|
8
|
+
module SqlLiteralDecorator
|
9
|
+
def name
|
10
|
+
self
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
Arel::Nodes::SqlLiteral.prepend(Arel::Nodes::SqlLiteralDecorator)
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "arel/visitors/postgresql"
|
4
|
+
|
5
|
+
module ActiveRecordExtended
|
6
|
+
module Visitors
|
7
|
+
module PostgreSQLDecorator
|
8
|
+
private
|
9
|
+
|
10
|
+
# rubocop:disable Naming/MethodName
|
11
|
+
|
12
|
+
def visit_Arel_Nodes_Overlaps(object, collector)
|
13
|
+
infix_value object, collector, " && "
|
14
|
+
end
|
15
|
+
|
16
|
+
def visit_Arel_Nodes_Contains(object, collector)
|
17
|
+
left_column = object.left.relation.name.classify.constantize.columns.detect do |col|
|
18
|
+
matchable_column?(col, object)
|
19
|
+
end
|
20
|
+
|
21
|
+
if [:hstore, :jsonb].include?(left_column&.type)
|
22
|
+
visit_Arel_Nodes_ContainsHStore(object, collector)
|
23
|
+
elsif left_column.try(:array)
|
24
|
+
visit_Arel_Nodes_ContainsArray(object, collector)
|
25
|
+
else
|
26
|
+
visit_Arel_Nodes_Inet_Contains(object, collector)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def visit_Arel_Nodes_ContainsArray(object, collector)
|
31
|
+
infix_value object, collector, " @> "
|
32
|
+
end
|
33
|
+
|
34
|
+
def visit_Arel_Nodes_ContainsHStore(object, collector)
|
35
|
+
infix_value object, collector, " @> "
|
36
|
+
end
|
37
|
+
|
38
|
+
def visit_Arel_Nodes_ContainedInHStore(object, collector)
|
39
|
+
infix_value object, collector, " <@ "
|
40
|
+
end
|
41
|
+
|
42
|
+
def visit_Arel_Nodes_ContainedInArray(object, collector)
|
43
|
+
infix_value object, collector, " <@ "
|
44
|
+
end
|
45
|
+
|
46
|
+
def visit_Arel_Nodes_Inet_ContainedWithin(object, collector)
|
47
|
+
infix_value object, collector, " << "
|
48
|
+
end
|
49
|
+
|
50
|
+
def visit_Arel_Nodes_RowToJson(object, collector)
|
51
|
+
aggregate "ROW_TO_JSON", object, collector
|
52
|
+
end
|
53
|
+
|
54
|
+
def visit_Arel_Nodes_JsonBuildObject(object, collector)
|
55
|
+
aggregate "JSON_BUILD_OBJECT", object, collector
|
56
|
+
end
|
57
|
+
|
58
|
+
def visit_Arel_Nodes_JsonbBuildObject(object, collector)
|
59
|
+
aggregate "JSONB_BUILD_OBJECT", object, collector
|
60
|
+
end
|
61
|
+
|
62
|
+
def visit_Arel_Nodes_ToJson(object, collector)
|
63
|
+
aggregate "TO_JSON", object, collector
|
64
|
+
end
|
65
|
+
|
66
|
+
def visit_Arel_Nodes_ToJsonb(object, collector)
|
67
|
+
aggregate "TO_JSONB", object, collector
|
68
|
+
end
|
69
|
+
|
70
|
+
def visit_Arel_Nodes_Array(object, collector)
|
71
|
+
aggregate "ARRAY", object, collector
|
72
|
+
end
|
73
|
+
|
74
|
+
def visit_Arel_Nodes_ArrayAgg(object, collector)
|
75
|
+
aggregate "ARRAY_AGG", object, collector
|
76
|
+
end
|
77
|
+
|
78
|
+
def visit_Arel_Nodes_AggregateFunctionName(object, collector)
|
79
|
+
collector << "#{object.name}("
|
80
|
+
collector << "DISTINCT " if object.distinct
|
81
|
+
collector = inject_join(object.expressions, collector, ", ")
|
82
|
+
|
83
|
+
if object.orderings
|
84
|
+
collector << " ORDER BY "
|
85
|
+
collector = inject_join(object.orderings, collector, ", ")
|
86
|
+
end
|
87
|
+
collector << ")"
|
88
|
+
|
89
|
+
if object.alias
|
90
|
+
collector << " AS "
|
91
|
+
visit object.alias, collector
|
92
|
+
else
|
93
|
+
collector
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def visit_Arel_Nodes_Inet_Contains(object, collector)
|
98
|
+
infix_value object, collector, " >> "
|
99
|
+
end
|
100
|
+
|
101
|
+
def visit_Arel_Nodes_Inet_ContainedWithinEquals(object, collector)
|
102
|
+
infix_value object, collector, " <<= "
|
103
|
+
end
|
104
|
+
|
105
|
+
def visit_Arel_Nodes_Inet_ContainsEquals(object, collector)
|
106
|
+
infix_value object, collector, " >>= "
|
107
|
+
end
|
108
|
+
|
109
|
+
def visit_Arel_Nodes_Inet_ContainsOrContainedWithin(object, collector)
|
110
|
+
infix_value object, collector, " && "
|
111
|
+
end
|
112
|
+
|
113
|
+
def matchable_column?(col, object)
|
114
|
+
col.name == object.left.name.to_s || col.name == object.left.relation.name.to_s
|
115
|
+
end
|
116
|
+
|
117
|
+
# rubocop:enable Naming/MethodName
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
Arel::Visitors::PostgreSQL.prepend(ActiveRecordExtended::Visitors::PostgreSQLDecorator)
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecordExtended
|
4
|
+
module WhereClause
|
5
|
+
def modified_predicates(&block)
|
6
|
+
::ActiveRecord::Relation::WhereClause.new(predicates.map(&block), binds)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
ActiveRecord::Relation::WhereClause.prepend(ActiveRecordExtended::WhereClause)
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecordExtended
|
4
|
+
module WhereClause
|
5
|
+
def modified_predicates(&block)
|
6
|
+
::ActiveRecord::Relation::WhereClause.new(predicates.map(&block))
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
ActiveRecord::Relation::WhereClause.prepend(ActiveRecordExtended::WhereClause)
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_record/relation/predicate_builder"
|
4
|
+
require "active_record/relation/predicate_builder/array_handler"
|
5
|
+
|
6
|
+
module ActiveRecordExtended
|
7
|
+
module ArrayHandlerDecorator
|
8
|
+
def call(attribute, value)
|
9
|
+
cache = ActiveRecord::Base.connection.schema_cache
|
10
|
+
if cache.data_source_exists?(attribute.relation.name)
|
11
|
+
column = cache.columns(attribute.relation.name).detect { |col| col.name.to_s == attribute.name.to_s }
|
12
|
+
return attribute.eq(value) if column.try(:array)
|
13
|
+
end
|
14
|
+
|
15
|
+
super(attribute, value)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
ActiveRecord::PredicateBuilder::ArrayHandler.prepend(ActiveRecordExtended::ArrayHandlerDecorator)
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecordExtended
|
4
|
+
module QueryMethods
|
5
|
+
module AnyOf
|
6
|
+
def any_of(*queries)
|
7
|
+
queries = hash_map_queries(queries)
|
8
|
+
build_query(queries) do |arel_query, binds|
|
9
|
+
if binds.any?
|
10
|
+
@scope.where(unprepared_query(arel_query.to_sql), *binds)
|
11
|
+
else
|
12
|
+
@scope.where(arel_query)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def none_of(*queries)
|
18
|
+
queries = hash_map_queries(queries)
|
19
|
+
build_query(queries) do |arel_query, binds|
|
20
|
+
if binds.any?
|
21
|
+
@scope.where.not(unprepared_query(arel_query.to_sql), *binds)
|
22
|
+
else
|
23
|
+
@scope.where.not(arel_query)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def hash_map_queries(queries)
|
31
|
+
if queries.size == 1 && queries.first.is_a?(Hash)
|
32
|
+
queries.first.each_pair.map { |attr, predicate| Hash[attr, predicate] }
|
33
|
+
else
|
34
|
+
queries
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def build_query(queries)
|
39
|
+
query_map = construct_query_mappings(queries)
|
40
|
+
query = yield(query_map[:arel_query], query_map[:binds])
|
41
|
+
query
|
42
|
+
.joins(query_map[:joins].to_a)
|
43
|
+
.includes(query_map[:includes].to_a)
|
44
|
+
.references(query_map[:references].to_a)
|
45
|
+
end
|
46
|
+
|
47
|
+
def construct_query_mappings(queries) # rubocop:disable Metrics/AbcSize
|
48
|
+
{ joins: Set.new, references: Set.new, includes: Set.new, arel_query: nil, binds: [] }.tap do |query_map|
|
49
|
+
query_map[:arel_query] = queries.map do |raw_query|
|
50
|
+
query = generate_where_clause(raw_query)
|
51
|
+
query_map[:joins] << translate_reference(query.joins_values) if query.joins_values.any?
|
52
|
+
query_map[:includes] << translate_reference(query.includes_values) if query.includes_values.any?
|
53
|
+
query_map[:references] << translate_reference(query.references_values) if query.references_values.any?
|
54
|
+
query_map[:binds] += bind_attributes(query)
|
55
|
+
query.arel.constraints.reduce(:and)
|
56
|
+
end.reduce(:or)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Rails 5.1 fix
|
61
|
+
# In Rails 5.2 the arel table maintains attribute binds
|
62
|
+
def bind_attributes(query)
|
63
|
+
return [] unless query.respond_to?(:bound_attributes)
|
64
|
+
|
65
|
+
query.bound_attributes.map(&:value)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Rails 5.1 fix
|
69
|
+
def unprepared_query(query)
|
70
|
+
query.gsub(/((?<!\\)'.*?(?<!\\)'|(?<!\\)".*?(?<!\\)")|(=\ \$\d+)/) do |match|
|
71
|
+
Regexp.last_match(2)&.gsub(/=\ \$\d+/, "= ?") || match
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def translate_reference(reference)
|
76
|
+
reference.map { |ref| ref.try(:to_sql) || ref }.compact
|
77
|
+
end
|
78
|
+
|
79
|
+
def generate_where_clause(query)
|
80
|
+
case query
|
81
|
+
when String, Hash
|
82
|
+
@scope.unscoped.where(query)
|
83
|
+
when Array
|
84
|
+
@scope.unscoped.where(*query)
|
85
|
+
else
|
86
|
+
query
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
ActiveRecord::QueryMethods::WhereChain.prepend(ActiveRecordExtended::QueryMethods::AnyOf)
|