~michaelforrest/use-case-mapper/trunk

« back to all changes in this revision

Viewing changes to vendor/rails/activerecord/lib/active_record/associations/has_many_association.rb

  • Committer: Michael Forrest
  • Date: 2010-10-15 16:28:50 UTC
  • Revision ID: michael.forrest@canonical.com-20101015162850-tj2vchanv0kr0dun
refrozeĀ gems

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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, force = false, validate = true)
 
60
          set_belongs_to_association_for(record)
 
61
          force ? record.save! : record.save(validate)
 
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
              @owner.class.update_counters(@owner.id, cached_counter_attribute_name => -records.size) if has_cached_counter?
 
78
          end
 
79
        end
 
80
 
 
81
        def target_obsolete?
 
82
          false
 
83
        end
 
84
 
 
85
        def construct_sql
 
86
          case
 
87
            when @reflection.options[:finder_sql]
 
88
              @finder_sql = interpolate_sql(@reflection.options[:finder_sql])
 
89
 
 
90
            when @reflection.options[:as]
 
91
              @finder_sql = 
 
92
                "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_id = #{owner_quoted_id} AND " +
 
93
                "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_type = #{@owner.class.quote_value(@owner.class.base_class.name.to_s)}"
 
94
              @finder_sql << " AND (#{conditions})" if conditions
 
95
            
 
96
            else
 
97
              @finder_sql = "#{@reflection.quoted_table_name}.#{@reflection.primary_key_name} = #{owner_quoted_id}"
 
98
              @finder_sql << " AND (#{conditions})" if conditions
 
99
          end
 
100
 
 
101
          if @reflection.options[:counter_sql]
 
102
            @counter_sql = interpolate_sql(@reflection.options[:counter_sql])
 
103
          elsif @reflection.options[:finder_sql]
 
104
            # replace the SELECT clause with COUNT(*), preserving any hints within /* ... */
 
105
            @reflection.options[:counter_sql] = @reflection.options[:finder_sql].sub(/SELECT (\/\*.*?\*\/ )?(.*)\bFROM\b/im) { "SELECT #{$1}COUNT(*) FROM" }
 
106
            @counter_sql = interpolate_sql(@reflection.options[:counter_sql])
 
107
          else
 
108
            @counter_sql = @finder_sql
 
109
          end
 
110
        end
 
111
 
 
112
        def construct_scope
 
113
          create_scoping = {}
 
114
          set_belongs_to_association_for(create_scoping)
 
115
          {
 
116
            :find => { :conditions => @finder_sql, :readonly => false, :order => @reflection.options[:order], :limit => @reflection.options[:limit], :include => @reflection.options[:include]},
 
117
            :create => create_scoping
 
118
          }
 
119
        end
 
120
    end
 
121
  end
 
122
end