Class: ActiveRecord::Associations::HasManyThroughAssociation

Inherits:
AssociationProxy show all
Defined in:
lib/active_record/associations/has_many_through_association.rb

Overview

:nodoc:

Instance Attribute Summary

Attributes inherited from AssociationProxy

#reflection

Instance Method Summary collapse

Methods inherited from AssociationProxy

#===, #aliased_table_name, #loaded, #loaded?, #proxy_owner, #proxy_reflection, #proxy_respond_to?, #proxy_target, #reload, #respond_to?, #target, #target=

Constructor Details

#initialize(owner, reflection) ⇒ HasManyThroughAssociation

Returns a new instance of HasManyThroughAssociation.



4
5
6
7
8
9
# File 'lib/active_record/associations/has_many_through_association.rb', line 4

def initialize(owner, reflection)
  super
  reflection.check_validity!
  @finder_sql = construct_conditions
  construct_sql
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args, &block) ⇒ Object (protected)



116
117
118
119
120
121
122
# File 'lib/active_record/associations/has_many_through_association.rb', line 116

def method_missing(method, *args, &block)
  if @target.respond_to?(method) || (!@reflection.klass.respond_to?(method) && Class.respond_to?(method))
    super
  else
    @reflection.klass.with_scope(construct_scope) { @reflection.klass.send(method, *args, &block) }
  end
end

Instance Method Details

#<<(*records) ⇒ Object

Adds records to the association. The source record and its associates must have ids in order to create records associating them, so this will raise ActiveRecord::HasManyThroughCantAssociateNewRecords if either is a new record. Calls create! so you can rescue errors.

The :before_add and :after_add callbacks are not yet supported.



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/active_record/associations/has_many_through_association.rb', line 49

def <<(*records)
  return if records.empty?
  through = @reflection.through_reflection
  raise ActiveRecord::HasManyThroughCantAssociateNewRecords.new(@owner, through) if @owner.new_record?

  load_target

  klass = through.klass
  klass.transaction do
    flatten_deeper(records).each do |associate|
      raise_on_type_mismatch(associate)
      raise ActiveRecord::HasManyThroughCantAssociateNewRecords.new(@owner, through) unless associate.respond_to?(:new_record?) && !associate.new_record?

      @owner.send(@reflection.through_reflection.name).proxy_target << klass.with_scope(:create => construct_join_attributes(associate)) { klass.create! }
      @target << associate
    end
  end

  self
end

#build(attrs = nil) ⇒ Object



90
91
92
# File 'lib/active_record/associations/has_many_through_association.rb', line 90

def build(attrs = nil)
  raise ActiveRecord::HasManyThroughCantAssociateNewRecords.new(@owner, @reflection.through_reflection)
end

#count(*args) ⇒ Object



105
106
107
108
109
110
111
112
113
# File 'lib/active_record/associations/has_many_through_association.rb', line 105

def count(*args)
  column_name, options = @reflection.klass.send(:construct_count_options_from_legacy_args, *args)
  if @reflection.options[:uniq]
    # This is needed becase 'SELECT count(DISTINCT *)..' is not valid sql statement.
    column_name = "#{@reflection.klass.table_name}.#{@reflection.klass.primary_key}" if column_name == :all
    options.merge!(:distinct => true) 
  end
  @reflection.klass.send(:with_scope, construct_scope) { @reflection.klass.count(column_name, options) } 
end

#create!(attrs = nil) ⇒ Object



94
95
96
97
98
# File 'lib/active_record/associations/has_many_through_association.rb', line 94

def create!(attrs = nil)
  @reflection.klass.transaction do
    self << @reflection.klass.with_scope(:create => attrs) { @reflection.klass.create! }
  end
end

#delete(*records) ⇒ Object

Remove records from this association. Does not destroy records.



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/active_record/associations/has_many_through_association.rb', line 73

def delete(*records)
  records = flatten_deeper(records)
  records.each { |associate| raise_on_type_mismatch(associate) }
  records.reject! { |associate| @target.delete(associate) if associate.new_record? }
  return if records.empty?
  
  @delete_join_finder ||= "find_all_by_#{@reflection.source_reflection.association_foreign_key}"
  through = @reflection.through_reflection
  through.klass.transaction do
    records.each do |associate|
      joins = @owner.send(through.name).send(@delete_join_finder, associate.id)
      @owner.send(through.name).delete(joins)
      @target.delete(associate)
    end
  end
end

#find(*args) ⇒ Object



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
# File 'lib/active_record/associations/has_many_through_association.rb', line 11

def find(*args)
  options = Base.send(:extract_options_from_args!, args)

  conditions = "#{@finder_sql}"
  if sanitized_conditions = sanitize_sql(options[:conditions])
    conditions << " AND (#{sanitized_conditions})"
  end
  options[:conditions] = conditions

  if options[:order] && @reflection.options[:order]
    options[:order] = "#{options[:order]}, #{@reflection.options[:order]}"
  elsif @reflection.options[:order]
    options[:order] = @reflection.options[:order]
  end

  options[:select]  = construct_select(options[:select])
  options[:from]  ||= construct_from
  options[:joins]   = construct_joins(options[:joins])
  options[:include] = @reflection.source_reflection.options[:include] if options[:include].nil?

  merge_options_from_reflection!(options)

  # Pass through args exactly as we received them.
  args << options
  @reflection.klass.find(*args)
end

#resetObject



38
39
40
41
# File 'lib/active_record/associations/has_many_through_association.rb', line 38

def reset
  @target = []
  @loaded = false
end

#sum(*args, &block) ⇒ Object

Calculate sum using SQL, not Enumerable



101
102
103
# File 'lib/active_record/associations/has_many_through_association.rb', line 101

def sum(*args, &block)
  calculate(:sum, *args, &block)
end