activerecord 1.15.6 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (185) hide show
  1. data/CHANGELOG +2454 -34
  2. data/README +1 -1
  3. data/RUNNING_UNIT_TESTS +3 -34
  4. data/Rakefile +98 -77
  5. data/install.rb +1 -1
  6. data/lib/active_record.rb +13 -22
  7. data/lib/active_record/aggregations.rb +38 -49
  8. data/lib/active_record/associations.rb +452 -333
  9. data/lib/active_record/associations/association_collection.rb +66 -20
  10. data/lib/active_record/associations/association_proxy.rb +9 -8
  11. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +46 -51
  12. data/lib/active_record/associations/has_many_association.rb +21 -57
  13. data/lib/active_record/associations/has_many_through_association.rb +38 -18
  14. data/lib/active_record/associations/has_one_association.rb +30 -14
  15. data/lib/active_record/attribute_methods.rb +253 -0
  16. data/lib/active_record/base.rb +719 -494
  17. data/lib/active_record/calculations.rb +62 -63
  18. data/lib/active_record/callbacks.rb +57 -83
  19. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +38 -9
  20. data/lib/active_record/connection_adapters/abstract/database_statements.rb +56 -15
  21. data/lib/active_record/connection_adapters/abstract/query_cache.rb +87 -0
  22. data/lib/active_record/connection_adapters/abstract/quoting.rb +23 -12
  23. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +191 -62
  24. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +37 -34
  25. data/lib/active_record/connection_adapters/abstract_adapter.rb +28 -17
  26. data/lib/active_record/connection_adapters/mysql_adapter.rb +119 -37
  27. data/lib/active_record/connection_adapters/postgresql_adapter.rb +473 -210
  28. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +34 -0
  29. data/lib/active_record/connection_adapters/sqlite_adapter.rb +91 -107
  30. data/lib/active_record/fixtures.rb +503 -113
  31. data/lib/active_record/locking/optimistic.rb +72 -34
  32. data/lib/active_record/migration.rb +80 -57
  33. data/lib/active_record/observer.rb +13 -10
  34. data/lib/active_record/query_cache.rb +16 -57
  35. data/lib/active_record/reflection.rb +35 -38
  36. data/lib/active_record/schema.rb +5 -5
  37. data/lib/active_record/schema_dumper.rb +35 -13
  38. data/lib/active_record/serialization.rb +98 -0
  39. data/lib/active_record/serializers/json_serializer.rb +71 -0
  40. data/lib/active_record/{xml_serialization.rb → serializers/xml_serializer.rb} +90 -83
  41. data/lib/active_record/timestamp.rb +20 -21
  42. data/lib/active_record/transactions.rb +39 -43
  43. data/lib/active_record/validations.rb +256 -107
  44. data/lib/active_record/version.rb +3 -3
  45. data/lib/activerecord.rb +1 -0
  46. data/test/aaa_create_tables_test.rb +15 -2
  47. data/test/abstract_unit.rb +24 -17
  48. data/test/active_schema_test_mysql.rb +20 -8
  49. data/test/adapter_test.rb +23 -5
  50. data/test/adapter_test_sqlserver.rb +15 -1
  51. data/test/aggregations_test.rb +16 -1
  52. data/test/all.sh +2 -2
  53. data/test/associations/ar_joins_test.rb +0 -0
  54. data/test/associations/callbacks_test.rb +51 -30
  55. data/test/associations/cascaded_eager_loading_test.rb +1 -29
  56. data/test/associations/eager_singularization_test.rb +145 -0
  57. data/test/associations/eager_test.rb +42 -6
  58. data/test/associations/extension_test.rb +6 -1
  59. data/test/associations/inner_join_association_test.rb +88 -0
  60. data/test/associations/join_model_test.rb +47 -16
  61. data/test/associations_test.rb +449 -226
  62. data/test/attribute_methods_test.rb +97 -0
  63. data/test/base_test.rb +251 -105
  64. data/test/binary_test.rb +22 -27
  65. data/test/calculations_test.rb +37 -5
  66. data/test/callbacks_test.rb +23 -0
  67. data/test/connection_test_firebird.rb +2 -2
  68. data/test/connection_test_mysql.rb +30 -0
  69. data/test/connections/native_mysql/connection.rb +3 -0
  70. data/test/connections/native_sqlite/connection.rb +5 -14
  71. data/test/connections/native_sqlite3/connection.rb +5 -14
  72. data/test/connections/native_sqlite3/in_memory_connection.rb +1 -1
  73. data/test/{copy_table_sqlite.rb → copy_table_test_sqlite.rb} +8 -3
  74. data/test/datatype_test_postgresql.rb +178 -27
  75. data/test/{empty_date_time_test.rb → date_time_test.rb} +13 -1
  76. data/test/defaults_test.rb +8 -1
  77. data/test/deprecated_finder_test.rb +7 -128
  78. data/test/finder_test.rb +192 -54
  79. data/test/fixtures/all/developers.yml +0 -0
  80. data/test/fixtures/all/people.csv +0 -0
  81. data/test/fixtures/all/tasks.yml +0 -0
  82. data/test/fixtures/author.rb +12 -5
  83. data/test/fixtures/binaries.yml +130 -435
  84. data/test/fixtures/category.rb +6 -0
  85. data/test/fixtures/company.rb +8 -1
  86. data/test/fixtures/computer.rb +1 -0
  87. data/test/fixtures/contact.rb +16 -0
  88. data/test/fixtures/customer.rb +2 -2
  89. data/test/fixtures/db_definitions/db2.drop.sql +1 -0
  90. data/test/fixtures/db_definitions/db2.sql +4 -0
  91. data/test/fixtures/db_definitions/firebird.drop.sql +3 -1
  92. data/test/fixtures/db_definitions/firebird.sql +6 -0
  93. data/test/fixtures/db_definitions/frontbase.drop.sql +1 -0
  94. data/test/fixtures/db_definitions/frontbase.sql +5 -0
  95. data/test/fixtures/db_definitions/openbase.sql +41 -25
  96. data/test/fixtures/db_definitions/oracle.drop.sql +2 -0
  97. data/test/fixtures/db_definitions/oracle.sql +5 -0
  98. data/test/fixtures/db_definitions/postgresql.drop.sql +7 -0
  99. data/test/fixtures/db_definitions/postgresql.sql +87 -58
  100. data/test/fixtures/db_definitions/postgresql2.sql +1 -2
  101. data/test/fixtures/db_definitions/schema.rb +280 -0
  102. data/test/fixtures/db_definitions/schema2.rb +11 -0
  103. data/test/fixtures/db_definitions/sqlite.drop.sql +1 -0
  104. data/test/fixtures/db_definitions/sqlite.sql +4 -0
  105. data/test/fixtures/db_definitions/sybase.drop.sql +1 -0
  106. data/test/fixtures/db_definitions/sybase.sql +4 -0
  107. data/test/fixtures/developer.rb +10 -0
  108. data/test/fixtures/example.log +1 -0
  109. data/test/fixtures/flowers.jpg +0 -0
  110. data/test/fixtures/item.rb +7 -0
  111. data/test/fixtures/items.yml +4 -0
  112. data/test/fixtures/joke.rb +0 -3
  113. data/test/fixtures/matey.rb +4 -0
  114. data/test/fixtures/mateys.yml +4 -0
  115. data/test/fixtures/minimalistic.rb +2 -0
  116. data/test/fixtures/minimalistics.yml +2 -0
  117. data/test/fixtures/mixins.yml +2 -100
  118. data/test/fixtures/parrot.rb +13 -0
  119. data/test/fixtures/parrots.yml +27 -0
  120. data/test/fixtures/parrots_pirates.yml +7 -0
  121. data/test/fixtures/pirate.rb +5 -0
  122. data/test/fixtures/pirates.yml +9 -0
  123. data/test/fixtures/post.rb +1 -0
  124. data/test/fixtures/project.rb +3 -2
  125. data/test/fixtures/reserved_words/distinct.yml +5 -0
  126. data/test/fixtures/reserved_words/distincts_selects.yml +11 -0
  127. data/test/fixtures/reserved_words/group.yml +14 -0
  128. data/test/fixtures/reserved_words/select.yml +8 -0
  129. data/test/fixtures/reserved_words/values.yml +7 -0
  130. data/test/fixtures/ship.rb +3 -0
  131. data/test/fixtures/ships.yml +5 -0
  132. data/test/fixtures/tagging.rb +4 -0
  133. data/test/fixtures/taggings.yml +8 -1
  134. data/test/fixtures/topic.rb +13 -1
  135. data/test/fixtures/treasure.rb +4 -0
  136. data/test/fixtures/treasures.yml +10 -0
  137. data/test/fixtures_test.rb +205 -24
  138. data/test/inheritance_test.rb +7 -1
  139. data/test/json_serialization_test.rb +180 -0
  140. data/test/lifecycle_test.rb +1 -1
  141. data/test/locking_test.rb +85 -2
  142. data/test/migration_test.rb +206 -40
  143. data/test/mixin_test.rb +13 -515
  144. data/test/pk_test.rb +3 -6
  145. data/test/query_cache_test.rb +104 -0
  146. data/test/reflection_test.rb +16 -0
  147. data/test/reserved_word_test_mysql.rb +177 -0
  148. data/test/schema_dumper_test.rb +38 -3
  149. data/test/serialization_test.rb +47 -0
  150. data/test/transactions_test.rb +74 -23
  151. data/test/unconnected_test.rb +1 -1
  152. data/test/validations_test.rb +322 -32
  153. data/test/xml_serialization_test.rb +121 -44
  154. metadata +48 -41
  155. data/examples/associations.rb +0 -87
  156. data/examples/shared_setup.rb +0 -15
  157. data/examples/validation.rb +0 -85
  158. data/lib/active_record/acts/list.rb +0 -256
  159. data/lib/active_record/acts/nested_set.rb +0 -211
  160. data/lib/active_record/acts/tree.rb +0 -96
  161. data/lib/active_record/connection_adapters/db2_adapter.rb +0 -228
  162. data/lib/active_record/connection_adapters/firebird_adapter.rb +0 -728
  163. data/lib/active_record/connection_adapters/frontbase_adapter.rb +0 -861
  164. data/lib/active_record/connection_adapters/openbase_adapter.rb +0 -350
  165. data/lib/active_record/connection_adapters/oracle_adapter.rb +0 -690
  166. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +0 -591
  167. data/lib/active_record/connection_adapters/sybase_adapter.rb +0 -662
  168. data/lib/active_record/deprecated_associations.rb +0 -104
  169. data/lib/active_record/deprecated_finders.rb +0 -44
  170. data/lib/active_record/vendor/simple.rb +0 -693
  171. data/lib/active_record/wrappers/yaml_wrapper.rb +0 -15
  172. data/lib/active_record/wrappings.rb +0 -58
  173. data/test/connections/native_sqlserver/connection.rb +0 -23
  174. data/test/connections/native_sqlserver_odbc/connection.rb +0 -25
  175. data/test/deprecated_associations_test.rb +0 -396
  176. data/test/fixtures/db_definitions/mysql.drop.sql +0 -32
  177. data/test/fixtures/db_definitions/mysql.sql +0 -234
  178. data/test/fixtures/db_definitions/mysql2.drop.sql +0 -2
  179. data/test/fixtures/db_definitions/mysql2.sql +0 -5
  180. data/test/fixtures/db_definitions/sqlserver.drop.sql +0 -34
  181. data/test/fixtures/db_definitions/sqlserver.sql +0 -243
  182. data/test/fixtures/db_definitions/sqlserver2.drop.sql +0 -2
  183. data/test/fixtures/db_definitions/sqlserver2.sql +0 -5
  184. data/test/fixtures/mixin.rb +0 -63
  185. data/test/mixin_nested_set_test.rb +0 -196
