Merged updates from trunk into stable branch
[feedcatcher.git] / vendor / rails / activerecord / lib / active_record / associations / has_one_association.rb
1 module ActiveRecord
2 module Associations
3 class HasOneAssociation < BelongsToAssociation #:nodoc:
4 def initialize(owner, reflection)
5 super
6 construct_sql
7 end
8
9 def create(attrs = {}, replace_existing = true)
10 new_record(replace_existing) do |reflection|
11 reflection.create_association(attrs)
12 end
13 end
14
15 def create!(attrs = {}, replace_existing = true)
16 new_record(replace_existing) do |reflection|
17 reflection.create_association!(attrs)
18 end
19 end
20
21 def build(attrs = {}, replace_existing = true)
22 new_record(replace_existing) do |reflection|
23 reflection.build_association(attrs)
24 end
25 end
26
27 def replace(obj, dont_save = false)
28 load_target
29
30 unless @target.nil? || @target == obj
31 if dependent? && !dont_save
32 case @reflection.options[:dependent]
33 when :delete
34 @target.delete unless @target.new_record?
35 @owner.clear_association_cache
36 when :destroy
37 @target.destroy unless @target.new_record?
38 @owner.clear_association_cache
39 when :nullify
40 @target[@reflection.primary_key_name] = nil
41 @target.save unless @owner.new_record? || @target.new_record?
42 end
43 else
44 @target[@reflection.primary_key_name] = nil
45 @target.save unless @owner.new_record? || @target.new_record?
46 end
47 end
48
49 if obj.nil?
50 @target = nil
51 else
52 raise_on_type_mismatch(obj)
53 set_belongs_to_association_for(obj)
54 @target = (AssociationProxy === obj ? obj.target : obj)
55 end
56
57 @loaded = true
58
59 unless @owner.new_record? or obj.nil? or dont_save
60 return (obj.save ? self : false)
61 else
62 return (obj.nil? ? nil : self)
63 end
64 end
65
66 protected
67 def owner_quoted_id
68 if @reflection.options[:primary_key]
69 @owner.class.quote_value(@owner.send(@reflection.options[:primary_key]))
70 else
71 @owner.quoted_id
72 end
73 end
74
75 private
76 def find_target
77 @reflection.klass.find(:first,
78 :conditions => @finder_sql,
79 :select => @reflection.options[:select],
80 :order => @reflection.options[:order],
81 :include => @reflection.options[:include],
82 :readonly => @reflection.options[:readonly]
83 )
84 end
85
86 def construct_sql
87 case
88 when @reflection.options[:as]
89 @finder_sql =
90 "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_id = #{owner_quoted_id} AND " +
91 "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_type = #{@owner.class.quote_value(@owner.class.base_class.name.to_s)}"
92 else
93 @finder_sql = "#{@reflection.quoted_table_name}.#{@reflection.primary_key_name} = #{owner_quoted_id}"
94 end
95 @finder_sql << " AND (#{conditions})" if conditions
96 end
97
98 def construct_scope
99 create_scoping = {}
100 set_belongs_to_association_for(create_scoping)
101 { :create => create_scoping }
102 end
103
104 def new_record(replace_existing)
105 # Make sure we load the target first, if we plan on replacing the existing
106 # instance. Otherwise, if the target has not previously been loaded
107 # elsewhere, the instance we create will get orphaned.
108 load_target if replace_existing
109 record = @reflection.klass.send(:with_scope, :create => construct_scope[:create]) do
110 yield @reflection
111 end
112
113 if replace_existing
114 replace(record, true)
115 else
116 record[@reflection.primary_key_name] = @owner.id unless @owner.new_record?
117 self.target = record
118 end
119
120 record
121 end
122 end
123 end
124 end