activerecord 4.1.1 → 4.1.2.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 (62) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +312 -0
  3. data/lib/active_record/association_relation.rb +4 -0
  4. data/lib/active_record/associations.rb +24 -3
  5. data/lib/active_record/associations/association.rb +1 -1
  6. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +10 -4
  7. data/lib/active_record/associations/builder/has_many.rb +1 -1
  8. data/lib/active_record/associations/collection_association.rb +5 -5
  9. data/lib/active_record/associations/collection_proxy.rb +4 -0
  10. data/lib/active_record/associations/has_many_association.rb +6 -5
  11. data/lib/active_record/associations/has_many_through_association.rb +6 -2
  12. data/lib/active_record/associations/join_dependency.rb +1 -1
  13. data/lib/active_record/associations/join_dependency/join_association.rb +1 -1
  14. data/lib/active_record/associations/preloader.rb +14 -35
  15. data/lib/active_record/associations/preloader/association.rb +26 -5
  16. data/lib/active_record/associations/singular_association.rb +3 -3
  17. data/lib/active_record/associations/through_association.rb +4 -2
  18. data/lib/active_record/attribute_methods.rb +2 -0
  19. data/lib/active_record/attribute_methods/dirty.rb +2 -2
  20. data/lib/active_record/attribute_methods/serialization.rb +24 -5
  21. data/lib/active_record/attribute_methods/write.rb +22 -14
  22. data/lib/active_record/autosave_association.rb +40 -35
  23. data/lib/active_record/base.rb +2 -2
  24. data/lib/active_record/callbacks.rb +2 -2
  25. data/lib/active_record/connection_adapters/abstract/database_statements.rb +10 -13
  26. data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -0
  27. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +7 -1
  28. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
  29. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +8 -6
  30. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +1 -4
  31. data/lib/active_record/connection_adapters/postgresql/oid.rb +10 -5
  32. data/lib/active_record/connection_adapters/postgresql/quoting.rb +9 -0
  33. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +11 -6
  34. data/lib/active_record/connection_adapters/postgresql_adapter.rb +7 -0
  35. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +6 -3
  36. data/lib/active_record/connection_handling.rb +2 -2
  37. data/lib/active_record/core.rb +3 -0
  38. data/lib/active_record/counter_cache.rb +2 -3
  39. data/lib/active_record/fixtures.rb +1 -1
  40. data/lib/active_record/gem_version.rb +2 -2
  41. data/lib/active_record/locking/optimistic.rb +1 -1
  42. data/lib/active_record/log_subscriber.rb +1 -1
  43. data/lib/active_record/migration.rb +1 -1
  44. data/lib/active_record/nested_attributes.rb +2 -2
  45. data/lib/active_record/null_relation.rb +19 -5
  46. data/lib/active_record/persistence.rb +8 -8
  47. data/lib/active_record/railties/databases.rake +3 -2
  48. data/lib/active_record/reflection.rb +45 -13
  49. data/lib/active_record/relation.rb +7 -6
  50. data/lib/active_record/relation/calculations.rb +10 -2
  51. data/lib/active_record/relation/delegation.rb +2 -2
  52. data/lib/active_record/relation/finder_methods.rb +1 -1
  53. data/lib/active_record/relation/merger.rb +10 -2
  54. data/lib/active_record/relation/predicate_builder.rb +2 -2
  55. data/lib/active_record/relation/query_methods.rb +2 -2
  56. data/lib/active_record/scoping/default.rb +3 -3
  57. data/lib/active_record/store.rb +14 -5
  58. data/lib/active_record/timestamp.rb +2 -2
  59. data/lib/active_record/transactions.rb +1 -1
  60. data/lib/active_record/validations/presence.rb +1 -1
  61. data/lib/active_record/validations/uniqueness.rb +2 -2
  62. metadata +27 -35
@@ -37,7 +37,7 @@ module ActiveRecord
37
37
  end
38
38
 
39
39
  # Given an attributes hash, +instantiate+ returns a new instance of
40
- # the appropriate class.
40
+ # the appropriate class. Accepts only keys as strings.
41
41
  #
42
42
  # For example, +Post.all+ may return Comments, Messages, and Emails
43
43
  # by storing the record's subclass in a +type+ attribute. By calling