@@ -7,7 +7,7 @@ module ActiveRecord
7
7
  load_target
8
8
  @target.to_ary
9
9
  end
10
-
10
+
11
11
  def reset
12
12
  reset_target!
13
13
  @loaded = false
@@ -17,7 +17,7 @@ module ActiveRecord
17
17
  # Since << flattens its argument list and inserts each record, +push+ and +concat+ behave identically.
18
18
  def <<(*records)
19
19
  result = true
20
- load_target
20
+ load_target if @owner.new_record?
21
21
 
22
22
  @owner.transaction do
23
23
  flatten_deeper(records).each do |record|
@@ -34,7 +34,7 @@ module ActiveRecord
34
34
 
35
35
  alias_method :push, :<<
36
36
  alias_method :concat, :<<
37
-
37
+
38
38
  # Remove all records from this association
39
39
  def delete_all
40
40
  load_target
@@ -66,9 +66,9 @@ module ActiveRecord
66
66
 
67
67
  # Removes all records from this association. Returns +self+ so method calls may be chained.
68
68
  def clear
69
- return self if length.zero? # forces load_target if hasn't happened already
69
+ return self if length.zero? # forces load_target if it hasn't happened already
70
70
 
71
- if @reflection.options[:dependent] && @reflection.options[:dependent] == :delete_all
71
+ if @reflection.options[:dependent] && @reflection.options[:dependent] == :destroy
72
72
  destroy_all
