Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 165 lines (137 sloc) 6.113 kb
db045db @dhh Initial
dhh authored
1 module ActiveRecord
2 module Associations
3 class HasAndBelongsToManyAssociation < AssociationCollection #:nodoc:
6abda69 @dhh Added preliminary support for join models [DHH] Added preliminary suppor...
dhh authored
4 def initialize(owner, reflection)
823554e @dhh Added support for associating unsaved objects #402 [Tim Bates]
dhh authored
5 super
6 construct_sql
db045db @dhh Initial
dhh authored
7 end
800b899 @jeremy Remove deprecated push_with_attributes.
jeremy authored
8
823554e @dhh Added support for associating unsaved objects #402 [Tim Bates]
dhh authored
9 def build(attributes = {})
10 load_target
a72c1ec @jeremy Refactor association create and build so before & after callbacks behave...
jeremy authored
11 build_record(attributes)
823554e @dhh Added support for associating unsaved objects #402 [Tim Bates]
dhh authored
12 end
13
06075a9 @technoweenie Fix the has_and_belongs_to_many #create doesn't populate the join for ne...
technoweenie authored
14 def create(attributes = {})
a72c1ec @jeremy Refactor association create and build so before & after callbacks behave...
jeremy authored
15 create_record(attributes) { |record| insert_record(record) }
06075a9 @technoweenie Fix the has_and_belongs_to_many #create doesn't populate the join for ne...
technoweenie authored
16 end
4979448 @technoweenie Ensure that modifying has_and_belongs_to_many actions clear the query ca...
technoweenie authored
17
7010ee3 @NZKoz Stop users from calling .create on a has_many / habtm association when t...
NZKoz authored
18 def create!(attributes = {})
a72c1ec @jeremy Refactor association create and build so before & after callbacks behave...
jeremy authored
19 create_record(attributes) { |record| insert_record(record, true) }
7010ee3 @NZKoz Stop users from calling .create on a has_many / habtm association when t...
NZKoz authored
20 end
06075a9 @technoweenie Fix the has_and_belongs_to_many #create doesn't populate the join for ne...
technoweenie authored
21
6bd672e @dhh Added that Base#find takes an optional options hash, including :conditio...
dhh authored
22 def find_first
823554e @dhh Added support for associating unsaved objects #402 [Tim Bates]
dhh authored
23 load_target.first
6bd672e @dhh Added that Base#find takes an optional options hash, including :conditio...
dhh authored
24 end
800b899 @jeremy Remove deprecated push_with_attributes.
jeremy authored
25
6bd672e @dhh Added that Base#find takes an optional options hash, including :conditio...
dhh authored
26 def find(*args)
edd68a5 @dhh Refactored in use of extract_options! (closes #9079) [josh]
dhh authored
27 options = args.extract_options!
6bd672e @dhh Added that Base#find takes an optional options hash, including :conditio...
dhh authored
28
29 # If using a custom finder_sql, scan the entire collection.
6abda69 @dhh Added preliminary support for join models [DHH] Added preliminary suppor...
dhh authored
30 if @reflection.options[:finder_sql]
34f9d30 @dhh Added support for calling constrained class methods on has_many and has_...
dhh authored
31 expects_array = args.first.kind_of?(Array)
32 ids = args.flatten.compact.uniq
33
6bd672e @dhh Added that Base#find takes an optional options hash, including :conditio...
dhh authored
34 if ids.size == 1
4307d7e @dhh Fixed various problems with has_and_belongs_to_many when using customer ...
dhh authored
35 id = ids.first.to_i
8b5f4e4 @jeremy Ruby 1.9 compat: fix warnings, shadowed block vars, and unitialized inst...
jeremy authored
36 record = load_target.detect { |r| id == r.id }
4307d7e @dhh Fixed various problems with has_and_belongs_to_many when using customer ...
dhh authored
37 expects_array ? [record] : record
6bd672e @dhh Added that Base#find takes an optional options hash, including :conditio...
dhh authored
38 else
8b5f4e4 @jeremy Ruby 1.9 compat: fix warnings, shadowed block vars, and unitialized inst...
jeremy authored
39 load_target.select { |r| ids.include?(r.id) }
6bd672e @dhh Added that Base#find takes an optional options hash, including :conditio...
dhh authored
40 end
db045db @dhh Initial
dhh authored
41 else
34f9d30 @dhh Added support for calling constrained class methods on has_many and has_...
dhh authored
42 conditions = "#{@finder_sql}"
6abda69 @dhh Added preliminary support for join models [DHH] Added preliminary suppor...
dhh authored
43
34f9d30 @dhh Added support for calling constrained class methods on has_many and has_...
dhh authored
44 if sanitized_conditions = sanitize_sql(options[:conditions])
328ef3f @jeremy Parenthesize :conditions
jeremy authored
45 conditions << " AND (#{sanitized_conditions})"
34f9d30 @dhh Added support for calling constrained class methods on has_many and has_...
dhh authored
46 end
6abda69 @dhh Added preliminary support for join models [DHH] Added preliminary suppor...
dhh authored
47
34f9d30 @dhh Added support for calling constrained class methods on has_many and has_...
dhh authored
48 options[:conditions] = conditions
53aa8da @dhh Fixed that records returned from has_and_belongs_to_many associations wi...
dhh authored
49 options[:joins] = @join_sql
2e47db2 @dhh Fixed that habtm associations should be able to set :select as part of t...
dhh authored
50 options[:readonly] = finding_with_ambiguous_select?(options[:select] || @reflection.options[:select])
34f9d30 @dhh Added support for calling constrained class methods on has_many and has_...
dhh authored
51
6abda69 @dhh Added preliminary support for join models [DHH] Added preliminary suppor...
dhh authored
52 if options[:order] && @reflection.options[:order]
53 options[:order] = "#{options[:order]}, #{@reflection.options[:order]}"
54 elsif @reflection.options[:order]
55 options[:order] = @reflection.options[:order]
db045db @dhh Initial
dhh authored
56 end
34f9d30 @dhh Added support for calling constrained class methods on has_many and has_...
dhh authored
57
e5d9ad3 @dhh Added option inheritance for find calls on has_and_belongs_to_many and h...
dhh authored
58 merge_options_from_reflection!(options)
59
2e47db2 @dhh Fixed that habtm associations should be able to set :select as part of t...
dhh authored
60 options[:select] ||= (@reflection.options[:select] || '*')
d4bf5e9 @NZKoz Make habtm respect the :select option. Closes #9207. [Aleksey Kondratenk...
NZKoz authored
61
34f9d30 @dhh Added support for calling constrained class methods on has_many and has_...
dhh authored
62 # Pass through args exactly as we received them.
63 args << options
6abda69 @dhh Added preliminary support for join models [DHH] Added preliminary suppor...
dhh authored
64 @reflection.klass.find(*args)
db045db @dhh Initial
dhh authored
65 end
66 end
67
68 protected
69 def count_records
823554e @dhh Added support for associating unsaved objects #402 [Tim Bates]
dhh authored
70 load_target.size
db045db @dhh Initial
dhh authored
71 end
72
7010ee3 @NZKoz Stop users from calling .create on a has_many / habtm association when t...
NZKoz authored
73 def insert_record(record, force=true)
85fbb22 @dhh Backed out of new_record? to new? transformation as it would screw up ex...
dhh authored
74 if record.new_record?
7010ee3 @NZKoz Stop users from calling .create on a has_many / habtm association when t...
NZKoz authored
75 if force
76 record.save!
77 else
78 return false unless record.save
79 end
a2f26b9 @dhh Fixed that adding a record to a has_and_belongs_to collection would alwa...
dhh authored
80 end
b29c01e @dhh Added that has_and_belongs_to_many associations with additional attribut...
dhh authored
81
6abda69 @dhh Added preliminary support for join models [DHH] Added preliminary suppor...
dhh authored
82 if @reflection.options[:insert_sql]
4979448 @technoweenie Ensure that modifying has_and_belongs_to_many actions clear the query ca...
technoweenie authored
83 @owner.connection.insert(interpolate_sql(@reflection.options[:insert_sql], record))
db045db @dhh Initial
dhh authored
84 else
6abda69 @dhh Added preliminary support for join models [DHH] Added preliminary suppor...
dhh authored
85 columns = @owner.connection.columns(@reflection.options[:join_table], "#{@reflection.options[:join_table]} Columns")
b29c01e @dhh Added that has_and_belongs_to_many associations with additional attribut...
dhh authored
86
8b5f4e4 @jeremy Ruby 1.9 compat: fix warnings, shadowed block vars, and unitialized inst...
jeremy authored
87 attributes = columns.inject({}) do |attrs, column|
b29c01e @dhh Added that has_and_belongs_to_many associations with additional attribut...
dhh authored
88 case column.name
6abda69 @dhh Added preliminary support for join models [DHH] Added preliminary suppor...
dhh authored
89 when @reflection.primary_key_name
8b5f4e4 @jeremy Ruby 1.9 compat: fix warnings, shadowed block vars, and unitialized inst...
jeremy authored
90 attrs[column.name] = @owner.quoted_id
6abda69 @dhh Added preliminary support for join models [DHH] Added preliminary suppor...
dhh authored
91 when @reflection.association_foreign_key
8b5f4e4 @jeremy Ruby 1.9 compat: fix warnings, shadowed block vars, and unitialized inst...
jeremy authored
92 attrs[column.name] = record.quoted_id
b29c01e @dhh Added that has_and_belongs_to_many associations with additional attribut...
dhh authored
93 else
8b5f4e4 @jeremy Ruby 1.9 compat: fix warnings, shadowed block vars, and unitialized inst...
jeremy authored
94 if record.has_attribute?(column.name)
85fbb22 @dhh Backed out of new_record? to new? transformation as it would screw up ex...
dhh authored
95 value = @owner.send(:quote_value, record[column.name], column)
8b5f4e4 @jeremy Ruby 1.9 compat: fix warnings, shadowed block vars, and unitialized inst...
jeremy authored
96 attrs[column.name] = value unless value.nil?
83e2f6a @jamis Allow unspecified join-table columns to use to their default values when...
jamis authored
97 end
b29c01e @dhh Added that has_and_belongs_to_many associations with additional attribut...
dhh authored
98 end
8b5f4e4 @jeremy Ruby 1.9 compat: fix warnings, shadowed block vars, and unitialized inst...
jeremy authored
99 attrs
b29c01e @dhh Added that has_and_belongs_to_many associations with additional attribut...
dhh authored
100 end
101
102 sql =
ebe3a0d @jeremy More thoroughly quote table names. Exposes some issues with sqlite2 adap...
jeremy authored
103 "INSERT INTO #{@owner.connection.quote_table_name @reflection.options[:join_table]} (#{@owner.send(:quoted_column_names, attributes).join(', ')}) " +
9870396 @dhh Fixed incompatibility in DB2 adapter with the new limit/offset approach ...
dhh authored
104 "VALUES (#{attributes.values.join(', ')})"
b29c01e @dhh Added that has_and_belongs_to_many associations with additional attribut...
dhh authored
105
4979448 @technoweenie Ensure that modifying has_and_belongs_to_many actions clear the query ca...
technoweenie authored
106 @owner.connection.insert(sql)
db045db @dhh Initial
dhh authored
107 end
800b899 @jeremy Remove deprecated push_with_attributes.
jeremy authored
108
b29c01e @dhh Added that has_and_belongs_to_many associations with additional attribut...
dhh authored
109 return true
db045db @dhh Initial
dhh authored
110 end
800b899 @jeremy Remove deprecated push_with_attributes.
jeremy authored
111
db045db @dhh Initial
dhh authored
112 def delete_records(records)
6abda69 @dhh Added preliminary support for join models [DHH] Added preliminary suppor...
dhh authored
113 if sql = @reflection.options[:delete_sql]
4979448 @technoweenie Ensure that modifying has_and_belongs_to_many actions clear the query ca...
technoweenie authored
114 records.each { |record| @owner.connection.delete(interpolate_sql(sql, record)) }
db045db @dhh Initial
dhh authored
115 else
116 ids = quoted_record_ids(records)
ebe3a0d @jeremy More thoroughly quote table names. Exposes some issues with sqlite2 adap...
jeremy authored
117 sql = "DELETE FROM #{@owner.connection.quote_table_name @reflection.options[:join_table]} WHERE #{@reflection.primary_key_name} = #{@owner.quoted_id} AND #{@reflection.association_foreign_key} IN (#{ids})"
4979448 @technoweenie Ensure that modifying has_and_belongs_to_many actions clear the query ca...
technoweenie authored
118 @owner.connection.delete(sql)
db045db @dhh Initial
dhh authored
119 end
120 end
800b899 @jeremy Remove deprecated push_with_attributes.
jeremy authored
121
823554e @dhh Added support for associating unsaved objects #402 [Tim Bates]
dhh authored
122 def construct_sql
6abda69 @dhh Added preliminary support for join models [DHH] Added preliminary suppor...
dhh authored
123 interpolate_sql_options!(@reflection.options, :finder_sql)
34f9d30 @dhh Added support for calling constrained class methods on has_many and has_...
dhh authored
124
6abda69 @dhh Added preliminary support for join models [DHH] Added preliminary suppor...
dhh authored
125 if @reflection.options[:finder_sql]
126 @finder_sql = @reflection.options[:finder_sql]
71bdf13 @dhh Removed the default order by id on has_and_belongs_to_many queries as it...
dhh authored
127 else
ebe3a0d @jeremy More thoroughly quote table names. Exposes some issues with sqlite2 adap...
jeremy authored
128 @finder_sql = "#{@owner.connection.quote_table_name @reflection.options[:join_table]}.#{@reflection.primary_key_name} = #{@owner.quoted_id} "
a3502c4 @technoweenie Use association's :conditions when eager loading. [jeremyevans0@gmail.co...
technoweenie authored
129 @finder_sql << " AND (#{conditions})" if conditions
71bdf13 @dhh Removed the default order by id on has_and_belongs_to_many queries as it...
dhh authored
130 end
d21ba5a @jeremy has_and_belongs_to_many: use JOIN instead of LEFT JOIN. References #293...
jeremy authored
131
ebe3a0d @jeremy More thoroughly quote table names. Exposes some issues with sqlite2 adap...
jeremy authored
132 @join_sql = "INNER JOIN #{@owner.connection.quote_table_name @reflection.options[:join_table]} ON #{@reflection.quoted_table_name}.#{@reflection.klass.primary_key} = #{@owner.connection.quote_table_name @reflection.options[:join_table]}.#{@reflection.association_foreign_key}"
823554e @dhh Added support for associating unsaved objects #402 [Tim Bates]
dhh authored
133 end
53aa8da @dhh Fixed that records returned from has_and_belongs_to_many associations wi...
dhh authored
134
02adc49 @jeremy Simplify association proxy implementation by factoring construct_scope o...
jeremy authored
135 def construct_scope
440f289 @jeremy Dynamic finders on association collections respect association :limit. C...
jeremy authored
136 { :find => { :conditions => @finder_sql,
137 :joins => @join_sql,
138 :readonly => false,
139 :order => @reflection.options[:order],
140 :limit => @reflection.options[:limit] } }
02adc49 @jeremy Simplify association proxy implementation by factoring construct_scope o...
jeremy authored
141 end
142
e3b49c0 @dhh Fixed spelling errors (closes #9706) [tarmo/rmm5t]
dhh authored
143 # Join tables with additional columns on top of the two foreign keys must be considered ambiguous unless a select
18a3333 @NZKoz Formatting, grammar and spelling fixes for the associations documentatio...
NZKoz authored
144 # clause has been explicitly defined. Otherwise you can get broken records back, if, for example, the join column also has
145 # an id column. This will then overwrite the id column of the records coming back.
e3b49c0 @dhh Fixed spelling errors (closes #9706) [tarmo/rmm5t]
dhh authored
146 def finding_with_ambiguous_select?(select_clause)
53aa8da @dhh Fixed that records returned from has_and_belongs_to_many associations wi...
dhh authored
147 !select_clause && @owner.connection.columns(@reflection.options[:join_table], "Join Table Columns").size != 2
148 end
a72c1ec @jeremy Refactor association create and build so before & after callbacks behave...
jeremy authored
149
150 private
151 def create_record(attributes)
152 # Can't use Base.create because the foreign key may be a protected attribute.
153 ensure_owner_is_not_new
154 if attributes.is_a?(Array)
155 attributes.collect { |attr| create(attr) }
156 else
157 record = build(attributes)
158 yield(record)
159 record
160 end
161 end
823554e @dhh Added support for associating unsaved objects #402 [Tim Bates]
dhh authored
162 end
db045db @dhh Initial
dhh authored
163 end
68d1056 @dhh Fixed that has_and_belongs_to_many would generate bad sql when naming co...
dhh authored
164 end
Something went wrong with that request. Please try again.