diff options
Diffstat (limited to 'activerecord/lib')
19 files changed, 305 insertions, 276 deletions
diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index b79da4565d..5942640c85 100644 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -30,7 +30,6 @@ $:.unshift(activemodel_path) if File.directory?(activemodel_path) && !$:.include require 'active_support' require 'active_model' -require 'arel' module ActiveRecord extend ActiveSupport::Autoload @@ -38,8 +37,8 @@ module ActiveRecord eager_autoload do autoload :VERSION - autoload :ActiveRecordError, 'active_record/base' - autoload :ConnectionNotEstablished, 'active_record/base' + autoload :ActiveRecordError, 'active_record/errors' + autoload :ConnectionNotEstablished, 'active_record/errors' autoload :Aggregations autoload :AssociationPreload @@ -106,12 +105,16 @@ module ActiveRecord eager_autoload do autoload :AbstractAdapter + autoload :ConnectionManagement, "active_record/connection_adapters/abstract/connection_pool" end end autoload :TestCase autoload :TestFixtures, 'active_record/fixtures' + + base_hook do + Arel::Table.engine = Arel::Sql::Engine.new(self) + end end -Arel::Table.engine = Arel::Sql::Engine.new(ActiveRecord::Base) -I18n.load_path << File.dirname(__FILE__) + '/active_record/locale/en.yml' +I18n.load_path << File.dirname(__FILE__) + '/active_record/locale/en.yml'
\ No newline at end of file diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb index a5b06460fe..6725d4e88b 100644 --- a/activerecord/lib/active_record/association_preload.rb +++ b/activerecord/lib/active_record/association_preload.rb @@ -126,7 +126,7 @@ module ActiveRecord parent_records.each do |parent_record| association_proxy = parent_record.send(reflection_name) association_proxy.loaded - association_proxy.target.push *Array.wrap(associated_record) + association_proxy.target.push(*Array.wrap(associated_record)) association_proxy.__send__(:set_inverse_instance, associated_record, parent_record) end diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 57785b4c93..b69577f8dd 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1537,58 +1537,42 @@ module ActiveRecord # has_one associated objects, according to the defined :dependent rule. def configure_dependency_for_has_one(reflection) if reflection.options.include?(:dependent) - case reflection.options[:dependent] - when :destroy - method_name = "has_one_dependent_destroy_for_#{reflection.name}".to_sym - define_method(method_name) do - association = send(reflection.name) - association.destroy unless association.nil? - end - before_destroy method_name - when :delete - method_name = "has_one_dependent_delete_for_#{reflection.name}".to_sym + name = reflection.options[:dependent] + method_name = :"has_one_dependent_#{name}_for_#{reflection.name}" + + case name + when :destroy, :delete define_method(method_name) do - # Retrieve the associated object and delete it. The retrieval - # is necessary because there may be multiple associated objects - # with foreign keys pointing to this object, and we only want - # to delete the correct one, not all of them. association = send(reflection.name) - association.delete unless association.nil? + association.send(name) if association end - before_destroy method_name when :nullify - method_name = "has_one_dependent_nullify_for_#{reflection.name}".to_sym define_method(method_name) do association = send(reflection.name) - association.update_attribute(reflection.primary_key_name, nil) unless association.nil? + association.update_attribute(reflection.primary_key_name, nil) if association end - before_destroy method_name else raise ArgumentError, "The :dependent option expects either :destroy, :delete or :nullify (#{reflection.options[:dependent].inspect})" end + + before_destroy method_name end end def configure_dependency_for_belongs_to(reflection) if reflection.options.include?(:dependent) - case reflection.options[:dependent] - when :destroy - method_name = "belongs_to_dependent_destroy_for_#{reflection.name}".to_sym - define_method(method_name) do - association = send(reflection.name) - association.destroy unless association.nil? - end - after_destroy method_name - when :delete - method_name = "belongs_to_dependent_delete_for_#{reflection.name}".to_sym - define_method(method_name) do - association = send(reflection.name) - association.delete unless association.nil? - end - after_destroy method_name - else - raise ArgumentError, "The :dependent option expects either :destroy or :delete (#{reflection.options[:dependent].inspect})" + name = reflection.options[:dependent] + + unless [:destroy, :delete].include?(name) + raise ArgumentError, "The :dependent option expects either :destroy or :delete (#{reflection.options[:dependent].inspect})" + end + + method_name = :"belongs_to_dependent_#{name}_for_#{reflection.name}" + define_method(method_name) do + association = send(reflection.name) + association.send(name) if association end + after_destroy method_name end end diff --git a/activerecord/lib/active_record/attribute_methods/primary_key.rb b/activerecord/lib/active_record/attribute_methods/primary_key.rb index 365fdeb55a..095814b635 100644 --- a/activerecord/lib/active_record/attribute_methods/primary_key.rb +++ b/activerecord/lib/active_record/attribute_methods/primary_key.rb @@ -39,6 +39,22 @@ module ActiveRecord end alias :primary_key= :set_primary_key end + + module InstanceMethods + + # Returns this record's primary key value wrapped in an Array + # or nil if the record is a new_record? + # This is done to comply with the AMo interface that expects + # every AMo compliant object to respond_to?(:to_key) and return + # an Enumerable object from that call, or nil if new_record? + # This method also takes custom primary keys specified via + # the +set_primary_key+ into account. + def to_key + new_record? ? nil : [ self.primary_key ] + end + + end + end end end diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 318bfbe826..52587ef251 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -11,174 +11,12 @@ require 'active_support/core_ext/hash/deep_merge' require 'active_support/core_ext/hash/indifferent_access' require 'active_support/core_ext/hash/slice' require 'active_support/core_ext/string/behavior' -require 'active_support/core_ext/object/metaclass' +require 'active_support/core_ext/object/singleton_class' require 'active_support/core_ext/module/delegation' +require 'arel' +require 'active_record/errors' module ActiveRecord #:nodoc: - # Generic Active Record exception class. - class ActiveRecordError < StandardError - end - - # Raised when the single-table inheritance mechanism fails to locate the subclass - # (for example due to improper usage of column that +inheritance_column+ points to). - class SubclassNotFound < ActiveRecordError #:nodoc: - end - - # Raised when an object assigned to an association has an incorrect type. - # - # class Ticket < ActiveRecord::Base - # has_many :patches - # end - # - # class Patch < ActiveRecord::Base - # belongs_to :ticket - # end - # - # # Comments are not patches, this assignment raises AssociationTypeMismatch. - # @ticket.patches << Comment.new(:content => "Please attach tests to your patch.") - class AssociationTypeMismatch < ActiveRecordError - end - - # Raised when unserialized object's type mismatches one specified for serializable field. - class SerializationTypeMismatch < ActiveRecordError - end - - # Raised when adapter not specified on connection (or configuration file <tt>config/database.yml</tt> misses adapter field). - class AdapterNotSpecified < ActiveRecordError - end - - # Raised when Active Record cannot find database adapter specified in <tt>config/database.yml</tt> or programmatically. - class AdapterNotFound < ActiveRecordError - end - - # Raised when connection to the database could not been established (for example when <tt>connection=</tt> is given a nil object). - class ConnectionNotEstablished < ActiveRecordError - end - - # Raised when Active Record cannot find record by given id or set of ids. - class RecordNotFound < ActiveRecordError - end - - # Raised by ActiveRecord::Base.save! and ActiveRecord::Base.create! methods when record cannot be - # saved because record is invalid. - class RecordNotSaved < ActiveRecordError - end - - # Raised when SQL statement cannot be executed by the database (for example, it's often the case for MySQL when Ruby driver used is too old). - class StatementInvalid < ActiveRecordError - end - - # Raised when SQL statement is invalid and the application gets a blank result. - class ThrowResult < ActiveRecordError - end - - # Parent class for all specific exceptions which wrap database driver exceptions - # provides access to the original exception also. - class WrappedDatabaseException < StatementInvalid - attr_reader :original_exception - - def initialize(message, original_exception) - super(message) - @original_exception = original_exception - end - end - - # Raised when a record cannot be inserted because it would violate a uniqueness constraint. - class RecordNotUnique < WrappedDatabaseException - end - - # Raised when a record cannot be inserted or updated because it references a non-existent record. - class InvalidForeignKey < WrappedDatabaseException - end - - # Raised when number of bind variables in statement given to <tt>:condition</tt> key (for example, when using +find+ method) - # does not match number of expected variables. - # - # For example, in - # - # Location.find :all, :conditions => ["lat = ? AND lng = ?", 53.7362] - # - # two placeholders are given but only one variable to fill them. - class PreparedStatementInvalid < ActiveRecordError - end - - # Raised on attempt to save stale record. Record is stale when it's being saved in another query after - # instantiation, for example, when two users edit the same wiki page and one starts editing and saves - # the page before the other. - # - # Read more about optimistic locking in ActiveRecord::Locking module RDoc. - class StaleObjectError < ActiveRecordError - end - - # Raised when association is being configured improperly or - # user tries to use offset and limit together with has_many or has_and_belongs_to_many associations. - class ConfigurationError < ActiveRecordError - end - - # Raised on attempt to update record that is instantiated as read only. - class ReadOnlyRecord < ActiveRecordError - end - - # <tt>ActiveRecord::Transactions::ClassMethods.transaction</tt> uses this exception - # to distinguish a deliberate rollback from other exceptional situations. - # Normally, raising an exception will cause the +transaction+ method to rollback - # the database transaction *and* pass on the exception. But if you raise an - # <tt>ActiveRecord::Rollback</tt> exception, then the database transaction will be rolled back, - # without passing on the exception. - # - # For example, you could do this in your controller to rollback a transaction: - # - # class BooksController < ActionController::Base - # def create - # Book.transaction do - # book = Book.new(params[:book]) - # book.save! - # if today_is_friday? - # # The system must fail on Friday so that our support department - # # won't be out of job. We silently rollback this transaction - # # without telling the user. - # raise ActiveRecord::Rollback, "Call tech support!" - # end - # end - # # ActiveRecord::Rollback is the only exception that won't be passed on - # # by ActiveRecord::Base.transaction, so this line will still be reached - # # even on Friday. - # redirect_to root_url - # end - # end - class Rollback < ActiveRecordError - end - - # Raised when attribute has a name reserved by Active Record (when attribute has name of one of Active Record instance methods). - class DangerousAttributeError < ActiveRecordError - end - - # Raised when unknown attributes are supplied via mass assignment. - class UnknownAttributeError < NoMethodError - end - - # Raised when an error occurred while doing a mass assignment to an attribute through the - # <tt>attributes=</tt> method. The exception has an +attribute+ property that is the name of the - # offending attribute. - class AttributeAssignmentError < ActiveRecordError - attr_reader :exception, :attribute - def initialize(message, exception, attribute) - @exception = exception - @attribute = attribute - @message = message - end - end - - # Raised when there are multiple errors while doing a mass assignment through the +attributes+ - # method. The exception has an +errors+ property that contains an array of AttributeAssignmentError - # objects, each corresponding to the error while assigning to an attribute. - class MultiparameterAssignmentErrors < ActiveRecordError - attr_reader :errors - def initialize(errors) - @errors = errors - end - end - # Active Record objects don't specify their attributes directly, but rather infer them from the table definition with # which they're linked. Adding, removing, and changing attributes and their type is done directly in the database. Any change # is instantly reflected in the Active Record objects. The mapping that binds a given Active Record class to a certain @@ -551,8 +389,8 @@ module ActiveRecord #:nodoc: class << self # Class methods def colorize_logging(*args) ActiveSupport::Deprecation.warn "ActiveRecord::Base.colorize_logging and " << - "config.active_record.colorize_logging are deprecated. Please use " << - "Rails::Subscriber.colorize_logging or config.colorize_logging instead", caller + "config.active_record.colorize_logging are deprecated. Please use " << + "Rails::LogSubscriber.colorize_logging or config.colorize_logging instead", caller end alias :colorize_logging= :colorize_logging @@ -1669,12 +1507,12 @@ module ActiveRecord #:nodoc: @attributes_cache = {} @new_record = true ensure_proper_type - self.attributes = attributes unless attributes.nil? if scope = self.class.send(:current_scoped_methods) create_with = scope.scope_for_create create_with.each { |att,value| self.send("#{att}=", value) } if create_with end + self.attributes = attributes unless attributes.nil? result = yield self if block_given? _run_initialize_callbacks @@ -1767,6 +1605,11 @@ module ActiveRecord #:nodoc: @destroyed || false end + # Returns if the record is persisted, i.e. it's not a new record and it was not destroyed. + def persisted? + !(new_record? || destroyed?) + end + # :call-seq: # save(options) # @@ -1816,7 +1659,7 @@ module ActiveRecord #:nodoc: # callbacks, Observer methods, or any <tt>:dependent</tt> association # options, use <tt>#destroy</tt>. def delete - self.class.delete(id) unless new_record? + self.class.delete(id) if persisted? @destroyed = true freeze end @@ -1824,7 +1667,7 @@ module ActiveRecord #:nodoc: # Deletes the record in the database and freezes this instance to reflect that no changes should # be made (since they can't be persisted). def destroy - unless new_record? + if persisted? self.class.unscoped.where(self.class.arel_table[self.class.primary_key].eq(id)).delete_all end @@ -1844,6 +1687,7 @@ module ActiveRecord #:nodoc: became.instance_variable_set("@attributes", @attributes) became.instance_variable_set("@attributes_cache", @attributes_cache) became.instance_variable_set("@new_record", new_record?) + became.instance_variable_set("@destroyed", destroyed?) became end @@ -1926,7 +1770,7 @@ module ActiveRecord #:nodoc: def reload(options = nil) clear_aggregation_cache clear_association_cache - @attributes.update(self.class.find(self.id, options).instance_variable_get('@attributes')) + @attributes.update(self.class.send(:with_exclusive_scope) { self.class.find(self.id, options) }.instance_variable_get('@attributes')) @attributes_cache = {} self end @@ -1995,10 +1839,9 @@ module ActiveRecord #:nodoc: # Returns a hash of all the attributes with their names as keys and the values of the attributes as values. def attributes - self.attribute_names.inject({}) do |attrs, name| - attrs[name] = read_attribute(name) - attrs - end + attrs = {} + attribute_names.each { |name| attrs[name] = read_attribute(name) } + attrs end # Returns an <tt>#inspect</tt>-like string for the value of the @@ -2042,8 +1885,7 @@ module ActiveRecord #:nodoc: def ==(comparison_object) comparison_object.equal?(self) || (comparison_object.instance_of?(self.class) && - comparison_object.id == id && - !comparison_object.new_record?) + comparison_object.id == id && !comparison_object.new_record?) end # Delegates to == @@ -2343,7 +2185,7 @@ module ActiveRecord #:nodoc: # Returns a comma-separated pair list, like "key1 = val1, key2 = val2". def comma_pair_list(hash) - hash.inject([]) { |list, pair| list << "#{pair.first} = #{pair.last}" }.join(", ") + hash.map { |k,v| "#{k} = #{v}" }.join(", ") end def quote_columns(quoter, hash) @@ -2397,8 +2239,10 @@ module ActiveRecord #:nodoc: include Aggregations, Transactions, Reflection, Serialization + NilClass.add_whiner(self) if NilClass.respond_to?(:add_whiner) end end # TODO: Remove this and make it work with LAZY flag require 'active_record/connection_adapters/abstract_adapter' +ActiveRecord.run_base_hooks(ActiveRecord::Base)
\ No newline at end of file diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb index 027d736484..abb695264e 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb @@ -113,7 +113,7 @@ module ActiveRecord def transaction(options = {}) options.assert_valid_keys :requires_new, :joinable - last_transaction_joinable = @transaction_joinable + last_transaction_joinable = defined?(@transaction_joinable) ? @transaction_joinable : nil if options.has_key?(:joinable) @transaction_joinable = options[:joinable] else diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb index 520f3c8c0c..64faaef4a0 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -319,16 +319,19 @@ module ActiveRecord def method_missing(symbol, *args) if symbol.to_s == 'xml' xml_column_fallback(args) + else + super end end def xml_column_fallback(*args) case @base.adapter_name.downcase - when 'sqlite', 'mysql' - options = args.extract_options! - column(args[0], :text, options) - end + when 'sqlite', 'mysql' + options = args.extract_options! + column(args[0], :text, options) end + end + # Appends a primary key definition to the table definition. # Can be called multiple times, but this is probably not a good idea. def primary_key(name) diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index 7e80347f75..6ffffc8654 100755 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -37,6 +37,7 @@ module ActiveRecord @@row_even = true def initialize(connection, logger = nil) #:nodoc: + @active = nil @connection, @logger = connection, logger @runtime = 0 @query_cache_enabled = false diff --git a/activerecord/lib/active_record/errors.rb b/activerecord/lib/active_record/errors.rb new file mode 100644 index 0000000000..cf5ddca2ba --- /dev/null +++ b/activerecord/lib/active_record/errors.rb @@ -0,0 +1,165 @@ +module ActiveRecord + # Generic Active Record exception class. + class ActiveRecordError < StandardError + end + + # Raised when the single-table inheritance mechanism fails to locate the subclass + # (for example due to improper usage of column that +inheritance_column+ points to). + class SubclassNotFound < ActiveRecordError #:nodoc: + end + + # Raised when an object assigned to an association has an incorrect type. + # + # class Ticket < ActiveRecord::Base + # has_many :patches + # end + # + # class Patch < ActiveRecord::Base + # belongs_to :ticket + # end + # + # # Comments are not patches, this assignment raises AssociationTypeMismatch. + # @ticket.patches << Comment.new(:content => "Please attach tests to your patch.") + class AssociationTypeMismatch < ActiveRecordError + end + + # Raised when unserialized object's type mismatches one specified for serializable field. + class SerializationTypeMismatch < ActiveRecordError + end + + # Raised when adapter not specified on connection (or configuration file <tt>config/database.yml</tt> misses adapter field). + class AdapterNotSpecified < ActiveRecordError + end + + # Raised when Active Record cannot find database adapter specified in <tt>config/database.yml</tt> or programmatically. + class AdapterNotFound < ActiveRecordError + end + + # Raised when connection to the database could not been established (for example when <tt>connection=</tt> is given a nil object). + class ConnectionNotEstablished < ActiveRecordError + end + + # Raised when Active Record cannot find record by given id or set of ids. + class RecordNotFound < ActiveRecordError + end + + # Raised by ActiveRecord::Base.save! and ActiveRecord::Base.create! methods when record cannot be + # saved because record is invalid. + class RecordNotSaved < ActiveRecordError + end + + # Raised when SQL statement cannot be executed by the database (for example, it's often the case for MySQL when Ruby driver used is too old). + class StatementInvalid < ActiveRecordError + end + + # Raised when SQL statement is invalid and the application gets a blank result. + class ThrowResult < ActiveRecordError + end + + # Parent class for all specific exceptions which wrap database driver exceptions + # provides access to the original exception also. + class WrappedDatabaseException < StatementInvalid + attr_reader :original_exception + + def initialize(message, original_exception) + super(message) + @original_exception = original_exception + end + end + + # Raised when a record cannot be inserted because it would violate a uniqueness constraint. + class RecordNotUnique < WrappedDatabaseException + end + + # Raised when a record cannot be inserted or updated because it references a non-existent record. + class InvalidForeignKey < WrappedDatabaseException + end + + # Raised when number of bind variables in statement given to <tt>:condition</tt> key (for example, when using +find+ method) + # does not match number of expected variables. + # + # For example, in + # + # Location.find :all, :conditions => ["lat = ? AND lng = ?", 53.7362] + # + # two placeholders are given but only one variable to fill them. + class PreparedStatementInvalid < ActiveRecordError + end + + # Raised on attempt to save stale record. Record is stale when it's being saved in another query after + # instantiation, for example, when two users edit the same wiki page and one starts editing and saves + # the page before the other. + # + # Read more about optimistic locking in ActiveRecord::Locking module RDoc. + class StaleObjectError < ActiveRecordError + end + + # Raised when association is being configured improperly or + # user tries to use offset and limit together with has_many or has_and_belongs_to_many associations. + class ConfigurationError < ActiveRecordError + end + + # Raised on attempt to update record that is instantiated as read only. + class ReadOnlyRecord < ActiveRecordError + end + + # ActiveRecord::Transactions::ClassMethods.transaction uses this exception + # to distinguish a deliberate rollback from other exceptional situations. + # Normally, raising an exception will cause the +transaction+ method to rollback + # the database transaction *and* pass on the exception. But if you raise an + # ActiveRecord::Rollback exception, then the database transaction will be rolled back, + # without passing on the exception. + # + # For example, you could do this in your controller to rollback a transaction: + # + # class BooksController < ActionController::Base + # def create + # Book.transaction do + # book = Book.new(params[:book]) + # book.save! + # if today_is_friday? + # # The system must fail on Friday so that our support department + # # won't be out of job. We silently rollback this transaction + # # without telling the user. + # raise ActiveRecord::Rollback, "Call tech support!" + # end + # end + # # ActiveRecord::Rollback is the only exception that won't be passed on + # # by ActiveRecord::Base.transaction, so this line will still be reached + # # even on Friday. + # redirect_to root_url + # end + # end + class Rollback < ActiveRecordError + end + + # Raised when attribute has a name reserved by Active Record (when attribute has name of one of Active Record instance methods). + class DangerousAttributeError < ActiveRecordError + end + + # Raised when unknown attributes are supplied via mass assignment. + class UnknownAttributeError < NoMethodError + end + + # Raised when an error occurred while doing a mass assignment to an attribute through the + # <tt>attributes=</tt> method. The exception has an +attribute+ property that is the name of the + # offending attribute. + class AttributeAssignmentError < ActiveRecordError + attr_reader :exception, :attribute + def initialize(message, exception, attribute) + @exception = exception + @attribute = attribute + @message = message + end + end + + # Raised when there are multiple errors while doing a mass assignment through the +attributes+ + # method. The exception has an +errors+ property that contains an array of AttributeAssignmentError + # objects, each corresponding to the error while assigning to an attribute. + class MultiparameterAssignmentErrors < ActiveRecordError + attr_reader :errors + def initialize(errors) + @errors = errors + end + end +end
\ No newline at end of file diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 068d2a25b2..fd5ffc6d77 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -1,4 +1,4 @@ -require 'active_support/core_ext/object/metaclass' +require 'active_support/core_ext/object/singleton_class' module ActiveRecord # Exception that can be raised to stop migrations from going backwards. @@ -303,7 +303,7 @@ module ActiveRecord case sym when :up, :down - metaclass.send(:alias_method_chain, sym, "benchmarks") + singleton_class.send(:alias_method_chain, sym, "benchmarks") end ensure @ignore_new_methods = false diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index ff6c041ef4..394e1587e1 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -1,6 +1,6 @@ require 'active_support/core_ext/array' require 'active_support/core_ext/hash/except' -require 'active_support/core_ext/object/metaclass' +require 'active_support/core_ext/object/singleton_class' module ActiveRecord module NamedScope @@ -26,7 +26,7 @@ module ActiveRecord if options.present? Scope.init(self, options, &block) else - current_scoped_methods ? unscoped.merge(current_scoped_methods) : unscoped.spawn + current_scoped_methods ? unscoped.merge(current_scoped_methods) : unscoped.clone end end @@ -112,7 +112,7 @@ module ActiveRecord options.call(*args) end, &block) end - metaclass.instance_eval do + singleton_class.instance_eval do define_method name do |*args| scopes[name].call(self, *args) end diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index e70b0d1bfb..60addd46c6 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -20,35 +20,43 @@ module ActiveRecord end # TODO If we require the wrong file, the error never comes up. - require "active_record/railties/subscriber" - subscriber ActiveRecord::Railties::Subscriber.new + require "active_record/railties/log_subscriber" + log_subscriber ActiveRecord::Railties::LogSubscriber.new initializer "active_record.initialize_timezone" do - ActiveRecord::Base.time_zone_aware_attributes = true - ActiveRecord::Base.default_timezone = :utc + ActiveRecord.base_hook do + self.time_zone_aware_attributes = true + self.default_timezone = :utc + end end initializer "active_record.logger" do - ActiveRecord::Base.logger ||= ::Rails.logger + ActiveRecord.base_hook { self.logger ||= ::Rails.logger } end initializer "active_record.set_configs" do |app| - app.config.active_record.each do |k,v| - ActiveRecord::Base.send "#{k}=", v + ActiveRecord.base_hook do + app.config.active_record.each do |k,v| + send "#{k}=", v + end end end # This sets the database configuration from Configuration#database_configuration # and then establishes the connection. initializer "active_record.initialize_database" do |app| - ActiveRecord::Base.configurations = app.config.database_configuration - ActiveRecord::Base.establish_connection + ActiveRecord.base_hook do + self.configurations = app.config.database_configuration + establish_connection + end end # Expose database runtime to controller for logging. initializer "active_record.log_runtime" do |app| require "active_record/railties/controller_runtime" - ActionController::Base.send :include, ActiveRecord::Railties::ControllerRuntime + ActionController.base_hook do + include ActiveRecord::Railties::ControllerRuntime + end end # Setup database middleware after initializers have run @@ -64,18 +72,22 @@ module ActiveRecord end initializer "active_record.load_observers" do - ActiveRecord::Base.instantiate_observers + ActiveRecord.base_hook { instantiate_observers } - ActionDispatch::Callbacks.to_prepare(:activerecord_instantiate_observers) do - ActiveRecord::Base.instantiate_observers + ActiveRecord.base_hook do + ActionDispatch::Callbacks.to_prepare(:activerecord_instantiate_observers) do + ActiveRecord::Base.instantiate_observers + end end end initializer "active_record.set_dispatch_hooks", :before => :set_clear_dependencies_hook do |app| - unless app.config.cache_classes - ActionDispatch::Callbacks.after do - ActiveRecord::Base.reset_subclasses - ActiveRecord::Base.clear_reloadable_connections! + ActiveRecord.base_hook do + unless app.config.cache_classes + ActionDispatch::Callbacks.after do + ActiveRecord::Base.reset_subclasses + ActiveRecord::Base.clear_reloadable_connections! + end end end end diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index ed7d2a045e..fa6caa4910 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -109,7 +109,7 @@ namespace :db do # Only connect to local databases local_database?(config) { drop_database(config) } rescue Exception => e - puts "Couldn't drop #{config['database']} : #{e.inspect}" + $stderr.puts "Couldn't drop #{config['database']} : #{e.inspect}" end end end @@ -121,7 +121,7 @@ namespace :db do begin drop_database(config) rescue Exception => e - puts "Couldn't drop #{config['database']} : #{e.inspect}" + $stderr.puts "Couldn't drop #{config['database']} : #{e.inspect}" end end @@ -129,7 +129,7 @@ namespace :db do if %w( 127.0.0.1 localhost ).include?(config['host']) || config['host'].blank? yield else - puts "This task only modifies local databases. #{config['database']} is on a remote host." + $stderr.puts "This task only modifies local databases. #{config['database']} is on a remote host." end end @@ -204,7 +204,7 @@ namespace :db do ActiveRecord::Base.establish_connection(config) puts ActiveRecord::Base.connection.encoding else - puts 'sorry, your database adapter is not supported yet, feel free to submit a patch' + $stderr.puts 'sorry, your database adapter is not supported yet, feel free to submit a patch' end end @@ -216,7 +216,7 @@ namespace :db do ActiveRecord::Base.establish_connection(config) puts ActiveRecord::Base.connection.collation else - puts 'sorry, your database adapter is not supported yet, feel free to submit a patch' + $stderr.puts 'sorry, your database adapter is not supported yet, feel free to submit a patch' end end diff --git a/activerecord/lib/active_record/railties/subscriber.rb b/activerecord/lib/active_record/railties/log_subscriber.rb index fd873dbff8..48b25032ce 100644 --- a/activerecord/lib/active_record/railties/subscriber.rb +++ b/activerecord/lib/active_record/railties/log_subscriber.rb @@ -1,6 +1,6 @@ module ActiveRecord module Railties - class Subscriber < Rails::Subscriber + class LogSubscriber < Rails::LogSubscriber def sql(event) name = '%s (%.1fms)' % [event.payload[:name], event.duration] sql = event.payload[:sql].squeeze(' ') diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 7bc3d3bf33..aca4629dd8 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -21,6 +21,10 @@ module ActiveRecord with_create_scope { @klass.new(*args, &block) } end + def initialize_copy(other) + reset + end + alias build new def create(*args, &block) diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index 65e5c0495c..7e83eccbb5 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -20,7 +20,7 @@ module ActiveRecord table = Arel::Table.new(table_name, :engine => @engine) end - attribute = table[column] || Arel::Attribute.new(table, column.to_sym) + attribute = table[column] case value when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::NamedScope::Scope diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 0266700f66..e00d9cdf27 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -8,7 +8,7 @@ module ActiveRecord class_eval <<-CEVAL def #{query_method}(*args) - new_relation = spawn + new_relation = clone value = Array.wrap(args.flatten).reject {|x| x.blank? } new_relation.#{query_method}_values += value if value.present? new_relation @@ -19,7 +19,7 @@ module ActiveRecord [:where, :having].each do |query_method| class_eval <<-CEVAL def #{query_method}(*args) - new_relation = spawn + new_relation = clone value = build_where(*args) new_relation.#{query_method}_values += [*value] if value.present? new_relation @@ -32,7 +32,7 @@ module ActiveRecord class_eval <<-CEVAL def #{query_method}(value = true) - new_relation = spawn + new_relation = clone new_relation.#{query_method}_value = value new_relation end @@ -41,12 +41,12 @@ module ActiveRecord end def lock(locks = true) - relation = spawn + relation = clone case locks when String, TrueClass, NilClass - spawn.tap {|new_relation| new_relation.lock_value = locks || true } + clone.tap {|new_relation| new_relation.lock_value = locks || true } else - spawn.tap {|new_relation| new_relation.lock_value = false } + clone.tap {|new_relation| new_relation.lock_value = false } end end @@ -174,7 +174,7 @@ module ActiveRecord arel = arel.lock when String arel = arel.lock(@lock_value) - end + end if defined?(@lock_value) arel end @@ -184,16 +184,16 @@ module ActiveRecord builder = PredicateBuilder.new(table.engine) - conditions = if [String, Array].include?(args.first.class) - @klass.send(:sanitize_sql, args.size > 1 ? args : args.first) - elsif args.first.is_a?(Hash) - attributes = @klass.send(:expand_hash_conditions_for_aggregates, args.first) + opts = args.first + case opts + when String, Array + @klass.send(:sanitize_sql, args.size > 1 ? args : opts) + when Hash + attributes = @klass.send(:expand_hash_conditions_for_aggregates, opts) builder.build_from_hash(attributes, table) else - args.first + opts end - - conditions end private diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb index cccf413e67..a18380f01c 100644 --- a/activerecord/lib/active_record/relation/spawn_methods.rb +++ b/activerecord/lib/active_record/relation/spawn_methods.rb @@ -1,11 +1,7 @@ module ActiveRecord module SpawnMethods - def spawn - clone.reset - end - def merge(r) - merged_relation = spawn + merged_relation = clone return merged_relation unless r merged_relation = merged_relation.eager_load(r.eager_load_values).preload(r.preload_values).includes(r.includes_values) @@ -83,7 +79,7 @@ module ActiveRecord :order, :select, :readonly, :group, :having, :from, :lock ] def apply_finder_options(options) - relation = spawn + relation = clone return relation unless options options.assert_valid_keys(VALID_FIND_OPTIONS) diff --git a/activerecord/lib/active_record/version.rb b/activerecord/lib/active_record/version.rb index 286ecd0289..eaf5dc6545 100644 --- a/activerecord/lib/active_record/version.rb +++ b/activerecord/lib/active_record/version.rb @@ -2,8 +2,9 @@ module ActiveRecord module VERSION #:nodoc: MAJOR = 3 MINOR = 0 - TINY = "0.beta1" + TINY = 0 + BUILD = "beta1" - STRING = [MAJOR, MINOR, TINY].join('.') + STRING = [MAJOR, MINOR, TINY, BUILD].join('.') end end |