aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--activerecord/CHANGELOG.md6
-rw-r--r--activerecord/lib/active_record/store.rb15
-rw-r--r--activerecord/test/cases/store_test.rb36
-rw-r--r--activerecord/test/models/admin/user.rb2
-rw-r--r--activerecord/test/schema/schema.rb2
5 files changed, 55 insertions, 6 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 31886c8212..befc5c9999 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,4 +1,10 @@
## Rails 4.0.0 (unreleased) ##
+* Added custom coders support for ActiveRecord::Store. Now you can set
+ your custom coder like this:
+
+ store :settings, accessors: [ :color, :homepage ], coder: JSON
+
+ *Andrey Voronkov*
* `mysql` and `mysql2` connections will set `SQL_MODE=STRICT_ALL_TABLES` by
default to avoid silent data loss. This can be disabled by specifying
diff --git a/activerecord/lib/active_record/store.rb b/activerecord/lib/active_record/store.rb
index 1c7b839e5e..a67d6a2288 100644
--- a/activerecord/lib/active_record/store.rb
+++ b/activerecord/lib/active_record/store.rb
@@ -10,15 +10,20 @@ module ActiveRecord
# Make sure that you declare the database column used for the serialized store as a text, so there's
# plenty of room.
#
+ # You can set custom coder to encode/decode your serialized attributes to/from different formats.
+ # JSON, YAML, Marshal are supported out of the box. Generally it can be any wrapper that provides +load+ and +dump+.
+ #
# Examples:
#
# class User < ActiveRecord::Base
- # store :settings, accessors: [ :color, :homepage ]
+ # store :settings, accessors: [ :color, :homepage ], coder: JSON
# end
#
# u = User.new(color: 'black', homepage: '37signals.com')
# u.color # Accessor stored attribute
- # u.settings[:country] = 'Denmark' # Any attribute, even if not specified with an accessor
+ # u.settings['country'] = 'Denmark' # Any attribute, even if not specified with an accessor
+ # String keys should be used for direct access to virtual attributes because of most of the coders do not
+ # distinguish symbols and strings as keys.
#
# # Add additional accessors to an existing store through store_accessor
# class SuperUser < User
@@ -29,7 +34,7 @@ module ActiveRecord
module ClassMethods
def store(store_attribute, options = {})
- serialize store_attribute, Hash
+ serialize store_attribute, options.fetch(:coder, Hash)
store_accessor(store_attribute, options[:accessors]) if options.has_key? :accessors
end
@@ -37,13 +42,13 @@ module ActiveRecord
keys.flatten.each do |key|
define_method("#{key}=") do |value|
send("#{store_attribute}=", {}) unless send(store_attribute).is_a?(Hash)
- send(store_attribute)[key] = value
+ send(store_attribute)[key.to_s] = value
send("#{store_attribute}_will_change!")
end
define_method(key) do
send("#{store_attribute}=", {}) unless send(store_attribute).is_a?(Hash)
- send(store_attribute)[key]
+ send(store_attribute)[key.to_s]
end
end
end
diff --git a/activerecord/test/cases/store_test.rb b/activerecord/test/cases/store_test.rb
index 40520d6da2..e1d0f1f799 100644
--- a/activerecord/test/cases/store_test.rb
+++ b/activerecord/test/cases/store_test.rb
@@ -4,7 +4,7 @@ require 'models/admin/user'
class StoreTest < ActiveRecord::TestCase
setup do
- @john = Admin::User.create(:name => 'John Doe', :color => 'black', :remember_login => true)
+ @john = Admin::User.create(:name => 'John Doe', :color => 'black', :remember_login => true, :height => 'tall', :is_a_good_guy => true)
end
test "reading store attributes through accessors" do
@@ -40,4 +40,38 @@ class StoreTest < ActiveRecord::TestCase
@john.remember_login = false
assert_equal false, @john.remember_login
end
+
+ test "reading store attributes through accessors encoded with JSON" do
+ assert_equal 'tall', @john.height
+ assert_nil @john.weight
+ end
+
+ test "writing store attributes through accessors encoded with JSON" do
+ @john.height = 'short'
+ @john.weight = 'heavy'
+
+ assert_equal 'short', @john.height
+ assert_equal 'heavy', @john.weight
+ end
+
+ test "accessing attributes not exposed by accessors encoded with JSON" do
+ @john.json_data['somestuff'] = 'somecoolstuff'
+ @john.save
+
+ assert_equal 'somecoolstuff', @john.reload.json_data['somestuff']
+ end
+
+ test "updating the store will mark it as changed encoded with JSON" do
+ @john.height = 'short'
+ assert @john.json_data_changed?
+ end
+
+ test "object initialization with not nullable column encoded with JSON" do
+ assert_equal true, @john.is_a_good_guy
+ end
+
+ test "writing with not nullable column encoded with JSON" do
+ @john.is_a_good_guy = false
+ assert_equal false, @john.is_a_good_guy
+ end
end
diff --git a/activerecord/test/models/admin/user.rb b/activerecord/test/models/admin/user.rb
index d0e628bd50..ad30039304 100644
--- a/activerecord/test/models/admin/user.rb
+++ b/activerecord/test/models/admin/user.rb
@@ -2,4 +2,6 @@ class Admin::User < ActiveRecord::Base
belongs_to :account
store :settings, :accessors => [ :color, :homepage ]
store :preferences, :accessors => [ :remember_login ]
+ store :json_data, :accessors => [ :height, :weight ], :coder => JSON
+ store :json_data_empty, :accessors => [ :is_a_good_guy ], :coder => JSON
end
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index cef08cd99c..464fdba555 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -41,6 +41,8 @@ ActiveRecord::Schema.define do
# MySQL does not allow default values for blobs. Fake it out with a
# big varchar below.
t.string :preferences, :null => false, :default => '', :limit => 1024
+ t.text :json_data, :null => true
+ t.text :json_data_empty, :null => false, :default => ""
t.references :account
end