73
73
  else
74
74
  delete_all
@@ -84,27 +84,24 @@ module ActiveRecord
84
84
 
85
85
  reset_target!
86
86
  end
87
-
88
- def create(attributes = {})
89
- # Can't use Base.create since the foreign key may be a protected attribute.
90
- if attributes.is_a?(Array)
91
- attributes.collect { |attr| create(attr) }
87
+
88
+ def create(attrs = {})
89
+ if attrs.is_a?(Array)
90
+ attrs.collect { |attr| create(attr) }
92
91
  else
93
- record = build(attributes)
94
- if @owner.new_record?
95
- ActiveSupport::Deprecation.warn("Calling .create on a has_many association without saving its owner will not work in rails 2.0, you probably want .build instead")
96
- else
97
- record.save
98
- end
99
- record
92
+ create_record(attrs) { |record| record.save }
100
93
  end
101
94
  end
102
95
 
96
+ def create!(attrs = {})
97
+ create_record(attrs) { |record| record.save! }
98
+ end
99
+
103
100
  # Returns the size of the collection by executing a SELECT COUNT(*) query if the collection hasn't been loaded and
104
101
  # calling collection.size if it has. If it's more likely than not that the collection does have a size larger than zero
105
102
  # and you need to fetch that collection afterwards, it'll take one less SELECT query if you use length.
