This repository has been archived by the owner on Mar 30, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 214
/
relation_extensions.rb
155 lines (130 loc) · 4.89 KB
/
relation_extensions.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
require 'squeel/adapters/active_record/relation_extensions'
module Squeel
module Adapters
module ActiveRecord
module RelationExtensions
# where.not is a pain. It calls the private `build_where` method on its
# scope, and since ActiveRecord::Relation already includes the original
# ActiveRecord::QueryMethods module, we have to find a way to trick the
# scope passed to the WhereChain initializer into having the original
# behavior. This is a way to do it that avoids using alias_method_chain.
module WhereChainCompatibility
include ::ActiveRecord::QueryMethods
define_method :build_where,
::ActiveRecord::QueryMethods.instance_method(:build_where)
end
def where(opts = :chain, *rest)
if block_given?
super(DSL.eval &Proc.new)
else
if opts == :chain
scope = spawn
scope.extend(WhereChainCompatibility)
::ActiveRecord::QueryMethods::WhereChain.new(scope)
else
super
end
end
end
def build_arel
arel = Arel::SelectManager.new(table.engine, table)
build_joins(arel, joins_values) unless joins_values.empty?
collapse_wheres(arel, where_visit((where_values - ['']).uniq))
arel.having(*having_visit(having_values.uniq.reject{|h| h.blank?})) unless having_values.empty?
arel.take(connection.sanitize_limit(limit_value)) if limit_value
arel.skip(offset_value.to_i) if offset_value
arel.group(*group_visit(group_values.uniq.reject{|g| g.blank?})) unless group_values.empty?
build_order(arel)
build_select(arel, select_visit(select_values.uniq))
arel.distinct(distinct_value)
arel.from(build_from) if from_value
arel.lock(lock_value) if lock_value
arel
end
def build_where(opts, other = [])
case opts
when String, Array
super
else # Let's prevent PredicateBuilder from doing its thing
[opts, *other].map do |arg|
case arg
when Array # Just in case there's an array in there somewhere
@klass.send(:sanitize_sql, arg)
when Hash
attrs = @klass.send(:expand_hash_conditions_for_aggregates, arg)
attrs.values.grep(::ActiveRecord::Relation) do |rel|
self.bind_values += rel.bind_values
end
attrs
when Squeel::Nodes::Node
arg.grep(::ActiveRecord::Relation) do |rel|
self.bind_values += rel.bind_values
end
arg
else
arg
end
end
end
end
def build_from
opts, name = from_visit(from_value)
case opts
when ::ActiveRecord::Relation
name ||= 'subquery'
opts.arel.as(name.to_s)
when ::Arel::SelectManager
name ||= 'subquery'
opts.as(name.to_s)
else
opts
end
end
def build_order(arel)
orders = order_visit(dehashified_order_values)
orders = reverse_sql_order(attrs_to_orderings(orders)) if reverse_order_value
orders = orders.uniq.reject(&:blank?).flat_map do |order|
case order
when Symbol
table[order].asc
when Hash
order.map { |field, dir| table[field].send(dir) }
else
order
end
end
arel.order(*orders) unless orders.empty?
end
# This is copied directly from 4.0.0's implementation, but adds an extra
# exclusion for Squeel::Nodes::Node to fix #248. Can be removed if/when
# rails/rails#11439 is merged.
def order!(*args)
args.flatten!
validate_order_args args
references = args.reject { |arg|
Arel::Node === arg || Squeel::Nodes::Node === arg
}
references.map! { |arg| arg =~ /^([a-zA-Z]\w*)\.(\w+)/ && $1 }.compact!
references!(references) if references.any?
# if a symbol is given we prepend the quoted table name
args = args.map { |arg|
arg.is_a?(Symbol) ? "#{quoted_table_name}.#{arg} ASC" : arg
}
self.order_values = args + self.order_values
self
end
private
def dehashified_order_values
order_values.map { |o|
if Hash === o && o.values.all? { |v| [:asc, :desc].include?(v) }
o.map { |field, dir| table[field].send(dir) }
else
o
end
}
end
end
end
end
end
ActiveRecord::Relation.send :include, Squeel::Adapters::ActiveRecord::RelationExtensions