activerecord 3.1.12 → 3.2.0.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 (99) hide show
  1. data/CHANGELOG.md +6263 -103
  2. data/README.rdoc +2 -2
  3. data/examples/performance.rb +55 -31
  4. data/lib/active_record.rb +28 -2
  5. data/lib/active_record/aggregations.rb +2 -2
  6. data/lib/active_record/associations.rb +82 -69
  7. data/lib/active_record/associations/association.rb +2 -37
  8. data/lib/active_record/associations/association_scope.rb +3 -30
  9. data/lib/active_record/associations/builder/association.rb +6 -4
  10. data/lib/active_record/associations/builder/belongs_to.rb +3 -3
  11. data/lib/active_record/associations/builder/collection_association.rb +2 -2
  12. data/lib/active_record/associations/builder/has_many.rb +4 -4
  13. data/lib/active_record/associations/builder/has_one.rb +5 -6
  14. data/lib/active_record/associations/builder/singular_association.rb +3 -16
  15. data/lib/active_record/associations/collection_association.rb +55 -28
  16. data/lib/active_record/associations/collection_proxy.rb +1 -35
  17. data/lib/active_record/associations/has_many_association.rb +5 -1
  18. data/lib/active_record/associations/has_many_through_association.rb +11 -8
  19. data/lib/active_record/associations/join_dependency.rb +1 -1
  20. data/lib/active_record/associations/preloader/association.rb +3 -1
  21. data/lib/active_record/attribute_assignment.rb +221 -0
  22. data/lib/active_record/attribute_methods.rb +212 -32
  23. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +32 -0
  24. data/lib/active_record/attribute_methods/dirty.rb +3 -3
  25. data/lib/active_record/attribute_methods/primary_key.rb +62 -25
  26. data/lib/active_record/attribute_methods/read.rb +69 -80
  27. data/lib/active_record/attribute_methods/serialization.rb +89 -0
  28. data/lib/active_record/attribute_methods/time_zone_conversion.rb +9 -14
  29. data/lib/active_record/attribute_methods/write.rb +27 -5
  30. data/lib/active_record/autosave_association.rb +23 -8
  31. data/lib/active_record/base.rb +223 -1712
  32. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +98 -132
  33. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +82 -29
  34. data/lib/active_record/connection_adapters/abstract/database_statements.rb +13 -42
  35. data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
  36. data/lib/active_record/connection_adapters/abstract/quoting.rb +7 -4
  37. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +36 -25
  38. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +41 -13
  39. data/lib/active_record/connection_adapters/abstract_adapter.rb +78 -43
  40. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +653 -0
  41. data/lib/active_record/connection_adapters/mysql2_adapter.rb +138 -578
  42. data/lib/active_record/connection_adapters/mysql_adapter.rb +86 -658
  43. data/lib/active_record/connection_adapters/postgresql_adapter.rb +144 -94
  44. data/lib/active_record/connection_adapters/schema_cache.rb +50 -0
  45. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +2 -6
  46. data/lib/active_record/connection_adapters/sqlite_adapter.rb +43 -22
  47. data/lib/active_record/counter_cache.rb +1 -1
  48. data/lib/active_record/dynamic_matchers.rb +79 -0
  49. data/lib/active_record/errors.rb +11 -1
  50. data/lib/active_record/explain.rb +83 -0
  51. data/lib/active_record/explain_subscriber.rb +21 -0
  52. data/lib/active_record/fixtures.rb +31 -76
  53. data/lib/active_record/fixtures/file.rb +65 -0
  54. data/lib/active_record/identity_map.rb +1 -7
  55. data/lib/active_record/inheritance.rb +167 -0
  56. data/lib/active_record/integration.rb +49 -0
  57. data/lib/active_record/locking/optimistic.rb +19 -11
  58. data/lib/active_record/locking/pessimistic.rb +1 -1
  59. data/lib/active_record/log_subscriber.rb +3 -3
  60. data/lib/active_record/migration.rb +38 -29
  61. data/lib/active_record/migration/command_recorder.rb +7 -7
  62. data/lib/active_record/model_schema.rb +362 -0
  63. data/lib/active_record/nested_attributes.rb +3 -2
  64. data/lib/active_record/persistence.rb +51 -1
  65. data/lib/active_record/querying.rb +58 -0
  66. data/lib/active_record/railtie.rb +24 -28
  67. data/lib/active_record/railties/controller_runtime.rb +3 -1
  68. data/lib/active_record/railties/databases.rake +133 -77
  69. data/lib/active_record/readonly_attributes.rb +26 -0
  70. data/lib/active_record/reflection.rb +7 -15
  71. data/lib/active_record/relation.rb +78 -35
  72. data/lib/active_record/relation/batches.rb +5 -2
  73. data/lib/active_record/relation/calculations.rb +27 -6
  74. data/lib/active_record/relation/delegation.rb +49 -0
  75. data/lib/active_record/relation/finder_methods.rb +5 -4
  76. data/lib/active_record/relation/predicate_builder.rb +13 -16
  77. data/lib/active_record/relation/query_methods.rb +59 -4
  78. data/lib/active_record/result.rb +1 -1
  79. data/lib/active_record/sanitization.rb +194 -0
  80. data/lib/active_record/schema_dumper.rb +5 -2
  81. data/lib/active_record/scoping.rb +152 -0
  82. data/lib/active_record/scoping/default.rb +140 -0
  83. data/lib/active_record/scoping/named.rb +202 -0
  84. data/lib/active_record/serialization.rb +1 -43
  85. data/lib/active_record/serializers/xml_serializer.rb +2 -44
  86. data/lib/active_record/session_store.rb +11 -11
  87. data/lib/active_record/store.rb +50 -0
  88. data/lib/active_record/test_case.rb +11 -7
  89. data/lib/active_record/timestamp.rb +16 -3
  90. data/lib/active_record/transactions.rb +5 -5
  91. data/lib/active_record/translation.rb +22 -0
  92. data/lib/active_record/validations.rb +1 -1
  93. data/lib/active_record/validations/associated.rb +5 -4
  94. data/lib/active_record/validations/uniqueness.rb +4 -4
  95. data/lib/active_record/version.rb +3 -3
  96. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +1 -5
  97. metadata +48 -38
  98. checksums.yaml +0 -7
  99. data/lib/active_record/named_scope.rb +0 -200
