mirror of https://github.com/rails/rails
135 lines
3.6 KiB
Ruby
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
|