aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/CHANGELOG11
-rw-r--r--activerecord/lib/active_record.rb1
-rw-r--r--activerecord/lib/active_record/base.rb2
-rw-r--r--activerecord/lib/active_record/store.rb49
-rw-r--r--activerecord/test/cases/store_test.rb29
-rw-r--r--activerecord/test/models/admin/user.rb1
-rw-r--r--activerecord/test/schema/schema.rb1
7 files changed, 93 insertions, 1 deletions
diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG
index 50203608c2..0f6a31d679 100644
--- a/activerecord/CHANGELOG
+++ b/activerecord/CHANGELOG
@@ -1,5 +1,16 @@
*Rails 3.2.0 (unreleased)*
+* Added ActiveRecord::Base.store for declaring simple single-column key/value stores [DHH]
+
+ class User < ActiveRecord::Base
+ store :settings, accessors: [ :color, :homepage ]
+ 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
+
+
* MySQL: case-insensitive uniqueness validation avoids calling LOWER when
the column already uses a case-insensitive collation. Fixes #561.
diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb
index 132dc12680..3572c640eb 100644
--- a/activerecord/lib/active_record.rb
+++ b/activerecord/lib/active_record.rb
@@ -69,6 +69,7 @@ module ActiveRecord
autoload :Schema
autoload :SchemaDumper
autoload :Serialization
+ autoload :Store
autoload :SessionStore
autoload :Timestamp
autoload :Transactions
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 78159d13d4..6dae90266e 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -2145,7 +2145,7 @@ MSG
# AutosaveAssociation needs to be included before Transactions, because we want
# #save_with_autosave_associations to be wrapped inside a transaction.
include AutosaveAssociation, NestedAttributes
- include Aggregations, Transactions, Reflection, Serialization
+ include Aggregations, Transactions, Reflection, Serialization, Store
NilClass.add_whiner(self) if NilClass.respond_to?(:add_whiner)
diff --git a/activerecord/lib/active_record/store.rb b/activerecord/lib/active_record/store.rb
new file mode 100644
index 0000000000..d5910df891
--- /dev/null
+++ b/activerecord/lib/active_record/store.rb
@@ -0,0 +1,49 @@
+module ActiveRecord
+ # Store gives you a thin wrapper around serialize for the purpose of storing hashes in a single column.
+ # It's like a simple key/value store backed into your record when you don't care about being able to
+ # query that store outside the context of a single record.
+ #
+ # You can then declare accessors to this store that are then accessible just like any other attribute
+ # of the model. This is very helpful for easily exposing store keys to a form or elsewhere that's
+ # already built around just accessing attributes on the model.
+ #
+ # Make sure that you declare the database column used for the serialized store as a text, so there's
+ # plenty of room.
+ #
+ # Examples:
+ #
+ # class User < ActiveRecord::Base
+ # store :settings, accessors: [ :color, :homepage ]
+ # 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
+ #
+ # # Add additional accessors to an existing store through store_accessor
+ # class SuperUser < User
+ # store_accessor :settings, :privileges, :servants
+ # end
+ module Store
+ extend ActiveSupport::Concern
+
+ module ClassMethods
+ def store(store_attribute, options = {})
+ serialize store_attribute, Hash
+ store_accessor(store_attribute, options[:accessors]) if options.has_key? :accessors
+ end
+
+ def store_accessor(store_attribute, *keys)
+ Array(keys).flatten.each do |key|
+ define_method("#{key}=") do |value|
+ send(store_attribute)[key] = value
+ end
+
+ define_method(key) do
+ send(store_attribute)[key]
+ end
+ end
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/activerecord/test/cases/store_test.rb b/activerecord/test/cases/store_test.rb
new file mode 100644
index 0000000000..3d056d93b6
--- /dev/null
+++ b/activerecord/test/cases/store_test.rb
@@ -0,0 +1,29 @@
+require 'cases/helper'
+require 'models/admin'
+require 'models/admin/user'
+
+class StoreTest < ActiveRecord::TestCase
+ setup do
+ @john = Admin::User.create(name: 'John Doe', color: 'black')
+ end
+
+ test "reading store attributes through accessors" do
+ assert_equal 'black', @john.color
+ assert_nil @john.homepage
+ end
+
+ test "writing store attributes through accessors" do
+ @john.color = 'red'
+ @john.homepage = '37signals.com'
+
+ assert_equal 'red', @john.color
+ assert_equal '37signals.com', @john.homepage
+ end
+
+ test "accessing attributes not exposed by accessors" do
+ @john.settings[:icecream] = 'graeters'
+ @john.save
+
+ assert 'graeters', @john.reload.settings[:icecream]
+ end
+end
diff --git a/activerecord/test/models/admin/user.rb b/activerecord/test/models/admin/user.rb
index 74bb21551e..275a03c344 100644
--- a/activerecord/test/models/admin/user.rb
+++ b/activerecord/test/models/admin/user.rb
@@ -1,3 +1,4 @@
class Admin::User < ActiveRecord::Base
belongs_to :account
+ store :settings, accessors: [ :color, :homepage ]
end \ No newline at end of file
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 9d5ad16a3c..bb08f5c181 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -37,6 +37,7 @@ ActiveRecord::Schema.define do
create_table :admin_users, :force => true do |t|
t.string :name
+ t.text :settings
t.references :account
end