@@ -1,6 +1,5 @@
1
1
  require 'active_support/core_ext/array/wrap'
2
2
  require 'active_support/core_ext/object/inclusion'
3
- require 'active_support/deprecation'
4
3
 
5
4
  module ActiveRecord
6
5
  module Associations
@@ -231,48 +230,14 @@ module ActiveRecord
231
230
  end
232
231
 
233
232
  def build_record(attributes, options)
234
- reflection.original_build_association_called = false
235
-
236
- record = reflection.build_association(attributes, options) do |r|
237
- r.assign_attributes(
238
- create_scope.except(*r.changed),
239
- :without_protection => true
240
- )
241
- end
242
-
243
- if !reflection.original_build_association_called &&
244
- (record.changed & create_scope.keys) != create_scope.keys
245
- # We have detected that there is an overridden AssociationReflection#build_association
246
- # method, but it looks like it has not passed through the block above. So try again and
247
- # show a noisy deprecation warning.
233
+ attributes = (attributes || {}).reverse_merge(creation_attributes)
248
234
 
235
+ reflection.build_association(attributes, options) do |record|
249
236
  record.assign_attributes(
250
237
  create_scope.except(*record.changed),
251
238
  :without_protection => true
252
239
  )
253
-
254
- method = reflection.method(:build_association)
255
- if RUBY_VERSION >= '1.9.2'
256
- source = method.source_location
257
- debug_info = "It looks like the method is defined in #{source[0]} at line #{source[1]}."
258
- else
259
- debug_info = "This might help you find the method: #{method}. If you run this on Ruby 1.9.2 we can tell you exactly where the method is."
260
- end
261
-
262
- ActiveSupport::Deprecation.warn <<-WARN
263
- It looks like ActiveRecord::Reflection::AssociationReflection#build_association has been redefined, either by you or by a plugin or library that you are using. The signature of this method has changed.
264
-
265
- Before: def build_association(*options)
266
- After: def build_association(*options, &block)
267
-
268
- The block argument now needs to be passed through to ActiveRecord::Base#new when this method is overridden, or else your associations will not function correctly in Rails 3.2.
269
-
270
- #{debug_info}
271
-
272
- WARN
273
240
  end
