diff options
author | Jon Leighton <j@jonathanleighton.com> | 2011-12-28 15:38:16 +0000 |
---|---|---|
committer | Jon Leighton <j@jonathanleighton.com> | 2011-12-28 18:27:41 +0000 |
commit | 93c1f11c0a5097a5431819a1551a02a869a16a38 (patch) | |
tree | fd88de442e03a600df63387dc4b2389e9c861c7b /activerecord/lib | |
parent | afe6e059ea216f01d160e4603116356b78df12e5 (diff) | |
download | rails-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.
Diffstat (limited to 'activerecord/lib')
19 files changed, 190 insertions, 181 deletions
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 |