activerecord 4.0.3 → 4.0.4.rc1

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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +412 -0
  3. data/lib/active_record/associations.rb +2 -2
  4. data/lib/active_record/associations/association.rb +7 -2
  5. data/lib/active_record/associations/association_scope.rb +7 -2
  6. data/lib/active_record/associations/builder/belongs_to.rb +9 -3
  7. data/lib/active_record/associations/builder/singular_association.rb +1 -1
  8. data/lib/active_record/associations/collection_association.rb +1 -1
  9. data/lib/active_record/associations/collection_proxy.rb +1 -1
  10. data/lib/active_record/associations/has_many_through_association.rb +1 -1
  11. data/lib/active_record/associations/has_one_association.rb +4 -2
  12. data/lib/active_record/associations/singular_association.rb +1 -1
  13. data/lib/active_record/attribute_methods.rb +32 -15
  14. data/lib/active_record/attribute_methods/serialization.rb +10 -0
  15. data/lib/active_record/autosave_association.rb +8 -2
  16. data/lib/active_record/callbacks.rb +2 -2
  17. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +1 -1
  18. data/lib/active_record/connection_adapters/abstract/database_statements.rb +14 -3
  19. data/lib/active_record/connection_adapters/abstract/quoting.rb +3 -1
  20. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +9 -9
  21. data/lib/active_record/connection_adapters/abstract/transaction.rb +4 -0
  22. data/lib/active_record/connection_adapters/abstract_adapter.rb +7 -0
  23. data/lib/active_record/connection_adapters/column.rb +11 -3
  24. data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -1
  25. data/lib/active_record/connection_adapters/mysql_adapter.rb +2 -2
  26. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +3 -2
  27. data/lib/active_record/connection_adapters/postgresql/cast.rb +12 -8
  28. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +2 -2
  29. data/lib/active_record/connection_adapters/postgresql/quoting.rb +1 -1
  30. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +13 -0
  31. data/lib/active_record/connection_adapters/postgresql_adapter.rb +20 -15
  32. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +7 -3
  33. data/lib/active_record/core.rb +17 -9
  34. data/lib/active_record/counter_cache.rb +1 -1
  35. data/lib/active_record/dynamic_matchers.rb +7 -2
  36. data/lib/active_record/inheritance.rb +15 -7
  37. data/lib/active_record/locking/pessimistic.rb +3 -3
  38. data/lib/active_record/migration.rb +10 -6
  39. data/lib/active_record/migration/command_recorder.rb +12 -5
  40. data/lib/active_record/persistence.rb +7 -6
  41. data/lib/active_record/railties/databases.rake +4 -5
  42. data/lib/active_record/relation/batches.rb +2 -5
  43. data/lib/active_record/relation/calculations.rb +3 -1
  44. data/lib/active_record/relation/finder_methods.rb +6 -5
  45. data/lib/active_record/relation/merger.rb +18 -2
  46. data/lib/active_record/relation/query_methods.rb +5 -6
  47. data/lib/active_record/sanitization.rb +6 -3
  48. data/lib/active_record/store.rb +1 -1
  49. data/lib/active_record/tasks/mysql_database_tasks.rb +2 -1
  50. data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
  51. data/lib/active_record/timestamp.rb +2 -2
  52. data/lib/active_record/transactions.rb +7 -3
  53. data/lib/active_record/validations/presence.rb +1 -1
  54. data/lib/active_record/validations/uniqueness.rb +13 -5
  55. data/lib/active_record/version.rb +1 -1
  56. metadata +9 -9
@@ -89,17 +89,22 @@ module ActiveRecord
89
89
  scope = scope.joins(join(foreign_table, constraint))
90
90
  end
91
91
 
92
+ is_first_chain = i == 0
93
+ klass = is_first_chain ? self.klass : reflection.klass
94
+
92
95
  # Exclude the scope of the association itself, because that
93
96
  # was already merged in the #scope method.
94
97
  scope_chain[i].each do |scope_chain_item|
95
- klass = i == 0 ? self.klass : reflection.klass
96
98
  item = eval_scope(klass, scope_chain_item)
97
99
 
98
100
  if scope_chain_item == self.reflection.scope
99
101
  scope.merge! item.except(:where, :includes)
100
102
  end
101
103
 