274
-
275
- record
276
241
  end
277
242
  end
278
243
  end
@@ -20,31 +20,19 @@ module ActiveRecord
20
20
  # It's okay to just apply all these like this. The options will only be present if the
21
21
  # association supports that option; this is enforced by the association builder.
22
22
  scope = scope.apply_finder_options(options.slice(
23
- :readonly, :include, :order, :limit, :joins, :group, :having, :offset))
23
+ :readonly, :include, :order, :limit, :joins, :group, :having, :offset, :select))
24
24
 
25
25
  if options[:through] && !options[:include]
26
26
  scope = scope.includes(source_options[:include])
27
27
  end
28
28
 
29
- if select = select_value
30
- scope = scope.select(select)
31
- end
29
+ scope = scope.uniq if options[:uniq]
32
30
 
33
31
  add_constraints(scope)
34
32
  end
35
33
 
36
34
  private
37
35
 
38
- def select_value
39
- select_value = options[:select]
40
-
41
- if reflection.collection?
42
- select_value ||= options[:uniq] && "DISTINCT #{reflection.quoted_table_name}.*"
43
- end
44
-
45
- select_value
46
- end
47
-
48
36
  def add_constraints(scope)
49
37
  tables = construct_tables
50
38
 
@@ -87,7 +75,7 @@ module ActiveRecord
87
75
 
88
76
  conditions.each do |condition|
89
77
  if options[:through] && condition.is_a?(Hash)
90
- condition = disambiguate_condition(table, condition)
78
+ condition = { table.name => condition }
91
79
  end
92
80
 
93
81
  scope = scope.where(interpolate(condition))
@@ -126,21 +114,6 @@ module ActiveRecord
126
114
  end
127
115
  end
128
116
 
129
- def disambiguate_condition(table, condition)
130
- if condition.is_a?(Hash)
131
- Hash[
132
- condition.map do |k, v|
133
- if v.is_a?(Hash)
134
- [k, v]
135
- else
136
- [table.table_alias || table.name, { k => v }]
137
- end
138
- end
139
- ]
140
- else
141
- condition
142
- end
143
- end
144
117
  end
145
118
  end
146
119
  end
@@ -16,6 +16,10 @@ module ActiveRecord::Associations::Builder
16
16
  @model, @name, @options = model, name, options
17
17
  end
18
18
 
19
+ def mixin
20
+ @model.generated_feature_methods
21
+ end
22
+
19
23
  def build
20
24
  validate_options
21
25
  reflection = model.create_reflection(self.class.macro, name, options, model)
@@ -36,16 +40,14 @@ module ActiveRecord::Associations::Builder
36
40
 
37
41
  def define_readers
38
42
  name = self.name
39
-
40
- model.redefine_method(name) do |*params|
43
+ mixin.redefine_method(name) do |*params|
41
44
  association(name).reader(*params)
42
45
  end
43
46
  end
44
47
 
45
48
  def define_writers
46
49
  name = self.name
47
-
48
- model.redefine_method("#{name}=") do |value|
50
+ mixin.redefine_method("#{name}=") do |value|
49
51
  association(name).writer(value)
50
52
  end
51
53
  end
@@ -25,14 +25,14 @@ module ActiveRecord::Associations::Builder
25
25
  name = self.name
26
26
 
27
27
  method_name = "belongs_to_counter_cache_after_create_for_#{name}"
28
- model.redefine_method(method_name) do
28
+ mixin.redefine_method(method_name) do
29
29
  record = send(name)
30
30
  record.class.increment_counter(cache_column, record.id) unless record.nil?
31
31
  end
32
32
  model.after_create(method_name)
33
33
 
34
34
  method_name = "belongs_to_counter_cache_before_destroy_for_#{name}"
35
- model.redefine_method(method_name) do
35
+ mixin.redefine_method(method_name) do
36
36
  record = send(name)
37
37
  record.class.decrement_counter(cache_column, record.id) unless record.nil?
