activerecord 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- data/CHANGELOG +6023 -0
- data/README.rdoc +222 -0
- data/examples/associations.png +0 -0
- data/examples/performance.rb +162 -0
- data/examples/simple.rb +14 -0
- data/lib/active_record.rb +124 -0
- data/lib/active_record/aggregations.rb +277 -0
- data/lib/active_record/association_preload.rb +403 -0
- data/lib/active_record/associations.rb +2254 -0
- data/lib/active_record/associations/association_collection.rb +562 -0
- data/lib/active_record/associations/association_proxy.rb +295 -0
- data/lib/active_record/associations/belongs_to_association.rb +91 -0
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +78 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +137 -0
- data/lib/active_record/associations/has_many_association.rb +128 -0
- data/lib/active_record/associations/has_many_through_association.rb +116 -0
- data/lib/active_record/associations/has_one_association.rb +143 -0
- data/lib/active_record/associations/has_one_through_association.rb +40 -0
- data/lib/active_record/associations/through_association_scope.rb +154 -0
- data/lib/active_record/attribute_methods.rb +60 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +33 -0
- data/lib/active_record/attribute_methods/dirty.rb +95 -0
- data/lib/active_record/attribute_methods/primary_key.rb +50 -0
- data/lib/active_record/attribute_methods/query.rb +39 -0
- data/lib/active_record/attribute_methods/read.rb +116 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -0
- data/lib/active_record/attribute_methods/write.rb +37 -0
- data/lib/active_record/autosave_association.rb +369 -0
- data/lib/active_record/base.rb +1867 -0
- data/lib/active_record/callbacks.rb +288 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +365 -0
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +113 -0
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +57 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +329 -0
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +81 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +72 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +739 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +543 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +212 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +643 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +1030 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +53 -0
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +401 -0
- data/lib/active_record/counter_cache.rb +115 -0
- data/lib/active_record/dynamic_finder_match.rb +53 -0
- data/lib/active_record/dynamic_scope_match.rb +32 -0
- data/lib/active_record/errors.rb +172 -0
- data/lib/active_record/fixtures.rb +1008 -0
- data/lib/active_record/locale/en.yml +40 -0
- data/lib/active_record/locking/optimistic.rb +172 -0
- data/lib/active_record/locking/pessimistic.rb +55 -0
- data/lib/active_record/log_subscriber.rb +48 -0
- data/lib/active_record/migration.rb +617 -0
- data/lib/active_record/named_scope.rb +138 -0
- data/lib/active_record/nested_attributes.rb +417 -0
- data/lib/active_record/observer.rb +140 -0
- data/lib/active_record/persistence.rb +291 -0
- data/lib/active_record/query_cache.rb +36 -0
- data/lib/active_record/railtie.rb +91 -0
- data/lib/active_record/railties/controller_runtime.rb +38 -0
- data/lib/active_record/railties/databases.rake +512 -0
- data/lib/active_record/reflection.rb +403 -0
- data/lib/active_record/relation.rb +393 -0
- data/lib/active_record/relation/batches.rb +89 -0
- data/lib/active_record/relation/calculations.rb +286 -0
- data/lib/active_record/relation/finder_methods.rb +355 -0
- data/lib/active_record/relation/predicate_builder.rb +41 -0
- data/lib/active_record/relation/query_methods.rb +261 -0
- data/lib/active_record/relation/spawn_methods.rb +112 -0
- data/lib/active_record/schema.rb +59 -0
- data/lib/active_record/schema_dumper.rb +195 -0
- data/lib/active_record/serialization.rb +60 -0
- data/lib/active_record/serializers/xml_serializer.rb +244 -0
- data/lib/active_record/session_store.rb +340 -0
- data/lib/active_record/test_case.rb +67 -0
- data/lib/active_record/timestamp.rb +88 -0
- data/lib/active_record/transactions.rb +356 -0
- data/lib/active_record/validations.rb +84 -0
- data/lib/active_record/validations/associated.rb +48 -0
- data/lib/active_record/validations/uniqueness.rb +185 -0
- data/lib/active_record/version.rb +9 -0
- data/lib/rails/generators/active_record.rb +27 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +25 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb +17 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +38 -0
- data/lib/rails/generators/active_record/model/templates/migration.rb +16 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +5 -0
- data/lib/rails/generators/active_record/model/templates/module.rb +5 -0
- data/lib/rails/generators/active_record/observer/observer_generator.rb +15 -0
- data/lib/rails/generators/active_record/observer/templates/observer.rb +2 -0
- data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +24 -0
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +16 -0
- metadata +224 -0
@@ -0,0 +1,295 @@
|
|
1
|
+
require 'active_support/core_ext/array/wrap'
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module Associations
|
5
|
+
# = Active Record Associations
|
6
|
+
#
|
7
|
+
# This is the root class of all association proxies:
|
8
|
+
#
|
9
|
+
# AssociationProxy
|
10
|
+
# BelongsToAssociation
|
11
|
+
# HasOneAssociation
|
12
|
+
# BelongsToPolymorphicAssociation
|
13
|
+
# AssociationCollection
|
14
|
+
# HasAndBelongsToManyAssociation
|
15
|
+
# HasManyAssociation
|
16
|
+
# HasManyThroughAssociation
|
17
|
+
# HasOneThroughAssociation
|
18
|
+
#
|
19
|
+
# Association proxies in Active Record are middlemen between the object that
|
20
|
+
# holds the association, known as the <tt>@owner</tt>, and the actual associated
|
21
|
+
# object, known as the <tt>@target</tt>. The kind of association any proxy is
|
22
|
+
# about is available in <tt>@reflection</tt>. That's an instance of the class
|
23
|
+
# ActiveRecord::Reflection::AssociationReflection.
|
24
|
+
#
|
25
|
+
# For example, given
|
26
|
+
#
|
27
|
+
# class Blog < ActiveRecord::Base
|
28
|
+
# has_many :posts
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# blog = Blog.find(:first)
|
32
|
+
#
|
33
|
+
# the association proxy in <tt>blog.posts</tt> has the object in +blog+ as
|
34
|
+
# <tt>@owner</tt>, the collection of its posts as <tt>@target</tt>, and
|
35
|
+
# the <tt>@reflection</tt> object represents a <tt>:has_many</tt> macro.
|
36
|
+
#
|
37
|
+
# This class has most of the basic instance methods removed, and delegates
|
38
|
+
# unknown methods to <tt>@target</tt> via <tt>method_missing</tt>. As a
|
39
|
+
# corner case, it even removes the +class+ method and that's why you get
|
40
|
+
#
|
41
|
+
# blog.posts.class # => Array
|
42
|
+
#
|
43
|
+
# though the object behind <tt>blog.posts</tt> is not an Array, but an
|
44
|
+
# ActiveRecord::Associations::HasManyAssociation.
|
45
|
+
#
|
46
|
+
# The <tt>@target</tt> object is not \loaded until needed. For example,
|
47
|
+
#
|
48
|
+
# blog.posts.count
|
49
|
+
#
|
50
|
+
# is computed directly through SQL and does not trigger by itself the
|
51
|
+
# instantiation of the actual post records.
|
52
|
+
class AssociationProxy #:nodoc:
|
53
|
+
alias_method :proxy_respond_to?, :respond_to?
|
54
|
+
alias_method :proxy_extend, :extend
|
55
|
+
delegate :to_param, :to => :proxy_target
|
56
|
+
instance_methods.each { |m| undef_method m unless m.to_s =~ /^(?:nil\?|send|object_id|to_a)$|^__|proxy_/ }
|
57
|
+
|
58
|
+
def initialize(owner, reflection)
|
59
|
+
@owner, @reflection = owner, reflection
|
60
|
+
@updated = false
|
61
|
+
reflection.check_validity!
|
62
|
+
Array.wrap(reflection.options[:extend]).each { |ext| proxy_extend(ext) }
|
63
|
+
reset
|
64
|
+
end
|
65
|
+
|
66
|
+
# Returns the owner of the proxy.
|
67
|
+
def proxy_owner
|
68
|
+
@owner
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns the reflection object that represents the association handled
|
72
|
+
# by the proxy.
|
73
|
+
def proxy_reflection
|
74
|
+
@reflection
|
75
|
+
end
|
76
|
+
|
77
|
+
# Returns the \target of the proxy, same as +target+.
|
78
|
+
def proxy_target
|
79
|
+
@target
|
80
|
+
end
|
81
|
+
|
82
|
+
# Does the proxy or its \target respond to +symbol+?
|
83
|
+
def respond_to?(*args)
|
84
|
+
proxy_respond_to?(*args) || (load_target && @target.respond_to?(*args))
|
85
|
+
end
|
86
|
+
|
87
|
+
# Forwards <tt>===</tt> explicitly to the \target because the instance method
|
88
|
+
# removal above doesn't catch it. Loads the \target if needed.
|
89
|
+
def ===(other)
|
90
|
+
load_target
|
91
|
+
other === @target
|
92
|
+
end
|
93
|
+
|
94
|
+
# Returns the name of the table of the related class:
|
95
|
+
#
|
96
|
+
# post.comments.aliased_table_name # => "comments"
|
97
|
+
#
|
98
|
+
def aliased_table_name
|
99
|
+
@reflection.klass.table_name
|
100
|
+
end
|
101
|
+
|
102
|
+
# Returns the SQL string that corresponds to the <tt>:conditions</tt>
|
103
|
+
# option of the macro, if given, or +nil+ otherwise.
|
104
|
+
def conditions
|
105
|
+
@conditions ||= interpolate_sql(@reflection.sanitized_conditions) if @reflection.sanitized_conditions
|
106
|
+
end
|
107
|
+
alias :sql_conditions :conditions
|
108
|
+
|
109
|
+
# Resets the \loaded flag to +false+ and sets the \target to +nil+.
|
110
|
+
def reset
|
111
|
+
@loaded = false
|
112
|
+
@target = nil
|
113
|
+
end
|
114
|
+
|
115
|
+
# Reloads the \target and returns +self+ on success.
|
116
|
+
def reload
|
117
|
+
reset
|
118
|
+
load_target
|
119
|
+
self unless @target.nil?
|
120
|
+
end
|
121
|
+
|
122
|
+
# Has the \target been already \loaded?
|
123
|
+
def loaded?
|
124
|
+
@loaded
|
125
|
+
end
|
126
|
+
|
127
|
+
# Asserts the \target has been loaded setting the \loaded flag to +true+.
|
128
|
+
def loaded
|
129
|
+
@loaded = true
|
130
|
+
end
|
131
|
+
|
132
|
+
# Returns the target of this proxy, same as +proxy_target+.
|
133
|
+
def target
|
134
|
+
@target
|
135
|
+
end
|
136
|
+
|
137
|
+
# Sets the target of this proxy to <tt>\target</tt>, and the \loaded flag to +true+.
|
138
|
+
def target=(target)
|
139
|
+
@target = target
|
140
|
+
loaded
|
141
|
+
end
|
142
|
+
|
143
|
+
# Forwards the call to the target. Loads the \target if needed.
|
144
|
+
def inspect
|
145
|
+
load_target
|
146
|
+
@target.inspect
|
147
|
+
end
|
148
|
+
|
149
|
+
def send(method, *args)
|
150
|
+
if proxy_respond_to?(method)
|
151
|
+
super
|
152
|
+
else
|
153
|
+
load_target
|
154
|
+
@target.send(method, *args)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
protected
|
159
|
+
# Does the association have a <tt>:dependent</tt> option?
|
160
|
+
def dependent?
|
161
|
+
@reflection.options[:dependent]
|
162
|
+
end
|
163
|
+
|
164
|
+
def interpolate_sql(sql, record = nil)
|
165
|
+
@owner.send(:interpolate_sql, sql, record)
|
166
|
+
end
|
167
|
+
|
168
|
+
# Forwards the call to the reflection class.
|
169
|
+
def sanitize_sql(sql, table_name = @reflection.klass.table_name)
|
170
|
+
@reflection.klass.send(:sanitize_sql, sql, table_name)
|
171
|
+
end
|
172
|
+
|
173
|
+
# Assigns the ID of the owner to the corresponding foreign key in +record+.
|
174
|
+
# If the association is polymorphic the type of the owner is also set.
|
175
|
+
def set_belongs_to_association_for(record)
|
176
|
+
if @reflection.options[:as]
|
177
|
+
record["#{@reflection.options[:as]}_id"] = @owner.id unless @owner.new_record?
|
178
|
+
record["#{@reflection.options[:as]}_type"] = @owner.class.base_class.name.to_s
|
179
|
+
else
|
180
|
+
unless @owner.new_record?
|
181
|
+
primary_key = @reflection.options[:primary_key] || :id
|
182
|
+
record[@reflection.primary_key_name] = @owner.send(primary_key)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
# Merges into +options+ the ones coming from the reflection.
|
188
|
+
def merge_options_from_reflection!(options)
|
189
|
+
options.reverse_merge!(
|
190
|
+
:group => @reflection.options[:group],
|
191
|
+
:having => @reflection.options[:having],
|
192
|
+
:limit => @reflection.options[:limit],
|
193
|
+
:offset => @reflection.options[:offset],
|
194
|
+
:joins => @reflection.options[:joins],
|
195
|
+
:include => @reflection.options[:include],
|
196
|
+
:select => @reflection.options[:select],
|
197
|
+
:readonly => @reflection.options[:readonly]
|
198
|
+
)
|
199
|
+
end
|
200
|
+
|
201
|
+
# Forwards +with_scope+ to the reflection.
|
202
|
+
def with_scope(*args, &block)
|
203
|
+
@reflection.klass.send :with_scope, *args, &block
|
204
|
+
end
|
205
|
+
|
206
|
+
private
|
207
|
+
# Forwards any missing method call to the \target.
|
208
|
+
def method_missing(method, *args)
|
209
|
+
if load_target
|
210
|
+
unless @target.respond_to?(method)
|
211
|
+
message = "undefined method `#{method.to_s}' for \"#{@target}\":#{@target.class.to_s}"
|
212
|
+
raise NoMethodError, message
|
213
|
+
end
|
214
|
+
|
215
|
+
if block_given?
|
216
|
+
@target.send(method, *args) { |*block_args| yield(*block_args) }
|
217
|
+
else
|
218
|
+
@target.send(method, *args)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
# Loads the \target if needed and returns it.
|
224
|
+
#
|
225
|
+
# This method is abstract in the sense that it relies on +find_target+,
|
226
|
+
# which is expected to be provided by descendants.
|
227
|
+
#
|
228
|
+
# If the \target is already \loaded it is just returned. Thus, you can call
|
229
|
+
# +load_target+ unconditionally to get the \target.
|
230
|
+
#
|
231
|
+
# ActiveRecord::RecordNotFound is rescued within the method, and it is
|
232
|
+
# not reraised. The proxy is \reset and +nil+ is the return value.
|
233
|
+
def load_target
|
234
|
+
return nil unless defined?(@loaded)
|
235
|
+
|
236
|
+
if !loaded? and (!@owner.new_record? || foreign_key_present)
|
237
|
+
@target = find_target
|
238
|
+
end
|
239
|
+
|
240
|
+
@loaded = true
|
241
|
+
@target
|
242
|
+
rescue ActiveRecord::RecordNotFound
|
243
|
+
reset
|
244
|
+
end
|
245
|
+
|
246
|
+
# Can be overwritten by associations that might have the foreign key
|
247
|
+
# available for an association without having the object itself (and
|
248
|
+
# still being a new record). Currently, only +belongs_to+ presents
|
249
|
+
# this scenario (both vanilla and polymorphic).
|
250
|
+
def foreign_key_present
|
251
|
+
false
|
252
|
+
end
|
253
|
+
|
254
|
+
# Raises ActiveRecord::AssociationTypeMismatch unless +record+ is of
|
255
|
+
# the kind of the class of the associated objects. Meant to be used as
|
256
|
+
# a sanity check when you are about to assign an associated record.
|
257
|
+
def raise_on_type_mismatch(record)
|
258
|
+
unless record.is_a?(@reflection.klass) || record.is_a?(@reflection.class_name.constantize)
|
259
|
+
message = "#{@reflection.class_name}(##{@reflection.klass.object_id}) expected, got #{record.class}(##{record.class.object_id})"
|
260
|
+
raise ActiveRecord::AssociationTypeMismatch, message
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
if RUBY_VERSION < '1.9.2'
|
265
|
+
# Array#flatten has problems with recursive arrays before Ruby 1.9.2.
|
266
|
+
# Going one level deeper solves the majority of the problems.
|
267
|
+
def flatten_deeper(array)
|
268
|
+
array.collect { |element| (element.respond_to?(:flatten) && !element.is_a?(Hash)) ? element.flatten : element }.flatten
|
269
|
+
end
|
270
|
+
else
|
271
|
+
def flatten_deeper(array)
|
272
|
+
array.flatten
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
# Returns the ID of the owner, quoted if needed.
|
277
|
+
def owner_quoted_id
|
278
|
+
@owner.quoted_id
|
279
|
+
end
|
280
|
+
|
281
|
+
def set_inverse_instance(record, instance)
|
282
|
+
return if record.nil? || !we_can_set_the_inverse_on_this?(record)
|
283
|
+
inverse_relationship = @reflection.inverse_of
|
284
|
+
unless inverse_relationship.nil?
|
285
|
+
record.send(:"set_#{inverse_relationship.name}_target", instance)
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
# Override in subclasses
|
290
|
+
def we_can_set_the_inverse_on_this?(record)
|
291
|
+
false
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
# = Active Record Belongs To Associations
|
3
|
+
module Associations
|
4
|
+
class BelongsToAssociation < AssociationProxy #:nodoc:
|
5
|
+
def create(attributes = {})
|
6
|
+
replace(@reflection.create_association(attributes))
|
7
|
+
end
|
8
|
+
|
9
|
+
def build(attributes = {})
|
10
|
+
replace(@reflection.build_association(attributes))
|
11
|
+
end
|
12
|
+
|
13
|
+
def replace(record)
|
14
|
+
counter_cache_name = @reflection.counter_cache_column
|
15
|
+
|
16
|
+
if record.nil?
|
17
|
+
if counter_cache_name && !@owner.new_record?
|
18
|
+
@reflection.klass.decrement_counter(counter_cache_name, previous_record_id) if @owner[@reflection.primary_key_name]
|
19
|
+
end
|
20
|
+
|
21
|
+
@target = @owner[@reflection.primary_key_name] = nil
|
22
|
+
else
|
23
|
+
raise_on_type_mismatch(record)
|
24
|
+
|
25
|
+
if counter_cache_name && !@owner.new_record? && record.id != @owner[@reflection.primary_key_name]
|
26
|
+
@reflection.klass.increment_counter(counter_cache_name, record.id)
|
27
|
+
@reflection.klass.decrement_counter(counter_cache_name, @owner[@reflection.primary_key_name]) if @owner[@reflection.primary_key_name]
|
28
|
+
end
|
29
|
+
|
30
|
+
@target = (AssociationProxy === record ? record.target : record)
|
31
|
+
@owner[@reflection.primary_key_name] = record_id(record) unless record.new_record?
|
32
|
+
@updated = true
|
33
|
+
end
|
34
|
+
|
35
|
+
set_inverse_instance(record, @owner)
|
36
|
+
|
37
|
+
loaded
|
38
|
+
record
|
39
|
+
end
|
40
|
+
|
41
|
+
def updated?
|
42
|
+
@updated
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
def find_target
|
47
|
+
find_method = if @reflection.options[:primary_key]
|
48
|
+
"find_by_#{@reflection.options[:primary_key]}"
|
49
|
+
else
|
50
|
+
"find"
|
51
|
+
end
|
52
|
+
|
53
|
+
options = @reflection.options.dup
|
54
|
+
(options.keys - [:select, :include, :readonly]).each do |key|
|
55
|
+
options.delete key
|
56
|
+
end
|
57
|
+
options[:conditions] = conditions
|
58
|
+
|
59
|
+
the_target = @reflection.klass.send(find_method,
|
60
|
+
@owner[@reflection.primary_key_name],
|
61
|
+
options
|
62
|
+
) if @owner[@reflection.primary_key_name]
|
63
|
+
set_inverse_instance(the_target, @owner)
|
64
|
+
the_target
|
65
|
+
end
|
66
|
+
|
67
|
+
def foreign_key_present
|
68
|
+
!@owner[@reflection.primary_key_name].nil?
|
69
|
+
end
|
70
|
+
|
71
|
+
# NOTE - for now, we're only supporting inverse setting from belongs_to back onto
|
72
|
+
# has_one associations.
|
73
|
+
def we_can_set_the_inverse_on_this?(record)
|
74
|
+
@reflection.has_inverse? && @reflection.inverse_of.macro == :has_one
|
75
|
+
end
|
76
|
+
|
77
|
+
def record_id(record)
|
78
|
+
record.send(@reflection.options[:primary_key] || :id)
|
79
|
+
end
|
80
|
+
|
81
|
+
def previous_record_id
|
82
|
+
@previous_record_id ||= if @reflection.options[:primary_key]
|
83
|
+
previous_record = @owner.send(@reflection.name)
|
84
|
+
previous_record.nil? ? nil : previous_record.id
|
85
|
+
else
|
86
|
+
@owner[@reflection.primary_key_name]
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
# = Active Record Belongs To Polymorphic Association
|
3
|
+
module Associations
|
4
|
+
class BelongsToPolymorphicAssociation < AssociationProxy #:nodoc:
|
5
|
+
def replace(record)
|
6
|
+
if record.nil?
|
7
|
+
@target = @owner[@reflection.primary_key_name] = @owner[@reflection.options[:foreign_type]] = nil
|
8
|
+
else
|
9
|
+
@target = (AssociationProxy === record ? record.target : record)
|
10
|
+
|
11
|
+
@owner[@reflection.primary_key_name] = record_id(record)
|
12
|
+
@owner[@reflection.options[:foreign_type]] = record.class.base_class.name.to_s
|
13
|
+
|
14
|
+
@updated = true
|
15
|
+
end
|
16
|
+
|
17
|
+
set_inverse_instance(record, @owner)
|
18
|
+
loaded
|
19
|
+
record
|
20
|
+
end
|
21
|
+
|
22
|
+
def updated?
|
23
|
+
@updated
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
# NOTE - for now, we're only supporting inverse setting from belongs_to back onto
|
29
|
+
# has_one associations.
|
30
|
+
def we_can_set_the_inverse_on_this?(record)
|
31
|
+
if @reflection.has_inverse?
|
32
|
+
inverse_association = @reflection.polymorphic_inverse_of(record.class)
|
33
|
+
inverse_association && inverse_association.macro == :has_one
|
34
|
+
else
|
35
|
+
false
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def set_inverse_instance(record, instance)
|
40
|
+
return if record.nil? || !we_can_set_the_inverse_on_this?(record)
|
41
|
+
inverse_relationship = @reflection.polymorphic_inverse_of(record.class)
|
42
|
+
unless inverse_relationship.nil?
|
43
|
+
record.send(:"set_#{inverse_relationship.name}_target", instance)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def find_target
|
48
|
+
return nil if association_class.nil?
|
49
|
+
|
50
|
+
target =
|
51
|
+
if @reflection.options[:conditions]
|
52
|
+
association_class.find(
|
53
|
+
@owner[@reflection.primary_key_name],
|
54
|
+
:select => @reflection.options[:select],
|
55
|
+
:conditions => conditions,
|
56
|
+
:include => @reflection.options[:include]
|
57
|
+
)
|
58
|
+
else
|
59
|
+
association_class.find(@owner[@reflection.primary_key_name], :select => @reflection.options[:select], :include => @reflection.options[:include])
|
60
|
+
end
|
61
|
+
set_inverse_instance(target, @owner)
|
62
|
+
target
|
63
|
+
end
|
64
|
+
|
65
|
+
def foreign_key_present
|
66
|
+
!@owner[@reflection.primary_key_name].nil?
|
67
|
+
end
|
68
|
+
|
69
|
+
def record_id(record)
|
70
|
+
record.send(@reflection.options[:primary_key] || :id)
|
71
|
+
end
|
72
|
+
|
73
|
+
def association_class
|
74
|
+
@owner[@reflection.options[:foreign_type]] ? @owner[@reflection.options[:foreign_type]].constantize : nil
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|