@@ -46,10 +46,10 @@ module ActiveRecord
46
46
  #
47
47
  # See +ActiveRecord::Inheritance#discriminate_class_for_record+ to see
48
48
  # how this "single-table" inheritance mapping is implemented.
49
- def instantiate(record, column_types = {})
50
- klass = discriminate_class_for_record(record)
49
+ def instantiate(attributes, column_types = {})
50
+ klass = discriminate_class_for_record(attributes)
51
51
  column_types = klass.decorate_columns(column_types.dup)
52
- klass.allocate.init_with('attributes' => record, 'column_types' => column_types)
52
+ klass.allocate.init_with('attributes' => attributes, 'column_types' => column_types)
53
53
  end
54
54
 
55
55
  private
@@ -479,24 +479,24 @@ module ActiveRecord
479
479
 
480
480
  def create_or_update
481
481
  raise ReadOnlyRecord if readonly?
482
- result = new_record? ? create_record : update_record
482
+ result = new_record? ? _create_record : _update_record
483
483
  result != false
484
484
  end
485
485
 
486
486
  # Updates the associated record with values matching those of the instance attributes.
487
487
  # Returns the number of affected rows.
488
- def update_record(attribute_names = @attributes.keys)
488
+ def _update_record(attribute_names = @attributes.keys)
489
489
  attributes_values = arel_attributes_with_values_for_update(attribute_names)
490
490
  if attributes_values.empty?
491
491
  0
492
492
  else
493
- self.class.unscoped.update_record attributes_values, id, id_was
493
+ self.class.unscoped._update_record attributes_values, id, id_was
494
494
  end
495
495
  end
496
496
 
497
497
  # Creates a record with values matching those of the instance attributes
498
498
  # and returns its id.
499
- def create_record(attribute_names = @attributes.keys)
499
+ def _create_record(attribute_names = @attributes.keys)
500
500
  attributes_values = arel_attributes_with_values_for_create(attribute_names)
501
501
 
502
502
  new_id = self.class.unscoped.insert attributes_values
@@ -186,7 +186,7 @@ db_namespace = namespace :db do
186
186
 
187
187
  fixtures_dir = File.join [base_dir, ENV['FIXTURES_DIR']].compact
188
188
 
189
- (ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/) : Dir["#{fixtures_dir}/**/*.yml"].map {|f| f[(fixtures_dir.size + 1)..-5] }).each do |fixture_file|
189
+ (ENV['FIXTURES'] ? ENV['FIXTURES'].split(',') : Dir["#{fixtures_dir}/**/*.yml"].map {|f| f[(fixtures_dir.size + 1)..-5] }).each do |fixture_file|
190
190
  ActiveRecord::FixtureSet.create_fixtures(fixtures_dir, fixture_file)
191
191
  end
192
192
  end
@@ -268,7 +268,8 @@ db_namespace = namespace :db do
268
268
  current_config = ActiveRecord::Tasks::DatabaseTasks.current_config
269
269
  ActiveRecord::Tasks::DatabaseTasks.structure_dump(current_config, filename)
270
270
 
271
- if ActiveRecord::Base.connection.supports_migrations?
271
+ if ActiveRecord::Base.connection.supports_migrations? &&
272
+ ActiveRecord::SchemaMigration.table_exists?
272
273
  File.open(filename, "a") do |f|
273
274
  f.puts ActiveRecord::Base.connection.dump_schema_information
274
275
  f.print "\n"
@@ -4,9 +4,9 @@ module ActiveRecord
4
4
  extend ActiveSupport::Concern
5
5
 
6
6
  included do
7
- class_attribute :reflections
7
+ class_attribute :_reflections
8
8
  class_attribute :aggregate_reflections
9
- self.reflections = {}
9
+ self._reflections = {}
10
10
  self.aggregate_reflections = {}
11
11
  end
12
12
 
@@ -22,7 +22,7 @@ module ActiveRecord
22
22
  end
23
23
 
24
24
  def self.add_reflection(ar, name, reflection)
25
- ar.reflections = ar.reflections.merge(name => reflection)
25
+ ar._reflections = ar._reflections.merge(name => reflection)
26
26
  end
27
27
 
28
28
  def self.add_aggregate_reflection(ar, name, reflection)
