activerecord 3.2.13 → 3.2.14.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 (37) hide show
  1. data/CHANGELOG.md +148 -2
  2. data/lib/active_record/associations/association.rb +9 -3
  3. data/lib/active_record/associations/belongs_to_association.rb +1 -1
  4. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +2 -1
  5. data/lib/active_record/associations/builder/belongs_to.rb +4 -1
  6. data/lib/active_record/associations/builder/belongs_to.rb.orig +95 -0
  7. data/lib/active_record/associations/collection_association.rb +1 -1
  8. data/lib/active_record/associations/has_many_association.rb +1 -2
  9. data/lib/active_record/associations/has_many_association.rb.orig +116 -0
  10. data/lib/active_record/associations/join_dependency.rb +1 -1
  11. data/lib/active_record/associations/join_dependency/join_association.rb +6 -1
  12. data/lib/active_record/associations/preloader/through_association.rb +1 -2
  13. data/lib/active_record/associations/through_association.rb +1 -1
  14. data/lib/active_record/autosave_association.rb +7 -12
  15. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +2 -2
  16. data/lib/active_record/connection_adapters/abstract/schema_statements.rb.orig +619 -0
  17. data/lib/active_record/connection_adapters/connection_specification.rb.orig +124 -0
  18. data/lib/active_record/connection_adapters/mysql2_adapter.rb +5 -3
  19. data/lib/active_record/connection_adapters/postgresql/cast.rb.orig +136 -0
  20. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb.orig +485 -0
  21. data/lib/active_record/connection_adapters/postgresql_adapter.rb +9 -3
  22. data/lib/active_record/core.rb.orig +452 -0
  23. data/lib/active_record/explain_subscriber.rb +1 -1
  24. data/lib/active_record/model_schema.rb +4 -2
  25. data/lib/active_record/nested_attributes.rb +41 -17
  26. data/lib/active_record/railtie.rb +6 -7
  27. data/lib/active_record/railties/databases.rake +2 -1
  28. data/lib/active_record/relation/calculations.rb +5 -6
  29. data/lib/active_record/relation/calculations.rb.orig +378 -0
  30. data/lib/active_record/relation/finder_methods.rb +1 -0
  31. data/lib/active_record/relation/finder_methods.rb.orig +405 -0
  32. data/lib/active_record/relation/spawn_methods.rb +34 -3
  33. data/lib/active_record/store.rb +1 -1
  34. data/lib/active_record/version.rb +2 -2
  35. data/lib/rails/generators/active_record/observer/observer_generator.rb.orig +15 -0
  36. metadata +117 -70
  37. checksums.yaml +0 -7
