Remove ActiveRecord::Model

In the end I think the pain of implementing this seamlessly was not
worth the gain provided.

The intention was that it would allow plain ruby objects that might not
live in your main application to be subclassed and have persistence
mixed in. But I've decided that the benefit of doing that is not worth
the amount of complexity that the implementation introduced.
This commit is contained in:
Jon Leighton 2012-10-26 15:51:02 +01:00
parent a27b5178d9
commit 9e4c41c903
40 changed files with 233 additions and 643 deletions

View File

@ -926,34 +926,6 @@
*Aaron Patterson*
* Added the `ActiveRecord::Model` module which can be included in a
class as an alternative to inheriting from `ActiveRecord::Base`:
class Post
include ActiveRecord::Model
end
Please note:
* Up until now it has been safe to assume that all AR models are
descendants of `ActiveRecord::Base`. This is no longer a safe
assumption, but it may transpire that there are areas of the
code which still make this assumption. So there may be
'teething difficulties' with this feature. (But please do try it
and report bugs.)
* Plugins & libraries etc that add methods to `ActiveRecord::Base`
will not be compatible with `ActiveRecord::Model`. Those libraries
should add to `ActiveRecord::Model` instead (which is included in
`Base`), or better still, avoid monkey-patching AR and instead
provide a module that users can include where they need it.
* To minimise the risk of conflicts with other code, it is
advisable to include `ActiveRecord::Model` early in your class
definition.
*Jon Leighton*
* PostgreSQL hstore records can be created.
*Aaron Patterson*

View File

@ -43,7 +43,6 @@ module ActiveRecord
autoload :Integration
autoload :Migration
autoload :Migrator, 'active_record/migration'
autoload :Model
autoload :ModelSchema
autoload :NestedAttributes
autoload :Observer

View File

@ -8,7 +8,7 @@ module ActiveRecord
attr_reader :aliases, :table_joins, :connection
# table_joins is an array of arel joins which might conflict with the aliases we assign here
def initialize(connection = ActiveRecord::Model.connection, table_joins = [])
def initialize(connection = Base.connection, table_joins = [])
@aliases = Hash.new { |h,k| h[k] = initial_count_for(k) }
@table_joins = table_joins
@connection = connection

View File

@ -59,7 +59,7 @@ module ActiveRecord
raise DangerousAttributeError, "#{method_name} is defined by ActiveRecord"
end
if [Base, Model].include?(active_record_super)
if superclass == Base
super
else
# If B < A and A defines its own attribute method, then we don't want to overwrite that.

View File

@ -2,11 +2,6 @@ require 'active_support/core_ext/module/attribute_accessors'
require 'active_support/deprecation'
module ActiveRecord
ActiveSupport.on_load(:active_record_config) do
mattr_accessor :partial_writes, instance_accessor: false
self.partial_writes = true
end
module AttributeMethods
module Dirty # :nodoc:
extend ActiveSupport::Concern
@ -18,7 +13,8 @@ module ActiveRecord
raise "You cannot include Dirty after Timestamp"
end
config_attribute :partial_writes
class_attribute :partial_writes, instance_writer: false
self.partial_writes = true
def self.partial_updates=(v); self.partial_writes = v; end
def self.partial_updates?; partial_writes?; end

View File

@ -1,8 +1,4 @@
module ActiveRecord
ActiveSupport.on_load(:active_record_config) do
mattr_accessor :attribute_types_cached_by_default, instance_accessor: false
end
module AttributeMethods
module Read
extend ActiveSupport::Concern
@ -10,7 +6,8 @@ module ActiveRecord
ATTRIBUTE_TYPES_CACHED_BY_DEFAULT = [:datetime, :timestamp, :time, :date]
included do
config_attribute :attribute_types_cached_by_default
class_attribute :attribute_types_cached_by_default, instance_writer: false
self.attribute_types_cached_by_default = ATTRIBUTE_TYPES_CACHED_BY_DEFAULT
end
module ClassMethods
@ -79,8 +76,6 @@ module ActiveRecord
end
end
ActiveRecord::Model.attribute_types_cached_by_default = ATTRIBUTE_TYPES_CACHED_BY_DEFAULT
# Returns the value of the attribute identified by <tt>attr_name</tt> after
# it has been typecast (for example, "2004-12-12" in a data column is cast
# to a date object, like Date.new(2004, 12, 12)).

View File

@ -1,13 +1,4 @@
module ActiveRecord
ActiveSupport.on_load(:active_record_config) do
mattr_accessor :time_zone_aware_attributes, instance_accessor: false
self.time_zone_aware_attributes = false
mattr_accessor :skip_time_zone_conversion_for_attributes, instance_accessor: false
self.skip_time_zone_conversion_for_attributes = []
end
module AttributeMethods
module TimeZoneConversion
class Type # :nodoc:
@ -28,8 +19,11 @@ module ActiveRecord
extend ActiveSupport::Concern
included do
config_attribute :time_zone_aware_attributes, global: true
config_attribute :skip_time_zone_conversion_for_attributes
mattr_accessor :time_zone_aware_attributes, instance_writer: false
self.time_zone_aware_attributes = false
class_attribute :skip_time_zone_conversion_for_attributes, instance_writer: false
self.skip_time_zone_conversion_for_attributes = []
end
module ClassMethods

View File