102
- scope.includes! item.includes_values
104
+ if is_first_chain
105
+ scope.includes! item.includes_values
106
+ end
107
+
103
108
  scope.where_values += item.where_values
104
109
  scope.order_values |= item.order_values
105
110
  end
@@ -5,7 +5,7 @@ module ActiveRecord::Associations::Builder
5
5
  end
6
6
 
7
7
  def valid_options
8
- super + [:foreign_type, :polymorphic, :touch]
8
+ super + [:foreign_type, :polymorphic, :touch, :counter_cache]
9
9
  end
10
10
 
11
11
  def constructable?
@@ -73,7 +73,13 @@ module ActiveRecord::Associations::Builder
73
73
  old_foreign_id = changed_attributes[foreign_key_field]
74
74
 
75
75
  if old_foreign_id
76
- klass = association(#{name.inspect}).klass
76
+ association = association(:#{name})
77
+ reflection = association.reflection
78
+ if reflection.polymorphic?
79
+ klass = send("#{reflection.foreign_type}_was").constantize
80
+ else
81
+ klass = association.klass
82
+ end
77
83
  old_record = klass.find_by(klass.primary_key => old_foreign_id)
78
84
 
79
85
  if old_record
@@ -82,7 +88,7 @@ module ActiveRecord::Associations::Builder
82
88
  end
83
89
 
84
90
  record = #{name}
85
- unless record.nil? || record.new_record?
91
+ if record && record.persisted?
86
92
  record.touch #{options[:touch].inspect if options[:touch] != true}
87
93
  end
88
94
  end
@@ -1,7 +1,7 @@
1
1
  module ActiveRecord::Associations::Builder
2
2
  class SingularAssociation < Association #:nodoc:
3
3
  def valid_options
4
- super + [:remote, :dependent, :counter_cache, :primary_key, :inverse_of]
4
+ super + [:remote, :dependent, :primary_key, :inverse_of]
5
5
  end
6
6
 
7
7
  def constructable?
@@ -193,7 +193,7 @@ module ActiveRecord
193
193
  relation = relation.distinct
194
194
  end
195
195
 
196
- value = relation.count(column_name)
196
+ value = relation.count(column_name, count_options)
197
197
 
198
198
  limit = options[:limit]
199
199
  offset = options[:offset]
@@ -282,7 +282,7 @@ module ActiveRecord
282
282
  # so method calls may be chained.
283
283
  #
284
284
  # class Person < ActiveRecord::Base
285
- # pets :has_many
285
+ # has_many :pets
286
286
  # end
287
287
  #
288
288
  # person.pets.size # => 0
@@ -149,7 +149,7 @@ module ActiveRecord
149
149
 
150
150
  delete_through_records(records)
151
151
 
152
- if source_reflection.options[:counter_cache]
152
+ if source_reflection.options[:counter_cache] && method != :destroy
153
153
  counter = source_reflection.counter_cache_column
154
154
  klass.decrement_counter counter, records.map(&:id)
155
155
  end
@@ -26,11 +26,13 @@ module ActiveRecord
26
26
  load_target
27
27
 
28
28
  return self.target if !(target || record)
29
- if (target != record) || record.changed?
29
+
30
+ assigning_another_record = target != record
31
+ if assigning_another_record || record.changed?
30
32
  save &&= owner.persisted?
31
33
 
32
34
  transaction_if(save) do
33
- remove_target!(options[:dependent]) if target && !target.destroyed?
35
+ remove_target!(options[:dependent]) if target && !target.destroyed? && assigning_another_record
34
36
 
35
37
  if record
36
38
  set_owner_attributes(record)
@@ -39,7 +39,7 @@ module ActiveRecord
39
39
  end
40
40
 
41
41
  def find_target
42
- scope.first.tap { |record| set_inverse_instance(record) }
42
+ scope.take.tap { |record| set_inverse_instance(record) }
43
43
  end
44
44
 
45
45
  # Implemented by subclasses
@@ -48,20 +48,19 @@ module ActiveRecord
48
48
  # Use a mutex; we don't want two thread simultaneously trying to define
49
49
  # attribute methods.
50
50
  generated_attribute_methods.synchronize do
51
- return if attribute_methods_generated?
51
+ return false if @attribute_methods_generated
52
52
  superclass.define_attribute_methods unless self == base_class
53
53
  super(column_names)
54
54
  @attribute_methods_generated = true
55
55
  end
56
- end
57
-
58
- def attribute_methods_generated? # :nodoc:
59
- @attribute_methods_generated
56
+ true
60
57
  end
61
58
 
62
59
  def undefine_attribute_methods # :nodoc:
63
- super if attribute_methods_generated?
64
- @attribute_methods_generated = false
60
+ generated_attribute_methods.synchronize do
61
+ super if @attribute_methods_generated
62
+ @attribute_methods_generated = false
63
+ end
65
64
  end
66
65
 
67
66
  # Raises a <tt>ActiveRecord::DangerousAttributeError</tt> exception when an
@@ -88,7 +87,8 @@ module ActiveRecord
88
87
  else
89
88
  # If B < A and A defines its own attribute method, then we don't want to overwrite that.
90
89
  defined = method_defined_within?(method_name, superclass, superclass.generated_attribute_methods)
91
- defined && !ActiveRecord::Base.method_defined?(method_name) || super
90
+ base_defined = Base.method_defined?(method_name) || Base.private_method_defined?(method_name)
91
+ defined && !base_defined || super
92
92
  end
93
93
  end
94
94
 
@@ -110,6 +110,16 @@ module ActiveRecord
110
110
  end
111
111
  end
112
112
 
113
+ def find_generated_attribute_method(method_name) # :nodoc:
114
+ klass = self
115
+ until klass == Base
116
+ gen_methods = klass.generated_attribute_methods
117
+ return gen_methods.instance_method(method_name) if method_defined_within?(method_name, gen_methods, Object)
118
+ klass = klass.superclass
119
+ end
120
+ nil
121
+ end
122
+
113
123
  # Returns +true+ if +attribute+ is an attribute method and table exists,
114
124
  # +false+ otherwise.
115
125
  #
@@ -143,13 +153,15 @@ module ActiveRecord
143
153
  # If we haven't generated any methods yet, generate them, then
144
154
  # see if we've created the method we're looking for.
145
155
  def method_missing(method, *args, &block) # :nodoc:
146
- unless self.class.attribute_methods_generated?
147
- self.class.define_attribute_methods
148
-
149
- if respond_to_without_attributes?(method)
150
- send(method, *args, &block)
156
+ self.class.define_attribute_methods
157
+ if respond_to_without_attributes?(method)
158
+ # make sure to invoke the correct attribute method, as we might have gotten here via a `super`
159
+ # call in a overwritten attribute method
160
+ if attribute_method = self.class.find_generated_attribute_method(method)
161
+ # this is probably horribly slow, but should only happen at most once for a given AR class
162
+ attribute_method.bind(self).call(*args, &block)
151
163
  else
152
- super
164
+ send(method, *args, &block)
153
165
  end
154
166
  else
155
167
  super
@@ -188,7 +200,7 @@ module ActiveRecord
188
200
  # person.respond_to(:nothing) # => false
189
201
  def respond_to?(name, include_private = false)
190
202
  name = name.to_s
191
- self.class.define_attribute_methods unless self.class.attribute_methods_generated?
203
+ self.class.define_attribute_methods
192
204
  result = super
193
205
 
194
206
  # If the result is false the answer is false.
@@ -244,6 +256,11 @@ module ActiveRecord
244
256
  }
245
257
  end
246
258
 
259
+ # Placeholder so it can be overriden when needed by serialization
260
+ def attributes_for_coder # :nodoc:
261
+ attributes
262
+ end
263
+
247
264
  # Returns an <tt>#inspect</tt>-like string for the value of the
248
265
  # attribute +attr_name+. String attributes are truncated upto 50
249
266
  # characters, and Date and Time attributes are returned in the
@@ -156,6 +156,16 @@ module ActiveRecord
156
156
  super
157
157
  end
158
158
  end
159
+
160
+ def attributes_for_coder
161
+ attribute_names.each_with_object({}) do |name, attrs|
162
+ attrs[name] = if self.class.serialized_attributes.include?(name)
163
+ @attributes[name].serialized_value
164
+ else
165
+ read_attribute(name)
166
+ end
167
+ end
168
+ end
159
169
  end
160
170
  end
161
171
  end
@@ -382,9 +382,10 @@ module ActiveRecord
382
382
 
383
383
  if autosave && record.marked_for_destruction?
384
384
  record.destroy
385
- else
385
+ elsif autosave != false
386
386
  key = reflection.options[:primary_key] ? send(reflection.options[:primary_key]) : id
387
- if autosave != false && (new_record? || record.new_record? || record[reflection.foreign_key] != key || autosave)
387
+
388
+ if (autosave && record.changed_for_autosave?) || new_record? || record_changed?(reflection, record, key)
388
389
  unless reflection.through_reflection
389
390
  record[reflection.foreign_key] = key
390
391
  end
@@ -397,6 +398,11 @@ module ActiveRecord
397
398
  end
398
399
  end
399
400
 
401
+ # If the record is new or it has changed, returns true.
402
+ def record_changed?(reflection, record, key)
403
+ record.new_record? || record[reflection.foreign_key] != key || record.changed_attributes.include?(reflection.foreign_key)
404
+ end
405
+
400
406
  # Saves the associated record if it's new or <tt>:autosave</tt> is enabled.
401
407
  #
402
408
  # In addition, it will destroy the association if it was marked for destruction.
@@ -128,7 +128,7 @@ module ActiveRecord
128
128
  # record.credit_card_number = decrypt(record.credit_card_number)
129
129
  # end
130
130
  #
131
- # alias_method :after_find, :after_save
131
+ # alias_method :after_initialize, :after_save
132
132
  #
133
133
  # private
134
134
  # def encrypt(value)
@@ -163,7 +163,7 @@ module ActiveRecord
163
163
  # record.send("#{@attribute}=", decrypt(record.send("#{@attribute}")))
164
164
  # end
165
165
  #
166
- # alias_method :after_find, :after_save
166
+ # alias_method :after_initialize, :after_save
167
167
  #
168
168
  # private
169
169
  # def encrypt(value)
@@ -398,7 +398,7 @@ module ActiveRecord
398
398
  synchronize do
399
399
  stale = Time.now - @dead_connection_timeout
400
400
  connections.dup.each do |conn|
401
- if conn.in_use? && stale > conn.last_use && !conn.active?
401
+ if conn.in_use? && stale > conn.last_use && !conn.active_threadsafe?
402
402
  remove conn
403
403
  end
404
404
  end
@@ -21,6 +21,14 @@ module ActiveRecord
21
21
  # Returns an array of record hashes with the column names as keys and
22
22
  # column values as values.
23
23
  def select_all(arel, name = nil, binds = [])
24
+ if arel.is_a?(Relation)
25
+ relation = arel
26
+ arel = relation.arel
27
+ if !binds || binds.empty?
28
+ binds = relation.bind_values
29
+ end
30
+ end
31
+
24
32
  select(to_sql(arel, binds), name, binds)
25
33
  end
26
34
 
@@ -41,13 +49,16 @@ module ActiveRecord
41
49
  # Returns an array of the values of the first column in a select:
42
50
  # select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]
43
51
  def select_values(arel, name = nil)
44
- result = select_rows(to_sql(arel, []), name)
45
- result.map { |v| v[0] }
52
+ binds = []
53
+ if arel.is_a?(Relation)
54
+ arel, binds = arel.arel, arel.bind_values
55
+ end
56
+ select_rows(to_sql(arel, binds), name, binds).map(&:first)
46
57
  end
47
58
 
48
59
  # Returns an array of arrays containing the field values.
49
60
  # Order is the same as that returned by +columns+.
50
- def select_rows(sql, name = nil)
61
+ def select_rows(sql, name = nil, binds = [])
51
62
  end
52
63
  undef_method :select_rows
53
64
 
@@ -44,7 +44,9 @@ module ActiveRecord
44
44
  # SQLite does not understand dates, so this method will convert a Date
45
45
  # to a String.
46
46
  def type_cast(value, column)
47
- return value.id if value.respond_to?(:quoted_id)
47
+ if value.respond_to?(:quoted_id) && value.respond_to?(:id)
48
+ return value.id
49
+ end
48
50
 
49
51
  case value
50
52
  when String, ActiveSupport::Multibyte::Chars
@@ -120,9 +120,9 @@ module ActiveRecord
120
120
  # The name of the primary key, if one is to be added automatically.
121
121
  # Defaults to +id+. If <tt>:id</tt> is false this option is ignored.
122
122
  #
123
- # Also note that this just sets the primary key in the table. You additionally
124
- # need to configure the primary key in the model via +self.primary_key=+.
125
- # Models do NOT auto-detect the primary key from their table definition.
123
+ # Note that Active Record models will automatically detect their
124
+ # primary key. This can be avoided by using +self.primary_key=+ on the model
125
+ # to define the key explicitly.
126
126
  #
127
127
  # [<tt>:options</tt>]
128
128
  # Any extra options you want appended to the table definition.
@@ -690,7 +690,7 @@ module ActiveRecord
690
690
 
691
691
  column_type_sql
692
692
  else
693
- type
693
+ type.to_s
694
694
  end
695
695
  end
696
696
 
@@ -719,7 +719,7 @@ module ActiveRecord
719
719
  # require the order columns appear in the SELECT.
720
720
  #
721
721
  # columns_for_distinct("posts.id", ["posts.created_at desc"])
722
- def columns_for_distinct(columns, orders) # :nodoc:
722
+ def columns_for_distinct(columns, orders) #:nodoc:
723
723
  columns
724
724
  end
725
725
 
@@ -741,6 +741,10 @@ module ActiveRecord
741
741
  remove_column table_name, :created_at
742
742
  end
743
743
 
744
+ def update_table_definition(table_name, base) #:nodoc:
745
+ Table.new(table_name, base)
746
+ end
747
+
744
748
  protected
745
749
  def add_index_sort_order(option_strings, column_names, options = {})
746
750
  if options.is_a?(Hash) && order = options[:order]
@@ -873,10 +877,6 @@ module ActiveRecord
873
877
  def create_alter_table(name)
874
878
  AlterTable.new create_table_definition(name, false, {})
875
879
  end
876
-
877
- def update_table_definition(table_name, base)
878
- Table.new(table_name, base)
879
- end
880
880
  end
881
881
  end
882
882
  end
@@ -23,6 +23,10 @@ module ActiveRecord
23
23
  @parent = nil
24
24
  end
25
25
 
26
+ def finalized?
27
+ @state
28
+ end
29
+
26
30
  def committed?
27
31
  @state == :committed
28
32
  end
@@ -39,6 +39,7 @@ module ActiveRecord
39
39
  autoload :ClosedTransaction
40
40
  autoload :RealTransaction
41
41
  autoload :SavepointTransaction
42
+ autoload :TransactionState
42
43
  end
43
44
 
44
45
  # Active Record supports multiple database systems. AbstractAdapter and
@@ -325,6 +326,12 @@ module ActiveRecord
325
326
  def active?
326
327
  end
327
328
 
329
+ # Adapter should redefine this if it needs a threadsafe way to approximate
330
+ # if the connection is active
331
+ def active_threadsafe?
332
+ active?
333
+ end
334
+
328
335
  # Disconnects from the database if already connected, and establishes a
329
336
  # new connection with the database. Implementors should call super if they
330
337
  # override the default implementation.
@@ -238,11 +238,19 @@ module ActiveRecord
238
238
  end
239
239
  end
240
240
 
241
- def new_time(year, mon, mday, hour, min, sec, microsec)
241
+ def new_time(year, mon, mday, hour, min, sec, microsec, offset = nil)
242
242
  # Treat 0000-00-00 00:00:00 as nil.
243
243
  return nil if year.nil? || (year == 0 && mon == 0 && mday == 0)
244
244
 
245
- Time.send(Base.default_timezone, year, mon, mday, hour, min, sec, microsec) rescue nil
245
+ if offset
246
+ time = Time.utc(year, mon, mday, hour, min, sec, microsec) rescue nil
247
+ return nil unless time
248
+
249
+ time -= offset
250
+ Base.default_timezone == :utc ? time : time.getlocal
251
+ else
252
+ Time.public_send(Base.default_timezone, year, mon, mday, hour, min, sec, microsec) rescue nil
253
+ end
246
254
  end
247
255
 
248
256
  def fast_string_to_date(string)
@@ -267,7 +275,7 @@ module ActiveRecord
267
275
  time_hash = Date._parse(string)
268
276
  time_hash[:sec_fraction] = microseconds(time_hash)
269
277
 
270
- new_time(*time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction))
278
+ new_time(*time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction, :offset))
271
279
  end
272
280
  end
273
281