1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00
rails--rails/activemodel/lib/active_model/configuration.rb
Jon Leighton 93c1f11c0a Support configuration on ActiveRecord::Model.
The problem: We need to be able to specify configuration in a way that
can be inherited to models that include ActiveRecord::Model. So it is
no longer sufficient to put 'top level' config on ActiveRecord::Base,
but we do want configuration specified on ActiveRecord::Base and
descendants to continue to work.

So we need something like class_attribute that can be defined on a
module but that is inherited when ActiveRecord::Model is included.

The solution: added ActiveModel::Configuration module which provides a
config_attribute macro. It's a bit specific hence I am not putting this
in Active Support or making it a 'public API' at present.
2011-12-28 18:27:41 +00:00

134 lines
3.6 KiB
Ruby

require 'active_support/concern'
require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/class/attribute_accessors'
module ActiveModel
# This API is for Rails' internal use and is not currently considered 'public', so
# it may change in the future without warning.
#
# It creates configuration attributes that can be inherited from a module down
# to a class that includes the module. E.g.
#
# module MyModel
# extend ActiveModel::Configuration
# config_attribute :awesome
# self.awesome = true
# end
#
# class Post
# include MyModel
# end
#
# Post.awesome # => true
#
# Post.awesome = false
# Post.awesome # => false
# MyModel.awesome # => true
#
# We assume that the module will have a ClassMethods submodule containing methods
# to be transferred to the including class' singleton class.
#
# Config options can also be defined directly on a class:
#
# class Post
# extend ActiveModel::Configuration
# config_attribute :awesome
# end
#
# So this allows us to define a module that doesn't care about whether it is being
# included in a class or a module:
#
# module Awesomeness
# extend ActiveSupport::Concern
#
# included do
# extend ActiveModel::Configuration
# config_attribute :awesome
# self.awesome = true
# end
# end
#
# class Post
# include Awesomeness
# end
#
# module AwesomeModel
# include Awesomeness
# end
module Configuration #:nodoc:
def config_attribute(name, options = {})
klass = self.is_a?(Class) ? ClassAttribute : ModuleAttribute
klass.new(self, name, options).define
end
class Attribute
attr_reader :host, :name, :options
def initialize(host, name, options)
@host, @name, @options = host, name, options
end
def instance_writer?
options.fetch(:instance_writer, false)
end
end
class ClassAttribute < Attribute
def define
if options[:global]
host.cattr_accessor name, :instance_writer => instance_writer?
else
host.class_attribute name, :instance_writer => instance_writer?
end
end
end
class ModuleAttribute < Attribute
def class_methods
@class_methods ||= begin
if host.const_defined?(:ClassMethods, false)
host.const_get(:ClassMethods)
else
host.const_set(:ClassMethods, Module.new)
end
end
end
def define
host.singleton_class.class_eval <<-CODE, __FILE__, __LINE__
attr_accessor :#{name}
def #{name}?; !!#{name}; end
CODE
name, host = self.name, self.host
class_methods.class_eval do
define_method(name) { host.send(name) }
define_method("#{name}?") { !!send(name) }
end
host.class_eval <<-CODE
def #{name}; defined?(@#{name}) ? @#{name} : self.class.#{name}; end
def #{name}?; !!#{name}; end
CODE
if options[:global]
class_methods.class_eval do
define_method("#{name}=") { |val| host.send("#{name}=", val) }
end
else
class_methods.class_eval <<-CODE, __FILE__, __LINE__
def #{name}=(val)
singleton_class.class_eval do
remove_possible_method(:#{name})
define_method(:#{name}) { val }
end
end
CODE
end
host.send(:attr_writer, name) if instance_writer?
end
end
end
end