106
103
  def size
107
- if loaded? && !@reflection.options[:uniq]
104
+ if @owner.new_record? || (loaded? && !@reflection.options[:uniq])
108
105
  @target.size
109
106
  elsif !loaded? && !@reflection.options[:uniq] && @target.is_a?(Array)
110
107
  unsaved_records = Array(@target.detect { |r| r.new_record? })
@@ -124,6 +121,14 @@ module ActiveRecord
124
121
  size.zero?
125
122
  end
126
123
 
124
+ def any?(&block)
125
+ if block_given?
126
+ method_missing(:any?, &block)
127
+ else
128
+ !empty?
129
+ end
130
+ end
131
+
127
132
  def uniq(collection = self)
128
133
  seen = Set.new
129
134
  collection.inject([]) do |kept, record|
@@ -150,7 +155,21 @@ module ActiveRecord
150
155
  end
151
156
  end
152
157
 
158
+
153
159
  protected
160
+ def method_missing(method, *args, &block)
161
+ if @target.respond_to?(method) || (!@reflection.klass.respond_to?(method) && Class.respond_to?(method))
162
+ super
163
+ else
164
+ @reflection.klass.send(:with_scope, construct_scope) { @reflection.klass.send(method, *args, &block) }
165
+ end
166
+ end
167
+
168
+ # overloaded in derived Association classes to provide useful scoping depending on association type.
169
+ def construct_scope
170
+ {}
171
+ end
172
+
154
173
  def reset_target!
155
174
  @target = Array.new
156
175
  end
@@ -167,6 +186,27 @@ module ActiveRecord
167
186
  end
168
187
 
169
188
  private
189
+
190
+ def create_record(attrs, &block)
191
+ ensure_owner_is_not_new
192
+ record = @reflection.klass.send(:with_scope, :create => construct_scope[:create]) { @reflection.klass.new(attrs) }
193
+ add_record_to_target_with_callbacks(record, &block)
194
+ end
195
+
196
+ def build_record(attrs, &block)
197
+ record = @reflection.klass.new(attrs)
198
+ add_record_to_target_with_callbacks(record, &block)
199
+ end
200
+
201
+ def add_record_to_target_with_callbacks(record)
202
+ callback(:before_add, record)
203
+ yield(record) if block_given?
204
+ @target ||= [] unless loaded?
205
+ @target << record
206
+ callback(:after_add, record)
207
+ record
208
+ end
209
+
170
210
  def callback(method, record)
171
211
  callbacks_for(method).each do |callback|
172
212
  case callback
@@ -187,8 +227,14 @@ module ActiveRecord
187
227
  def callbacks_for(callback_name)
188
228
  full_callback_name = "#{callback_name}_for_#{@reflection.name}"