38
38
  end
@@ -48,7 +48,7 @@ module ActiveRecord::Associations::Builder
48
48
  method_name = "belongs_to_touch_after_save_or_destroy_for_#{name}"
49
49
  touch = options[:touch]
50
50
 
51
- model.redefine_method(method_name) do
51
+ mixin.redefine_method(method_name) do
52
52
  record = send(name)
53
53
 
54
54
  unless record.nil?
@@ -58,7 +58,7 @@ module ActiveRecord::Associations::Builder
58
58
  super
59
59
 
60
60
  name = self.name
61
- model.redefine_method("#{name.to_s.singularize}_ids") do
61
+ mixin.redefine_method("#{name.to_s.singularize}_ids") do
62
62
  association(name).ids_reader
63
63
  end
64
64
  end
@@ -67,7 +67,7 @@ module ActiveRecord::Associations::Builder
67
67
  super
68
68
 
69
69
  name = self.name
70
- model.redefine_method("#{name.to_s.singularize}_ids=") do |ids|
70
+ mixin.redefine_method("#{name.to_s.singularize}_ids=") do |ids|
71
71
  association(name).ids_writer(ids)
72
72
  end
73
73
  end
@@ -28,7 +28,7 @@ module ActiveRecord::Associations::Builder
28
28
 
29
29
  def define_destroy_dependency_method
30
30
  name = self.name
31
- model.send(:define_method, dependency_method_name) do
31
+ mixin.redefine_method(dependency_method_name) do
32
32
  send(name).each do |o|
33
33
  # No point in executing the counter update since we're going to destroy the parent anyway
34
34
  counter_method = ('belongs_to_counter_cache_before_destroy_for_' + self.class.name.downcase).to_sym
@@ -45,21 +45,21 @@ module ActiveRecord::Associations::Builder
45
45
 
46
46
  def define_delete_all_dependency_method
47
47
  name = self.name
48
- model.send(:define_method, dependency_method_name) do
48
+ mixin.redefine_method(dependency_method_name) do
49
49
  association(name).delete_all_on_destroy
50
50
  end
51
51
  end
52
52
 
53
53
  def define_nullify_dependency_method
54
54
  name = self.name
55
- model.send(:define_method, dependency_method_name) do
55
+ mixin.redefine_method(dependency_method_name) do
56
56
  send(name).delete_all
57
57
  end
58
58
  end
59
59
 
60
60
  def define_restrict_dependency_method
61
61
  name = self.name
62
- model.send(:define_method, dependency_method_name) do
62
+ mixin.redefine_method(dependency_method_name) do
63
63
  raise ActiveRecord::DeleteRestrictionError.new(name) unless send(name).empty?
64
64
  end
65
65
  end
@@ -44,18 +44,17 @@ module ActiveRecord::Associations::Builder
44
44
  end
45
45
 
46
46
  def define_destroy_dependency_method
