diff options
Diffstat (limited to 'activerecord/lib')
41 files changed, 466 insertions, 438 deletions
diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index 4d143146d5..4f194cb6cf 100644 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -57,9 +57,9 @@ module ActiveRecord autoload :Base autoload :Callbacks - autoload :Configuration autoload :Core autoload :CounterCache + autoload :ConnectionHandling autoload :DynamicMatchers autoload :DynamicFinderMatch autoload :DynamicScopeMatch diff --git a/activerecord/lib/active_record/associations/association_scope.rb b/activerecord/lib/active_record/associations/association_scope.rb index 6f8b76abda..090f690f0d 100644 --- a/activerecord/lib/active_record/associations/association_scope.rb +++ b/activerecord/lib/active_record/associations/association_scope.rb @@ -20,7 +20,7 @@ module ActiveRecord # It's okay to just apply all these like this. The options will only be present if the # association supports that option; this is enforced by the association builder. scope = scope.apply_finder_options(options.slice( - :readonly, :include, :order, :limit, :joins, :group, :having, :offset, :select)) + :readonly, :include, :eager_load, :order, :limit, :joins, :group, :having, :offset, :select)) if options[:through] && !options[:include] scope = scope.includes(source_options[:include]) diff --git a/activerecord/lib/active_record/associations/builder/association.rb b/activerecord/lib/active_record/associations/builder/association.rb index d4f59100e8..776f0d0469 100644 --- a/activerecord/lib/active_record/associations/builder/association.rb +++ b/activerecord/lib/active_record/associations/builder/association.rb @@ -1,7 +1,7 @@ module ActiveRecord::Associations::Builder class Association #:nodoc: class_attribute :valid_options - self.valid_options = [:class_name, :foreign_key, :select, :conditions, :include, :extend, :readonly, :validate] + self.valid_options = [:class_name, :foreign_key, :select, :conditions, :include, :eager_load, :extend, :readonly, :validate] # Set by subclasses class_attribute :macro diff --git a/activerecord/lib/active_record/associations/preloader/association.rb b/activerecord/lib/active_record/associations/preloader/association.rb index 779f8164cc..298decb0f1 100644 --- a/activerecord/lib/active_record/associations/preloader/association.rb +++ b/activerecord/lib/active_record/associations/preloader/association.rb @@ -100,6 +100,7 @@ module ActiveRecord scope = scope.select(preload_options[:select] || options[:select] || table[Arel.star]) scope = scope.includes(preload_options[:include] || options[:include]) + scope = scope.eager_load(preload_options[:eager_load] || options[:eager_load]) if options[:as] scope = scope.where( diff --git a/activerecord/lib/active_record/associations/preloader/through_association.rb b/activerecord/lib/active_record/associations/preloader/through_association.rb index ad6374d09a..97898c53ae 100644 --- a/activerecord/lib/active_record/associations/preloader/through_association.rb +++ b/activerecord/lib/active_record/associations/preloader/through_association.rb @@ -53,6 +53,7 @@ module ActiveRecord else if options[:conditions] through_options[:include] = options[:include] || options[:source] + through_options[:eager_load] = options[:eager_load] || options[:source] through_options[:conditions] = options[:conditions] end diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index 44b0956e4e..c5834e2fef 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -61,7 +61,7 @@ module ActiveRecord raise DangerousAttributeError, "#{method_name} is defined by ActiveRecord" end - if active_record_super == Base + if [Base, Model].include?(active_record_super) super else # If B < A and A defines its own attribute method, then we don't want to overwrite that. 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..d29cf82dad 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -328,9 +328,7 @@ module ActiveRecord #:nodoc: # instances in the current object space. class Base include ActiveRecord::Model - self.connection_handler = ConnectionAdapters::ConnectionHandler.new end end -require 'active_record/connection_adapters/abstract/connection_specification' ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Model::DeprecationProxy) 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/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb index 5749d45a18..ea738cb305 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -370,7 +370,7 @@ connection. For example: ActiveRecord::Base.connection.close def retrieve_connection_pool(klass) pool = @class_to_pool[klass.name] return pool if pool - return nil if ActiveRecord::Base == klass + return nil if ActiveRecord::Model == klass retrieve_connection_pool klass.active_record_super end end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb deleted file mode 100644 index 63e4020113..0000000000 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb +++ /dev/null @@ -1,184 +0,0 @@ -require 'active_support/core_ext/module/delegation' - -module ActiveRecord - module Core - class ConnectionSpecification #:nodoc: - attr_reader :config, :adapter_method - def initialize (config, adapter_method) - @config, @adapter_method = config, adapter_method - end - - ## - # Builds a ConnectionSpecification from user input - class Resolver # :nodoc: - attr_reader :config, :klass, :configurations - - def initialize(config, configurations) - @config = config - @configurations = configurations - end - - def spec - case config - when nil - raise AdapterNotSpecified unless defined?(Rails.env) - resolve_string_connection Rails.env - when Symbol, String - resolve_string_connection config.to_s - when Hash - resolve_hash_connection config - end - end - - private - def resolve_string_connection(spec) # :nodoc: - hash = configurations.fetch(spec) do |k| - connection_url_to_hash(k) - end - - raise(AdapterNotSpecified, "#{spec} database is not configured") unless hash - - resolve_hash_connection hash - end - - def resolve_hash_connection(spec) # :nodoc: - spec = spec.symbolize_keys - - raise(AdapterNotSpecified, "database configuration does not specify adapter") unless spec.key?(:adapter) - - begin - require "active_record/connection_adapters/#{spec[:adapter]}_adapter" - rescue LoadError => e - raise LoadError, "Please install the #{spec[:adapter]} adapter: `gem install activerecord-#{spec[:adapter]}-adapter` (#{e.message})", e.backtrace - end - - adapter_method = "#{spec[:adapter]}_connection" - - ConnectionSpecification.new(spec, adapter_method) - end - - def connection_url_to_hash(url) # :nodoc: - config = URI.parse url - adapter = config.scheme - adapter = "postgresql" if adapter == "postgres" - spec = { :adapter => adapter, - :username => config.user, - :password => config.password, - :port => config.port, - :database => config.path.sub(%r{^/},""), - :host => config.host } - spec.reject!{ |_,value| !value } - if config.query - options = Hash[config.query.split("&").map{ |pair| pair.split("=") }].symbolize_keys - spec.merge!(options) - end - spec - end - end - end - - # Returns the connection currently associated with the class. This can - # also be used to "borrow" the connection to do database work that isn't - # easily done without going straight to SQL. - def connection - self.class.connection - end - - module ClassMethods - # Establishes the connection to the database. Accepts a hash as input where - # the <tt>:adapter</tt> key must be specified with the name of a database adapter (in lower-case) - # example for regular databases (MySQL, Postgresql, etc): - # - # ActiveRecord::Base.establish_connection( - # :adapter => "mysql", - # :host => "localhost", - # :username => "myuser", - # :password => "mypass", - # :database => "somedatabase" - # ) - # - # Example for SQLite database: - # - # ActiveRecord::Base.establish_connection( - # :adapter => "sqlite", - # :database => "path/to/dbfile" - # ) - # - # Also accepts keys as strings (for parsing from YAML for example): - # - # ActiveRecord::Base.establish_connection( - # "adapter" => "sqlite", - # "database" => "path/to/dbfile" - # ) - # - # Or a URL: - # - # ActiveRecord::Base.establish_connection( - # "postgres://myuser:mypass@localhost/somedatabase" - # ) - # - # The exceptions AdapterNotSpecified, AdapterNotFound and ArgumentError - # may be returned on an error. - def establish_connection(spec = ENV["DATABASE_URL"]) - resolver = ConnectionSpecification::Resolver.new spec, configurations - spec = resolver.spec - - unless respond_to?(spec.adapter_method) - raise AdapterNotFound, "database configuration specifies nonexistent #{spec.config[:adapter]} adapter" - end - - remove_connection - connection_handler.establish_connection name, spec - end - - # Returns the connection currently associated with the class. This can - # also be used to "borrow" the connection to do database work unrelated - # to any of the specific Active Records. - def connection - retrieve_connection - end - - def connection_id - Thread.current['ActiveRecord::Base.connection_id'] - end - - def connection_id=(connection_id) - Thread.current['ActiveRecord::Base.connection_id'] = connection_id - end - - # Returns the configuration of the associated connection as a hash: - # - # ActiveRecord::Base.connection_config - # # => {:pool=>5, :timeout=>5000, :database=>"db/development.sqlite3", :adapter=>"sqlite3"} - # - # Please use only for reading. - def connection_config - connection_pool.spec.config - end - - def connection_pool - connection_handler.retrieve_connection_pool(self) or raise ConnectionNotEstablished - end - - def retrieve_connection - connection_handler.retrieve_connection(self) - end - - # Returns true if Active Record is connected. - def connected? - connection_handler.connected?(self) - end - - def remove_connection(klass = self) - connection_handler.remove_connection(klass) - end - - def clear_active_connections! - connection_handler.clear_active_connections! - end - - delegate :clear_reloadable_connections!, - :clear_all_connections!,:verify_active_connections!, :to => :connection_handler - end - end -end diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index 5a2493f69d..edea414db7 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -11,6 +11,7 @@ module ActiveRecord extend ActiveSupport::Autoload autoload :Column + autoload :ConnectionSpecification autoload_under 'abstract' do autoload :IndexDefinition, 'active_record/connection_adapters/abstract/schema_definitions' @@ -26,7 +27,6 @@ module ActiveRecord autoload :ConnectionPool autoload :ConnectionHandler, 'active_record/connection_adapters/abstract/connection_pool' autoload :ConnectionManagement, 'active_record/connection_adapters/abstract/connection_pool' - autoload :ConnectionSpecification autoload :QueryCache end diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb new file mode 100644 index 0000000000..3e67c3a2b7 --- /dev/null +++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb @@ -0,0 +1,79 @@ +module ActiveRecord + module ConnectionAdapters + class ConnectionSpecification #:nodoc: + attr_reader :config, :adapter_method + + def initialize (config, adapter_method) + @config, @adapter_method = config, adapter_method + end + + ## + # Builds a ConnectionSpecification from user input + class Resolver # :nodoc: + attr_reader :config, :klass, :configurations + + def initialize(config, configurations) + @config = config + @configurations = configurations + end + + def spec + case config + when nil + raise AdapterNotSpecified unless defined?(Rails.env) + resolve_string_connection Rails.env + when Symbol, String + resolve_string_connection config.to_s + when Hash + resolve_hash_connection config + end + end + + private + def resolve_string_connection(spec) # :nodoc: + hash = configurations.fetch(spec) do |k| + connection_url_to_hash(k) + end + + raise(AdapterNotSpecified, "#{spec} database is not configured") unless hash + + resolve_hash_connection hash + end + + def resolve_hash_connection(spec) # :nodoc: + spec = spec.symbolize_keys + + raise(AdapterNotSpecified, "database configuration does not specify adapter") unless spec.key?(:adapter) + + begin + require "active_record/connection_adapters/#{spec[:adapter]}_adapter" + rescue LoadError => e + raise LoadError, "Please install the #{spec[:adapter]} adapter: `gem install activerecord-#{spec[:adapter]}-adapter` (#{e.message})", e.backtrace + end + + adapter_method = "#{spec[:adapter]}_connection" + + ConnectionSpecification.new(spec, adapter_method) + end + + def connection_url_to_hash(url) # :nodoc: + config = URI.parse url + adapter = config.scheme + adapter = "postgresql" if adapter == "postgres" + spec = { :adapter => adapter, + :username => config.user, + :password => config.password, + :port => config.port, + :database => config.path.sub(%r{^/},""), + :host => config.host } + spec.reject!{ |_,value| !value } + if config.query + options = Hash[config.query.split("&").map{ |pair| pair.split("=") }].symbolize_keys + spec.merge!(options) + end + spec + end + end + end + end +end diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb index e51796871a..6086c32dbe 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb @@ -4,7 +4,7 @@ gem 'mysql2', '~> 0.3.10' require 'mysql2' module ActiveRecord - module Core::ClassMethods + module ConnectionHandling # Establishes a connection to the database that's used by all Active Record objects. def mysql2_connection(config) config[:username] = 'root' if config[:username].nil? diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index 901d8422f2..e432c5af32 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -18,7 +18,7 @@ class Mysql end module ActiveRecord - module Core::ClassMethods + module ConnectionHandling # Establishes a connection to the database that's used by all Active Record objects. def mysql_connection(config) # :nodoc: config = config.symbolize_keys diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 74a9be99bd..d2361b5378 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -7,7 +7,7 @@ gem 'pg', '~> 0.11' require 'pg' module ActiveRecord - module Core::ClassMethods + module ConnectionHandling # Establishes a connection to the database that's used by all Active Record objects def postgresql_connection(config) # :nodoc: config = config.symbolize_keys diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index ac3fb72b6e..ee5d10859c 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -4,7 +4,7 @@ gem 'sqlite3', '~> 1.3.5' require 'sqlite3' module ActiveRecord - module Core::ClassMethods + module ConnectionHandling # sqlite3 adapter reuses sqlite_connection. def sqlite3_connection(config) # :nodoc: # Require database. diff --git a/activerecord/lib/active_record/connection_handling.rb b/activerecord/lib/active_record/connection_handling.rb new file mode 100644 index 0000000000..e9cd3d5e57 --- /dev/null +++ b/activerecord/lib/active_record/connection_handling.rb @@ -0,0 +1,100 @@ +require 'active_support/core_ext/module/delegation' + +module ActiveRecord + module ConnectionHandling + # Establishes the connection to the database. Accepts a hash as input where + # the <tt>:adapter</tt> key must be specified with the name of a database adapter (in lower-case) + # example for regular databases (MySQL, Postgresql, etc): + # + # ActiveRecord::Base.establish_connection( + # :adapter => "mysql", + # :host => "localhost", + # :username => "myuser", + # :password => "mypass", + # :database => "somedatabase" + # ) + # + # Example for SQLite database: + # + # ActiveRecord::Base.establish_connection( + # :adapter => "sqlite", + # :database => "path/to/dbfile" + # ) + # + # Also accepts keys as strings (for parsing from YAML for example): + # + # ActiveRecord::Base.establish_connection( + # "adapter" => "sqlite", + # "database" => "path/to/dbfile" + # ) + # + # Or a URL: + # + # ActiveRecord::Base.establish_connection( + # "postgres://myuser:mypass@localhost/somedatabase" + # ) + # + # The exceptions AdapterNotSpecified, AdapterNotFound and ArgumentError + # may be returned on an error. + def establish_connection(spec = ENV["DATABASE_URL"]) + resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new spec, configurations + spec = resolver.spec + + unless respond_to?(spec.adapter_method) + raise AdapterNotFound, "database configuration specifies nonexistent #{spec.config[:adapter]} adapter" + end + + remove_connection + connection_handler.establish_connection name, spec + end + + # Returns the connection currently associated with the class. This can + # also be used to "borrow" the connection to do database work unrelated + # to any of the specific Active Records. + def connection + retrieve_connection + end + + def connection_id + Thread.current['ActiveRecord::Base.connection_id'] + end + + def connection_id=(connection_id) + Thread.current['ActiveRecord::Base.connection_id'] = connection_id + end + + # Returns the configuration of the associated connection as a hash: + # + # ActiveRecord::Base.connection_config + # # => {:pool=>5, :timeout=>5000, :database=>"db/development.sqlite3", :adapter=>"sqlite3"} + # + # Please use only for reading. + def connection_config + connection_pool.spec.config + end + + def connection_pool + connection_handler.retrieve_connection_pool(self) or raise ConnectionNotEstablished + end + + def retrieve_connection + connection_handler.retrieve_connection(self) + end + + # Returns true if Active Record is connected. + def connected? + connection_handler.connected?(self) + end + + def remove_connection(klass = self) + connection_handler.remove_connection(klass) + end + + def clear_active_connections! + connection_handler.clear_active_connections! + end + + delegate :clear_reloadable_connections!, + :clear_all_connections!,:verify_active_connections!, :to => :connection_handler + end +end diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb index 4f118e46a9..980f8fe50f 100644 --- a/activerecord/lib/active_record/core.rb +++ b/activerecord/lib/active_record/core.rb @@ -4,70 +4,73 @@ 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: - # The connection handler - class_attribute :connection_handler, :instance_writer => false + # 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 - initialize_generated_modules unless self == Base + ## + # :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 + config_attribute :connection_handler + self.connection_handler = ConnectionAdapters::ConnectionHandler.new end module ClassMethods @@ -114,13 +117,7 @@ module ActiveRecord end def arel_engine - @arel_engine ||= begin - if self == ActiveRecord::Base - ActiveRecord::Base - else - connection_handler.connection_pools[name] ? self : active_record_super.arel_engine - end - end + @arel_engine ||= connection_handler.connection_pools[name] ? self : active_record_super.arel_engine end private @@ -299,6 +296,13 @@ module ActiveRecord @readonly = true end + # Returns the connection currently associated with the class. This can + # also be used to "borrow" the connection to do database work that isn't + # easily done without going straight to SQL. + def connection + self.class.connection + end + # Returns the contents of the record as a nicely formatted string. def inspect inspection = if @attributes 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/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index 65c7f3afbb..deffc7cec5 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -486,8 +486,8 @@ module ActiveRecord # Cap primary key sequences to max(pk). if connection.respond_to?(:reset_pk_sequence!) - table_names.each do |table_name| - connection.reset_pk_sequence!(table_name.tr('/', '_')) + fixture_files.each do |ff| + connection.reset_pk_sequence!(ff.table_name) end end end diff --git a/activerecord/lib/active_record/inheritance.rb b/activerecord/lib/active_record/inheritance.rb index ec57151d40..eaa7deac5a 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 @@ -17,8 +17,10 @@ module ActiveRecord if sup.abstract_class? sup.descends_from_active_record? + elsif self == Base + false else - sup == Base || !columns_hash.include?(inheritance_column) + [Base, Model].include?(sup) || !columns_hash.include?(inheritance_column) end end @@ -81,18 +83,12 @@ module ActiveRecord instance end - # If this class includes ActiveRecord::Model then it won't have a - # superclass. So this provides a way to get to the 'root' (ActiveRecord::Base), - # through inheritance hierarchy, ending in Base, whether or not that is - # actually an ancestor of the class. + # For internal use. # - # Mainly for internal use. + # If this class includes ActiveRecord::Model then it won't have a + # superclass. So this provides a way to get to the 'root' (ActiveRecord::Model). def active_record_super #:nodoc: - if self == Base || superclass && superclass < Model::Tag - superclass - else - Base - end + superclass < Model ? superclass : Model end protected @@ -100,12 +96,12 @@ 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 sup = klass.active_record_super - if klass == Base || sup == Base || sup.abstract_class? + if [Base, Model].include?(klass) || [Base, Model].include?(sup) || sup.abstract_class? klass else class_of_active_record_descendant(sup) 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..86de5ab2fa 100644 --- a/activerecord/lib/active_record/model.rb +++ b/activerecord/lib/active_record/model.rb @@ -9,54 +9,74 @@ 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 + include ConnectionHandling end def self.included(base) - return if base < Tag + return if base.singleton_class < ClassMethods base.class_eval do - include Tag + extend ClassMethods + Callbacks::Register.setup(self) + initialize_generated_modules unless self == Base + end + end - include Configuration + extend ActiveModel::Configuration + extend ActiveModel::Callbacks + extend ActiveModel::MassAssignmentSecurity::ClassMethods + extend ActiveModel::AttributeMethods::ClassMethods + extend Callbacks::Register + extend Explain + extend ConnectionHandling - include ActiveRecord::Persistence - extend ActiveModel::Naming - extend QueryCache::ClassMethods - extend ActiveSupport::Benchmarkable - extend ActiveSupport::DescendantsTracker + def self.extend(*modules) + ClassMethods.send(:include, *modules) + end - 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 + 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 - # 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 + class << self + def arel_engine + self + end - include Core + def abstract_class? + false + end - self.connection_handler = Base.connection_handler + def inheritance_column + 'type' end end diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb index adf85c6436..c1a8583119 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 @@ -143,11 +143,7 @@ module ActiveRecord # The name of the column containing the object's class when Single Table Inheritance is used def inheritance_column - if self == Base - 'type' - else - (@inheritance_column ||= nil) || active_record_super.inheritance_column - end + (@inheritance_column ||= nil) || active_record_super.inheritance_column end # Sets the value of inheritance_column 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/railtie.rb b/activerecord/lib/active_record/railtie.rb index 08cf9fb504..165d0750dd 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -108,7 +108,7 @@ module ActiveRecord config.after_initialize do ActiveSupport.on_load(:active_record) do - instantiate_observers + ActiveRecord::Base.instantiate_observers ActionDispatch::Reloader.to_prepare do ActiveRecord::Base.instantiate_observers diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index de299dbe91..180424641a 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -112,8 +112,7 @@ db_namespace = namespace :db do end end else - # Bug with 1.9.2 Calling return within begin still executes else - $stderr.puts "#{config['database']} already exists" unless config['adapter'] =~ /sqlite/ + $stderr.puts "#{config['database']} already exists" end 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/relation.rb b/activerecord/lib/active_record/relation.rb index 80b85ce498..6f2248fa21 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- require 'active_support/core_ext/object/blank' +require 'active_support/deprecation' module ActiveRecord # = Active Record Relation @@ -521,7 +522,18 @@ module ActiveRecord # always convert table names to downcase as in Oracle quoted table names are in uppercase joined_tables = joined_tables.flatten.compact.map { |t| t.downcase }.uniq - (tables_in_string(to_sql) - joined_tables).any? + referenced_tables = (tables_in_string(to_sql) - joined_tables) + if referenced_tables.any? + ActiveSupport::Deprecation.warn( + "Your query appears to reference tables (#{referenced_tables.join(', ')}) that are not " \ + "explicitly joined. This implicit joining is deprecated, so you must explicitly " \ + "reference the tables. For example, instead of Author.includes(:posts).where(\"posts.name = 'foo'\"), " \ + "you should write Author.eager_load(:posts).where(\"posts.name = 'foo'\")." + ) + true + else + false + end end def tables_in_string(string) diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 311bf4dc0f..e58c726e09 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -249,7 +249,7 @@ module ActiveRecord end def construct_limited_ids_condition(relation) - orders = relation.order_values.map { |val| val.presence }.compact + orders = (relation.reorder_value || relation.order_values).map { |val| val.presence }.compact values = @klass.connection.distinct("#{@klass.connection.quote_table_name table_name}.#{primary_key}", orders) relation = relation.dup diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index eee198e760..88081edae2 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -15,44 +15,49 @@ module ActiveRecord table = Arel::Table.new(table_name, engine) end - attribute = table[column.to_sym] - - case value - when ActiveRecord::Relation - value = value.select(value.klass.arel_table[value.klass.primary_key]) if value.select_values.empty? - attribute.in(value.arel.ast) - when Array, ActiveRecord::Associations::CollectionProxy - values = value.to_a.map {|x| x.is_a?(ActiveRecord::Model) ? x.id : x} - ranges, values = values.partition {|v| v.is_a?(Range) || v.is_a?(Arel::Relation)} - - array_predicates = ranges.map {|range| attribute.in(range)} + build(table[column.to_sym], value) + end + end + predicates.flatten + end - if values.include?(nil) - values = values.compact - if values.empty? - array_predicates << attribute.eq(nil) - else - array_predicates << attribute.in(values.compact).or(attribute.eq(nil)) - end + private + def self.build(attribute, value) + case value + when ActiveRecord::Relation + value = value.select(value.klass.arel_table[value.klass.primary_key]) if value.select_values.empty? + attribute.in(value.arel.ast) + when Array, ActiveRecord::Associations::CollectionProxy + values = value.to_a.map {|x| x.is_a?(ActiveRecord::Model) ? x.id : x} + ranges, values = values.partition {|v| v.is_a?(Range) || v.is_a?(Arel::Relation)} + + array_predicates = ranges.map {|range| attribute.in(range)} + + if values.include?(nil) + values = values.compact + case values.length + when 0 + array_predicates << attribute.eq(nil) + when 1 + array_predicates << attribute.eq(values.first).or(attribute.eq(nil)) else - array_predicates << attribute.in(values) + array_predicates << attribute.in(values).or(attribute.eq(nil)) end - - array_predicates.inject {|composite, predicate| composite.or(predicate)} - when Range, Arel::Relation - attribute.in(value) - when ActiveRecord::Model - attribute.eq(value.id) - when Class - # FIXME: I think we need to deprecate this behavior - attribute.eq(value.name) else - attribute.eq(value) + array_predicates << attribute.in(values) end + + array_predicates.inject {|composite, predicate| composite.or(predicate)} + when Range, Arel::Relation + attribute.in(value) + when ActiveRecord::Model + attribute.eq(value.id) + when Class + # FIXME: I think we need to deprecate this behavior + attribute.eq(value.name) + else + attribute.eq(value) end end - - predicates.flatten - end end end diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb index ba882beca9..a5194beae5 100644 --- a/activerecord/lib/active_record/relation/spawn_methods.rb +++ b/activerecord/lib/active_record/relation/spawn_methods.rb @@ -113,7 +113,7 @@ module ActiveRecord result end - VALID_FIND_OPTIONS = [ :conditions, :include, :joins, :limit, :offset, :extend, + VALID_FIND_OPTIONS = [ :conditions, :include, :joins, :limit, :offset, :extend, :eager_load, :order, :select, :readonly, :group, :having, :from, :lock ] def apply_finder_options(options) @@ -124,7 +124,7 @@ module ActiveRecord finders = options.dup finders.delete_if { |key, value| value.nil? && key != :limit } - ([:joins, :select, :group, :order, :having, :limit, :offset, :from, :lock, :readonly] & finders.keys).each do |finder| + ([:joins, :select, :group, :order, :having, :limit, :offset, :from, :lock, :readonly, :eager_load] & finders.keys).each do |finder| relation = relation.send(finder, finders[finder]) 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/scoping/named.rb b/activerecord/lib/active_record/scoping/named.rb index 17122740da..0edc3f1dcc 100644 --- a/activerecord/lib/active_record/scoping/named.rb +++ b/activerecord/lib/active_record/scoping/named.rb @@ -118,16 +118,16 @@ module ActiveRecord # when they are used. For example, the following would be incorrect: # # class Post < ActiveRecord::Base - # scope :recent, where('published_at >= ?', Time.now - 1.week) + # scope :recent, where('published_at >= ?', Time.current - 1.week) # end # - # The example above would be 'frozen' to the <tt>Time.now</tt> value when the <tt>Post</tt> + # The example above would be 'frozen' to the <tt>Time.current</tt> value when the <tt>Post</tt> # class was defined, and so the resultant SQL query would always be the same. The correct # way to do this would be via a lambda, which will re-evaluate the scope each time # it is called: # # class Post < ActiveRecord::Base - # scope :recent, lambda { where('published_at >= ?', Time.now - 1.week) } + # scope :recent, lambda { where('published_at >= ?', Time.current - 1.week) } # end # # Named \scopes can also have extensions, just as with <tt>has_many</tt> declarations: 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 |