data/CHANGELOG.md CHANGED
@@ -1,11 +1,147 @@
1
- ## Rails 3.2.13 (Feb 17, 2013) ##
1
+ ## Rails 3.2.14.rc1 (Jul 8, 2013) ##
2
+
3
+ * Do not shallow the original exception in `exec_cache` on PostgreSQL adapter.
4
+
5
+ Fixes #11260.
6
+
7
+ *Rafael Mendonça França*
8
+
9
+ * Fix `ActiveRecord::Store` incorrectly tracking changes of its attributes.
10
+ Fixes #10373.
11
+
12
+ *Janko Marohnić*
13
+
14
+ * Fix a bug that prevented the use of the default STI inheritance column
15
+ (ActiveRecord::Base.inheritance_column = 'some_column'.)
16
+
17
+ *chapmajs + Takehiro Adachi*
18
+
19
+ * Fix mysql2 adapter raises the correct exception when executing a query on a
20
+ closed connection.
21
+
22
+ *Yves Senn*
23
+
24
+ * Fixes bug where `Company.new.contract_ids` would incorrectly load
25
+ all non-associated contracts.
26
+
27
+ Example:
28
+
29
+ company = Company.new # Company has many :contracts
30
+
31
+ # before
32
+ company.contract_ids # => SELECT ... WHERE `contracts`.`company_id` IS NULL
33
+
34
+ # after
35
+ company.contract_ids # => []
36
+
37
+ *Jared Armstrong*
38
+
39
+ * Fix the `:primary_key` option for `has_many` associations.
40
+ Fixes #10693.
41
+
42
+ *Yves Senn*
43
+
44
+ * fixes bug introduced by #3329. Now, when autosaving associations,
45
+ deletions happen before inserts and saves. This prevents a 'duplicate
46
+ unique value' database error that would occur if a record being created had
47
+ the same value on a unique indexed field as that of a record being destroyed.
48
+
49
+ Backport of #10417
50
+
51
+ *Johnny Holton*
52
+
53
+ * Fix that under some conditions, Active Record could produce invalid SQL of the sort:
54
+ "SELECT DISTINCT DISTINCT".
55
+
56
+ Backport of #6792.
57
+
58
+ *Ben Woosley*
59
+
60
+ * Require `ActiveRecord::Base` in railtie hooks for rake_tasks, console and runner to
61
+ avoid circular constant loading issues.
62
+
63
+ Backport #7695.
64
+
65
+ Fixes #7683 and #882
66
+
67
+ *Ben Holley*
68
+
69
+ * Maintain context for joins within ActiveRecord::Relation merges.
70
+ Backport #10164.
71
+
72
+ *Neeraj Singh + Andrew Horner*
73
+
74
+ * Make sure the `EXPLAIN` command is never triggered by a `select_db` call.
75
+
76
+ *Daniel Schierbeck*
77
+
78
+ * Revert changes on `pluck` that was ignoring the select clause when the relation already
79
+ has one. This caused a regression since it changed the behavior in a stable release.
80
+
81
+ Fixes #9777.
82
+
83
+ *Rafael Mendonça França*
84
+
85
+ * Confirm a record has not already been destroyed before decrementing counter cache.
86
+
87
+ *Ben Tucker*
88
+
89
+ * Default values for PostgreSQL bigint types now get parsed and dumped to the
90
+ schema correctly.
91
+ Backport #10098.
92
+
93
+ *Erik Peterson*
94
+
95
+ * Removed warning when `auto_explain_threshold_in_seconds` is set and the
96
+ connection adapter doesn't support explain.
97
+ This is causing a regression since the Active Record Railtie is trying to
98
+ connect to the development database in the application boot.
99
+
100
+ *Rafael Mendonça França*
101
+
102
+ * Do not reset `inheritance_column` when it's set explicitly.
103
+ Backport of #5327.
104
+
105
+ *kennyj + Fred Wu*
106
+
107
+ * Fix a problem wrong exception is occured
108
+ when raising no translatable exception in PostgreSQL.
109
+
110
+ *kennyj*
111
+
112
+ * Resets the postgres search path in the structure.sql after the structure
113
+ is dumped in order to find schema_migrations table when multiples schemas
114
+ are used.
115
+ Fixes #9796.
116
+
117
+ *Juan M. Cuello + Dembskiy Alexander*
118
+
119
+ * Reload the association target if it's stale. `@stale_state` should be nil
120
+ when a model isn't saved.
121
+ Fixes #7526.
122
+
123
+ *Larry Lv*
124
+
125
+ * Don't read CSV files during execution of `db:fixtures:load`. CSV support for
126
+ fixtures was removed some time ago but the task was still loading them, even
127
+ though later the code was looking for the related yaml file instead.
128
+
129
+ *kennyj*
130
+
131
+
132
+ ## Rails 3.2.13 (Mar 18, 2013) ##
133
+
134
+ * Chaining multiple preloaded scopes will correctly preload all the scopes
135
+ at the same time.
136
+
137
+ *Chris Geihsler*
2
138
 
3
139
  * Reverted 921a296a3390192a71abeec6d9a035cc6d1865c8, 'Quote numeric values
4
140
  compared to string columns.' This caused several regressions.
5
141
 
6
142
  *Steve Klabnik*
7
143
 
8
- * Fix overriding of attributes by default_scope on `ActiveRecord::Base#dup`.
144
+ * Fix overriding of attributes by `default_scope` on `ActiveRecord::Base#dup`.
9
145
 
10
146
  *Hiroshige UMINO*
11
147
 
@@ -223,6 +359,16 @@
223
359
 
224
360
  *Victor Costan*
225
361
 
