diff options
author | Yves Senn <yves.senn@gmail.com> | 2013-10-10 14:41:14 +0200 |
---|---|---|
committer | Yves Senn <yves.senn@gmail.com> | 2013-10-25 08:35:35 +0200 |
commit | 0492ea6d39e48c5bdb1d15eb4afdd54b00ece091 (patch) | |
tree | 06677dc32ad6034e837bf22f26a1ec1642290ca6 /activerecord/lib/active_record | |
parent | bf43b4c33fe323448a714854327fdabdcb3c7a14 (diff) | |
download | rails-0492ea6d39e48c5bdb1d15eb4afdd54b00ece091.tar.gz rails-0492ea6d39e48c5bdb1d15eb4afdd54b00ece091.tar.bz2 rails-0492ea6d39e48c5bdb1d15eb4afdd54b00ece091.zip |
`ActiveRecord::Store` works together with PG `hstore` columns.
This is necessary because as of 5ac2341 `hstore` columns are always stored
as `Hash` with `String` keys. `ActiveRecord::Store` expected the attribute to
be an instance of `HashWithIndifferentAccess`, which led to the bug.
Diffstat (limited to 'activerecord/lib/active_record')
4 files changed, 62 insertions, 14 deletions
diff --git a/activerecord/lib/active_record/attribute_methods/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb index 1287de0d0d..5701804168 100644 --- a/activerecord/lib/active_record/attribute_methods/serialization.rb +++ b/activerecord/lib/active_record/attribute_methods/serialization.rb @@ -66,6 +66,10 @@ module ActiveRecord def type @column.type end + + def accessor + ActiveRecord::Store::IndifferentHashAccessor + end end class Attribute < Struct.new(:coder, :value, :state) # :nodoc: diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb index dab876af14..4babee07b4 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb @@ -234,6 +234,10 @@ module ActiveRecord ConnectionAdapters::PostgreSQLColumn.string_to_hstore value end + + def accessor + ActiveRecord::Store::StringKeyedHashAccessor + end end class Cidr < Type @@ -250,6 +254,10 @@ module ActiveRecord ConnectionAdapters::PostgreSQLColumn.string_to_json value end + + def accessor + ActiveRecord::Store::StringKeyedHashAccessor + end end class TypeMap diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index c1cc905606..3668aecd4b 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -148,6 +148,10 @@ module ActiveRecord @oid_type.type_cast value end + def accessor + @oid_type.accessor + end + private def has_default_function?(default_value, default) diff --git a/activerecord/lib/active_record/store.rb b/activerecord/lib/active_record/store.rb index 5fc80cdb6d..b841b977fc 100644 --- a/activerecord/lib/active_record/store.rb +++ b/activerecord/lib/active_record/store.rb @@ -104,26 +104,58 @@ module ActiveRecord protected def read_store_attribute(store_attribute, key) - attribute = initialize_store_attribute(store_attribute) - attribute[key] + accessor = store_accessor_for(store_attribute) + accessor.read(self, store_attribute, key) end def write_store_attribute(store_attribute, key, value) - attribute = initialize_store_attribute(store_attribute) - if value != attribute[key] - send :"#{store_attribute}_will_change!" - attribute[key] = value - end + accessor = store_accessor_for(store_attribute) + accessor.write(self, store_attribute, key, value) end private - def initialize_store_attribute(store_attribute) - attribute = send(store_attribute) - unless attribute.is_a?(ActiveSupport::HashWithIndifferentAccess) - attribute = IndifferentCoder.as_indifferent_hash(attribute) - send :"#{store_attribute}=", attribute + def store_accessor_for(store_attribute) + @column_types[store_attribute.to_s].accessor + end + + class HashAccessor + def self.read(object, attribute, key) + prepare(object, attribute) + object.public_send(attribute)[key] + end + + def self.write(object, attribute, key, value) + prepare(object, attribute) + if value != read(object, attribute, key) + object.public_send :"#{attribute}_will_change!" + object.public_send(attribute)[key] = value + end + end + + def self.prepare(object, attribute) + object.public_send :"#{attribute}=", {} unless object.send(attribute) + end + end + + class StringKeyedHashAccessor < HashAccessor + def self.read(object, attribute, key) + super object, attribute, key.to_s + end + + def self.write(object, attribute, key, value) + super object, attribute, key.to_s, value + end + end + + class IndifferentHashAccessor < ActiveRecord::Store::HashAccessor + def self.prepare(object, store_attribute) + attribute = object.send(store_attribute) + unless attribute.is_a?(ActiveSupport::HashWithIndifferentAccess) + attribute = IndifferentCoder.as_indifferent_hash(attribute) + object.send :"#{store_attribute}=", attribute + end + attribute end - attribute end class IndifferentCoder # :nodoc: @@ -141,7 +173,7 @@ module ActiveRecord end def load(yaml) - self.class.as_indifferent_hash @coder.load(yaml) + self.class.as_indifferent_hash(@coder.load(yaml)) end def self.as_indifferent_hash(obj) |