aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/base.rb
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record/base.rb')
-rw-r--r--[-rwxr-xr-x]activerecord/lib/active_record/base.rb119
1 files changed, 63 insertions, 56 deletions
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 3b6ffa46f2..c8795e4496 100755..100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -2,6 +2,7 @@ require 'yaml'
require 'set'
require 'active_support/benchmarkable'
require 'active_support/dependencies'
+require 'active_support/descendants_tracker'
require 'active_support/time'
require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/class/attribute_accessors'
@@ -14,13 +15,17 @@ require 'active_support/core_ext/hash/slice'
require 'active_support/core_ext/string/behavior'
require 'active_support/core_ext/kernel/singleton_class'
require 'active_support/core_ext/module/delegation'
+require 'active_support/core_ext/module/deprecation'
require 'active_support/core_ext/module/introspection'
require 'active_support/core_ext/object/duplicable'
require 'active_support/core_ext/object/blank'
require 'arel'
require 'active_record/errors'
+require 'active_record/log_subscriber'
module ActiveRecord #:nodoc:
+ # = Active Record
+ #
# 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
@@ -274,27 +279,17 @@ module ActiveRecord #:nodoc:
# on to any new database connections made and which can be retrieved on both a class and instance level by calling +logger+.
cattr_accessor :logger, :instance_writer => false
- def self.inherited(child) #:nodoc:
- @@subclasses[self] ||= []
- @@subclasses[self] << child
- super
- end
+ class << self
+ def reset_subclasses #:nodoc:
+ ActiveSupport::Deprecation.warn 'ActiveRecord::Base.reset_subclasses no longer does anything in Rails 3. It will be removed in the final release; please update your apps and plugins.', caller
+ end
- def self.reset_subclasses #:nodoc:
- nonreloadables = []
- subclasses.each do |klass|
- unless ActiveSupport::Dependencies.autoloaded? klass
- nonreloadables << klass
- next
- end
- klass.instance_variables.each { |var| klass.send(:remove_instance_variable, var) }
- klass.instance_methods(false).each { |m| klass.send :undef_method, m }
+ def subclasses
+ descendants
end
- @@subclasses = {}
- nonreloadables.each { |klass| (@@subclasses[klass.superclass] ||= []) << klass }
- end
- @@subclasses = {}
+ deprecate :subclasses => :descendants
+ end
##
# :singleton-method:
@@ -403,7 +398,7 @@ module ActiveRecord #:nodoc:
delegate :find, :first, :last, :all, :destroy, :destroy_all, :exists?, :delete, :delete_all, :update, :update_all, :to => :scoped
delegate :find_each, :find_in_batches, :to => :scoped
- delegate :select, :group, :order, :limit, :joins, :where, :preload, :eager_load, :includes, :from, :lock, :readonly, :having, :to => :scoped
+ delegate :select, :group, :order, :limit, :joins, :where, :preload, :eager_load, :includes, :from, :lock, :readonly, :having, :create_with, :to => :scoped
delegate :count, :average, :minimum, :maximum, :sum, :calculate, :to => :scoped
# Executes a custom SQL query against your database and returns all the results. The results will
@@ -725,14 +720,6 @@ module ActiveRecord #:nodoc:
end
alias :sequence_name= :set_sequence_name
- # Turns the +table_name+ back into a class name following the reverse rules of +table_name+.
- def class_name(table_name = table_name) # :nodoc:
- # remove any prefix and/or suffix from the table name
- class_name = table_name[table_name_prefix.length..-(table_name_suffix.length + 1)].camelize
- class_name = class_name.singularize if pluralize_table_names
- class_name
- end
-
# Indicates whether the table associated with this class exists
def table_exists?
connection.table_exists?(table_name)
@@ -806,11 +793,11 @@ module ActiveRecord #:nodoc:
def reset_column_information
undefine_attribute_methods
@column_names = @columns = @columns_hash = @content_columns = @dynamic_methods_hash = @inheritance_column = nil
- @arel_engine = @unscoped = @arel_table = nil
+ @arel_engine = @relation = @arel_table = nil
end
def reset_column_information_and_inheritable_attributes_for_all_subclasses#:nodoc:
- subclasses.each { |klass| klass.reset_inheritable_attributes; klass.reset_column_information }
+ descendants.each { |klass| klass.reset_inheritable_attributes; klass.reset_column_information }
end
def attribute_method?(attribute)
@@ -909,9 +896,9 @@ module ActiveRecord #:nodoc:
store_full_sti_class ? name : name.demodulize
end
- def unscoped
- @unscoped ||= Relation.new(self, arel_table)
- finder_needs_type_condition? ? @unscoped.where(type_condition) : @unscoped
+ def relation
+ @relation ||= Relation.new(self, arel_table)
+ finder_needs_type_condition? ? @relation.where(type_condition) : @relation
end
def arel_table
@@ -928,6 +915,31 @@ module ActiveRecord #:nodoc:
end
end
+ # Returns a scope for this class without taking into account the default_scope.
+ #
+ # class Post < ActiveRecord::Base
+ # default_scope :published => true
+ # end
+ #
+ # Post.all # Fires "SELECT * FROM posts WHERE published = true"
+ # Post.unscoped.all # Fires "SELECT * FROM posts"
+ #
+ # This method also accepts a block meaning that all queries inside the block will
+ # not use the default_scope:
+ #
+ # Post.unscoped {
+ # limit(10) # Fires "SELECT * FROM posts LIMIT 10"
+ # }
+ #
+ def unscoped
+ block_given? ? relation.scoping { yield } : relation
+ end
+
+ def scoped_methods #:nodoc:
+ key = :"#{self}_scoped_methods"
+ Thread.current[key] = Thread.current[key].presence || self.default_scoping.dup
+ end
+
private
# Finder methods must instantiate through this method to work with the
# single-table inheritance model that makes it possible to create
@@ -935,8 +947,8 @@ module ActiveRecord #:nodoc:
def instantiate(record)
object = find_sti_class(record[inheritance_column]).allocate
- object.instance_variable_set(:'@attributes', record)
- object.instance_variable_set(:'@attributes_cache', {})
+ object.instance_variable_set(:@attributes, record)
+ object.instance_variable_set(:@attributes_cache, {})
object.instance_variable_set(:@new_record, false)
object.instance_variable_set(:@readonly, false)
object.instance_variable_set(:@destroyed, false)
@@ -975,7 +987,7 @@ module ActiveRecord #:nodoc:
def type_condition
sti_column = arel_table[inheritance_column]
condition = sti_column.eq(sti_name)
- subclasses.each{|subclass| condition = condition.or(sti_column.eq(subclass.sti_name)) }
+ descendants.each { |subclass| condition = condition.or(sti_column.eq(subclass.sti_name)) }
condition
end
@@ -1162,16 +1174,22 @@ module ActiveRecord #:nodoc:
# Works like with_scope, but discards any nested properties.
def with_exclusive_scope(method_scoping = {}, &block)
- with_scope(method_scoping, :overwrite, &block)
- end
+ if method_scoping.values.any? { |e| e.is_a?(ActiveRecord::Relation) }
+ raise ArgumentError, <<-MSG
+New finder API can not be used with_exclusive_scope. You can either call unscoped to get an anonymous scope not bound to the default_scope:
- # Returns a list of all subclasses of this class, meaning all descendants.
- def subclasses
- @@subclasses[self] ||= []
- @@subclasses[self] + @@subclasses[self].inject([]) {|list, subclass| list + subclass.subclasses }
- end
+ User.unscoped.where(:active => true)
- public :subclasses
+Or call unscoped with a block:
+
+ User.unscoped do
+ User.where(:active => true).all
+ end
+
+MSG
+ end
+ with_scope(method_scoping, :overwrite, &block)
+ end
# Sets the default options for the model. The format of the
# <tt>options</tt> argument is the same as in find.
@@ -1183,11 +1201,6 @@ module ActiveRecord #:nodoc:
self.default_scoping << construct_finder_arel(options, default_scoping.pop)
end
- def scoped_methods #:nodoc:
- key = :"#{self}_scoped_methods"
- Thread.current[key] = Thread.current[key].presence || self.default_scoping.dup
- end
-
def current_scoped_methods #:nodoc:
scoped_methods.last
end
@@ -1440,14 +1453,6 @@ module ActiveRecord #:nodoc:
# as it copies the object's attributes only, not its associations. The extent of a "deep" clone is
# application specific and is therefore left to the application to implement according to its need.
def initialize_copy(other)
- # Think the assertion which fails if the after_initialize callback goes at the end of the method is wrong. The
- # deleted clone method called new which therefore called the after_initialize callback. It then went on to copy
- # over the attributes. But if it's copying the attributes afterwards then it hasn't finished initializing right?
- # For example in the test suite the topic model's after_initialize method sets the author_email_address to
- # test@test.com. I would have thought this would mean that all cloned models would have an author email address
- # of test@test.com. However the test_clone test method seems to test that this is not the case. As a result the
- # after_initialize callback has to be run *before* the copying of the atrributes rather than afterwards in order
- # for all tests to pass. This makes no sense to me.
callback(:after_initialize) if respond_to_without_attributes?(:after_initialize)
cloned_attributes = other.clone_attributes(:read_attribute_before_type_cast)
cloned_attributes.delete(self.class.primary_key)
@@ -1460,6 +1465,7 @@ module ActiveRecord #:nodoc:
end
clear_aggregation_cache
+ clear_association_cache
@attributes_cache = {}
@new_record = true
ensure_proper_type
@@ -1900,6 +1906,7 @@ module ActiveRecord #:nodoc:
extend ActiveModel::Naming
extend QueryCache::ClassMethods
extend ActiveSupport::Benchmarkable
+ extend ActiveSupport::DescendantsTracker
include ActiveModel::Conversion
include Validations