@@ -51,6 +51,24 @@ module ActiveRecord
51
51
  aggregate_reflections[aggregation]
52
52
  end
53
53
 
54
+ # Returns a Hash of name of the reflection as the key and a AssociationReflection as the value.
55
+ #
56
+ # Account.reflections # => {balance: AggregateReflection}
57
+ #
58
+ # @api public
59
+ def reflections
60
+ ref = {}
61
+ _reflections.each do |name, reflection|
62
+ parent_name, parent_reflection = reflection.parent_reflection
63
+ if parent_name
64
+ ref[parent_name] = parent_reflection
65
+ else
66
+ ref[name] = reflection
67
+ end
68
+ end
69
+ ref
70
+ end
71
+
54
72
  # Returns an array of AssociationReflection objects for all the
55
73
  # associations in the class. If you only want to reflect on a certain
56
74
  # association type, pass in the symbol (<tt>:has_many</tt>, <tt>:has_one</tt>,
@@ -61,6 +79,7 @@ module ActiveRecord
61
79
  # Account.reflect_on_all_associations # returns an array of all associations
62
80
  # Account.reflect_on_all_associations(:has_many) # returns an array of all has_many associations
63
81
  #
82
+ # @api public
64
83
  def reflect_on_all_associations(macro = nil)
65
84
  association_reflections = reflections.values
66
85
  macro ? association_reflections.select { |reflection| reflection.macro == macro } : association_reflections
@@ -71,11 +90,19 @@ module ActiveRecord
71
90
  # Account.reflect_on_association(:owner) # returns the owner AssociationReflection
72
91
  # Invoice.reflect_on_association(:line_items).macro # returns :has_many
73
92
  #
93
+ # @api public
74
94
  def reflect_on_association(association)
75
95
  reflections[association]
76
96
  end
77
97
 
98
+ # @api private
99
+ def _reflect_on_association(association) #:nodoc:
100
+ _reflections[association]
101
+ end
102
+
78
103
  # Returns an array of AssociationReflection objects for all associations which have <tt>:autosave</tt> enabled.
104
+ #
105
+ # @api public
79
106
  def reflect_on_all_autosave_associations
80
107
  reflections.values.select { |reflection| reflection.options[:autosave] }
81
108
  end
@@ -127,6 +154,10 @@ module ActiveRecord
127
154
  def autosave=(autosave)
128
155
  @automatic_inverse_of = false
129
156
  @options[:autosave] = autosave
157
+ _, parent_reflection = self.parent_reflection
158
+ if parent_reflection
159
+ parent_reflection.autosave = autosave
160
+ end
130
161
  end
131
162
 
132
163
  # Returns the class for the macro.
@@ -191,10 +222,11 @@ module ActiveRecord
191
222
  end
192
223
 
193
224
  attr_reader :type, :foreign_type
225
+ attr_accessor :parent_reflection # [:name, Reflection]
194
226
 
195
227
  def initialize(macro, name, scope, options, active_record)
196
228
  super
197
- @collection = :has_many == macro
229
+ @collection = [:has_many, :has_and_belongs_to_many].include?(macro)
198
230
  @automatic_inverse_of = nil
199
231
  @type = options[:as] && "#{options[:as]}_type"
200
232
  @foreign_type = options[:foreign_type] || "#{name}_type"
@@ -297,12 +329,12 @@ module ActiveRecord
297
329
  def inverse_of
298
330
  return unless inverse_name
299
331
 
300
- @inverse_of ||= klass.reflect_on_association inverse_name
332
+ @inverse_of ||= klass._reflect_on_association inverse_name
301
333
  end
302
334
 
303
335
  def polymorphic_inverse_of(associated_class)
304
336
  if has_inverse?
305
- if inverse_relationship = associated_class.reflect_on_association(options[:inverse_of])
337
+ if inverse_relationship = associated_class._reflect_on_association(options[:inverse_of])
306
338
  inverse_relationship
307
339
  else
308
340
  raise InverseOfAssociationNotFoundError.new(self, associated_class)
@@ -403,7 +435,7 @@ module ActiveRecord
403
435
  inverse_name = ActiveSupport::Inflector.underscore(active_record.name).to_sym
404
436
 
405
437
  begin
406
- reflection = klass.reflect_on_association(inverse_name)
438
+ reflection = klass._reflect_on_association(inverse_name)
407
439
  rescue NameError