47
- model.send(:class_eval, <<-eoruby, __FILE__, __LINE__ + 1)
48
- def #{dependency_method_name}
49
- association(#{name.to_sym.inspect}).delete
50
- end
51
- eoruby
47
+ name = self.name
48
+ mixin.redefine_method(dependency_method_name) do
49
+ association(name).delete
50
+ end
52
51
  end
53
52
  alias :define_delete_dependency_method :define_destroy_dependency_method
54
53
  alias :define_nullify_dependency_method :define_destroy_dependency_method
55
54
 
56
55
  def define_restrict_dependency_method
57
56
  name = self.name
58
- model.redefine_method(dependency_method_name) do
57
+ mixin.redefine_method(dependency_method_name) do
59
58
  raise ActiveRecord::DeleteRestrictionError.new(name) unless send(name).nil?
60
59
  end
61
60
  end
@@ -13,31 +13,18 @@ module ActiveRecord::Associations::Builder
13
13
 
14
14
  private
15
15
 
16
- def define_readers
17
- super
18
- name = self.name
19
-
20
- model.redefine_method("#{name}_loaded?") do
21
- ActiveSupport::Deprecation.warn(
22
- "Calling obj.#{name}_loaded? is deprecated. Please use " \
23
- "obj.association(:#{name}).loaded? instead."
24
- )
25
- association(name).loaded?
26
- end
27
- end
28
-
29
16
  def define_constructors
30
17
  name = self.name
31
18
 
32
- model.redefine_method("build_#{name}") do |*params, &block|
19
+ mixin.redefine_method("build_#{name}") do |*params, &block|
33
20
  association(name).build(*params, &block)
34
21
  end
35
22
 
36
- model.redefine_method("create_#{name}") do |*params, &block|
23
+ mixin.redefine_method("create_#{name}") do |*params, &block|
37
24
  association(name).create(*params, &block)
38
25
  end
39
26
 
40
- model.redefine_method("create_#{name}!") do |*params, &block|
27
+ mixin.redefine_method("create_#{name}!") do |*params, &block|
41
28
  association(name).create!(*params, &block)
42
29
  end
43
30
  end
@@ -49,10 +49,18 @@ module ActiveRecord
49
49
  end
50
50
  else
51
51
  column = "#{reflection.quoted_table_name}.#{reflection.association_primary_key}"
52
+ relation = scoped
52
53
 
53
- scoped.select(column).except(:includes).map! do |record|
54
- record.send(reflection.association_primary_key)
54
+ including = (relation.eager_load_values + relation.includes_values).uniq
55
+
56
+ if including.any?
57
+ join_dependency = ActiveRecord::Associations::JoinDependency.new(reflection.klass, including, [])
58
+ relation = join_dependency.join_associations.inject(relation) do |r, association|
59
+ association.join_relation(r)
60
+ end
55
61
  end
62
+
63
+ relation.uniq.pluck(column)
56
64
  end
57
65
  end
58
66
 
@@ -115,22 +123,16 @@ module ActiveRecord
115
123
  create_record(attributes, options, true, &block)
116
124
  end
117
125
 
118
- # Add +records+ to this association. Returns +self+ so method calls may be chained.
126
+ # Add +records+ to this association. Returns +self+ so method calls may be chained.
119
127
  # Since << flattens its argument list and inserts each record, +push+ and +concat+ behave identically.
120
128
  def concat(*records)
121
- result = true
122
129
  load_target if owner.new_record?
123
130
 
124
- transaction do
125
- records.flatten.each do |record|
126
- raise_on_type_mismatch(record)
127
- add_to_target(record) do |r|
128
- result &&= insert_record(record) unless owner.new_record?
129
- end
130
- end
131
+ if owner.new_record?
132
+ concat_records(records)
133
+ else
134
+ transaction { concat_records(records) }
131
135
  end
132
-
133
- result && records
134
136
  end
135
137
 
136
138
  # Starts a transaction in the association class's database connection.
@@ -248,7 +250,7 @@ module ActiveRecord
248
250
  # This method is abstract in the sense that it relies on
249
251
  # +count_records+, which is a method descendants have to provide.
250
252
  def size
251
- if owner.new_record? || (loaded? && !options[:uniq])
253
+ if !find_target? || (loaded? && !options[:uniq])
252
254
  target.size
253
255
  elsif !loaded? && options[:group]
254
256
  load_target.size
@@ -306,14 +308,10 @@ module ActiveRecord
306
308
  other_array.each { |val| raise_on_type_mismatch(val) }
307
309
  original_target = load_target.dup
308
310
 
309
- transaction do
310
- delete(target - other_array)
311
-
312
- unless concat(other_array - target)
313
- @target = original_target
314
- raise RecordNotSaved, "Failed to replace #{reflection.name} because one or more of the " \
315
- "new records could not be saved."
316
- end
311
+ if owner.new_record?
312
+ replace_records(other_array, original_target)
313
+ else
314
+ transaction { replace_records(other_array, original_target) }
317
315
  end
318
316
  end
319
317
 
@@ -453,14 +451,20 @@ module ActiveRecord
453
451
  records.each { |record| raise_on_type_mismatch(record) }
454
452
  existing_records = records.reject { |r| r.new_record? }
455
453
 
456
- transaction do
457
- records.each { |record| callback(:before_remove, record) }
454
+ if existing_records.empty?
455
+ remove_records(existing_records, records, method)
456
+ else
457
+ transaction { remove_records(existing_records, records, method) }
458
+ end
459
+ end
460
+
461
+ def remove_records(existing_records, records, method)
462
+ records.each { |record| callback(:before_remove, record) }
458
463
 
459
- delete_records(existing_records, method) if existing_records.any?
460
- records.each { |record| target.delete(record) }
464
+ delete_records(existing_records, method) if existing_records.any?
465
+ records.each { |record| target.delete(record) }
461
466
 
462
- records.each { |record| callback(:after_remove, record) }
463
- end
467
+ records.each { |record| callback(:after_remove, record) }
464
468
  end
465
469
 
466
470
  # Delete the given records from the association, using one of the methods :destroy,
@@ -469,6 +473,29 @@ module ActiveRecord
469
473
  raise NotImplementedError
470
474
  end
471
475
 
476
+ def replace_records(new_target, original_target)
477
+ delete(target - new_target)
478
+
479
+ unless concat(new_target - target)
480
+ @target = original_target
481
+ raise RecordNotSaved, "Failed to replace #{reflection.name} because one or more of the " \
482
+ "new records could not be saved."
483
+ end
484
+ end
485
+
486
+ def concat_records(records)
487
+ result = true
488
+
489
+ records.flatten.each do |record|
490
+ raise_on_type_mismatch(record)
491
+ add_to_target(record) do |r|
492
+ result &&= insert_record(record) unless owner.new_record?
493
+ end
494
+ end
495
+
496
+ result && records
497
+ end
498
+
472
499
  def callback(method, record)
473
500
  callbacks_for(method).each do |callback|
474
501
  case callback
@@ -1,12 +1,5 @@
1
- require 'active_support/deprecation'
2
-
3
1
  module ActiveRecord
4
2
  module Associations
5
- AssociationCollection = ActiveSupport::Deprecation::DeprecatedConstantProxy.new(
6
- 'ActiveRecord::Associations::AssociationCollection',
7
- 'ActiveRecord::Associations::CollectionProxy'
8
- )
9
-
10
3
  # Association proxies in Active Record are middlemen between the object that
11
4
  # holds the association, known as the <tt>@owner</tt>, and the actual associated
12
5
  # object, known as the <tt>@target</tt>. The kind of association any proxy is
@@ -46,7 +39,7 @@ module ActiveRecord
46
39
  instance_methods.each { |m| undef_method m unless m.to_s =~ /^(?:nil\?|send|object_id|to_a)$|^__|^respond_to|proxy_/ }
47
40
 
48
41
  delegate :group, :order, :limit, :joins, :where, :preload, :eager_load, :includes, :from,
49
- :lock, :readonly, :having, :to => :scoped
42
+ :lock, :readonly, :having, :pluck, :to => :scoped
50
43
 
51
44
  delegate :target, :load_target, :loaded?, :to => :@association
52
45
 
@@ -134,33 +127,6 @@ module ActiveRecord
134
127
  proxy_association.reload
135
128
  self
136
129
  end
137
-
138
- def proxy_owner
139
- ActiveSupport::Deprecation.warn(
140
- "Calling record.#{@association.reflection.name}.proxy_owner is deprecated. Please use " \
141
- "record.association(:#{@association.reflection.name}).owner instead. Or, from an " \
142
- "association extension you can access proxy_association.owner."
143
- )
144
- proxy_association.owner
145
- end
146
-
147
- def proxy_target
148
- ActiveSupport::Deprecation.warn(
149
- "Calling record.#{@association.reflection.name}.proxy_target is deprecated. Please use " \
150
- "record.association(:#{@association.reflection.name}).target instead. Or, from an " \
151
- "association extension you can access proxy_association.target."
152
- )
153
- proxy_association.target
154
- end
155
-
156
- def proxy_reflection
157
- ActiveSupport::Deprecation.warn(
158
- "Calling record.#{@association.reflection.name}.proxy_reflection is deprecated. Please use " \
159
- "record.association(:#{@association.reflection.name}).reflection instead. Or, from an " \
160
- "association extension you can access proxy_association.reflection."
161
- )
162
- proxy_association.reflection
163
- end
164
130
  end
165
131
  end
166
132
  end