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
|
require 'active_support/concern'
require 'mutex_m'
module ActiveRecord
module Delegation # :nodoc:
extend ActiveSupport::Concern
# This module creates compiled delegation methods dynamically at runtime, which makes
# subsequent calls to that method faster by avoiding method_missing. The delegations
# may vary depending on the klass of a relation, so we create a subclass of Relation
# for each different klass, and the delegations are compiled into that subclass only.
delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to_ary, :to => :to_a
delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key,
:connection, :columns_hash, :auto_explain_threshold_in_seconds, :to => :klass
module ClassSpecificRelation
extend ActiveSupport::Concern
included do
@delegation_mutex = Mutex.new
end
module ClassMethods
def name
superclass.name
end
def delegate_to_scoped_klass(method)
@delegation_mutex.synchronize do
return if method_defined?(method)
if method.to_s =~ /\A[a-zA-Z_]\w*[!?]?\z/
module_eval <<-RUBY, __FILE__, __LINE__ + 1
def #{method}(*args, &block)
scoping { @klass.#{method}(*args, &block) }
end
RUBY
else
module_eval <<-RUBY, __FILE__, __LINE__ + 1
def #{method}(*args, &block)
scoping { @klass.send(#{method.inspect}, *args, &block) }
end
RUBY
end
end
end
def delegate(method, opts = {})
@delegation_mutex.synchronize do
return if method_defined?(method)
super
end
end
end
protected
def method_missing(method, *args, &block)
if @klass.respond_to?(method)
self.class.delegate_to_scoped_klass(method)
scoping { @klass.send(method, *args, &block) }
elsif Array.method_defined?(method)
self.class.delegate method, :to => :to_a
to_a.send(method, *args, &block)
elsif arel.respond_to?(method)
self.class.delegate method, :to => :arel
arel.send(method, *args, &block)
else
super
end
end
end
module ClassMethods
# This hash is keyed by klass.name to avoid memory leaks in development mode
@@subclasses = Hash.new { |h, k| h[k] = {} }.extend(Mutex_m)
def new(klass, *args)
relation = relation_class_for(klass).allocate
relation.__send__(:initialize, klass, *args)
relation
end
# Cache the constants in @@subclasses because looking them up via const_get
# make instantiation significantly slower.
def relation_class_for(klass)
if klass && klass.name
if subclass = @@subclasses.synchronize { @@subclasses[self][klass.name] }
subclass
else
subclass = const_get("#{name.gsub('::', '_')}_#{klass.name.gsub('::', '_')}", false)
@@subclasses.synchronize { @@subclasses[self][klass.name] = subclass }
subclass
end
else
ActiveRecord::Relation
end
end
# Check const_defined? in case another thread has already defined the constant.
# I am not sure whether this is strictly necessary.
def const_missing(name)
@@subclasses.synchronize {
if const_defined?(name)
const_get(name)
else
const_set(name, Class.new(self) { include ClassSpecificRelation })
end
}
end
end
def respond_to?(method, include_private = false)
super || Array.method_defined?(method) ||
@klass.respond_to?(method, include_private) ||
arel.respond_to?(method, include_private)
end
protected
def method_missing(method, *args, &block)
if @klass.respond_to?(method)
scoping { @klass.send(method, *args, &block) }
elsif Array.method_defined?(method)
to_a.send(method, *args, &block)
elsif arel.respond_to?(method)
arel.send(method, *args, &block)
else
super
end
end
end
end
|