408
440
  # Give up: we couldn't compute the klass type so we won't be able
409
441
  # to find any associations either.
@@ -449,9 +481,9 @@ module ActiveRecord
449
481
  end
450
482
 
451
483
  def derive_class_name
452
- class_name = name.to_s.camelize
484
+ class_name = name.to_s
453
485
  class_name = class_name.singularize if collection?
454
- class_name
486
+ class_name.camelize
455
487
  end
456
488
 
457
489
  def derive_foreign_key
@@ -502,7 +534,7 @@ module ActiveRecord
502
534
  # # => <ActiveRecord::Reflection::AssociationReflection: @macro=:belongs_to, @name=:tag, @active_record=Tagging, @plural_name="tags">
503
535
  #
504
536
  def source_reflection
505
- through_reflection.klass.reflect_on_association(source_reflection_name)
537
+ through_reflection.klass._reflect_on_association(source_reflection_name)
506
538
  end
507
539
 
508
540
  # Returns the AssociationReflection object specified in the <tt>:through</tt> option
@@ -518,7 +550,7 @@ module ActiveRecord
518
550
  # # => <ActiveRecord::Reflection::AssociationReflection: @macro=:has_many, @name=:taggings, @active_record=Post, @plural_name="taggings">
519
551
  #
520
552
  def through_reflection
521
- active_record.reflect_on_association(options[:through])
553
+ active_record._reflect_on_association(options[:through])
522
554
  end
523
555
 
524
556
  # Returns an array of reflections which are involved in this association. Each item in the
@@ -621,11 +653,11 @@ module ActiveRecord
621
653
  end
622
654
 
623
655
  def source_reflection_name # :nodoc:
624
- return @source_reflection_name if @source_reflection_name
656
+ return @source_reflection_name.to_sym if @source_reflection_name
625
657
 
626
658
  names = [name.to_s.singularize, name].collect { |n| n.to_sym }.uniq
627
659
  names = names.find_all { |n|
628
- through_reflection.klass.reflect_on_association(n)
660
+ through_reflection.klass._reflect_on_association(n)
629
661
  }
630
662
 
631
663
  if names.length > 1
@@ -70,7 +70,7 @@ module ActiveRecord
70
70
  binds)
71
71
  end
72
72
 
73
- def update_record(values, id, id_was) # :nodoc:
73
+ def _update_record(values, id, id_was) # :nodoc:
74
74
  substitutes, binds = substitute_values values
75
75
  um = @klass.unscoped.where(@klass.arel_table[@klass.primary_key].eq(id_was || id)).arel.compile_update(substitutes, @klass.primary_key)
76
76
 
@@ -238,7 +238,7 @@ module ActiveRecord
238
238
 
239
239
  # Returns size of the records.
240
240
  def size
241
- loaded? ? @records.length : count
241
+ loaded? ? @records.length : count(:all)
242
242
  end
243
243
 
244
244
  # Returns true if there are no records.
@@ -248,8 +248,7 @@ module ActiveRecord
248
248
  if limit_value == 0
249
249
  true
250
250
  else
251
- # FIXME: This count is not compatible with #select('authors.*') or other select narrows
252
- c = count
251
+ c = count(:all)
253
252
  c.respond_to?(:zero?) ? c.zero? : c.empty?
254
253
  end
255
254
  end
@@ -529,9 +528,9 @@ module ActiveRecord
529
528
  #
530
529
  # User.where(name: 'Oscar').where_values_hash
531
530
  # # => {name: "Oscar"}
532
- def where_values_hash
531
+ def where_values_hash(relation_table_name = table_name)
533
532
  equalities = where_values.grep(Arel::Nodes::Equality).find_all { |node|
534
- node.left.relation.name == table_name
533
+ node.left.relation.name == relation_table_name
535
534
  }
536
535
 
537
536
  binds = Hash[bind_values.find_all(&:first).map { |column, v| [column.name, v] }]
@@ -570,6 +569,8 @@ module ActiveRecord
570
569
  # Compares two relations for equality.
571
570
  def ==(other)
572
571
  case other
572
+ when Associations::CollectionProxy, AssociationRelation
573
+ self == other.to_a
573
574
  when Relation
