aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJon Leighton <j@jonathanleighton.com>2011-12-28 15:38:16 +0000
committerJon Leighton <j@jonathanleighton.com>2011-12-28 18:27:41 +0000
commit93c1f11c0a5097a5431819a1551a02a869a16a38 (patch)
treefd88de442e03a600df63387dc4b2389e9c861c7b
parentafe6e059ea216f01d160e4603116356b78df12e5 (diff)
downloadrails-93c1f11c0a5097a5431819a1551a02a869a16a38.tar.gz
rails-93c1f11c0a5097a5431819a1551a02a869a16a38.tar.bz2
rails-93c1f11c0a5097a5431819a1551a02a869a16a38.zip
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.
-rw-r--r--activemodel/lib/active_model.rb1
-rw-r--r--activemodel/lib/active_model/attribute_methods.rb3
-rw-r--r--activemodel/lib/active_model/configuration.rb134
-rw-r--r--activemodel/lib/active_model/mass_assignment_security.rb22
-rw-r--r--activemodel/lib/active_model/serializers/json.rb3
-rw-r--r--activemodel/lib/active_model/validations.rb5
-rw-r--r--activemodel/test/cases/configuration_test.rb154
-rw-r--r--activerecord/lib/active_record.rb1
-rw-r--r--activerecord/lib/active_record/attribute_methods/dirty.rb2
-rw-r--r--activerecord/lib/active_record/attribute_methods/read.rb5
-rw-r--r--activerecord/lib/active_record/attribute_methods/serialization.rb2
-rw-r--r--activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb7
-rw-r--r--activerecord/lib/active_record/base.rb1
-rw-r--r--activerecord/lib/active_record/callbacks.rb51
-rw-r--r--activerecord/lib/active_record/configuration.rb36
-rw-r--r--activerecord/lib/active_record/core.rb123
-rw-r--r--activerecord/lib/active_record/explain.rb8
-rw-r--r--activerecord/lib/active_record/inheritance.rb6
-rw-r--r--activerecord/lib/active_record/locking/optimistic.rb5
-rw-r--r--activerecord/lib/active_record/model.rb89
-rw-r--r--activerecord/lib/active_record/model_schema.rb24
-rw-r--r--activerecord/lib/active_record/nested_attributes.rb2
-rw-r--r--activerecord/lib/active_record/readonly_attributes.rb2
-rw-r--r--activerecord/lib/active_record/reflection.rb3
-rw-r--r--activerecord/lib/active_record/scoping/default.rb2
-rw-r--r--activerecord/lib/active_record/timestamp.rb2
-rw-r--r--activerecord/test/cases/attribute_methods/read_test.rb1
-rw-r--r--activerecord/test/cases/configuration_test.rb26
28 files changed, 498 insertions, 222 deletions
diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb
index d0e2a6f39c..9fc308375f 100644
--- a/activemodel/lib/active_model.rb
+++ b/activemodel/lib/active_model.rb
@@ -32,6 +32,7 @@ module ActiveModel
autoload :AttributeMethods
autoload :BlockValidator, 'active_model/validator'
autoload :Callbacks
+ autoload :Configuration
autoload :Conversion
autoload :Dirty
autoload :EachValidator, 'active_model/validator'
diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb
index d7ec7e6ca5..71ab1501c8 100644
--- a/activemodel/lib/active_model/attribute_methods.rb
+++ b/activemodel/lib/active_model/attribute_methods.rb
@@ -61,7 +61,8 @@ module ActiveModel
CALL_COMPILABLE_REGEXP = /\A[a-zA-Z_]\w*[!?]?\z/
included do
- class_attribute :attribute_method_matchers, :instance_writer => false
+ extend ActiveModel::Configuration
+ config_attribute :attribute_method_matchers
self.attribute_method_matchers = [ClassMethods::AttributeMethodMatcher.new]
end
diff --git a/activemodel/lib/active_model/configuration.rb b/activemodel/lib/active_model/configuration.rb
new file mode 100644
index 0000000000..1757c12ebf
--- /dev/null
+++ b/activemodel/lib/active_model/configuration.rb
@@ -0,0 +1,134 @@
+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
diff --git a/activemodel/lib/active_model/mass_assignment_security.rb b/activemodel/lib/active_model/mass_assignment_security.rb
index c895968f77..9b12d9d281 100644
--- a/activemodel/lib/active_model/mass_assignment_security.rb
+++ b/activemodel/lib/active_model/mass_assignment_security.rb
@@ -10,11 +10,13 @@ module ActiveModel
extend ActiveSupport::Concern
included do
- class_attribute :_accessible_attributes
- class_attribute :_protected_attributes
- class_attribute :_active_authorizer
+ extend ActiveModel::Configuration
- class_attribute :_mass_assignment_sanitizer
+ config_attribute :_accessible_attributes
+ config_attribute :_protected_attributes
+ config_attribute :_active_authorizer
+
+ config_attribute :_mass_assignment_sanitizer
self.mass_assignment_sanitizer = :logger
end
@@ -56,7 +58,7 @@ module ActiveModel
# You can specify your own sanitizer object eg. MySanitizer.new.
# See <tt>ActiveModel::MassAssignmentSecurity::LoggerSanitizer</tt> for example implementation.
#
- #
+ #
module ClassMethods
# Attributes named in this macro are protected from mass-assignment
# whenever attributes are sanitized before assignment. A role for the
@@ -70,13 +72,13 @@ module ActiveModel
#
# class Customer
# include ActiveModel::MassAssignmentSecurity
- #
+ #
# attr_accessor :name, :email, :logins_count
- #
+ #
# attr_protected :logins_count
# # Suppose that admin can not change email for customer
- # attr_protected :logins_count, :email, :as => :admin
- #
+ # attr_protected :logins_count, :email, :as => :admin
+ #
# def assign_attributes(values, options = {})
# sanitize_for_mass_assignment(values, options[:as]).each do |k, v|
# send("#{k}=", v)
@@ -99,7 +101,7 @@ module ActiveModel
# customer.name # => "David"
# customer.email # => nil
# customer.logins_count # => nil
- #
+ #
# customer.email = "c@d.com"
# customer.email # => "c@d.com"
#
diff --git a/activemodel/lib/active_model/serializers/json.rb b/activemodel/lib/active_model/serializers/json.rb
index c845440120..63ab8e7edc 100644
--- a/activemodel/lib/active_model/serializers/json.rb
+++ b/activemodel/lib/active_model/serializers/json.rb
@@ -10,8 +10,9 @@ module ActiveModel
included do
extend ActiveModel::Naming
+ extend ActiveModel::Configuration
- class_attribute :include_root_in_json
+ config_attribute :include_root_in_json
self.include_root_in_json = true
end
diff --git a/activemodel/lib/active_model/validations.rb b/activemodel/lib/active_model/validations.rb
index 8ed392abca..1779efd526 100644
--- a/activemodel/lib/active_model/validations.rb
+++ b/activemodel/lib/active_model/validations.rb
@@ -42,9 +42,9 @@ module ActiveModel
#
module Validations
extend ActiveSupport::Concern
- include ActiveSupport::Callbacks
included do
+ extend ActiveModel::Callbacks
extend ActiveModel::Translation
extend HelperMethods
@@ -53,7 +53,8 @@ module ActiveModel
attr_accessor :validation_context
define_callbacks :validate, :scope => :name
- class_attribute :_validators
+ extend ActiveModel::Configuration
+ config_attribute :_validators
self._validators = Hash.new { |h,k| h[k] = [] }
end
diff --git a/activemodel/test/cases/configuration_test.rb b/activemodel/test/cases/configuration_test.rb
new file mode 100644
index 0000000000..a172fa26a3
--- /dev/null
+++ b/activemodel/test/cases/configuration_test.rb
@@ -0,0 +1,154 @@
+require 'cases/helper'
+
+class ConfigurationOnModuleTest < ActiveModel::TestCase
+ def setup
+ @mod = mod = Module.new do
+ extend ActiveSupport::Concern
+ extend ActiveModel::Configuration
+
+ config_attribute :omg
+ self.omg = "default"
+
+ config_attribute :wtf, global: true
+ self.wtf = "default"
+
+ config_attribute :boolean
+
+ config_attribute :lol, instance_writer: true
+ end
+
+ @klass = Class.new do
+ include mod
+ end
+
+ @subklass = Class.new(@klass)
+ end
+
+ test "default" do
+ assert_equal "default", @mod.omg
+ assert_equal "default", @klass.omg
+ assert_equal "default", @klass.new.omg
+ end
+
+ test "setting" do
+ @mod.omg = "lol"
+ assert_equal "lol", @mod.omg
+ end
+
+ test "setting on class including the module" do
+ @klass.omg = "lol"
+ assert_equal "lol", @klass.omg
+ assert_equal "lol", @klass.new.omg
+ assert_equal "default", @mod.omg
+ end
+
+ test "setting on subclass of class including the module" do
+ @subklass.omg = "lol"
+ assert_equal "lol", @subklass.omg
+ assert_equal "default", @klass.omg
+ assert_equal "default", @mod.omg
+ end
+
+ test "setting on instance" do
+ assert !@klass.new.respond_to?(:omg=)
+
+ @klass.lol = "lol"
+ obj = @klass.new
+ assert_equal "lol", obj.lol
+ obj.lol = "omg"
+ assert_equal "omg", obj.lol
+ assert_equal "lol", @klass.lol
+ assert_equal "lol", @klass.new.lol
+ obj.lol = false
+ assert !obj.lol?
+ end
+
+ test "global attribute" do
+ assert_equal "default", @mod.wtf
+ assert_equal "default", @klass.wtf
+
+ @mod.wtf = "wtf"
+
+ assert_equal "wtf", @mod.wtf
+ assert_equal "wtf", @klass.wtf
+
+ @klass.wtf = "lol"
+
+ assert_equal "lol", @mod.wtf
+ assert_equal "lol", @klass.wtf
+ end
+
+ test "boolean" do
+ assert_equal false, @mod.boolean?
+ assert_equal false, @klass.new.boolean?
+ @mod.boolean = true
+ assert_equal true, @mod.boolean?
+ assert_equal true, @klass.new.boolean?
+ end
+end
+
+class ConfigurationOnClassTest < ActiveModel::TestCase
+ def setup
+ @klass = Class.new do
+ extend ActiveModel::Configuration
+
+ config_attribute :omg
+ self.omg = "default"
+
+ config_attribute :wtf, global: true
+ self.wtf = "default"
+
+ config_attribute :omg2, instance_writer: true
+ config_attribute :wtf2, instance_writer: true, global: true
+ end
+
+ @subklass = Class.new(@klass)
+ end
+
+ test "defaults" do
+ assert_equal "default", @klass.omg
+ assert_equal "default", @klass.wtf
+ assert_equal "default", @subklass.omg
+ assert_equal "default", @subklass.wtf
+ end
+
+ test "changing" do
+ @klass.omg = "lol"
+ assert_equal "lol", @klass.omg
+ assert_equal "lol", @subklass.omg
+ end
+
+ test "changing in subclass" do
+ @subklass.omg = "lol"
+ assert_equal "lol", @subklass.omg
+ assert_equal "default", @klass.omg
+ end
+
+ test "changing global" do
+ @klass.wtf = "wtf"
+ assert_equal "wtf", @klass.wtf
+ assert_equal "wtf", @subklass.wtf
+
+ @subklass.wtf = "lol"
+ assert_equal "lol", @klass.wtf
+ assert_equal "lol", @subklass.wtf
+ end
+
+ test "instance_writer" do
+ obj = @klass.new
+
+ @klass.omg2 = "omg"
+ @klass.wtf2 = "wtf"
+
+ assert_equal "omg", obj.omg2
+ assert_equal "wtf", obj.wtf2
+
+ obj.omg2 = "lol"
+ obj.wtf2 = "lol"
+
+ assert_equal "lol", obj.omg2
+ assert_equal "lol", obj.wtf2
+ assert_equal "omg", @klass.omg2
+ assert_equal "lol", @klass.wtf2
+ end
+end
diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb
index 4d143146d5..360b1218e6 100644
--- a/activerecord/lib/active_record.rb
+++ b/activerecord/lib/active_record.rb
@@ -57,7 +57,6 @@ module ActiveRecord
autoload :Base
autoload :Callbacks
- autoload :Configuration
autoload :Core
autoload :CounterCache
autoload :DynamicMatchers
diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb
index f61e016f46..40e4a97e73 100644
--- a/activerecord/lib/active_record/attribute_methods/dirty.rb
+++ b/activerecord/lib/active_record/attribute_methods/dirty.rb
@@ -13,7 +13,7 @@ module ActiveRecord
raise "You cannot include Dirty after Timestamp"
end
- class_attribute :partial_updates
+ config_attribute :partial_updates
self.partial_updates = true
end
diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb
index 7d2d1db4b5..964c4123ef 100644
--- a/activerecord/lib/active_record/attribute_methods/read.rb
+++ b/activerecord/lib/active_record/attribute_methods/read.rb
@@ -5,7 +5,10 @@ module ActiveRecord
ATTRIBUTE_TYPES_CACHED_BY_DEFAULT = [:datetime, :timestamp, :time, :date]
- Configuration.define :attribute_types_cached_by_default, ATTRIBUTE_TYPES_CACHED_BY_DEFAULT
+ included do
+ config_attribute :attribute_types_cached_by_default, :global => true
+ self.attribute_types_cached_by_default = ATTRIBUTE_TYPES_CACHED_BY_DEFAULT
+ end
module ClassMethods
# +cache_attributes+ allows you to declare which converted attribute values should
diff --git a/activerecord/lib/active_record/attribute_methods/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb
index 2ffd91f796..0c8e4e4b9a 100644
--- a/activerecord/lib/active_record/attribute_methods/serialization.rb
+++ b/activerecord/lib/active_record/attribute_methods/serialization.rb
@@ -6,7 +6,7 @@ module ActiveRecord
included do
# Returns a hash of all the attributes that have been specified for serialization as
# keys and their class restriction as values.
- class_attribute :serialized_attributes
+ config_attribute :serialized_attributes
self.serialized_attributes = {}
end
diff --git a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
index 5e5392441b..2f86e32f41 100644
--- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
+++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
@@ -6,10 +6,11 @@ module ActiveRecord
module TimeZoneConversion
extend ActiveSupport::Concern
- Configuration.define :time_zone_aware_attributes, false
-
included do
- class_attribute :skip_time_zone_conversion_for_attributes, :instance_writer => false
+ config_attribute :time_zone_aware_attributes, :global => true
+ self.time_zone_aware_attributes = false
+
+ config_attribute :skip_time_zone_conversion_for_attributes
self.skip_time_zone_conversion_for_attributes = []
end
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 6085df7d9f..fbdb50f6b3 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -328,7 +328,6 @@ module ActiveRecord #:nodoc:
# instances in the current object space.
class Base
include ActiveRecord::Model
- self.connection_handler = ConnectionAdapters::ConnectionHandler.new
end
end
diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb
index a175bf003c..8b2fc69b00 100644
--- a/activerecord/lib/active_record/callbacks.rb
+++ b/activerecord/lib/active_record/callbacks.rb
@@ -25,7 +25,7 @@ module ActiveRecord
# Check out <tt>ActiveRecord::Transactions</tt> for more details about <tt>after_commit</tt> and
# <tt>after_rollback</tt>.
#
- # Lastly an <tt>after_find</tt> and <tt>after_initialize</tt> callback is triggered for each object that
+ # Lastly an <tt>after_find</tt> and <tt>after_initialize</tt> callback is triggered for each object that
# is found and instantiated by a finder, with <tt>after_initialize</tt> being triggered after new objects
# are instantiated as well.
#
@@ -215,24 +215,48 @@ module ActiveRecord
# instead of quietly returning +false+.
#
# == Debugging callbacks
- #
- # The callback chain is accessible via the <tt>_*_callbacks</tt> method on an object. ActiveModel Callbacks support
+ #
+ # The callback chain is accessible via the <tt>_*_callbacks</tt> method on an object. ActiveModel Callbacks support
# <tt>:before</tt>, <tt>:after</tt> and <tt>:around</tt> as values for the <tt>kind</tt> property. The <tt>kind</tt> property
# defines what part of the chain the callback runs in.
- #
- # To find all callbacks in the before_save callback chain:
- #
+ #
+ # To find all callbacks in the before_save callback chain:
+ #
# Topic._save_callbacks.select { |cb| cb.kind.eql?(:before) }
- #
+ #
# Returns an array of callback objects that form the before_save chain.
- #
+ #
# To further check if the before_save chain contains a proc defined as <tt>rest_when_dead</tt> use the <tt>filter</tt> property of the callback object:
- #
+ #
# Topic._save_callbacks.select { |cb| cb.kind.eql?(:before) }.collect(&:filter).include?(:rest_when_dead)
- #
+ #
# Returns true or false depending on whether the proc is contained in the before_save callback chain on a Topic model.
- #
+ #
module Callbacks
+ # We can't define callbacks directly on ActiveRecord::Model because
+ # it is a module. So we queue up the definitions and execute them
+ # when ActiveRecord::Model is included.
+ module Register #:nodoc:
+ def self.extended(base)
+ base.config_attribute :_callbacks_register
+ base._callbacks_register = []
+ end
+
+ def self.setup(base)
+ base._callbacks_register.each do |item|
+ base.send(*item)
+ end
+ end
+
+ def define_callbacks(*args)
+ self._callbacks_register << [:define_callbacks, *args]
+ end
+
+ def define_model_callbacks(*args)
+ self._callbacks_register << [:define_model_callbacks, *args]
+ end
+ end
+
extend ActiveSupport::Concern
CALLBACKS = [
@@ -242,8 +266,11 @@ module ActiveRecord
:before_destroy, :around_destroy, :after_destroy, :after_commit, :after_rollback
]
+ module ClassMethods
+ include ActiveModel::Callbacks
+ end
+
included do
- extend ActiveModel::Callbacks
include ActiveModel::Validations::Callbacks
define_model_callbacks :initialize, :find, :touch, :only => :after
diff --git a/activerecord/lib/active_record/configuration.rb b/activerecord/lib/active_record/configuration.rb
deleted file mode 100644
index d58ed82258..0000000000
--- a/activerecord/lib/active_record/configuration.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-require 'active_support/concern'
-
-module ActiveRecord
- # This module allows configuration options to be specified in a way such that
- # ActiveRecord::Base and ActiveRecord::Model will have access to the same value,
- # and will automatically get the appropriate readers and writers defined.
- #
- # In the future, we should probably move away from defining global config
- # directly on ActiveRecord::Base / ActiveRecord::Model.
- module Configuration #:nodoc:
- extend ActiveSupport::Concern
-
- module ClassMethods
- end
-
- def self.define(name, default = nil)
- singleton_class.send(:attr_accessor, name)
-
- [self, ClassMethods].each do |klass|
- klass.class_eval <<-CODE, __FILE__, __LINE__
- def #{name}
- ActiveRecord::Configuration.#{name}
- end
- CODE
- end
-
- ClassMethods.class_eval <<-CODE, __FILE__, __LINE__
- def #{name}=(val)
- ActiveRecord::Configuration.#{name} = val
- end
- CODE
-
- send("#{name}=", default) unless default.nil?
- end
- end
-end
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index 4f118e46a9..c29a0032bc 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -4,70 +4,75 @@ module ActiveRecord
module Core
extend ActiveSupport::Concern
- ##
- # :singleton-method:
- # Accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class,
- # which is then passed on to any new database connections made and which can be retrieved on both
- # a class and instance level by calling +logger+.
- Configuration.define :logger
-
- ##
- # :singleton-method:
- # Contains the database configuration - as is typically stored in config/database.yml -
- # as a Hash.
- #
- # For example, the following database.yml...
- #
- # development:
- # adapter: sqlite3
- # database: db/development.sqlite3
- #
- # production:
- # adapter: sqlite3
- # database: db/production.sqlite3
- #
- # ...would result in ActiveRecord::Base.configurations to look like this:
- #
- # {
- # 'development' => {
- # 'adapter' => 'sqlite3',
- # 'database' => 'db/development.sqlite3'
- # },
- # 'production' => {
- # 'adapter' => 'sqlite3',
- # 'database' => 'db/production.sqlite3'
- # }
- # }
- Configuration.define :configurations, {}
-
- ##
- # :singleton-method:
- # Determines whether to use Time.local (using :local) or Time.utc (using :utc) when pulling
- # dates and times from the database. This is set to :local by default.
- Configuration.define :default_timezone, :local
-
- ##
- # :singleton-method:
- # Specifies the format to use when dumping the database schema with Rails'
- # Rakefile. If :sql, the schema is dumped as (potentially database-
- # specific) SQL statements. If :ruby, the schema is dumped as an
- # ActiveRecord::Schema file which can be loaded into any database that
- # supports migrations. Use :ruby if you want to have different database
- # adapters for, e.g., your development and test environments.
- Configuration.define :schema_format, :ruby
-
- ##
- # :singleton-method:
- # Specify whether or not to use timestamps for migration versions
- Configuration.define :timestamped_migrations, true
-
included do
##
# :singleton-method:
+ # Accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class,
+ # which is then passed on to any new database connections made and which can be retrieved on both
+ # a class and instance level by calling +logger+.
+ config_attribute :logger, :global => true
+
+ ##
+ # :singleton-method:
+ # Contains the database configuration - as is typically stored in config/database.yml -
+ # as a Hash.
+ #
+ # For example, the following database.yml...
+ #
+ # development:
+ # adapter: sqlite3
+ # database: db/development.sqlite3
+ #
+ # production:
+ # adapter: sqlite3
+ # database: db/production.sqlite3
+ #
+ # ...would result in ActiveRecord::Base.configurations to look like this:
+ #
+ # {
+ # 'development' => {
+ # 'adapter' => 'sqlite3',
+ # 'database' => 'db/development.sqlite3'
+ # },
+ # 'production' => {
+ # 'adapter' => 'sqlite3',
+ # 'database' => 'db/production.sqlite3'
+ # }
+ # }
+ config_attribute :configurations, :global => true
+ self.configurations = {}
+
+ ##
+ # :singleton-method:
+ # Determines whether to use Time.local (using :local) or Time.utc (using :utc) when pulling
+ # dates and times from the database. This is set to :local by default.
+ config_attribute :default_timezone, :global => true
+ self.default_timezone = :local
+
+ ##
+ # :singleton-method:
+ # Specifies the format to use when dumping the database schema with Rails'
+ # Rakefile. If :sql, the schema is dumped as (potentially database-
+ # specific) SQL statements. If :ruby, the schema is dumped as an
+ # ActiveRecord::Schema file which can be loaded into any database that
+ # supports migrations. Use :ruby if you want to have different database
+ # adapters for, e.g., your development and test environments.
+ config_attribute :schema_format, :global => true
+ self.schema_format = :ruby
+
+ ##
+ # :singleton-method:
+ # Specify whether or not to use timestamps for migration versions
+ config_attribute :timestamped_migrations, :global => true
+ self.timestamped_migrations = true
+
+ ##
+ # :singleton-method:
# The connection handler
- class_attribute :connection_handler, :instance_writer => false
+ config_attribute :connection_handler
+ self.connection_handler = ConnectionAdapters::ConnectionHandler.new
- initialize_generated_modules unless self == Base
+ #
end
module ClassMethods
diff --git a/activerecord/lib/active_record/explain.rb b/activerecord/lib/active_record/explain.rb
index b5a67afd88..e502d7e52b 100644
--- a/activerecord/lib/active_record/explain.rb
+++ b/activerecord/lib/active_record/explain.rb
@@ -2,9 +2,11 @@ require 'active_support/core_ext/class/attribute'
module ActiveRecord
module Explain
- # If a query takes longer than these many seconds we log its query plan
- # automatically. nil disables this feature.
- Configuration.define :auto_explain_threshold_in_seconds
+ def self.extended(base)
+ # If a query takes longer than these many seconds we log its query plan
+ # automatically. nil disables this feature.
+ base.config_attribute :auto_explain_threshold_in_seconds, :global => true
+ end
# If auto explain is enabled, this method triggers EXPLAIN logging for the
# queries triggered by the block if it takes more than the threshold as a
diff --git a/activerecord/lib/active_record/inheritance.rb b/activerecord/lib/active_record/inheritance.rb
index ec57151d40..0e92b7cf41 100644
--- a/activerecord/lib/active_record/inheritance.rb
+++ b/activerecord/lib/active_record/inheritance.rb
@@ -6,7 +6,7 @@ module ActiveRecord
included do
# Determine whether to store the full constant name including namespace when using STI
- class_attribute :store_full_sti_class
+ config_attribute :store_full_sti_class
self.store_full_sti_class = true
end
@@ -88,7 +88,7 @@ module ActiveRecord
#
# Mainly for internal use.
def active_record_super #:nodoc:
- if self == Base || superclass && superclass < Model::Tag
+ if self == Base || superclass && superclass < Model
superclass
else
Base
@@ -100,7 +100,7 @@ module ActiveRecord
# Returns the class descending directly from ActiveRecord::Base or an
# abstract class, if any, in the inheritance hierarchy.
def class_of_active_record_descendant(klass)
- unless klass < Model::Tag
+ unless klass < Model
raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord"
end
diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb
index b80d01db81..e643c0d437 100644
--- a/activerecord/lib/active_record/locking/optimistic.rb
+++ b/activerecord/lib/active_record/locking/optimistic.rb
@@ -48,7 +48,10 @@ module ActiveRecord
module Optimistic
extend ActiveSupport::Concern
- Configuration.define :lock_optimistically, true
+ included do
+ config_attribute :lock_optimistically, :global => true
+ self.lock_optimistically = true
+ end
def locking_enabled? #:nodoc:
self.class.locking_enabled?
diff --git a/activerecord/lib/active_record/model.rb b/activerecord/lib/active_record/model.rb
index f87be257db..86c4d18534 100644
--- a/activerecord/lib/active_record/model.rb
+++ b/activerecord/lib/active_record/model.rb
@@ -1,4 +1,5 @@
require 'active_support/deprecation'
+require 'active_support/concern'
module ActiveRecord
# <tt>ActiveRecord::Model</tt> can be included into a class to add Active Record persistence.
@@ -9,57 +10,61 @@ module ActiveRecord
# end
#
module Model
- # So we can recognise an AR class even while self.included is being
- # executed. (At that time, klass < Model == false.)
- module Tag #:nodoc:
+ module ClassMethods #:nodoc:
+ include ActiveSupport::Callbacks::ClassMethods
+ include ActiveModel::Naming
+ include QueryCache::ClassMethods
+ include ActiveSupport::Benchmarkable
+ include ActiveSupport::DescendantsTracker
+
+ include Querying
+ include Translation
+ include DynamicMatchers
+ include CounterCache
+ include Explain
end
def self.included(base)
- return if base < Tag
+ return if base.singleton_class < ClassMethods
base.class_eval do
- include Tag
-
- include Configuration
-
- include ActiveRecord::Persistence
- extend ActiveModel::Naming
- extend QueryCache::ClassMethods
- extend ActiveSupport::Benchmarkable
- extend ActiveSupport::DescendantsTracker
-
- extend Querying
- include ReadonlyAttributes
- include ModelSchema
- extend Translation
- include Inheritance
- include Scoping
- extend DynamicMatchers
- include Sanitization
- include Integration
- include AttributeAssignment
- include ActiveModel::Conversion
- include Validations
- extend CounterCache
- include Locking::Optimistic, Locking::Pessimistic
- include AttributeMethods
- include Callbacks, ActiveModel::Observing, Timestamp
- include Associations
- include IdentityMap
- include ActiveModel::SecurePassword
- extend Explain
-
- # AutosaveAssociation needs to be included before Transactions, because we want
- # #save_with_autosave_associations to be wrapped inside a transaction.
- include AutosaveAssociation, NestedAttributes
- include Aggregations, Transactions, Reflection, Serialization, Store
+ extend ClassMethods
+ Callbacks::Register.setup(self)
+ initialize_generated_modules unless self == Base
+ end
+ end
- include Core
+ extend ActiveModel::Configuration
+ extend ActiveModel::Callbacks
+ extend ActiveModel::MassAssignmentSecurity::ClassMethods
+ extend ActiveModel::AttributeMethods::ClassMethods
+ extend Callbacks::Register
+ extend Explain
- self.connection_handler = Base.connection_handler
- end
+ def self.extend(*modules)
+ ClassMethods.send(:include, *modules)
end
+ include Persistence
+ include ReadonlyAttributes
+ include ModelSchema
+ include Inheritance
+ include Scoping
+ include Sanitization
+ include Integration
+ include AttributeAssignment
+ include ActiveModel::Conversion
+ include Validations
+ include Locking::Optimistic, Locking::Pessimistic
+ include AttributeMethods
+ include Callbacks, ActiveModel::Observing, Timestamp
+ include Associations
+ include IdentityMap
+ include ActiveModel::SecurePassword
+ include AutosaveAssociation, NestedAttributes
+ include Aggregations, Transactions, Reflection, Serialization, Store
+ include Core
+
module DeprecationProxy #:nodoc:
class << self
instance_methods.each { |m| undef_method m unless m =~ /^__|^object_id$|^instance_eval$/ }
diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb
index adf85c6436..d9d18a9752 100644
--- a/activerecord/lib/active_record/model_schema.rb
+++ b/activerecord/lib/active_record/model_schema.rb
@@ -5,18 +5,18 @@ module ActiveRecord
module ModelSchema
extend ActiveSupport::Concern
- ##
- # :singleton-method:
- # Accessor for the prefix type that will be prepended to every primary key column name.
- # The options are :table_name and :table_name_with_underscore. If the first is specified,
- # the Product class will look for "productid" instead of "id" as the primary column. If the
- # latter is specified, the Product class will look for "product_id" instead of "id". Remember
- # that this is a global setting for all Active Records.
- Configuration.define :primary_key_prefix_type
-
included do
##
# :singleton-method:
+ # Accessor for the prefix type that will be prepended to every primary key column name.
+ # The options are :table_name and :table_name_with_underscore. If the first is specified,
+ # the Product class will look for "productid" instead of "id" as the primary column. If the
+ # latter is specified, the Product class will look for "product_id" instead of "id". Remember
+ # that this is a global setting for all Active Records.
+ config_attribute :primary_key_prefix_type, :global => true
+
+ ##
+ # :singleton-method:
# Accessor for the name of the prefix string to prepend to every table name. So if set
# to "basecamp_", all table names will be named like "basecamp_projects", "basecamp_people",
# etc. This is a convenient way of creating a namespace for tables in a shared database.
@@ -25,14 +25,14 @@ module ActiveRecord
# If you are organising your models within modules you can add a prefix to the models within
# a namespace by defining a singleton method in the parent module called table_name_prefix which
# returns your chosen prefix.
- class_attribute :table_name_prefix, :instance_writer => false
+ config_attribute :table_name_prefix
self.table_name_prefix = ""
##
# :singleton-method:
# Works like +table_name_prefix+, but appends instead of prepends (set to "_basecamp" gives "projects_basecamp",
# "people_basecamp"). By default, the suffix is the empty string.
- class_attribute :table_name_suffix, :instance_writer => false
+ config_attribute :table_name_suffix
self.table_name_suffix = ""
##
@@ -40,7 +40,7 @@ module ActiveRecord
# Indicates whether table names should be the pluralized versions of the corresponding class names.
# If true, the default table name for a Product class will be +products+. If false, it would just be +product+.
# See table_name for the full rules on table/class naming. This is true, by default.
- class_attribute :pluralize_table_names, :instance_writer => false
+ config_attribute :pluralize_table_names
self.pluralize_table_names = true
end
diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb
index d2065d701f..a8ee43b598 100644
--- a/activerecord/lib/active_record/nested_attributes.rb
+++ b/activerecord/lib/active_record/nested_attributes.rb
@@ -12,7 +12,7 @@ module ActiveRecord
extend ActiveSupport::Concern
included do
- class_attribute :nested_attributes_options, :instance_writer => false
+ config_attribute :nested_attributes_options
self.nested_attributes_options = {}
end
diff --git a/activerecord/lib/active_record/readonly_attributes.rb b/activerecord/lib/active_record/readonly_attributes.rb
index bf37ab5f14..836b15e2ce 100644
--- a/activerecord/lib/active_record/readonly_attributes.rb
+++ b/activerecord/lib/active_record/readonly_attributes.rb
@@ -6,7 +6,7 @@ module ActiveRecord
extend ActiveSupport::Concern
included do
- class_attribute :_attr_readonly, :instance_writer => false
+ config_attribute :_attr_readonly
self._attr_readonly = []
end
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index 794a0526fb..60b73e9fe5 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -7,7 +7,8 @@ module ActiveRecord
extend ActiveSupport::Concern
included do
- class_attribute :reflections
+ extend ActiveModel::Configuration
+ config_attribute :reflections
self.reflections = {}
end
diff --git a/activerecord/lib/active_record/scoping/default.rb b/activerecord/lib/active_record/scoping/default.rb
index 6b5070808a..5f05d146f2 100644
--- a/activerecord/lib/active_record/scoping/default.rb
+++ b/activerecord/lib/active_record/scoping/default.rb
@@ -7,7 +7,7 @@ module ActiveRecord
included do
# Stores the default scope for the class
- class_attribute :default_scopes, :instance_writer => false
+ config_attribute :default_scopes
self.default_scopes = []
end
diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb
index 0c760e9850..c717fdea47 100644
--- a/activerecord/lib/active_record/timestamp.rb
+++ b/activerecord/lib/active_record/timestamp.rb
@@ -33,7 +33,7 @@ module ActiveRecord
extend ActiveSupport::Concern
included do
- class_attribute :record_timestamps
+ config_attribute :record_timestamps, :instance_writer => true
self.record_timestamps = true
end
diff --git a/activerecord/test/cases/attribute_methods/read_test.rb b/activerecord/test/cases/attribute_methods/read_test.rb
index 375c207d20..0df9ffc0c5 100644
--- a/activerecord/test/cases/attribute_methods/read_test.rb
+++ b/activerecord/test/cases/attribute_methods/read_test.rb
@@ -18,7 +18,6 @@ module ActiveRecord
def self.active_record_super; Base; end
def self.base_class; self; end
- include ActiveRecord::Configuration
include ActiveRecord::AttributeMethods
def self.column_names
diff --git a/activerecord/test/cases/configuration_test.rb b/activerecord/test/cases/configuration_test.rb
deleted file mode 100644
index 872f1fc33b..0000000000
--- a/activerecord/test/cases/configuration_test.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-require 'cases/helper'
-
-class ConfigurationTest < ActiveRecord::TestCase
- def test_configuration
- @klass = Class.new do
- include ActiveRecord::Configuration
- end
-
- ActiveRecord::Configuration.define :omg
-
- ActiveRecord::Configuration.omg = "omg"
-
- assert_equal "omg", @klass.new.omg
- assert !@klass.new.respond_to?(:omg=)
- assert_equal "omg", @klass.omg
-
- @klass.omg = "wtf"
-
- assert_equal "wtf", @klass.omg
- assert_equal "wtf", @klass.new.omg
- ensure
- ActiveRecord::Configuration.send(:undef_method, :omg)
- ActiveRecord::Configuration::ClassMethods.send(:undef_method, :omg)
- ActiveRecord::Configuration::ClassMethods.send(:undef_method, :omg=)
- end
-end