rails/activemodel/lib/active_model/configuration.rb

135 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