189
229
  @owner.class.read_inheritable_attribute(full_callback_name.to_sym) || []
190
- end
230
+ end
191
231
 
232
+ def ensure_owner_is_not_new
233
+ if @owner.new_record?
234
+ raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved"
235
+ end
236
+ end
237
+
192
238
  end
193
239
  end
194
- end
240
+ end
@@ -46,8 +46,8 @@ module ActiveRecord
46
46
  alias :sql_conditions :conditions
47
47
 
48
48
  def reset
49
- @target = nil
50
49
  @loaded = false
50
+ @target = nil
51
51
  end
52
52
 
53
53
  def reload
@@ -72,9 +72,14 @@ module ActiveRecord
72
72
  loaded
73
73
  end
74
74
 
75
+ def inspect
76
+ reload unless loaded?
77
+ @target.inspect
78
+ end
79
+
75
80
  protected
76
81
  def dependent?
77
- @reflection.options[:dependent] || false
82
+ @reflection.options[:dependent]
78
83
  end
79
84
 
80
85
  def quoted_record_ids(records)
@@ -93,10 +98,6 @@ module ActiveRecord
93
98
  @reflection.klass.send(:sanitize_sql, sql)
94
99
  end
95
100
 
96
- def extract_options_from_args!(args)
97
- @owner.send(:extract_options_from_args!, args)
98
- end
99
-
100
101
  def set_belongs_to_association_for(record)
101
102
  if @reflection.options[:as]
102
103
  record["#{@reflection.options[:as]}_id"] = @owner.id unless @owner.new_record?
@@ -138,14 +139,14 @@ module ActiveRecord
138
139
  end
139
140
 
140
141
  # Can be overwritten by associations that might have the foreign key available for an association without
141
- # having the object itself (and still being a new record). Currently, only belongs_to present this scenario.
142
+ # having the object itself (and still being a new record). Currently, only belongs_to presents this scenario.
142
143
  def foreign_key_present
143
144
  false
144
145
  end
145
146
 
146
147
  def raise_on_type_mismatch(record)
147
148
  unless record.is_a?(@reflection.klass)
148
- raise ActiveRecord::AssociationTypeMismatch, "#{@reflection.class_name} expected, got #{record.class}"
149
+ raise ActiveRecord::AssociationTypeMismatch, "#{@reflection.klass} expected, got #{record.class}"
149
150
  end
150
151
  end
151
152
 
@@ -5,31 +5,26 @@ module ActiveRecord
5
5
  super
6
6
  construct_sql
7
7
  end
8
-
8
+
9
9
  def build(attributes = {})
10
10
  load_target
11
- record = @reflection.klass.new(attributes)
12
- @target << record
13
- record
11
+ build_record(attributes)
14
12
  end
15
13
 
16
14
  def create(attributes = {})
17
- # Can't use Base.create since the foreign key may be a protected attribute.
18
- if attributes.is_a?(Array)
19
- attributes.collect { |attr| create(attr) }
20
- else
21
- record = build(attributes)
22
- insert_record(record) unless @owner.new_record?
23
- record
24
- end
15
+ create_record(attributes) { |record| insert_record(record) }
16
+ end
17
+
18
+ def create!(attributes = {})
19
+ create_record(attributes) { |record| insert_record(record, true) }
25
20
  end
26
21
 
27
22
  def find_first
28
23
  load_target.first
29
24
  end
30
-
25
+
31
26
  def find(*args)
32
- options = Base.send(:extract_options_from_args!, args)
27
+ options = args.extract_options!
33
28
 
34
29
  # If using a custom finder_sql, scan the entire collection.
35
30
  if @reflection.options[:finder_sql]
@@ -52,7 +47,7 @@ module ActiveRecord
52
47
 
53
48
  options[:conditions] = conditions
54
49
  options[:joins] = @join_sql
55
- options[:readonly] = finding_with_ambigious_select?(options[:select])
50
+ options[:readonly] = finding_with_ambiguous_select?(options[:select] || @reflection.options[:select])
56
51
 
57
52
  if options[:order] && @reflection.options[:order]
58
53
  options[:order] = "#{options[:order]}, #{@reflection.options[:order]}"
@@ -62,47 +57,26 @@ module ActiveRecord
62
57
 
63
58
  merge_options_from_reflection!(options)
64
59
 
60
+ options[:select] ||= (@reflection.options[:select] || '*')
61
+
65
62
  # Pass through args exactly as we received them.