@ -321,8 +321,48 @@ module ActiveRecord #:nodoc:
# So it's possible to assign a logger to the class through <tt>Base.logger=</tt> which will then be used by all
# instances in the current object space.
class Base
include ActiveRecord::Model
end
end
extend ActiveModel::Observing::ClassMethods
extend ActiveModel::Naming
ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Model::DeprecationProxy.new)
extend ActiveSupport::Benchmarkable
extend ActiveSupport::DescendantsTracker
extend ConnectionHandling
extend QueryCache::ClassMethods
extend Querying
extend Translation
extend DynamicMatchers
extend Explain
extend ConnectionHandling
include Persistence
include ReadonlyAttributes
include ModelSchema
include Inheritance
include Scoping
include Sanitization
include AttributeAssignment
include ActiveModel::Conversion
include Integration
include Validations
include CounterCache
include Locking::Optimistic
include Locking::Pessimistic
include AttributeMethods
include Callbacks
include ActiveModel::Observing
include Timestamp
include Associations
include ActiveModel::SecurePassword
include AutosaveAssociation
include NestedAttributes
include Aggregations
include Transactions
include Reflection
include Serialization
include Store
include Core
end
ActiveSupport.run_load_hooks(:active_record, Base)
end

View File

@ -441,11 +441,11 @@ module ActiveRecord
end
def new_connection
ActiveRecord::Model.send(spec.adapter_method, spec.config)
Base.send(spec.adapter_method, spec.config)
end
def current_connection_id #:nodoc:
ActiveRecord::Model.connection_id ||= Thread.current.object_id
Base.connection_id ||= Thread.current.object_id
end
def checkout_new_connection
@ -567,10 +567,10 @@ module ActiveRecord
class_to_pool[klass] ||= begin
until pool = pool_for(klass)
klass = klass.superclass
break unless klass < ActiveRecord::Tag
break unless klass <= Base
end
class_to_pool[klass] = pool || pool_for(ActiveRecord::Model)
class_to_pool[klass] = pool
end
end

View File

@ -1,4 +1,3 @@
module ActiveRecord
module ConnectionHandling
# Establishes the connection to the database. Accepts a hash as input where

View File

@ -4,73 +4,6 @@ require 'thread'
module ActiveRecord
ActiveSupport.on_load(:active_record_config) do
##
# :singleton-method:
#
# Accepts a logger conforming to the interface of Log4r 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+.
mattr_accessor :logger, instance_accessor: false
##
# :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'
# }
# }
mattr_accessor :configurations, instance_accessor: false
self.configurations = {}
##
# :singleton-method:
# Determines whether to use Time.utc (using :utc) or Time.local (using :local) when pulling
# dates and times from the database. This is set to :utc by default.
mattr_accessor :default_timezone, instance_accessor: false
self.default_timezone = :utc
##
# :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.
mattr_accessor :schema_format, instance_accessor: false
self.schema_format = :ruby
##
# :singleton-method:
# Specify whether or not to use timestamps for migration versions
mattr_accessor :timestamped_migrations, instance_accessor: false
self.timestamped_migrations = true
mattr_accessor :connection_handler, instance_accessor: false
self.connection_handler = ConnectionAdapters::ConnectionHandler.new
mattr_accessor :dependent_restrict_raises, instance_accessor: false
self.dependent_restrict_raises = true
end
module Core
@ -79,12 +12,71 @@ module ActiveRecord
included do
##
# :singleton-method:
# The connection handler
config_attribute :connection_handler
#
# Accepts a logger conforming to the interface of Log4r 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+.
mattr_accessor :logger, instance_writer: false
%w(logger configurations default_timezone schema_format timestamped_migrations).each do |name|
config_attribute name, global: true
end
##
# :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'
# }
# }
mattr_accessor :configurations, instance_writer: false
self.configurations = {}
##
# :singleton-method:
# Determines whether to use Time.utc (using :utc) or Time.local (using :local) when pulling
# dates and times from the database. This is set to :utc by default.
mattr_accessor :default_timezone, instance_writer: false
self.default_timezone = :utc
##
# :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.
mattr_accessor :schema_format, instance_writer: false
self.schema_format = :ruby
##
# :singleton-method:
# Specify whether or not to use timestamps for migration versions
mattr_accessor :timestamped_migrations, instance_writer: false
self.timestamped_migrations = true
class_attribute :connection_handler, instance_writer: false
self.connection_handler = ConnectionAdapters::ConnectionHandler.new
mattr_accessor :dependent_restrict_raises, instance_writer: false
self.dependent_restrict_raises = true
end
module ClassMethods
@ -139,7 +131,13 @@ module ActiveRecord
# Returns the Arel engine.
def arel_engine
@arel_engine ||= connection_handler.retrieve_connection_pool(self) ? self : active_record_super.arel_engine
@arel_engine ||= begin
if Base == self || connection_handler.retrieve_connection_pool(self)
self
else
superclass.arel_engine
end
end
end
private

View File

@ -1,12 +1,10 @@
require 'active_support/lazy_load_hooks'
module ActiveRecord
ActiveSupport.on_load(:active_record_config) do
mattr_accessor :auto_explain_threshold_in_seconds, instance_accessor: false
end
module Explain
delegate :auto_explain_threshold_in_seconds, :auto_explain_threshold_in_seconds=, to: 'ActiveRecord::Model'
def self.extended(base)
base.mattr_accessor :auto_explain_threshold_in_seconds, instance_accessor: false
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

View File

@ -553,7 +553,7 @@ module ActiveRecord
rows[table_name] = fixtures.map do |label, fixture|
row = fixture.to_hash
if model_class && model_class < ActiveRecord::Model
if model_class && model_class < ActiveRecord::Base
# fill in timestamp columns if they aren't specified and the model is set to record_timestamps
if model_class.record_timestamps
timestamp_column_names.each do |c_name|

View File