362
+ * `#pluck` can be used on a relation with `select` clause.
363
+ Fixes #7551.
364
+ Backport of #8176.
365
+
366
+ Example:
367
+
368
+ Topic.select([:approved, :id]).order(:id).pluck(:id)
369
+
370
+ *Yves Senn*
371
+
226
372
  * Use `nil?` instead of `blank?` to check whether dynamic finder with a bang
227
373
  should raise RecordNotFound.
228
374
  Fixes #7238.
@@ -46,6 +46,7 @@ module ActiveRecord
46
46
  @loaded = false
47
47
  IdentityMap.remove(target) if IdentityMap.enabled? && target
48
48
  @target = nil
49
+ @stale_state = nil
49
50
  end
50
51
 
51
52
  # Reloads the \target and returns +self+ on success.
@@ -128,16 +129,21 @@ module ActiveRecord
128
129
  # This method is abstract in the sense that it relies on +find_target+,
129
130
  # which is expected to be provided by descendants.
130
131
  #
131
- # If the \target is already \loaded it is just returned. Thus, you can call
132
- # +load_target+ unconditionally to get the \target.
132
+ # If the \target is stale(the target no longer points to the record(s) that the
133
+ # relevant foreign_key(s) refers to.), force reload the \target.
134
+ #
135
+ # Otherwise if the \target is already \loaded it is just returned. Thus, you can
136
+ # call +load_target+ unconditionally to get the \target.
133
137
  #
134
138
  # ActiveRecord::RecordNotFound is rescued within the method, and it is
135
139
  # not reraised. The proxy is \reset and +nil+ is the return value.
136
140
  def load_target
137
- if find_target?
141
+ if (@stale_state && stale_target?) || find_target?
138
142
  begin
139
143
  if IdentityMap.enabled? && association_class && association_class.respond_to?(:base_class)
140
144
  @target = IdentityMap.get(association_class, owner[reflection.foreign_key])
145
+ elsif @stale_state && stale_target?
146
+ @target = find_target
141
147
  end
142
148
  rescue NameError
143
149
  nil
@@ -72,7 +72,7 @@ module ActiveRecord
72
72
  end
73
73
 
74
74
  def stale_state
75
- owner[reflection.foreign_key].to_s
75
+ owner[reflection.foreign_key] && owner[reflection.foreign_key].to_s
76
76
  end
77
77
  end
78
78
  end
@@ -27,7 +27,8 @@ module ActiveRecord
27
27
  end
28
28
 
29
29
  def stale_state
30
- [super, owner[reflection.foreign_type].to_s]
30
+ foreign_key = super
31
+ foreign_key && [foreign_key.to_s, owner[reflection.foreign_type].to_s]
31
32
  end
32
33
  end
33
34
  end
@@ -34,7 +34,10 @@ module ActiveRecord::Associations::Builder
34
34
  method_name = "belongs_to_counter_cache_before_destroy_for_#{name}"
35
35
  mixin.redefine_method(method_name) do
36
36
  record = send(name)
37
- record.class.decrement_counter(cache_column, record.id) unless record.nil?
37
+
38
+ if record && !self.destroyed?
39
+ record.class.decrement_counter(cache_column, record.id)
40
+ end
38
41
  end
39
42
  model.before_destroy(method_name)
40
43
 
