store.rb 2.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12
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.
  #
13 14 15
  # 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+.
  #
R
Rafael Mendonça França 已提交
16 17 18
  # 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.
  #
19 20 21
  # Examples:
  #
  #   class User < ActiveRecord::Base
22
  #     store :settings, accessors: [ :color, :homepage ], coder: JSON
23
  #   end
24
  #
25
  #   u = User.new(color: 'black', homepage: '37signals.com')
R
Rafael Mendonça França 已提交
26
  #   u.color                           # Accessor stored attribute
27
  #   u.settings['country'] = 'Denmark' # Any attribute, even if not specified with an accessor
28 29 30 31 32 33 34
  #
  #   # Add additional accessors to an existing store through store_accessor
  #   class SuperUser < User
  #     store_accessor :settings, :privileges, :servants
  #   end
  module Store
    extend ActiveSupport::Concern
35

36 37
    module ClassMethods
      def store(store_attribute, options = {})
38
        serialize store_attribute, options.fetch(:coder, Hash)
39 40 41 42
        store_accessor(store_attribute, options[:accessors]) if options.has_key? :accessors
      end

      def store_accessor(store_attribute, *keys)
A
Aaron Patterson 已提交
43
        keys.flatten.each do |key|
44
          define_method("#{key}=") do |value|
45
            send("#{store_attribute}=", {}) unless send(store_attribute).is_a?(Hash)
46
            send(store_attribute)[key.to_s] = value
47
            send("#{store_attribute}_will_change!")
48
          end
49

50
          define_method(key) do
51
            send("#{store_attribute}=", {}) unless send(store_attribute).is_a?(Hash)
52
            send(store_attribute)[key.to_s]
53 54 55 56 57
          end
        end
      end
    end
  end
58
end