66
63
  args << options
67
64
  @reflection.klass.find(*args)
68
65
  end
69
- end
70
-
71
- # Deprecated as of Rails 1.2. If your associations require attributes
72
- # you should be using has_many :through
73
- def push_with_attributes(record, join_attributes = {})
74
- raise_on_type_mismatch(record)
75
- join_attributes.each { |key, value| record[key.to_s] = value }
76
-
77
- callback(:before_add, record)
78
- insert_record(record) unless @owner.new_record?
79
- @target << record
80
- callback(:after_add, record)
81
-
82
- self
83
66
  end
84
- deprecate :push_with_attributes => "consider using has_many :through instead"
85
-
86
- alias :concat_with_attributes :push_with_attributes
87
67
 
88
68
  protected
89
- def method_missing(method, *args, &block)
90
- if @target.respond_to?(method) || (!@reflection.klass.respond_to?(method) && Class.respond_to?(method))
91
- super
92
- else
93
- @reflection.klass.with_scope(:find => { :conditions => @finder_sql, :joins => @join_sql, :readonly => false }) do
94
- @reflection.klass.send(method, *args, &block)
95
- end
96
- end
97
- end
98
-
99
69
  def count_records
100
70
  load_target.size
101
71
  end
102
72
 
103
- def insert_record(record)
73
+ def insert_record(record, force=true)
104
74
  if record.new_record?
105
- return false unless record.save
75
+ if force
76
+ record.save!
77
+ else
78
+ return false unless record.save
79
+ end
106
80
  end
107
81
 
108
82
  if @reflection.options[:insert_sql]
@@ -131,10 +105,10 @@ module ActiveRecord
131
105
 
132
106
  @owner.connection.execute(sql)
133
107
  end
134
-
108
+
135
109
  return true
136
110
  end
137
-
111
+
138
112
  def delete_records(records)
139
113
  if sql = @reflection.options[:delete_sql]
140
114
  records.each { |record| @owner.connection.execute(interpolate_sql(sql, record)) }
@@ -144,7 +118,7 @@ module ActiveRecord
144
118
  @owner.connection.execute(sql)
145
119
  end
146
120
  end
147
-
121
+
148
122
  def construct_sql
149
123
  interpolate_sql_options!(@reflection.options, :finder_sql)
150
124
 
@@ -158,12 +132,33 @@ module ActiveRecord
158
132
  @join_sql = "INNER JOIN #{@reflection.options[:join_table]} ON #{@reflection.klass.table_name}.#{@reflection.klass.primary_key} = #{@reflection.options[:join_table]}.#{@reflection.association_foreign_key}"
159
133
  end
160
134
 
161
- # Join tables with additional columns on top of the two foreign keys must be considered ambigious unless a select
162
- # clause has been explicitly defined. Otherwise you can get broken records back, if, say, the join column also has
163
- # and id column, which will then overwrite the id column of the records coming back.
164
- def finding_with_ambigious_select?(select_clause)
135
+ def construct_scope
136
+ { :find => { :conditions => @finder_sql,
137
+ :joins => @join_sql,
138
+ :readonly => false,
139
+ :order => @reflection.options[:order],
140
+ :limit => @reflection.options[:limit] } }
141
+ end
142
+
143
+ # Join tables with additional columns on top of the two foreign keys must be considered ambiguous unless a select
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.
146
+ def finding_with_ambiguous_select?(select_clause)
165
147
  !select_clause && @owner.connection.columns(@reflection.options[:join_table], "Join Table Columns").size != 2
166
148
  end
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
167
162
  end
168
163
  end
169
164
  end
@@ -10,34 +10,9 @@ module ActiveRecord
10
10
  if attributes.is_a?(Array)
11
11
  attributes.collect { |attr| build(attr) }
12
12
  else
13
- record = @reflection.klass.new(attributes)
14
- set_belongs_to_association_for(record)
15
-
16
- @target ||= [] unless loaded?
17
- @target << record
18
-
19
- record
20
- end
21
- end
22
-
23
- # DEPRECATED.
24
- def find_all(runtime_conditions = nil, orderings = nil, limit = nil, joins = nil)
25
- if @reflection.options[:finder_sql]
26
- @reflection.klass.find_by_sql(@finder_sql)
27
- else
28
- conditions = @finder_sql
29
- conditions += " AND (#{sanitize_sql(runtime_conditions)})" if runtime_conditions
30
- orderings ||= @reflection.options[:order]
31
- @reflection.klass.find_all(conditions, orderings, limit, joins)
13
+ build_record(attributes) { |record| set_belongs_to_association_for(record) }
32
14
  end
