Froze rails gems
[depot.git] / vendor / rails / activerecord / lib / active_record / associations / has_many_association.rb
1 module ActiveRecord
2 module Associations
3 # This is the proxy that handles a has many association.
4 #
5 # If the association has a <tt>:through</tt> option further specialization
6 # is provided by its child HasManyThroughAssociation.
7 class HasManyAssociation < AssociationCollection #:nodoc:
8 protected
9 def owner_quoted_id
10 if @reflection.options[:primary_key]
11 quote_value(@owner.send(@reflection.options[:primary_key]))
12 else
13 @owner.quoted_id
14 end
15 end
16
17 # Returns the number of records in this collection.
18 #
19 # If the association has a counter cache it gets that value. Otherwise
20 # it will attempt to do a count via SQL, bounded to <tt>:limit</tt> if
21 # there's one. Some configuration options like :group make it impossible
22 # to do a SQL count, in those cases the array count will be used.
23 #
24 # That does not depend on whether the collection has already been loaded
25 # or not. The +size+ method is the one that takes the loaded flag into
26 # account and delegates to +count_records+ if needed.
27 #
28 # If the collection is empty the target is set to an empty array and
29 # the loaded flag is set to true as well.
30 def count_records
31 count = if has_cached_counter?
32 @owner.send(:read_attribute, cached_counter_attribute_name)
33 elsif @reflection.options[:counter_sql]
34 @reflection.klass.count_by_sql(@counter_sql)
35 else
36 @reflection.klass.count(:conditions => @counter_sql, :include => @reflection.options[:include])
37 end
38
39 # If there's nothing in the database and @target has no new records
40 # we are certain the current target is an empty array. This is a
41 # documented side-effect of the method that may avoid an extra SELECT.
42 @target ||= [] and loaded if count == 0
43
44 if @reflection.options[:limit]
45 count = [ @reflection.options[:limit], count ].min
46 end
47
48 return count
49 end
50
51 def has_cached_counter?
52 @owner.attribute_present?(cached_counter_attribute_name)
53 end
54
55 def cached_counter_attribute_name
56 "#{@reflection.name}_count"
57 end
58
59 def insert_record(record)
60 set_belongs_to_association_for(record)
61 record.save
62 end
63
64 # Deletes the records according to the <tt>:dependent</tt> option.
65 def delete_records(records)
66 case @reflection.options[:dependent]
67 when :destroy
68 records.each { |r| r.destroy }
69 when :delete_all
70 @reflection.klass.delete(records.map { |record| record.id })
71 else
72 ids = quoted_record_ids(records)
73 @reflection.klass.update_all(
74 "#{@reflection.primary_key_name} = NULL",
75 "#{@reflection.primary_key_name} = #{owner_quoted_id} AND #{@reflection.klass.primary_key} IN (#{ids})"
76 )
77 end
78 end
79
80 def target_obsolete?
81 false
82 end
83
84 def construct_sql
85 case
86 when @reflection.options[:finder_sql]
87 @finder_sql = interpolate_sql(@reflection.options[:finder_sql])
88
89 when @reflection.options[:as]
90 @finder_sql =
91 "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_id = #{owner_quoted_id} AND " +
92 "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_type = #{@owner.class.quote_value(@owner.class.base_class.name.to_s)}"
93 @finder_sql << " AND (#{conditions})" if conditions
94
95 else
96 @finder_sql = "#{@reflection.quoted_table_name}.#{@reflection.primary_key_name} = #{owner_quoted_id}"
97 @finder_sql << " AND (#{conditions})" if conditions
98 end
99
100 if @reflection.options[:counter_sql]
101 @counter_sql = interpolate_sql(@reflection.options[:counter_sql])
102 elsif @reflection.options[:finder_sql]
103 # replace the SELECT clause with COUNT(*), preserving any hints within /* ... */
104 @reflection.options[:counter_sql] = @reflection.options[:finder_sql].sub(/SELECT (\/\*.*?\*\/ )?(.*)\bFROM\b/im) { "SELECT #{$1}COUNT(*) FROM" }
105 @counter_sql = interpolate_sql(@reflection.options[:counter_sql])
106 else
107 @counter_sql = @finder_sql
108 end
109 end
110
111 def construct_scope
112 create_scoping = {}
113 set_belongs_to_association_for(create_scoping)
114 {
115 :find => { :conditions => @finder_sql, :readonly => false, :order => @reflection.options[:order], :limit => @reflection.options[:limit], :include => @reflection.options[:include]},
116 :create => create_scoping
117 }
118 end
119 end
120 end
121 end