@@ -0,0 +1,95 @@
1
+ require 'active_support/core_ext/object/inclusion'
2
+
3
+ module ActiveRecord::Associations::Builder
4
+ class BelongsTo < SingularAssociation #:nodoc:
5
+ self.macro = :belongs_to
6
+
7
+ self.valid_options += [:foreign_type, :polymorphic, :touch]
8
+
9
+ def constructable?
10
+ !options[:polymorphic]
11
+ end
12
+
13
+ def build
14
+ reflection = super
15
+ add_counter_cache_callbacks(reflection) if options[:counter_cache]
16
+ add_touch_callbacks(reflection) if options[:touch]
17
+ configure_dependency
18
+ reflection
19
+ end
20
+
21
+ private
22
+
23
+ def add_counter_cache_callbacks(reflection)
24
+ cache_column = reflection.counter_cache_column
25
+ name = self.name
26
+
27
+ <<<<<<< HEAD
28
+ method_name = "belongs_to_counter_cache_after_create_for_#{name}"
29
+ mixin.redefine_method(method_name) do
30
+ record = send(name)
31
+ record.class.increment_counter(cache_column, record.id) unless record.nil?
32
+ =======
33
+ def belongs_to_counter_cache_before_destroy_for_#{name}
34
+ unless destroyed_by_association && destroyed_by_association.foreign_key.to_sym == #{foreign_key.to_sym.inspect}
35
+ record = #{name}
36
+ if record && !self.destroyed?
37
+ record.class.decrement_counter(:#{cache_column}, record.id)
38
+ end
39
+ end
40
+ >>>>>>> 19d32e8... Merge pull request #10489 from greenriver/ar_counter_cache_multiple_destroy
41
+ end
42
+ model.after_create(method_name)
43
+
44
+ method_name = "belongs_to_counter_cache_before_destroy_for_#{name}"
45
+ mixin.redefine_method(method_name) do
46
+ record = send(name)
47
+ record.class.decrement_counter(cache_column, record.id) unless record.nil?
48
+ end
49
+ model.before_destroy(method_name)
50
+
51
+ model.send(:module_eval,
52
+ "#{reflection.class_name}.send(:attr_readonly,\"#{cache_column}\".intern) if defined?(#{reflection.class_name}) && #{reflection.class_name}.respond_to?(:attr_readonly)", __FILE__, __LINE__
53
+ )
54
+ end
55
+
56
+ def add_touch_callbacks(reflection)
57
+ name = self.name
58
+ method_name = "belongs_to_touch_after_save_or_destroy_for_#{name}"
59
+ touch = options[:touch]
60
+
61
+ mixin.redefine_method(method_name) do
62
+ record = send(name)
63
+
64
+ unless record.nil?
65
+ if touch == true
66
+ record.touch
67
+ else
68
+ record.touch(touch)
69
+ end
70
+ end
71
+ end
72
+
73
+ model.after_save(method_name)
74
+ model.after_touch(method_name)
75
+ model.after_destroy(method_name)
76
+ end
77
+
78
+ def configure_dependency
79
+ if options[:dependent]
80
+ unless options[:dependent].in?([:destroy, :delete])
81
+ raise ArgumentError, "The :dependent option expects either :destroy or :delete (#{options[:dependent].inspect})"
82
+ end
83
+
84
+ method_name = "belongs_to_dependent_#{options[:dependent]}_for_#{name}"
85
+ model.send(:class_eval, <<-eoruby, __FILE__, __LINE__ + 1)
86
+ def #{method_name}
87
+ association = #{name}
88
+ association.#{options[:dependent]} if association
89
+ end
90
+ eoruby
91
+ model.after_destroy method_name
92
+ end
93
+ end
94
+ end
95
+ end
@@ -43,7 +43,7 @@ module ActiveRecord
43
43
 
44
44
  # Implements the ids reader method, e.g. foo.item_ids for Foo.has_many :items
45
45
  def ids_reader
46
- if loaded? || options[:finder_sql]
46
+ if owner.new_record? || loaded? || options[:finder_sql]
47
47
  load_target.map do |record|
48
48
  record.send(reflection.association_primary_key)
49
49
  end
@@ -89,8 +89,7 @@ module ActiveRecord
89
89
  records.each { |r| r.destroy }
90
90
  update_counter(-records.length) unless inverse_updates_counter_cache?
91
91
  else
92
- keys = records.map { |r| r[reflection.association_primary_key] }
93
- scope = scoped.where(reflection.association_primary_key => keys)
92
+ scope = self.scoped.where(reflection.klass.primary_key => records)
94
93
 
95
94
  if method == :delete_all
96
95
  update_counter(-scope.delete_all)
@@ -0,0 +1,116 @@
1
+ module ActiveRecord
2
+ # = Active Record Has Many Association
3
+ module Associations
4
+ # This is the proxy that handles a has many association.
5
+ #
6
+ # If the association has a <tt>:through</tt> option further specialization
7
+ # is provided by its child HasManyThroughAssociation.
8
+ class HasManyAssociation < CollectionAssociation #:nodoc:
9
+
10
+ def insert_record(record, validate = true, raise = false)
11
+ set_owner_attributes(record)
12
+
13
+ if raise
14
+ record.save!(:validate => validate)
15
+ else
16
+ record.save(:validate => validate)
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ # Returns the number of records in this collection.
23
+ #
24
+ # If the association has a counter cache it gets that value. Otherwise
25
+ # it will attempt to do a count via SQL, bounded to <tt>:limit</tt> if
26
+ # there's one. Some configuration options like :group make it impossible
27
+ # to do an SQL count, in those cases the array count will be used.
28
+ #
29
+ # That does not depend on whether the collection has already been loaded
30
+ # or not. The +size+ method is the one that takes the loaded flag into
31
+ # account and delegates to +count_records+ if needed.
32
+ #
33
+ # If the collection is empty the target is set to an empty array and
34
+ # the loaded flag is set to true as well.
35
+ def count_records
36
+ count = if has_cached_counter?
37
+ owner.send(:read_attribute, cached_counter_attribute_name)
38
+ elsif options[:counter_sql] || options[:finder_sql]
39
+ reflection.klass.count_by_sql(custom_counter_sql)
40
+ else
41
+ scoped.count
42
+ end
43
+
44
+ # If there's nothing in the database and @target has no new records
45
+ # we are certain the current target is an empty array. This is a
46
+ # documented side-effect of the method that may avoid an extra SELECT.
47
+ @target ||= [] and loaded! if count == 0
48
+
49
+ [options[:limit], count].compact.min
50
+ end
51
+
52
+ def has_cached_counter?(reflection = reflection)
53
+ owner.attribute_present?(cached_counter_attribute_name(reflection))
54
+ end
55
+
56
+ def cached_counter_attribute_name(reflection = reflection)
57
+ "#{reflection.name}_count"
58
+ end
59
+
60
+ def update_counter(difference, reflection = reflection)
61
+ if has_cached_counter?(reflection)
62
+ counter = cached_counter_attribute_name(reflection)
63
+ owner.class.update_counters(owner.id, counter => difference)
64
+ owner[counter] += difference
65
+ owner.changed_attributes.delete(counter) # eww
66
+ end
67
+ end
68
+
69
+ # This shit is nasty. We need to avoid the following situation:
70
+ #
71
+ # * An associated record is deleted via record.destroy
72
+ # * Hence the callbacks run, and they find a belongs_to on the record with a
73
+ # :counter_cache options which points back at our owner. So they update the
74
+ # counter cache.
75
+ # * In which case, we must make sure to *not* update the counter cache, or else
76
+ # it will be decremented twice.
77
+ #
78
+ # Hence this method.
79
+ def inverse_updates_counter_cache?(reflection = reflection)
80
+ counter_name = cached_counter_attribute_name(reflection)
81
+ reflection.klass.reflect_on_all_associations(:belongs_to).any? { |inverse_reflection|
82
+ inverse_reflection.counter_cache_column == counter_name
83
+ }
84
+ end
85
+
86
+ # Deletes the records according to the <tt>:dependent</tt> option.
87
+ def delete_records(records, method)
88
+ if method == :destroy
89
+ records.each { |r| r.destroy }
90
+ update_counter(-records.length) unless inverse_updates_counter_cache?
91
+ else
92
+ <<<<<<< HEAD
93
+ keys = records.map { |r| r[reflection.association_primary_key] }
94
+ scope = scoped.where(reflection.association_primary_key => keys)
95
+ =======
96
+ if records == :all
97
+ scope = self.scope
98
+ else
99
+ scope = self.scope.where(reflection.klass.primary_key => records)
100
+ end
101
+ >>>>>>> 030d30d... Merge pull request #10713 from senny/10693_fix_primary_key_option_on_has_many
102
+
103
+ if method == :delete_all
104
+ update_counter(-scope.delete_all)
105
+ else
106
+ update_counter(-scope.update_all(reflection.foreign_key => nil))
107
+ end
108
+ end
109
+ end
110
+
111
+ def foreign_key_present?
112
+ owner.attribute_present?(reflection.association_primary_key)
113
+ end
114
+ end
115
+ end
116
+ end