@ -1,29 +1,22 @@
module ActiveRecord
ActiveSupport.on_load(:active_record_config) do
# Determine whether to store the full constant name including namespace when using STI
mattr_accessor :store_full_sti_class, instance_accessor: false
self.store_full_sti_class = true
end
module Inheritance
extend ActiveSupport::Concern
included do
config_attribute :store_full_sti_class
# Determine whether to store the full constant name including namespace when using STI
class_attribute :store_full_sti_class, instance_writer: false
self.store_full_sti_class = true
end
module ClassMethods
# True if this isn't a concrete subclass needing a STI type condition.
def descends_from_active_record?
sup = active_record_super
if sup.abstract_class?
sup.descends_from_active_record?
elsif self == Base
if self == Base
false
elsif superclass.abstract_class?
superclass.descends_from_active_record?
else
[Base, Model].include?(sup) || !columns_hash.include?(inheritance_column)
superclass == Base || !columns_hash.include?(inheritance_column)
end
end
@ -40,9 +33,8 @@ module ActiveRecord
@symbolized_sti_name ||= sti_name.present? ? sti_name.to_sym : symbolized_base_class
end
# Returns the class descending directly from ActiveRecord::Base (or
# that includes ActiveRecord::Model), or an abstract class, if any, in
# the inheritance hierarchy.
# Returns the class descending directly from ActiveRecord::Base, or
# an abstract class, if any, in the inheritance hierarchy.
#
# If A extends AR::Base, A.base_class will return A. If B descends from A
# through some arbitrarily deep hierarchy, B.base_class will return A.
@ -50,15 +42,14 @@ module ActiveRecord
# If B < A and C < B and if A is an abstract_class then both B.base_class
# and C.base_class would return B as the answer since A is an abstract_class.
def base_class
unless self < ActiveRecord::Tag
unless self < Base
raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord"
end
sup = active_record_super
if sup == Base || sup == Model || sup.abstract_class?
if superclass == Base || superclass.abstract_class?
self
else
sup.base_class
superclass.base_class
end
end
@ -97,14 +88,6 @@ module ActiveRecord
sti_class.allocate.init_with('attributes' => record, 'column_types' => column_types)
end
# 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:
superclass < Model ? superclass : Model
end
protected
# Returns the class type of the record using the current module as a prefix. So descendants of

View File

@ -1,9 +1,4 @@
module ActiveRecord
ActiveSupport.on_load(:active_record_config) do
mattr_accessor :lock_optimistically, instance_accessor: false
self.lock_optimistically = true
end
module Locking
# == What is Optimistic Locking
#
@ -56,7 +51,8 @@ module ActiveRecord
extend ActiveSupport::Concern
included do
config_attribute :lock_optimistically
class_attribute :lock_optimistically, instance_writer: false
self.lock_optimistically = true
end
def locking_enabled? #:nodoc:

View File

@ -1,167 +0,0 @@
require 'active_support/core_ext/module/attribute_accessors'
module ActiveRecord
module Configuration # :nodoc:
# This just abstracts out how we define configuration options in AR. Essentially we
# have mattr_accessors on the ActiveRecord:Model constant that define global defaults.
# Classes that then use AR get class_attributes defined, which means that when they
# are assigned the default will be overridden for that class and subclasses. (Except
# when options[:global] == true, in which case there is one global value always.)
def config_attribute(name, options = {})
if options[:global]
class_eval <<-CODE, __FILE__, __LINE__ + 1
def self.#{name}; ActiveRecord::Model.#{name}; end
def #{name}; ActiveRecord::Model.#{name}; end
def self.#{name}=(val); ActiveRecord::Model.#{name} = val; end
CODE
else
options[:instance_writer] ||= false
class_attribute name, options
singleton_class.class_eval <<-CODE, __FILE__, __LINE__ + 1
remove_method :#{name}
def #{name}; ActiveRecord::Model.#{name}; end
CODE
end
end
end
# This allows us to detect an ActiveRecord::Model while it's in the process of
# being included.
module Tag; end
# <tt>ActiveRecord::Model</tt> can be included into a class to add Active Record
# persistence. This is an alternative to inheriting from <tt>ActiveRecord::Base</tt>.
#
# class Post
# include ActiveRecord::Model
# end
module Model
extend ActiveSupport::Concern
extend ConnectionHandling
extend ActiveModel::Observing::ClassMethods
def self.append_features(base)
base.class_eval do
include Tag
extend Configuration
end
super
end
included do
extend ActiveModel::Naming
extend ActiveSupport::Benchmarkable
extend ActiveSupport::DescendantsTracker
extend QueryCache::ClassMethods
extend Querying
extend Translation
extend DynamicMatchers
extend Explain
extend ConnectionHandling
initialize_generated_modules unless self == Base
end
include Persistence
include ReadonlyAttributes
include ModelSchema
include Inheritance
include Scoping
include Sanitization
include AttributeAssignment
include ActiveModel::Conversion
include Integration
include Validations
include CounterCache
include Locking::Optimistic
include Locking::Pessimistic
include AttributeMethods
include Callbacks
include ActiveModel::Observing
include Timestamp
include Associations
include ActiveModel::SecurePassword
include AutosaveAssociation
include NestedAttributes
include Aggregations
include Transactions
include Reflection
include Serialization
include Store
include Core
class << self
def arel_engine
self
end
def abstract_class?
false
end
# Defines the name of the table column which will store the class name on single-table
# inheritance situations.
#
# The default inheritance column name is +type+, which means it's a
# reserved word inside Active Record. To be able to use single-table
# inheritance with another column name, or to use the column +type+ in
# your own model for something else, you can set +inheritance_column+:
#
# self.inheritance_column = 'zoink'
def inheritance_column
'type'
end
end
class DeprecationProxy < BasicObject #:nodoc:
def initialize(model = Model, base = Base)
@model = model
@base = base
end
def method_missing(name, *args, &block)
if @model.respond_to?(name, true)
@model.send(name, *args, &block)
else
::ActiveSupport::Deprecation.warn(
"The object passed to the active_record load hook was previously ActiveRecord::Base " \
"(a Class). Now it is ActiveRecord::Model (a Module). You have called `#{name}' which " \
"is only defined on ActiveRecord::Base. Please change your code so that it works with " \
"a module rather than a class. (Model is included in Base, so anything added to Model " \
"will be available on Base as well.)"
)
@base.send(name, *args, &block)
end
end
alias send method_missing
def extend(*mods)
::ActiveSupport::Deprecation.warn(
"The object passed to the active_record load hook was previously ActiveRecord::Base " \
"(a Class). Now it is ActiveRecord::Model (a Module). You have called `extend' which " \
"would add singleton methods to Model. This is presumably not what you want, since the " \
"methods would not be inherited down to Base. Rather than using extend, please use " \
"ActiveSupport::Concern + include, which will ensure that your class methods are " \
"inherited."
)
@base.extend(*mods)
end
end
end
# This hook is where config accessors on Model get defined.
#
# We don't want to just open the Model module and add stuff to it in other files, because
# that would cause Model to load, which causes all sorts of loading order issues.
#
# We need this hook rather than just using the :active_record one, because users of the
# :active_record hook may need to use config options.
ActiveSupport.run_load_hooks(:active_record_config, Model)
# Load Base at this point, because the active_record load hook is run in that file.
Base
end

