Skip to content
This repository
Browse code

Merge pull request #5807 from Antiarchitect/store-improvement

Custom coders support for ActiveRecord::Store.
  • Loading branch information...
commit f443e119cd448b78bcb1584d7a9bd8b5775b5577 2 parents d3c83a4 + 3c0bf04
Rafael Mendonça França authored May 13, 2012
6  activerecord/CHANGELOG.md
Source Rendered
... ...
@@ -1,4 +1,10 @@
1 1
 ## Rails 4.0.0 (unreleased) ##
  2
+*   Added custom coders support for ActiveRecord::Store. Now you can set
  3
+    your custom coder like this:
  4
+
  5
+        store :settings, accessors: [ :color, :homepage ], coder: JSON
  6
+
  7
+    *Andrey Voronkov*
2 8
 
3 9
 *   `mysql` and `mysql2` connections will set `SQL_MODE=STRICT_ALL_TABLES` by
4 10
     default to avoid silent data loss. This can be disabled by specifying
15  activerecord/lib/active_record/store.rb
@@ -10,15 +10,20 @@ module ActiveRecord
10 10
   # Make sure that you declare the database column used for the serialized store as a text, so there's
11 11
   # plenty of room.
12 12
   #
  13
+  # You can set custom coder to encode/decode your serialized attributes to/from different formats.
  14
+  # JSON, YAML, Marshal are supported out of the box. Generally it can be any wrapper that provides +load+ and +dump+.
  15
+  #
13 16
   # Examples:
14 17
   #
15 18
   #   class User < ActiveRecord::Base
16  
-  #     store :settings, accessors: [ :color, :homepage ]
  19
+  #     store :settings, accessors: [ :color, :homepage ], coder: JSON
17 20
   #   end
18 21
   #
19 22
   #   u = User.new(color: 'black', homepage: '37signals.com')
20 23
   #   u.color                          # Accessor stored attribute
21  
-  #   u.settings[:country] = 'Denmark' # Any attribute, even if not specified with an accessor
  24
+  #   u.settings['country'] = 'Denmark' # Any attribute, even if not specified with an accessor
  25
+  #   String keys should be used for direct access to virtual attributes because of most of the coders do not
  26
+  #   distinguish symbols and strings as keys.
22 27
   #
23 28
   #   # Add additional accessors to an existing store through store_accessor
24 29
   #   class SuperUser < User
@@ -29,7 +34,7 @@ module Store
29 34
 
30 35
     module ClassMethods
31 36
       def store(store_attribute, options = {})
32  
-        serialize store_attribute, Hash
  37
+        serialize store_attribute, options.fetch(:coder, Hash)
33 38
         store_accessor(store_attribute, options[:accessors]) if options.has_key? :accessors
34 39
       end
35 40
 
@@ -37,13 +42,13 @@ def store_accessor(store_attribute, *keys)
37 42
         keys.flatten.each do |key|
38 43
           define_method("#{key}=") do |value|
39 44
             send("#{store_attribute}=", {}) unless send(store_attribute).is_a?(Hash)
40  
-            send(store_attribute)[key] = value
  45
+            send(store_attribute)[key.to_s] = value
41 46
             send("#{store_attribute}_will_change!")
42 47
           end
43 48
 
44 49
           define_method(key) do
45 50
             send("#{store_attribute}=", {}) unless send(store_attribute).is_a?(Hash)
46  
-            send(store_attribute)[key]
  51
+            send(store_attribute)[key.to_s]
47 52
           end
48 53
         end
49 54
       end
36  activerecord/test/cases/store_test.rb
@@ -4,7 +4,7 @@
4 4
 
5 5
 class StoreTest < ActiveRecord::TestCase
6 6
   setup do
7  
-    @john = Admin::User.create(:name => 'John Doe', :color => 'black', :remember_login => true)
  7
+    @john = Admin::User.create(:name => 'John Doe', :color => 'black', :remember_login => true, :height => 'tall', :is_a_good_guy => true)
8 8
   end
9 9
 
10 10
   test "reading store attributes through accessors" do
@@ -40,4 +40,38 @@ class StoreTest < ActiveRecord::TestCase
40 40
     @john.remember_login = false
41 41
     assert_equal false, @john.remember_login
42 42
   end
  43
+
  44
+  test "reading store attributes through accessors encoded with JSON" do
  45
+    assert_equal 'tall', @john.height
  46
+    assert_nil @john.weight
  47
+  end
  48
+
  49
+  test "writing store attributes through accessors encoded with JSON" do
  50
+    @john.height = 'short'
  51
+    @john.weight = 'heavy'
  52
+
  53
+    assert_equal 'short', @john.height
  54
+    assert_equal 'heavy', @john.weight
  55
+  end
  56
+
  57
+  test "accessing attributes not exposed by accessors encoded with JSON" do
  58
+    @john.json_data['somestuff'] = 'somecoolstuff'
  59
+    @john.save
  60
+
  61
+    assert_equal 'somecoolstuff', @john.reload.json_data['somestuff']
  62
+  end
  63
+
  64
+  test "updating the store will mark it as changed encoded with JSON" do
  65
+    @john.height = 'short'
  66
+    assert @john.json_data_changed?
  67
+  end
  68
+
  69
+  test "object initialization with not nullable column encoded with JSON" do
  70
+    assert_equal true, @john.is_a_good_guy
  71
+  end
  72
+
  73
+  test "writing with not nullable column encoded with JSON" do
  74
+    @john.is_a_good_guy = false
  75
+    assert_equal false, @john.is_a_good_guy
  76
+  end
43 77
 end
2  activerecord/test/models/admin/user.rb
@@ -2,4 +2,6 @@ class Admin::User < ActiveRecord::Base
2 2
   belongs_to :account
3 3
   store :settings, :accessors => [ :color, :homepage ]
4 4
   store :preferences, :accessors => [ :remember_login ]
  5
+  store :json_data, :accessors => [ :height, :weight ], :coder => JSON
  6
+  store :json_data_empty, :accessors => [ :is_a_good_guy ], :coder => JSON
5 7
 end
2  activerecord/test/schema/schema.rb
@@ -41,6 +41,8 @@ def create_table(*args, &block)
41 41
     # MySQL does not allow default values for blobs. Fake it out with a
42 42
     # big varchar below.
43 43
     t.string :preferences, :null => false, :default => '', :limit => 1024
  44
+    t.text :json_data, :null => true
  45
+    t.text :json_data_empty, :null => false, :default => ""
44 46
     t.references :account
45 47
   end
46 48
 

0 notes on commit f443e11

Please sign in to comment.
Something went wrong with that request. Please try again.