Class: ActiveRecord::Associations::HasAndBelongsToManyAssociation

Inherits:
AssociationCollection show all
Defined in:
lib/active_record/associations/has_and_belongs_to_many_association.rb

Overview

:nodoc:

Instance Method Summary collapse

Methods inherited from AssociationCollection

#<<, #create, #delete, #destroy_all, #empty?, #length, #replace, #reset, #to_ary, #uniq

Methods inherited from AssociationProxy

#loaded, #loaded?, #method_missing, #proxy_respond_to?, #reload, #respond_to?, #target, #target=

Constructor Details

#initialize(owner, association_name, association_class_name, association_class_primary_key_name, options) ⇒ HasAndBelongsToManyAssociation

Returns a new instance of HasAndBelongsToManyAssociation.



4
5
6
7
8
9
10
11
12
13
# File 'lib/active_record/associations/has_and_belongs_to_many_association.rb', line 4

def initialize(owner, association_name, association_class_name, association_class_primary_key_name, options)
  super

  @association_foreign_key = options[:association_foreign_key] || Inflector.underscore(Inflector.demodulize(association_class_name)) + "_id"
  @association_table_name = options[:table_name] || @association_class.table_name
  @join_table = options[:join_table]
  @order = options[:order]

  construct_sql
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class ActiveRecord::Associations::AssociationProxy

Instance Method Details

#build(attributes = {}) ⇒ Object



15
16
17
18
19
20
# File 'lib/active_record/associations/has_and_belongs_to_many_association.rb', line 15

def build(attributes = {})
  load_target
  record = @association_class.new(attributes)
  @target << record
  record
end

#clearObject

Removes all records from this association. Returns self so method calls may be chained.



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/active_record/associations/has_and_belongs_to_many_association.rb', line 23

def clear
  return self if size == 0 # forces load_target if hasn't happened already

  if sql = @options[:delete_sql]
    each { |record| @owner.connection.execute(sql) }
  elsif @options[:conditions] 
    sql = 
      "DELETE FROM #{@join_table} WHERE #{@association_class_primary_key_name} = #{@owner.quoted_id} " +
      "AND #{@association_foreign_key} IN (#{collect { |record| record.id }.join(", ")})"
    @owner.connection.execute(sql)
  else
    sql = "DELETE FROM #{@join_table} WHERE #{@association_class_primary_key_name} = #{@owner.quoted_id}"
    @owner.connection.execute(sql)
  end

  @target = []
  self
end

#find(*args) ⇒ Object



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/active_record/associations/has_and_belongs_to_many_association.rb', line 46

def find(*args)
  # Return an Array if multiple ids are given.
  expects_array = args.first.kind_of?(Array)

  ids = args.flatten.compact.uniq

  # If no block is given, raise RecordNotFound.
  if ids.empty?
    raise RecordNotFound, "Couldn't find #{@association_class.name} without an ID"

  # If using a custom finder_sql, scan the entire collection.
  elsif @options[:finder_sql]
    if ids.size == 1
      id = ids.first
      record = load_target.detect { |record| id == record.id }
      expects_array? ? [record] : record
    else
      load_target.select { |record| ids.include?(record.id) }
    end

  # Otherwise, construct a query.
  else
    ids_list = ids.map { |id| @owner.send(:quote, id) }.join(',')
    records = find_target(@finder_sql.sub(/(ORDER BY|$)/, " AND j.#{@association_foreign_key} IN (#{ids_list}) \\1"))
    if records.size == ids.size
      if ids.size == 1 and !expects_array
        records.first
      else
        records
      end
    else
      raise RecordNotFound, "Couldn't find #{@association_class.name} with ID in (#{ids_list})"
    end
  end
end

#find_firstObject



42
43
44
# File 'lib/active_record/associations/has_and_belongs_to_many_association.rb', line 42

def find_first
  load_target.first
end

#push_with_attributes(record, join_attributes = {}) ⇒ Object Also known as: concat_with_attributes



82
83
84
85
86
87
88
89
90
# File 'lib/active_record/associations/has_and_belongs_to_many_association.rb', line 82

def push_with_attributes(record, join_attributes = {})
  raise_on_type_mismatch(record)
  join_attributes.each { |key, value| record[key.to_s] = value }
  callback(:before_add, record)
  insert_record(record) unless @owner.new_record?
  @target << record
  callback(:after_add, record)
  self
end

#sizeObject



94
95
96
# File 'lib/active_record/associations/has_and_belongs_to_many_association.rb', line 94

def size
  @options[:uniq] ? count_records : super
end