View File

@ -1,18 +1,4 @@
module ActiveRecord
ActiveSupport.on_load(:active_record_config) do
mattr_accessor :primary_key_prefix_type, instance_accessor: false
mattr_accessor :table_name_prefix, instance_accessor: false
self.table_name_prefix = ""
mattr_accessor :table_name_suffix, instance_accessor: false
self.table_name_suffix = ""
mattr_accessor :pluralize_table_names, instance_accessor: false
self.pluralize_table_names = true
end
module ModelSchema
extend ActiveSupport::Concern
@ -24,7 +10,7 @@ module ActiveRecord
# 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
mattr_accessor :primary_key_prefix_type, instance_writer: false
##
# :singleton-method:
@ -36,20 +22,25 @@ 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.
config_attribute :table_name_prefix
class_attribute :table_name_prefix, instance_writer: false
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.
config_attribute :table_name_suffix
class_attribute :table_name_suffix, instance_writer: false
self.table_name_suffix = ""
##
# :singleton-method:
# 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.
config_attribute :pluralize_table_names
class_attribute :pluralize_table_names, instance_writer: false
self.pluralize_table_names = true
self.inheritance_column = 'type'
end
module ClassMethods
@ -144,9 +135,9 @@ module ActiveRecord
# Computes the table name, (re)sets it internally, and returns it.
def reset_table_name #:nodoc:
self.table_name = if abstract_class?
active_record_super == Base ? nil : active_record_super.table_name
elsif active_record_super.abstract_class?
active_record_super.table_name || compute_table_name
superclass == Base ? nil : superclass.table_name
elsif superclass.abstract_class?
superclass.table_name || compute_table_name
else
compute_table_name
end
@ -156,9 +147,17 @@ module ActiveRecord
(parents.detect{ |p| p.respond_to?(:table_name_prefix) } || self).table_name_prefix
end
# The name of the column containing the object's class when Single Table Inheritance is used
# Defines the name of the table column which will store the class name on single-table
# inheritance situations.
#
# The default inheritance column name is +type+, which means it's a
# reserved word inside Active Record. To be able to use single-table
# inheritance with another column name, or to use the column +type+ in
# your own model for something else, you can set +inheritance_column+:
#
# self.inheritance_column = 'zoink'
def inheritance_column
(@inheritance_column ||= nil) || active_record_super.inheritance_column
(@inheritance_column ||= nil) || superclass.inheritance_column
end
# Sets the value of inheritance_column
@ -331,7 +330,7 @@ module ActiveRecord
base = base_class
if self == base
# Nested classes are prefixed with singular parent table name.
if parent < ActiveRecord::Model && !parent.abstract_class?
if parent < Base && !parent.abstract_class?
contained = parent.table_name
contained = contained.singularize if parent.pluralize_table_names
contained += '_'

View File

@ -3,11 +3,6 @@ require 'active_support/core_ext/object/try'
require 'active_support/core_ext/hash/indifferent_access'
module ActiveRecord
ActiveSupport.on_load(:active_record_config) do
mattr_accessor :nested_attributes_options, instance_accessor: false
self.nested_attributes_options = {}
end
module NestedAttributes #:nodoc:
class TooManyRecords < ActiveRecordError
end
@ -15,7 +10,8 @@ module ActiveRecord
extend ActiveSupport::Concern
included do
config_attribute :nested_attributes_options
class_attribute :nested_attributes_options, instance_writer: false
self.nested_attributes_options = {}
end
# = Active Record Nested Attributes

View File