574
575
  other.to_sql == to_sql
575
576
  when Array
@@ -19,6 +19,14 @@ module ActiveRecord
19
19
  #
20
20
  # Person.group(:city).count
21
21
  # # => { 'Rome' => 5, 'Paris' => 3 }
22
+ #
23
+ # If +count+ is used with +select+, it will count the selected columns:
24
+ #
25
+ # Person.select(:age).count
26
+ # # => counts the number of different age values
27
+ #
28
+ # Note: not all valid +select+ expressions are valid +count+ expressions. The specifics differ
29
+ # between databases. In invalid cases, an error from the databsae is thrown.
22
30
  def count(column_name = nil, options = {})
23
31
  # TODO: Remove options argument as soon we remove support to
24
32
  # activerecord-deprecated_finders.
@@ -188,7 +196,7 @@ module ActiveRecord
188
196
  private
189
197
 
190
198
  def has_include?(column_name)
191
- eager_loading? || (includes_values.present? && (column_name || references_eager_loaded_tables?))
199
+ eager_loading? || (includes_values.present? && ((column_name && column_name != :all) || references_eager_loaded_tables?))
192
200
  end
193
201
 
194
202
  def perform_calculation(operation, column_name, options = {})
@@ -265,7 +273,7 @@ module ActiveRecord
265
273
  group_attrs = group_values
266
274
 
267
275
  if group_attrs.first.respond_to?(:to_sym)
268
- association = @klass.reflect_on_association(group_attrs.first.to_sym)
276
+ association = @klass._reflect_on_association(group_attrs.first.to_sym)
269
277
  associated = group_attrs.size == 1 && association && association.macro == :belongs_to # only count belongs_to associations
270
278
  group_fields = Array(associated ? association.foreign_key : group_attrs)
271
279
  else
@@ -40,10 +40,10 @@ module ActiveRecord
40
40
  BLACKLISTED_ARRAY_METHODS = [
41
41
  :compact!, :flatten!, :reject!, :reverse!, :rotate!, :map!,
42
42
  :shuffle!, :slice!, :sort!, :sort_by!, :delete_if,
43
- :keep_if, :pop, :shift, :delete_at, :compact
43
+ :keep_if, :pop, :shift, :delete_at, :compact, :select!
44
44
  ].to_set # :nodoc:
45
45
 
46
- delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to_ary, to: :to_a
46
+ delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to_ary, :join, to: :to_a
47
47
 
48
48
  delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key,
49
49
  :connection, :columns_hash, :to => :klass
@@ -240,7 +240,7 @@ module ActiveRecord
240
240
  # If no order is defined it will order by primary key.
241
241
  #
242
242
  # Person.forty_two # returns the forty-second object fetched by SELECT * FROM people
243
- # Person.offset(3).forty_two # returns the fifth object from OFFSET 3 (which is OFFSET 44)
243
+ # Person.offset(3).forty_two # returns the forty-second object from OFFSET 3 (which is OFFSET 44)
244
244
  # Person.where(["user_name = :u", { u: user_name }]).forty_two
245
245
  def forty_two
246
246
  find_nth(:forty_two, offset_value ? offset_value + 41 : 41)
@@ -30,6 +30,8 @@ module ActiveRecord
30
30
  else
31
31
  other.joins!(*v)
32
32
  end
33
+ elsif k == :select
34
+ other._select!(v)
33
35
  else
34
36
  other.send("#{k}!", v)
35
37
  end
@@ -62,7 +64,13 @@ module ActiveRecord
62
64
  # expensive), most of the time the value is going to be `nil` or `.blank?`, the only catch is that
63
65
  # `false.blank?` returns `true`, so there needs to be an extra check so that explicit `false` values
64
66
  # don't fall through the cracks.
65
- relation.send("#{name}!", *value) unless value.nil? || (value.blank? && false != value)
67
+ unless value.nil? || (value.blank? && false != value)
68
+ if name == :select
69
+ relation._select!(*value)
70
+ else
71
+ relation.send("#{name}!", *value)
72
+ end
73
+ end
66
74
  end
67
75
 
68
76
  merge_multi_values
@@ -149,7 +157,7 @@ module ActiveRecord
149
157
  def filter_binds(lhs_binds, removed_wheres)
150
158
  return lhs_binds if removed_wheres.empty?