33
15
  end
34
- deprecate :find_all => "use find(:all, ...) instead"
35
-
36
- # DEPRECATED. Find the first associated record. All arguments are optional.
37
- def find_first(conditions = nil, orderings = nil)
38
- find_all(conditions, orderings, 1).first
39
- end
40
- deprecate :find_first => "use find(:first, ...) instead"
41
16
 
42
17
  # Count the number of associated records. All arguments are optional.
43
18
  def count(*args)
@@ -46,7 +21,7 @@ module ActiveRecord
46
21
  elsif @reflection.options[:finder_sql]
47
22
  @reflection.klass.count_by_sql(@finder_sql)
48
23
  else
49
- column_name, options = @reflection.klass.send(:construct_count_options_from_legacy_args, *args)
24
+ column_name, options = @reflection.klass.send(:construct_count_options_from_args, *args)
50
25
  options[:conditions] = options[:conditions].nil? ?
51
26
  @finder_sql :
52
27
  @finder_sql + " AND (#{sanitize_sql(options[:conditions])})"
@@ -57,12 +32,12 @@ module ActiveRecord
57
32
  end
58
33
 
59
34
  def find(*args)
60
- options = Base.send(:extract_options_from_args!, args)
35
+ options = args.extract_options!
61
36
 
62
37
  # If using a custom finder_sql, scan the entire collection.
63
38
  if @reflection.options[:finder_sql]
64
39
  expects_array = args.first.kind_of?(Array)
65
- ids = args.flatten.compact.uniq
40
+ ids = args.flatten.compact.uniq.map(&:to_i)
66
41
 
67
42
  if ids.size == 1
68
43
  id = ids.first
@@ -93,26 +68,6 @@ module ActiveRecord
93
68
  end
94
69
 
95
70
  protected
96
- def method_missing(method, *args, &block)
97
- if @target.respond_to?(method) || (!@reflection.klass.respond_to?(method) && Class.respond_to?(method))
98
- super
99
- else
100
- create_scoping = {}
101
- set_belongs_to_association_for(create_scoping)
102
-
103
- @reflection.klass.with_scope(
104
- :create => create_scoping,
105
- :find => {
106
- :conditions => @finder_sql,
107
- :joins => @join_sql,
108
- :readonly => false
109
- }
110
- ) do
111
- @reflection.klass.send(method, *args, &block)
112
- end
113
- end
114
- end
115
-
116
71
  def load_target
117
72
  if !@owner.new_record? || foreign_key_present
118
73
  begin
@@ -164,14 +119,17 @@ module ActiveRecord
164
119
  end
165
120
 
166
121
  def delete_records(records)
167
- if @reflection.options[:dependent]
168
- records.each { |r| r.destroy }
169
- else
170
- ids = quoted_record_ids(records)
171
- @reflection.klass.update_all(
172
- "#{@reflection.primary_key_name} = NULL",
173
- "#{@reflection.primary_key_name} = #{@owner.quoted_id} AND #{@reflection.klass.primary_key} IN (#{ids})"
174
- )
122
+ case @reflection.options[:dependent]
123
+ when :destroy
124
+ records.each(&:destroy)
125
+ when :delete_all
126
+ @reflection.klass.delete(records.map(&:id))
127
+ else
128
+ ids = quoted_record_ids(records)
129
+ @reflection.klass.update_all(
130
+ "#{@reflection.primary_key_name} = NULL",
131
+ "#{@reflection.primary_key_name} = #{@owner.quoted_id} AND #{@reflection.klass.primary_key} IN (#{ids})"
132
+ )
175
133
  end
176
134
  end
177
135
 
@@ -205,6 +163,12 @@ module ActiveRecord
205
163
  @counter_sql = @finder_sql
206
164
  end
207
165
  end
166
+
167
+ def construct_scope
168
+ create_scoping = {}
169
+ set_belongs_to_association_for(create_scoping)
170
+ { :find => { :conditions => @finder_sql, :readonly => false, :order => @reflection.options[:order], :limit => @reflection.options[:limit] }, :create => create_scoping }
171
+ end
208
172
  end
209
173
  end
210
174
  end