@ -80,7 +80,7 @@ module ActiveRecord
if File.file?(filename)
cache = Marshal.load File.binread filename
if cache.version == ActiveRecord::Migrator.current_version
ActiveRecord::Model.connection.schema_cache = cache
self.connection.schema_cache = cache
else
warn "Ignoring db/schema_cache.dump because it has expired. The current schema version is #{ActiveRecord::Migrator.current_version}, but the one in the cache is #{cache.version}."
end
@ -122,8 +122,8 @@ module ActiveRecord
ActiveSupport.on_load(:active_record) do
ActionDispatch::Reloader.send(hook) do
ActiveRecord::Model.clear_reloadable_connections!
ActiveRecord::Model.clear_cache!
ActiveRecord::Base.clear_reloadable_connections!
ActiveRecord::Base.clear_cache!
end
end
end
@ -135,13 +135,12 @@ module ActiveRecord
config.after_initialize do |app|
ActiveSupport.on_load(:active_record) do
ActiveRecord::Model.instantiate_observers
instantiate_observers
ActionDispatch::Reloader.to_prepare do
ActiveRecord::Model.instantiate_observers
ActiveRecord::Base.instantiate_observers
end
end
end
end
end

View File

@ -159,7 +159,7 @@ module ActiveRecord
# Person.exists?(false)
# Person.exists?
def exists?(conditions = :none)
conditions = conditions.id if ActiveRecord::Model === conditions
conditions = conditions.id if Base === conditions
return false if !conditions
join_dependency = construct_join_dependency_for_association_find

View File

@ -40,7 +40,7 @@ module ActiveRecord
#
# For polymorphic relationships, find the foreign key and type:
# PriceEstimate.where(:estimate_of => treasure)
if klass && value.class < ActiveRecord::Tag && reflection = klass.reflect_on_association(column.to_sym)
if klass && value.class < Base && reflection = klass.reflect_on_association(column.to_sym)
if reflection.polymorphic?
queries << build(table[reflection.foreign_type], value.class.base_class)
end
@ -67,7 +67,7 @@ module ActiveRecord
def self.build(attribute, value)
case value
when Array, ActiveRecord::Associations::CollectionProxy
values = value.to_a.map {|x| x.is_a?(ActiveRecord::Model) ? x.id : x}
values = value.to_a.map {|x| x.is_a?(Base) ? x.id : x}
ranges, values = values.partition {|v| v.is_a?(Range)}
values_predicate = if values.include?(nil)
@ -93,7 +93,7 @@ module ActiveRecord
attribute.in(value.arel.ast)
when Range
attribute.in(value)
when ActiveRecord::Model
when ActiveRecord::Base
attribute.eq(value.id)
when Class
# FIXME: I think we need to deprecate this behavior

View File

@ -56,7 +56,7 @@ module ActiveRecord
@hash_rows ||=
begin
# We freeze the strings to prevent them getting duped when
# used as keys in ActiveRecord::Model's @attributes hash
# used as keys in ActiveRecord::Base's @attributes hash
columns = @columns.map { |c| c.dup.freeze }
@rows.map { |row|
Hash[columns.zip(row)]

View File

@ -1,19 +1,12 @@
module ActiveRecord #:nodoc:
ActiveSupport.on_load(:active_record_config) do
mattr_accessor :include_root_in_json, instance_accessor: false
self.include_root_in_json = true
end
# = Active Record Serialization
module Serialization
extend ActiveSupport::Concern
include ActiveModel::Serializers::JSON
included do
singleton_class.class_eval do
remove_method :include_root_in_json
delegate :include_root_in_json, to: 'ActiveRecord::Model'
end
mattr_accessor :include_root_in_json, instance_accessor: false
self.include_root_in_json = true
end
def serializable_hash(options = nil)

View File

@ -1,8 +1,6 @@
module ActiveRecord
ActiveSupport.on_load(:active_record_config) do
mattr_accessor :record_timestamps, instance_accessor: false
self.record_timestamps = true
end
# = Active Record Timestamp
@ -37,7 +35,8 @@ module ActiveRecord
extend ActiveSupport::Concern
included do
config_attribute :record_timestamps, instance_writer: true
class_attribute :record_timestamps
self.record_timestamps = true
end
def initialize_dup(other) # :nodoc:

View File

@ -69,16 +69,16 @@ module ActiveRecord
def test_not_specifying_database_name_for_cross_database_selects
begin
assert_nothing_raised do
ActiveRecord::Model.establish_connection(ActiveRecord::Base.configurations['arunit'].except(:database))
ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['arunit'].except(:database))
config = ARTest.connection_config
ActiveRecord::Model.connection.execute(
ActiveRecord::Base.connection.execute(
"SELECT #{config['arunit']['database']}.pirates.*, #{config['arunit2']['database']}.courses.* " \
"FROM #{config['arunit']['database']}.pirates, #{config['arunit2']['database']}.courses"
)
end
ensure
ActiveRecord::Model.establish_connection 'arunit'
ActiveRecord::Base.establish_connection 'arunit'
end
end
end

View File

@ -3,13 +3,13 @@ require "cases/helper"
class MysqlConnectionTest < ActiveRecord::TestCase
def setup
super
@connection = ActiveRecord::Model.connection
@connection = ActiveRecord::Base.connection
end
def test_mysql_reconnect_attribute_after_connection_with_reconnect_true
run_without_connection do |orig_connection|
ActiveRecord::Model.establish_connection(orig_connection.merge({:reconnect => true}))
assert ActiveRecord::Model.connection.raw_connection.reconnect
ActiveRecord::Base.establish_connection(orig_connection.merge({:reconnect => true}))
assert ActiveRecord::Base.connection.raw_connection.reconnect
end
end
@ -25,8 +25,8 @@ class MysqlConnectionTest < ActiveRecord::TestCase
def test_mysql_reconnect_attribute_after_connection_with_reconnect_false
run_without_connection do |orig_connection|
ActiveRecord::Model.establish_connection(orig_connection.merge({:reconnect => false}))
assert !ActiveRecord::Model.connection.raw_connection.reconnect
ActiveRecord::Base.establish_connection(orig_connection.merge({:reconnect => false}))
assert !ActiveRecord::Base.connection.raw_connection.reconnect
end
end
@ -117,7 +117,7 @@ class MysqlConnectionTest < ActiveRecord::TestCase
# Test that MySQL allows multiple results for stored procedures
if defined?(Mysql) && Mysql.const_defined?(:CLIENT_MULTI_RESULTS)
def test_multi_results
rows = ActiveRecord::Model.connection.select_rows('CALL ten();')
rows = ActiveRecord::Base.connection.select_rows('CALL ten();')
assert_equal 10, rows[0][0].to_i, "ten() did not return 10 as expected: #{rows.inspect}"
assert @connection.active?, "Bad connection use by 'MysqlAdapter.select_rows'"
end
@ -130,9 +130,9 @@ class MysqlConnectionTest < ActiveRecord::TestCase
def test_mysql_strict_mode_disabled_dont_override_global_sql_mode
run_without_connection do |orig_connection|
ActiveRecord::Model.establish_connection(orig_connection.merge({:strict => false}))
global_sql_mode = ActiveRecord::Model.connection.exec_query "SELECT @@GLOBAL.sql_mode"
session_sql_mode = ActiveRecord::Model.connection.exec_query "SELECT @@SESSION.sql_mode"
ActiveRecord::Base.establish_connection(orig_connection.merge({:strict => false}))
global_sql_mode = ActiveRecord::Base.connection.exec_query "SELECT @@GLOBAL.sql_mode"
session_sql_mode = ActiveRecord::Base.connection.exec_query "SELECT @@SESSION.sql_mode"
assert_equal global_sql_mode.rows, session_sql_mode.rows
end
end
@ -140,11 +140,11 @@ class MysqlConnectionTest < ActiveRecord::TestCase
private
def run_without_connection
original_connection = ActiveRecord::Model.remove_connection
original_connection = ActiveRecord::Base.remove_connection
begin
yield original_connection
ensure
ActiveRecord::Model.establish_connection(original_connection)
ActiveRecord::Base.establish_connection(original_connection)
end
end
end

View File

@ -3,7 +3,7 @@ require "cases/helper"
class MysqlConnectionTest < ActiveRecord::TestCase
def setup
super
@connection = ActiveRecord::Model.connection
@connection = ActiveRecord::Base.connection
@connection.extend(LogIntercepter)
@connection.intercepted = true
end
@ -46,9 +46,9 @@ class MysqlConnectionTest < ActiveRecord::TestCase
def test_mysql_strict_mode_disabled_dont_override_global_sql_mode
run_without_connection do |orig_connection|
ActiveRecord::Model.establish_connection(orig_connection.merge({:strict => false}))
global_sql_mode = ActiveRecord::Model.connection.exec_query "SELECT @@GLOBAL.sql_mode"
session_sql_mode = ActiveRecord::Model.connection.exec_query "SELECT @@SESSION.sql_mode"
ActiveRecord::Base.establish_connection(orig_connection.merge({:strict => false}))
global_sql_mode = ActiveRecord::Base.connection.exec_query "SELECT @@GLOBAL.sql_mode"
session_sql_mode = ActiveRecord::Base.connection.exec_query "SELECT @@SESSION.sql_mode"
assert_equal global_sql_mode.rows, session_sql_mode.rows
end
end
@ -76,11 +76,11 @@ class MysqlConnectionTest < ActiveRecord::TestCase
private
def run_without_connection
original_connection = ActiveRecord::Model.remove_connection
original_connection = ActiveRecord::Base.remove_connection
begin
yield original_connection
ensure
ActiveRecord::Model.establish_connection(original_connection)
ActiveRecord::Base.establish_connection(original_connection)
end
end
end

View File

@ -11,10 +11,8 @@ module ActiveRecord
def setup
@klass = Class.new do
def self.superclass; Base; end
def self.active_record_super; Base; end
def self.base_class; self; end
extend ActiveRecord::Configuration
include ActiveRecord::AttributeMethods
def self.define_attribute_methods

View File

@ -23,7 +23,6 @@ require 'models/edge'
require 'models/joke'
require 'models/bulb'
require 'models/bird'
require 'models/teapot'
require 'rexml/document'
require 'active_support/core_ext/exception'
@ -1196,10 +1195,6 @@ class BasicsTest < ActiveRecord::TestCase
# Concrete subclasses an abstract class which has a type column.
assert !SubStiPost.descends_from_active_record?
assert Teapot.descends_from_active_record?
assert !OtherTeapot.descends_from_active_record?
assert CoolTeapot.descends_from_active_record?
end
def test_find_on_abstract_base_class_doesnt_use_type_condition
@ -1453,15 +1448,6 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal scope, Bird.uniq
end
def test_active_record_super
assert_equal ActiveRecord::Model, ActiveRecord::Base.active_record_super
assert_equal ActiveRecord::Base, Topic.active_record_super
assert_equal Topic, ImportantTopic.active_record_super
assert_equal ActiveRecord::Model, Teapot.active_record_super
assert_equal Teapot, OtherTeapot.active_record_super
assert_equal ActiveRecord::Model, CoolTeapot.active_record_super
end
def test_table_name_with_2_abstract_subclasses
assert_equal "photos", Photo.table_name
end

View File

@ -4,8 +4,8 @@ module ActiveRecord
module ConnectionAdapters
class ConnectionHandlerTest < ActiveRecord::TestCase
def setup
@klass = Class.new { include ActiveRecord::Tag }
@subklass = Class.new(@klass) { include ActiveRecord::Tag }
@klass = Class.new(Base)
@subklass = Class.new(@klass)
@handler = ConnectionHandler.new
@handler.establish_connection @klass, Base.connection_pool.spec

View File

@ -52,12 +52,12 @@ if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
self.use_transactional_fixtures = false
def using_strict(strict)
connection = ActiveRecord::Model.remove_connection
ActiveRecord::Model.establish_connection connection.merge(strict: strict)
connection = ActiveRecord::Base.remove_connection
ActiveRecord::Base.establish_connection connection.merge(strict: strict)
yield
ensure
ActiveRecord::Model.remove_connection
ActiveRecord::Model.establish_connection connection
ActiveRecord::Base.remove_connection
ActiveRecord::Base.establish_connection connection
end
# MySQL cannot have defaults on text/blob columns. It reports the

View File

@ -1,133 +0,0 @@
require 'cases/helper'
require 'models/teapot'
class BasicInclusionModelTest < ActiveRecord::TestCase
def test_basic_model
Teapot.create!(:name => "Ronnie Kemper")
assert_equal "Ronnie Kemper", Teapot.first.name
end
def test_initialization
t = Teapot.new(:name => "Bob")
assert_equal "Bob", t.name
end
def test_inherited_model
teapot = CoolTeapot.create!(:name => "Bob")
teapot.reload
assert_equal "Bob", teapot.name
assert_equal "mmm", teapot.aaahhh
end
def test_generated_feature_methods
assert Teapot < Teapot::GeneratedFeatureMethods
end
def test_exists
t = Teapot.create!(:name => "Ronnie Kemper")
assert Teapot.exists?(t)
end
def test_predicate_builder
t = Teapot.create!(:name => "Bob")
assert_equal "Bob", Teapot.where(:id => [t]).first.name
assert_equal "Bob", Teapot.where(:id => t).first.name
end
def test_nested_model
assert_equal "ceiling_teapots", Ceiling::Teapot.table_name
end
end
class InclusionUnitTest < ActiveRecord::TestCase
def setup
@klass = Class.new { include ActiveRecord::Model }
end
def test_non_abstract_class
assert !@klass.abstract_class?
end
def test_abstract_class
@klass.abstract_class = true
assert @klass.abstract_class?
end
def test_establish_connection
assert @klass.respond_to?(:establish_connection)
assert ActiveRecord::Model.respond_to?(:establish_connection)
end
def test_adapter_connection
name = "#{ActiveRecord::Base.connection_config[:adapter]}_connection"
assert @klass.respond_to?(name)
assert ActiveRecord::Model.respond_to?(name)
end
def test_connection_handler
assert_equal ActiveRecord::Base.connection_handler, @klass.connection_handler
end
def test_mirrored_configuration
ActiveRecord::Base.time_zone_aware_attributes = true
assert @klass.time_zone_aware_attributes
ActiveRecord::Base.time_zone_aware_attributes = false
assert !@klass.time_zone_aware_attributes
ensure
ActiveRecord::Base.time_zone_aware_attributes = false
end
# Doesn't really test anything, but this is here to ensure warnings don't occur
def test_included_twice
@klass.send :include, ActiveRecord::Model
end
def test_deprecation_proxy
proxy = ActiveRecord::Model::DeprecationProxy.new
assert_equal ActiveRecord::Model.name, proxy.name
assert_equal ActiveRecord::Base.superclass, assert_deprecated { proxy.superclass }
sup, sup2 = nil, nil
ActiveSupport.on_load(:__test_active_record_model_deprecation) do
sup = superclass
sup2 = send(:superclass)
end
assert_deprecated do
ActiveSupport.run_load_hooks(:__test_active_record_model_deprecation, proxy)
end
assert_equal ActiveRecord::Base.superclass, sup
assert_equal ActiveRecord::Base.superclass, sup2
end
test "including in deprecation proxy" do
model, base = ActiveRecord::Model.dup, ActiveRecord::Base.dup
proxy = ActiveRecord::Model::DeprecationProxy.new(model, base)
mod = Module.new
proxy.include mod
assert model < mod
end
test "extending in deprecation proxy" do
model, base = ActiveRecord::Model.dup, ActiveRecord::Base.dup
proxy = ActiveRecord::Model::DeprecationProxy.new(model, base)
mod = Module.new
assert_deprecated { proxy.extend mod }
assert base.singleton_class < mod
end
end
class InclusionFixturesTest < ActiveRecord::TestCase
fixtures :teapots
def test_fixtured_record
assert_equal "Bob", teapots(:bob).name
end
def test_timestamped_fixture
assert_not_nil teapots(:bob).created_at
end
end

View File

@ -4,7 +4,6 @@ require 'models/person'
require 'models/post'
require 'models/project'
require 'models/subscriber'
require 'models/teapot'
require 'models/vegetables'
class InheritanceTest < ActiveRecord::TestCase
@ -81,10 +80,6 @@ class InheritanceTest < ActiveRecord::TestCase
assert_equal SubStiPost, SubStiPost.base_class
end
def test_active_record_model_included_base_class
assert_equal Teapot, Teapot.base_class
end
def test_abstract_inheritance_base_class
assert_equal LoosePerson, LoosePerson.base_class
assert_equal LooseDescendant, LooseDescendant.base_class
@ -93,11 +88,7 @@ class InheritanceTest < ActiveRecord::TestCase
end
def test_base_class_activerecord_error
klass = Class.new {
extend ActiveRecord::Configuration
include ActiveRecord::Inheritance
}
klass = Class.new { include ActiveRecord::Inheritance }
assert_raise(ActiveRecord::ActiveRecordError) { klass.base_class }
end

View File

@ -96,12 +96,12 @@ class MultipleDbTest < ActiveRecord::TestCase
unless in_memory_db?
def test_associations_should_work_when_model_has_no_connection
begin
ActiveRecord::Model.remove_connection
ActiveRecord::Base.remove_connection
assert_nothing_raised ActiveRecord::ConnectionNotEstablished do
College.first.courses.first
end
ensure
ActiveRecord::Model.establish_connection 'arunit'
ActiveRecord::Base.establish_connection 'arunit'
end
end
end

View File

@ -7,17 +7,17 @@ class PooledConnectionsTest < ActiveRecord::TestCase
def setup
@per_test_teardown = []
@connection = ActiveRecord::Model.remove_connection
@connection = ActiveRecord::Base.remove_connection
end
def teardown
ActiveRecord::Model.clear_all_connections!
ActiveRecord::Model.establish_connection(@connection)
ActiveRecord::Base.clear_all_connections!
ActiveRecord::Base.establish_connection(@connection)
@per_test_teardown.each {|td| td.call }
end
def checkout_connections
ActiveRecord::Model.establish_connection(@connection.merge({:pool => 2, :checkout_timeout => 0.3}))
ActiveRecord::Base.establish_connection(@connection.merge({:pool => 2, :checkout_timeout => 0.3}))
@connections = []
@timed_out = 0
@ -34,15 +34,15 @@ class PooledConnectionsTest < ActiveRecord::TestCase
# Will deadlock due to lack of Monitor timeouts in 1.9
def checkout_checkin_connections(pool_size, threads)
ActiveRecord::Model.establish_connection(@connection.merge({:pool => pool_size, :checkout_timeout => 0.5}))
ActiveRecord::Base.establish_connection(@connection.merge({:pool => pool_size, :checkout_timeout => 0.5}))
@connection_count = 0
@timed_out = 0
threads.times do
Thread.new do
begin
conn = ActiveRecord::Model.connection_pool.checkout
conn = ActiveRecord::Base.connection_pool.checkout
sleep 0.1
ActiveRecord::Model.connection_pool.checkin conn
ActiveRecord::Base.connection_pool.checkin conn
@connection_count += 1
rescue ActiveRecord::ConnectionTimeoutError
@timed_out += 1
@ -55,13 +55,13 @@ class PooledConnectionsTest < ActiveRecord::TestCase
checkout_checkin_connections 1, 2
assert_equal 2, @connection_count
assert_equal 0, @timed_out
assert_equal 1, ActiveRecord::Model.connection_pool.connections.size
assert_equal 1, ActiveRecord::Base.connection_pool.connections.size
end
private
def add_record(name)
ActiveRecord::Model.connection_pool.with_connection { Project.create! :name => name }
ActiveRecord::Base.connection_pool.with_connection { Project.create! :name => name }
end
end unless current_adapter?(:FrontBase) || in_memory_db?

View File

@ -188,14 +188,14 @@ class PrimaryKeyWithNoConnectionTest < ActiveRecord::TestCase
def test_set_primary_key_with_no_connection
return skip("disconnect wipes in-memory db") if in_memory_db?
connection = ActiveRecord::Model.remove_connection
connection = ActiveRecord::Base.remove_connection
model = Class.new(ActiveRecord::Base)
model.primary_key = 'foo'
assert_equal 'foo', model.primary_key
ActiveRecord::Model.establish_connection(connection)
ActiveRecord::Base.establish_connection(connection)
assert_equal 'foo', model.primary_key
end

View File

@ -7,13 +7,13 @@ class TestUnconnectedAdapter < ActiveRecord::TestCase
self.use_transactional_fixtures = false
def setup
@underlying = ActiveRecord::Model.connection
@specification = ActiveRecord::Model.remove_connection
@underlying = ActiveRecord::Base.connection
@specification = ActiveRecord::Base.remove_connection
end
def teardown
@underlying = nil
ActiveRecord::Model.establish_connection(@specification)
ActiveRecord::Base.establish_connection(@specification)
load_schema if in_memory_db?
end

View File

@ -1,35 +0,0 @@
class Teapot
# I'm a little teapot,
# Short and stout,
# Here is my handle
# Here is my spout
# When I get all steamed up,
# Hear me shout,
# Tip me over and pour me out!
#
# HELL YEAH TEAPOT SONG
include ActiveRecord::Model
end
class OtherTeapot < Teapot
end
class OMFGIMATEAPOT
def aaahhh
"mmm"
end
end
class CoolTeapot < OMFGIMATEAPOT
include ActiveRecord::Model
self.table_name = "teapots"
end
class Ceiling
include ActiveRecord::Model
class Teapot
include ActiveRecord::Model
end
end

View File

@ -648,12 +648,6 @@ ActiveRecord::Schema.define do
t.datetime :ending
end
create_table :teapots, :force => true do |t|
t.string :name
t.string :type
t.timestamps
end
create_table :topics, :force => true do |t|
t.string :title
t.string :author_name

View File

@ -13,9 +13,9 @@ module ARTest
def self.connect
puts "Using #{connection_name}"
ActiveRecord::Model.logger = ActiveSupport::Logger.new("debug.log", 0, 100 * 1024 * 1024)
ActiveRecord::Model.configurations = connection_config
ActiveRecord::Model.establish_connection 'arunit'
ActiveRecord::Base.logger = ActiveSupport::Logger.new("debug.log", 0, 100 * 1024 * 1024)
ActiveRecord::Base.configurations = connection_config
ActiveRecord::Base.establish_connection 'arunit'
ARUnit2Model.establish_connection 'arunit2'
end
end