151
159
 
152
- set = Set.new removed_wheres.map { |x| x.left.name }
160
+ set = Set.new removed_wheres.map { |x| x.left.name.to_s }
153
161
  lhs_binds.dup.delete_if { |col,_| set.include? col.name }
154
162
  end
155
163
 
@@ -26,7 +26,7 @@ module ActiveRecord
26
26
  queries << '1=0'
27
27
  else
28
28
  table = Arel::Table.new(column, default_table.engine)
29
- association = klass.reflect_on_association(column.to_sym)
29
+ association = klass._reflect_on_association(column.to_sym)
30
30
 
31
31
  value.each do |k, v|
32
32
  queries.concat expand(association && association.klass, table, k, v)
@@ -55,7 +55,7 @@ module ActiveRecord
55
55
  #
56
56
  # For polymorphic relationships, find the foreign key and type:
57
57
  # PriceEstimate.where(estimate_of: treasure)
58
- if klass && reflection = klass.reflect_on_association(column.to_sym)
58
+ if klass && reflection = klass._reflect_on_association(column.to_sym)
59
59
  if reflection.polymorphic? && base_class = polymorphic_base_class_from_value(value)
60
60
  queries << build(table[reflection.foreign_type], base_class)
61
61
  end
@@ -235,11 +235,11 @@ module ActiveRecord
235
235
  to_a.select { |*block_args| yield(*block_args) }
236
236
  else
237
237
  raise ArgumentError, 'Call this with at least one field' if fields.empty?
238
- spawn.select!(*fields)
238
+ spawn._select!(*fields)
239
239
  end
240
240
  end
241
241
 
242
- def select!(*fields) # :nodoc:
242
+ def _select!(*fields) # :nodoc:
243
243
  fields.flatten!
244
244
  fields.map! do |field|
245
245
  klass.attribute_alias?(field) ? klass.attribute_alias(field) : field
@@ -93,14 +93,14 @@ module ActiveRecord
93
93
  self.default_scopes += [scope]
94
94
  end
95
95
 
96
- def build_default_scope # :nodoc:
96
+ def build_default_scope(base_rel = relation) # :nodoc:
97
97
  if !Base.is_a?(method(:default_scope).owner)
98
98
  # The user has defined their own default scope method, so call that
99
99
  evaluate_default_scope { default_scope }
100
100
  elsif default_scopes.any?
101
101
  evaluate_default_scope do
102
- default_scopes.inject(relation) do |default_scope, scope|
103
- default_scope.merge(unscoped { scope.call })
102
+ default_scopes.inject(base_rel) do |default_scope, scope|
103
+ default_scope.merge(base_rel.scoping { scope.call })
104
104
  end
105
105
  end
106
106
  end
@@ -66,8 +66,9 @@ module ActiveRecord
66
66
  extend ActiveSupport::Concern
67
67
 
68
68
  included do
69
- class_attribute :stored_attributes, instance_accessor: false
70
- self.stored_attributes = {}
69
+ class << self
70
+ attr_accessor :local_stored_attributes
71
+ end
71
72
  end
72
73
 
73
74
  module ClassMethods
@@ -93,9 +94,9 @@ module ActiveRecord
93
94
 
94
95
  # assign new store attribute and create new hash to ensure that each class in the hierarchy
95
96
  # has its own hash of stored attributes.
96
- self.stored_attributes = {} if self.stored_attributes.blank?
97
- self.stored_attributes[store_attribute] ||= []
98
- self.stored_attributes[store_attribute] |= keys
97
+ self.local_stored_attributes ||= {}
98
+ self.local_stored_attributes[store_attribute] ||= []
99
+ self.local_stored_attributes[store_attribute] |= keys
99
100
  end
100
101
 
101
102
  def _store_accessors_module
@@ -105,6 +106,14 @@ module ActiveRecord
105
106
  mod
106
107
  end
107
108
  end
109
+
110
+ def stored_attributes
111
+ parent = superclass.respond_to?(:stored_attributes) ? superclass.stored_attributes : {}
112
+ if self.local_stored_attributes
113
+ parent.merge!(self.local_stored_attributes) { |k, a, b| a | b }
114
+ end
115
+ parent
116
+ end
108
117
  end
109
118
 
110
119
  protected