mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Added ActiveRecord::Base.store for declaring simple single-column key/value stores [DHH]
This commit is contained in:
parent
8f11d53506
commit
85b64f98d1
7 changed files with 93 additions and 1 deletions
|
@ -1,5 +1,16 @@
|
||||||
*Rails 3.2.0 (unreleased)*
|
*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
|
* MySQL: case-insensitive uniqueness validation avoids calling LOWER when
|
||||||
the column already uses a case-insensitive collation. Fixes #561.
|
the column already uses a case-insensitive collation. Fixes #561.
|
||||||
|
|
||||||
|
|
|
@ -69,6 +69,7 @@ module ActiveRecord
|
||||||
autoload :Schema
|
autoload :Schema
|
||||||
autoload :SchemaDumper
|
autoload :SchemaDumper
|
||||||
autoload :Serialization
|
autoload :Serialization
|
||||||
|
autoload :Store
|
||||||
autoload :SessionStore
|
autoload :SessionStore
|
||||||
autoload :Timestamp
|
autoload :Timestamp
|
||||||
autoload :Transactions
|
autoload :Transactions
|
||||||
|
|
|
@ -2145,7 +2145,7 @@ MSG
|
||||||
# AutosaveAssociation needs to be included before Transactions, because we want
|
# AutosaveAssociation needs to be included before Transactions, because we want
|
||||||
# #save_with_autosave_associations to be wrapped inside a transaction.
|
# #save_with_autosave_associations to be wrapped inside a transaction.
|
||||||
include AutosaveAssociation, NestedAttributes
|
include AutosaveAssociation, NestedAttributes
|
||||||
include Aggregations, Transactions, Reflection, Serialization
|
include Aggregations, Transactions, Reflection, Serialization, Store
|
||||||
|
|
||||||
NilClass.add_whiner(self) if NilClass.respond_to?(:add_whiner)
|
NilClass.add_whiner(self) if NilClass.respond_to?(:add_whiner)
|
||||||
|
|
||||||
|
|
49
activerecord/lib/active_record/store.rb
Normal file
49
activerecord/lib/active_record/store.rb
Normal file
|
@ -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
|
29
activerecord/test/cases/store_test.rb
Normal file
29
activerecord/test/cases/store_test.rb
Normal file
|
@ -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
|
|
@ -1,3 +1,4 @@
|
||||||
class Admin::User < ActiveRecord::Base
|
class Admin::User < ActiveRecord::Base
|
||||||
belongs_to :account
|
belongs_to :account
|
||||||
|
store :settings, accessors: [ :color, :homepage ]
|
||||||
end
|
end
|
|
@ -37,6 +37,7 @@ ActiveRecord::Schema.define do
|
||||||
|
|
||||||
create_table :admin_users, :force => true do |t|
|
create_table :admin_users, :force => true do |t|
|
||||||
t.string :name
|
t.string :name
|
||||||
|
t.text :settings
|
||||||
t.references :account
|
t.references :account
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue