aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/CHANGELOG12
-rw-r--r--activerecord/lib/active_record/associations/through_association.rb2
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb57
-rw-r--r--activerecord/lib/active_record/attribute_methods/read.rb2
-rw-r--r--activerecord/lib/active_record/attribute_methods/write.rb14
-rw-r--r--activerecord/lib/active_record/autosave_association.rb5
-rw-r--r--activerecord/lib/active_record/base.rb3
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql_adapter.rb39
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb62
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb43
-rw-r--r--activerecord/lib/active_record/connection_adapters/statement_pool.rb40
-rw-r--r--activerecord/lib/active_record/persistence.rb2
-rw-r--r--activerecord/lib/active_record/railties/databases.rake12
-rw-r--r--activerecord/lib/active_record/relation.rb43
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb4
-rw-r--r--activerecord/lib/active_record/timestamp.rb4
-rw-r--r--activerecord/test/cases/adapters/mysql/statement_pool_test.rb23
-rw-r--r--activerecord/test/cases/adapters/postgresql/schema_test.rb6
-rw-r--r--activerecord/test/cases/adapters/postgresql/statement_pool_test.rb23
-rw-r--r--activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb24
-rw-r--r--activerecord/test/cases/associations/nested_through_associations_test.rb8
-rw-r--r--activerecord/test/cases/attribute_methods/read_test.rb1
-rw-r--r--activerecord/test/cases/attribute_methods_test.rb37
-rw-r--r--activerecord/test/cases/base_test.rb32
-rw-r--r--activerecord/test/cases/connection_adapters/connection_handler_test.rb21
-rw-r--r--activerecord/test/cases/finder_test.rb13
-rw-r--r--activerecord/test/cases/persistence_test.rb17
-rw-r--r--activerecord/test/cases/relations_test.rb128
-rw-r--r--activerecord/test/cases/serialization_test.rb13
-rw-r--r--activerecord/test/cases/session_store/session_test.rb2
-rw-r--r--activerecord/test/models/contact.rb13
-rw-r--r--activerecord/test/models/topic.rb9
-rw-r--r--activerecord/test/schema/schema.rb8
35 files changed, 642 insertions, 84 deletions
diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG
index 700e11ff94..e82906186e 100644
--- a/activerecord/CHANGELOG
+++ b/activerecord/CHANGELOG
@@ -1,4 +1,14 @@
-*Rails 3.2.0 (unreleased)*
+Wed Sep 7 15:25:02 2011 Aaron Patterson <aaron@tenderlovemaking.com>
+
+ * lib/active_record/connection_adapters/mysql_adapter.rb: LRU cache
+ keys are per process id.
+ * lib/active_record/connection_adapters/sqlite_adapter.rb: ditto
+
+* Add first_or_create, first_or_create!, first_or_build and first_or_new methods to Active Record. This is a better approach over the old find_or_create_by dynamic methods because it's clearer which arguments are used to find the record and which are used to create it:
+
+ User.where(:first_name => "Scarlett").first_or_create!(:last_name => "Johansson", :hot => true)
+
+ [Andrés Mejía]
* Support bulk change_table in mysql2 adapter, as well as the mysql one. [Jon Leighton]
diff --git a/activerecord/lib/active_record/associations/through_association.rb b/activerecord/lib/active_record/associations/through_association.rb
index b010b5e9ef..b347a94978 100644
--- a/activerecord/lib/active_record/associations/through_association.rb
+++ b/activerecord/lib/active_record/associations/through_association.rb
@@ -16,7 +16,7 @@ module ActiveRecord
chain[1..-1].each do |reflection|
scope = scope.merge(
reflection.klass.scoped.with_default_scope.
- except(:select, :create_with, :includes, :preload)
+ except(:select, :create_with, :includes, :preload, :joins, :eager_load)
)
end
scope
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index d0687ed0b6..dc6dc2e63a 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -1,4 +1,5 @@
require 'active_support/core_ext/enumerable'
+require 'active_support/deprecation'
module ActiveRecord
# = Active Record Attribute Methods
@@ -11,17 +12,30 @@ module ActiveRecord
# accessors, mutators and query methods.
def define_attribute_methods
return if attribute_methods_generated?
- super(column_names)
- @attribute_methods_generated = true
+
+ if base_class == self
+ super(column_names)
+ @attribute_methods_generated = true
+ else
+ base_class.define_attribute_methods
+ end
end
def attribute_methods_generated?
- @attribute_methods_generated ||= false
+ if base_class == self
+ @attribute_methods_generated ||= false
+ else
+ base_class.attribute_methods_generated?
+ end
end
def undefine_attribute_methods(*args)
- super
- @attribute_methods_generated = false
+ if base_class == self
+ super
+ @attribute_methods_generated = false
+ else
+ base_class.undefine_attribute_methods(*args)
+ end
end
# Checks whether the method is defined in the model or any of its subclasses
@@ -36,7 +50,7 @@ module ActiveRecord
@@_defined_activerecord_methods ||= defined_activerecord_methods
raise DangerousAttributeError, "#{method_name} is defined by ActiveRecord" if @@_defined_activerecord_methods.include?(method_name)
- @_defined_class_methods.include?(method_name)
+ @_defined_class_methods.include?(method_name) || generated_attribute_methods.method_defined?(method_name)
end
def defined_activerecord_methods
@@ -48,19 +62,36 @@ module ActiveRecord
end
end
- def method_missing(method_id, *args, &block)
- # If we haven't generated any methods yet, generate them, then
- # see if we've created the method we're looking for.
- if !self.class.attribute_methods_generated?
+ # If we haven't generated any methods yet, generate them, then
+ # see if we've created the method we're looking for.
+ def method_missing(method, *args, &block)
+ unless self.class.attribute_methods_generated?
self.class.define_attribute_methods
- method_name = method_id.to_s
- guard_private_attribute_method!(method_name, args)
- send(method_id, *args, &block)
+
+ if respond_to_without_attributes?(method)
+ send(method, *args, &block)
+ else
+ super
+ end
else
super
end
end
+ def attribute_missing(match, *args, &block)
+ if self.class.columns_hash[match.attr_name]
+ ActiveSupport::Deprecation.warn(
+ "The method `#{match.method_name}', matching the attribute `#{match.attr_name}' has " \
+ "dispatched through method_missing. This shouldn't happen, because `#{match.attr_name}' " \
+ "is a column of the table. If this error has happened through normal usage of Active " \
+ "Record (rather than through your own code or external libraries), please report it as " \
+ "a bug."
+ )
+ end
+
+ super
+ end
+
def respond_to?(name, include_private = false)
self.class.define_attribute_methods unless self.class.attribute_methods_generated?
super
diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb
index 9a50a20fbc..4174e4da09 100644
--- a/activerecord/lib/active_record/attribute_methods/read.rb
+++ b/activerecord/lib/active_record/attribute_methods/read.rb
@@ -6,8 +6,6 @@ module ActiveRecord
ATTRIBUTE_TYPES_CACHED_BY_DEFAULT = [:datetime, :timestamp, :time, :date]
included do
- attribute_method_suffix ""
-
cattr_accessor :attribute_types_cached_by_default, :instance_writer => false
self.attribute_types_cached_by_default = ATTRIBUTE_TYPES_CACHED_BY_DEFAULT
diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb
index c77a3ac145..e9cdb130db 100644
--- a/activerecord/lib/active_record/attribute_methods/write.rb
+++ b/activerecord/lib/active_record/attribute_methods/write.rb
@@ -17,6 +17,10 @@ module ActiveRecord
write_attribute(attr_name, new_value)
end
end
+
+ if attr_name == primary_key && attr_name != "id"
+ generated_attribute_methods.module_eval("alias :id= :'#{primary_key}='")
+ end
end
end
@@ -24,12 +28,16 @@ module ActiveRecord
# for fixnum and float columns are turned into +nil+.
def write_attribute(attr_name, value)
attr_name = attr_name.to_s
- attr_name = self.class.primary_key if attr_name == 'id'
+ attr_name = self.class.primary_key if attr_name == 'id' && self.class.primary_key
@attributes_cache.delete(attr_name)
- if (column = column_for_attribute(attr_name)) && column.number?
+ column = column_for_attribute(attr_name)
+
+ if column && column.number?
@attributes[attr_name] = convert_number_column_value(value)
- else
+ elsif column || @attributes.has_key?(attr_name)
@attributes[attr_name] = value
+ else
+ raise ActiveModel::MissingAttributeError, "can't write unknown attribute `#{attr_name}'"
end
end
alias_method :raw_write_attribute, :write_attribute
diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb
index 085fdba639..056170d82a 100644
--- a/activerecord/lib/active_record/autosave_association.rb
+++ b/activerecord/lib/active_record/autosave_association.rb
@@ -370,7 +370,10 @@ module ActiveRecord
else
key = reflection.options[:primary_key] ? send(reflection.options[:primary_key]) : id
if autosave != false && (new_record? || record.new_record? || record[reflection.foreign_key] != key || autosave)
- record[reflection.foreign_key] = key
+ unless reflection.through_reflection
+ record[reflection.foreign_key] = key
+ end
+
saved = record.save(:validate => !autosave)
raise ActiveRecord::Rollback if !saved && autosave
saved
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 374791deb1..92f80c6eaa 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -442,6 +442,7 @@ module ActiveRecord #:nodoc:
class << self # Class methods
delegate :find, :first, :first!, :last, :last!, :all, :exists?, :any?, :many?, :to => :scoped
+ delegate :first_or_create, :first_or_create!, :first_or_new, :first_or_build, :to => :scoped
delegate :destroy, :destroy_all, :delete, :delete_all, :update, :update_all, :to => :scoped
delegate :find_each, :find_in_batches, :to => :scoped
delegate :select, :group, :order, :except, :reorder, :limit, :offset, :joins, :where, :preload, :eager_load, :includes, :from, :lock, :readonly, :having, :create_with, :to => :scoped
@@ -1331,7 +1332,7 @@ MSG
# Returns the class descending directly from ActiveRecord::Base or an
# abstract class, if any, in the inheritance hierarchy.
def class_of_active_record_descendant(klass)
- if klass.superclass == Base || klass.superclass.abstract_class?
+ if klass == Base || klass.superclass == Base || klass.superclass.abstract_class?
klass
elsif klass.superclass.nil?
raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord"
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
index 61994d4a47..20863e73aa 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -421,7 +421,7 @@ module ActiveRecord
# can be used as an argument for establish_connection, for easily
# re-establishing the connection.
def remove_connection(klass)
- pool = @connection_pools[klass.name]
+ pool = @connection_pools.delete(klass.name)
return nil unless pool
pool.automatic_reconnect = false
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb
index 7312e34f01..c08c0263b9 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb
@@ -126,7 +126,7 @@ module ActiveRecord
end
def connection_pool
- connection_handler.retrieve_connection_pool(self)
+ connection_handler.retrieve_connection_pool(self) or raise ConnectionNotEstablished
end
def retrieve_connection
diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
index e9bdcc2104..a1824fe396 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -1,4 +1,5 @@
require 'active_record/connection_adapters/abstract_mysql_adapter'
+require 'active_record/connection_adapters/statement_pool'
require 'active_support/core_ext/hash/keys'
gem 'mysql', '~> 2.8.1'
@@ -90,9 +91,42 @@ module ActiveRecord
ADAPTER_NAME = 'MySQL'
+ class StatementPool < ConnectionAdapters::StatementPool
+ def initialize(connection, max = 1000)
+ super
+ @cache = Hash.new { |h,pid| h[pid] = {} }
+ end
+
+ def each(&block); cache.each(&block); end
+ def key?(key); cache.key?(key); end
+ def [](key); cache[key]; end
+ def length; cache.length; end
+ def delete(key); cache.delete(key); end
+
+ def []=(sql, key)
+ while @max <= cache.size
+ cache.shift.last[:stmt].close
+ end
+ cache[sql] = key
+ end
+
+ def clear
+ cache.values.each do |hash|
+ hash[:stmt].close
+ end
+ cache.clear
+ end
+
+ private
+ def cache
+ @cache[$$]
+ end
+ end
+
def initialize(connection, logger, connection_options, config)
super
- @statements = {}
+ @statements = StatementPool.new(@connection,
+ config.fetch(:statement_limit) { 1000 })
@client_encoding = nil
connect
end
@@ -187,9 +221,6 @@ module ActiveRecord
# Clears the prepared statements cache.
def clear_cache!
- @statements.values.each do |cache|
- cache[:stmt].close
- end
@statements.clear
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index ba4a6c7a78..5402918b1d 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -1,5 +1,6 @@
require 'active_record/connection_adapters/abstract_adapter'
require 'active_support/core_ext/object/blank'
+require 'active_record/connection_adapters/statement_pool'
# Make sure we're using pg high enough for PGResult#values
gem 'pg', '~> 0.11'
@@ -246,6 +247,47 @@ module ActiveRecord
true
end
+ class StatementPool < ConnectionAdapters::StatementPool
+ def initialize(connection, max)
+ super
+ @counter = 0
+ @cache = Hash.new { |h,pid| h[pid] = {} }
+ end
+
+ def each(&block); cache.each(&block); end
+ def key?(key); cache.key?(key); end
+ def [](key); cache[key]; end
+ def length; cache.length; end
+
+ def next_key
+ "a#{@counter + 1}"
+ end
+
+ def []=(sql, key)
+ while @max <= cache.size
+ dealloc(cache.shift.last)
+ end
+ @counter += 1
+ cache[sql] = key
+ end
+
+ def clear
+ cache.each_value do |stmt_key|
+ dealloc stmt_key
+ end
+ cache.clear
+ end
+
+ private
+ def cache
+ @cache[$$]
+ end
+
+ def dealloc(key)
+ @connection.query "DEALLOCATE #{key}"
+ end
+ end
+
# Initializes and connects a PostgreSQL adapter.
def initialize(connection, logger, connection_parameters, config)
super(connection, logger)
@@ -254,9 +296,10 @@ module ActiveRecord
# @local_tz is initialized as nil to avoid warnings when connect tries to use it
@local_tz = nil
@table_alias_length = nil
- @statements = {}
connect
+ @statements = StatementPool.new @connection,
+ config.fetch(:statement_limit) { 1000 }
if postgresql_version < 80200
raise "Your version of PostgreSQL (#{postgresql_version}) is too old, please upgrade!"
@@ -271,9 +314,6 @@ module ActiveRecord
# Clears the prepared statements cache.
def clear_cache!
- @statements.each_value do |value|
- @connection.query "DEALLOCATE #{value}"
- end
@statements.clear
end
@@ -683,12 +723,12 @@ module ActiveRecord
binds << [nil, schema] if schema
exec_query(<<-SQL, 'SCHEMA', binds).rows.first[0].to_i > 0
- SELECT COUNT(*)
- FROM pg_class c
- LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
- WHERE c.relkind in ('v','r')
- AND c.relname = $1
- AND n.nspname = #{schema ? '$2' : 'ANY (current_schemas(false))'}
+ SELECT COUNT(*)
+ FROM pg_class c
+ LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
+ WHERE c.relkind in ('v','r')
+ AND c.relname = $1
+ AND n.nspname = #{schema ? '$2' : 'ANY (current_schemas(false))'}
SQL
end
@@ -996,7 +1036,7 @@ module ActiveRecord
def exec_cache(sql, binds)
unless @statements.key? sql
- nextkey = "a#{@statements.length + 1}"
+ nextkey = @statements.next_key
@connection.prepare nextkey, sql
@statements[sql] = nextkey
end
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
index a90c675bf6..1932a849ee 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
@@ -1,4 +1,5 @@
require 'active_record/connection_adapters/abstract_adapter'
+require 'active_record/connection_adapters/statement_pool'
require 'active_support/core_ext/string/encoding'
module ActiveRecord
@@ -48,9 +49,45 @@ module ActiveRecord
end
end
+ class StatementPool < ConnectionAdapters::StatementPool
+ def initialize(connection, max)
+ super
+ @cache = Hash.new { |h,pid| h[pid] = {} }
+ end
+
+ def each(&block); cache.each(&block); end
+ def key?(key); cache.key?(key); end
+ def [](key); cache[key]; end
+ def length; cache.length; end
+
+ def []=(sql, key)
+ while @max <= cache.size
+ dealloc(cache.shift.last[:stmt])
+ end
+ cache[sql] = key
+ end
+
+ def clear
+ cache.values.each do |hash|
+ dealloc hash[:stmt]
+ end
+ cache.clear
+ end
+
+ private
+ def cache
+ @cache[$$]
+ end
+
+ def dealloc(stmt)
+ stmt.close unless stmt.closed?
+ end
+ end
+
def initialize(connection, logger, config)
super(connection, logger)
- @statements = {}
+ @statements = StatementPool.new(@connection,
+ config.fetch(:statement_limit) { 1000 })
@config = config
end
@@ -107,10 +144,6 @@ module ActiveRecord
# Clears the prepared statements cache.
def clear_cache!
- @statements.values.map { |hash| hash[:stmt] }.each { |stmt|
- stmt.close unless stmt.closed?
- }
-
@statements.clear
end
diff --git a/activerecord/lib/active_record/connection_adapters/statement_pool.rb b/activerecord/lib/active_record/connection_adapters/statement_pool.rb
new file mode 100644
index 0000000000..c6b1bc8b5b
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/statement_pool.rb
@@ -0,0 +1,40 @@
+module ActiveRecord
+ module ConnectionAdapters
+ class StatementPool
+ include Enumerable
+
+ def initialize(connection, max = 1000)
+ @connection = connection
+ @max = max
+ end
+
+ def each
+ raise NotImplementedError
+ end
+
+ def key?(key)
+ raise NotImplementedError
+ end
+
+ def [](key)
+ raise NotImplementedError
+ end
+
+ def length
+ raise NotImplementedError
+ end
+
+ def []=(sql, key)
+ raise NotImplementedError
+ end
+
+ def clear
+ raise NotImplementedError
+ end
+
+ def delete(key)
+ raise NotImplementedError
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index 2dac9ea0fb..5e65e46a7d 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -314,7 +314,7 @@ module ActiveRecord
new_id = self.class.unscoped.insert attributes_values
- self.id ||= new_id
+ self.id ||= new_id if self.class.primary_key
IdentityMap.add(self) if IdentityMap.enabled?
@new_record = false
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index 13c41350fb..b3316fd1a2 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -203,11 +203,13 @@ db_namespace = namespace :db do
end
db_list = ActiveRecord::Base.connection.select_values("SELECT version FROM #{ActiveRecord::Migrator.schema_migrations_table_name}")
file_list = []
- Dir.foreach(File.join(Rails.root, 'db', 'migrate')) do |file|
- # only files matching "20091231235959_some_name.rb" pattern
- if match_data = /^(\d{14})_(.+)\.rb$/.match(file)
- status = db_list.delete(match_data[1]) ? 'up' : 'down'
- file_list << [status, match_data[1], match_data[2].humanize]
+ ActiveRecord::Migrator.migrations_paths.each do |path|
+ Dir.foreach(path) do |file|
+ # only files matching "20091231235959_some_name.rb" pattern
+ if match_data = /^(\d{14})_(.+)\.rb$/.match(file)
+ status = db_list.delete(match_data[1]) ? 'up' : 'down'
+ file_list << [status, match_data[1], match_data[2].humanize]
+ end
end
end
db_list.map! do |version|
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index 15fd1a58c8..d3f1347e03 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -94,6 +94,49 @@ module ActiveRecord
scoping { @klass.create!(*args, &block) }
end
+ # Tries to load the first record; if it fails, then <tt>create</tt> is called with the same arguments as this method.
+ #
+ # Expects arguments in the same format as <tt>Base.create</tt>.
+ #
+ # ==== Examples
+ # # Find the first user named Penélope or create a new one.
+ # User.where(:first_name => 'Penélope').first_or_create
+ # # => <User id: 1, first_name: 'Penélope', last_name: nil>
+ #
+ # # Find the first user named Penélope or create a new one.
+ # # We already have one so the existing record will be returned.
+ # User.where(:first_name => 'Penélope').first_or_create
+ # # => <User id: 1, first_name: 'Penélope', last_name: nil>
+ #
+ # # Find the first user named Scarlett or create a new one with a particular last name.
+ # User.where(:first_name => 'Scarlett').first_or_create(:last_name => 'Johansson')
+ # # => <User id: 2, first_name: 'Scarlett', last_name: 'Johansson'>
+ #
+ # # Find the first user named Scarlett or create a new one with a different last name.
+ # # We already have one so the existing record will be returned.
+ # User.where(:first_name => 'Scarlett').first_or_create do |user|
+ # user.last_name = "O'Hara"
+ # end
+ # # => <User id: 2, first_name: 'Scarlett', last_name: 'Johansson'>
+ def first_or_create(attributes = nil, options = {}, &block)
+ first || create(attributes, options, &block)
+ end
+
+ # Like <tt>first_or_create</tt> but calls <tt>create!</tt> so an exception is raised if the created record is invalid.
+ #
+ # Expects arguments in the same format as <tt>Base.create!</tt>.
+ def first_or_create!(attributes = nil, options = {}, &block)
+ first || create!(attributes, options, &block)
+ end
+
+ # Like <tt>first_or_create</tt> but calls <tt>new</tt> instead of <tt>create</tt>.
+ #
+ # Expects arguments in the same format as <tt>Base.new</tt>.
+ def first_or_new(attributes = nil, options = {}, &block)
+ first || new(attributes, options, &block)
+ end
+ alias :first_or_build :first_or_new
+
def respond_to?(method, include_private = false)
arel.respond_to?(method, include_private) ||
Array.method_defined?(method) ||
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 83d650d3f4..7eeb3dde70 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -184,7 +184,9 @@ module ActiveRecord
# Person.exists?(:name => "David")
# Person.exists?(['name LIKE ?', "%#{query}%"])
# Person.exists?
- def exists?(id = nil)
+ def exists?(id = false)
+ return false if id.nil?
+
id = id.id if ActiveRecord::Base === id
join_dependency = construct_join_dependency_for_association_find
diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb
index cccac6ffd7..4d5e469a7f 100644
--- a/activerecord/lib/active_record/timestamp.rb
+++ b/activerecord/lib/active_record/timestamp.rb
@@ -48,7 +48,9 @@ module ActiveRecord
current_time = current_time_from_proper_timezone
all_timestamp_attributes.each do |column|
- write_attribute(column.to_s, current_time) if respond_to?(column) && self.send(column).nil?
+ if respond_to?(column) && respond_to?("#{column}=") && self.send(column).nil?
+ write_attribute(column.to_s, current_time)
+ end
end
end
diff --git a/activerecord/test/cases/adapters/mysql/statement_pool_test.rb b/activerecord/test/cases/adapters/mysql/statement_pool_test.rb
new file mode 100644
index 0000000000..83de90f179
--- /dev/null
+++ b/activerecord/test/cases/adapters/mysql/statement_pool_test.rb
@@ -0,0 +1,23 @@
+require 'cases/helper'
+
+module ActiveRecord::ConnectionAdapters
+ class MysqlAdapter
+ class StatementPoolTest < ActiveRecord::TestCase
+ def test_cache_is_per_pid
+ return skip('must support fork') unless Process.respond_to?(:fork)
+
+ cache = StatementPool.new nil, 10
+ cache['foo'] = 'bar'
+ assert_equal 'bar', cache['foo']
+
+ pid = fork {
+ lookup = cache['foo'];
+ exit!(!lookup)
+ }
+
+ Process.waitpid pid
+ assert $?.success?, 'process should exit successfully'
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/adapters/postgresql/schema_test.rb b/activerecord/test/cases/adapters/postgresql/schema_test.rb
index 4c6d865d59..76c73e9dfa 100644
--- a/activerecord/test/cases/adapters/postgresql/schema_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/schema_test.rb
@@ -91,6 +91,12 @@ class SchemaTest < ActiveRecord::TestCase
end
end
+ def test_table_exists_quoted_table
+ with_schema_search_path(SCHEMA_NAME) do
+ assert(@connection.table_exists?('"things.table"'), "table should exist")
+ end
+ end
+
def test_with_schema_prefixed_table_name
assert_nothing_raised do
assert_equal COLUMNS, columns("#{SCHEMA_NAME}.#{TABLE_NAME}")
diff --git a/activerecord/test/cases/adapters/postgresql/statement_pool_test.rb b/activerecord/test/cases/adapters/postgresql/statement_pool_test.rb
new file mode 100644
index 0000000000..a82c6f67d6
--- /dev/null
+++ b/activerecord/test/cases/adapters/postgresql/statement_pool_test.rb
@@ -0,0 +1,23 @@
+require 'cases/helper'
+
+module ActiveRecord::ConnectionAdapters
+ class PostgreSQLAdapter < AbstractAdapter
+ class StatementPoolTest < ActiveRecord::TestCase
+ def test_cache_is_per_pid
+ return skip('must support fork') unless Process.respond_to?(:fork)
+
+ cache = StatementPool.new nil, 10
+ cache['foo'] = 'bar'
+ assert_equal 'bar', cache['foo']
+
+ pid = fork {
+ lookup = cache['foo'];
+ exit!(!lookup)
+ }
+
+ Process.waitpid pid
+ assert $?.success?, 'process should exit successfully'
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb b/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb
new file mode 100644
index 0000000000..ae272e2c4b
--- /dev/null
+++ b/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb
@@ -0,0 +1,24 @@
+require 'cases/helper'
+
+module ActiveRecord::ConnectionAdapters
+ class SQLiteAdapter
+ class StatementPoolTest < ActiveRecord::TestCase
+ def test_cache_is_per_pid
+ return skip('must support fork') unless Process.respond_to?(:fork)
+
+ cache = StatementPool.new nil, 10
+ cache['foo'] = 'bar'
+ assert_equal 'bar', cache['foo']
+
+ pid = fork {
+ lookup = cache['foo'];
+ exit!(!lookup)
+ }
+
+ Process.waitpid pid
+ assert $?.success?, 'process should exit successfully'
+ end
+ end
+ end
+end
+
diff --git a/activerecord/test/cases/associations/nested_through_associations_test.rb b/activerecord/test/cases/associations/nested_through_associations_test.rb
index 6122098623..530f5212a2 100644
--- a/activerecord/test/cases/associations/nested_through_associations_test.rb
+++ b/activerecord/test/cases/associations/nested_through_associations_test.rb
@@ -356,11 +356,13 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
assert_equal categories(:general), members(:groucho).club_category
end
- def test_has_one_through_has_one_through_with_belongs_to_source_reflection_with_default_scope_preload_on_the_through_model
+ def test_joins_and_includes_from_through_models_not_included_in_association
prev_default_scope = Club.default_scopes
- Club.default_scopes = [Club.preload(:category)]
- assert_equal categories(:general), members(:groucho).club_category
+ [:includes, :preload, :joins, :eager_load].each do |q|
+ Club.default_scopes = [Club.send(q, :category)]
+ assert_equal categories(:general), members(:groucho).reload.club_category
+ end
ensure
Club.default_scopes = prev_default_scope
end
diff --git a/activerecord/test/cases/attribute_methods/read_test.rb b/activerecord/test/cases/attribute_methods/read_test.rb
index 3641031d12..e03ed33591 100644
--- a/activerecord/test/cases/attribute_methods/read_test.rb
+++ b/activerecord/test/cases/attribute_methods/read_test.rb
@@ -35,6 +35,7 @@ module ActiveRecord
end
def self.serialized_attributes; {}; end
+ def self.base_class; self; end
end
end
diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb
index dbf5a1ba76..e324a252dd 100644
--- a/activerecord/test/cases/attribute_methods_test.rb
+++ b/activerecord/test/cases/attribute_methods_test.rb
@@ -608,7 +608,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
topic = @target.new(:title => "The pros and cons of programming naked.")
assert !topic.respond_to?(:title)
exception = assert_raise(NoMethodError) { topic.title }
- assert_match %r(^Attempt to call private method), exception.message
+ assert exception.message.include?("private method")
assert_equal "I'm private", topic.send(:title)
end
@@ -618,7 +618,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
topic = @target.new
assert !topic.respond_to?(:title=)
exception = assert_raise(NoMethodError) { topic.title = "Pants"}
- assert_match %r(^Attempt to call private method), exception.message
+ assert exception.message.include?("private method")
topic.send(:title=, "Very large pants")
end
@@ -628,7 +628,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
topic = @target.new(:title => "Isaac Newton's pants")
assert !topic.respond_to?(:title?)
exception = assert_raise(NoMethodError) { topic.title? }
- assert_match %r(^Attempt to call private method), exception.message
+ assert exception.message.include?("private method")
assert topic.send(:title?)
end
@@ -659,6 +659,37 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_equal %w(preferences), Contact.serialized_attributes.keys
end
+ def test_instance_method_should_be_defined_on_the_base_class
+ subklass = Class.new(Topic)
+
+ Topic.define_attribute_methods
+
+ instance = subklass.new
+ instance.id = 5
+ assert_equal 5, instance.id
+ assert subklass.method_defined?(:id), "subklass is missing id method"
+
+ Topic.undefine_attribute_methods
+
+ assert_equal 5, instance.id
+ assert subklass.method_defined?(:id), "subklass is missing id method"
+ end
+
+ def test_dispatching_column_attributes_through_method_missing_deprecated
+ Topic.define_attribute_methods
+
+ topic = Topic.new(:id => 5)
+ topic.id = 5
+
+ topic.method(:id).owner.send(:remove_method, :id)
+
+ assert_deprecated do
+ assert_equal 5, topic.id
+ end
+ ensure
+ Topic.undefine_attribute_methods
+ end
+
private
def cached_columns
@cached_columns ||= (time_related_columns_on_topic + serialized_columns_on_topic).map(&:name)
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index 87f5b5ee81..cb92f79e0e 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -22,6 +22,7 @@ require 'models/person'
require 'models/edge'
require 'models/joke'
require 'models/bulb'
+require 'models/bird'
require 'rexml/document'
require 'active_support/core_ext/exception'
require 'bcrypt'
@@ -278,6 +279,37 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal(true, cb.frickinawesome)
end
+ def test_first_or_create
+ parrot = Bird.first_or_create(:color => 'green', :name => 'parrot')
+ assert parrot.persisted?
+ the_same_parrot = Bird.first_or_create(:color => 'yellow', :name => 'macaw')
+ assert_equal parrot, the_same_parrot
+ end
+
+ def test_first_or_create_bang
+ assert_raises(ActiveRecord::RecordInvalid) { Bird.first_or_create! }
+ parrot = Bird.first_or_create!(:color => 'green', :name => 'parrot')
+ assert parrot.persisted?
+ the_same_parrot = Bird.first_or_create!(:color => 'yellow', :name => 'macaw')
+ assert_equal parrot, the_same_parrot
+ end
+
+ def test_first_or_new
+ parrot = Bird.first_or_new(:color => 'green', :name => 'parrot')
+ assert_kind_of Bird, parrot
+ assert !parrot.persisted?
+ assert parrot.new_record?
+ assert parrot.valid?
+ end
+
+ def test_first_or_build
+ parrot = Bird.first_or_build(:color => 'green', :name => 'parrot')
+ assert_kind_of Bird, parrot
+ assert !parrot.persisted?
+ assert parrot.new_record?
+ assert parrot.valid?
+ end
+
def test_load
topics = Topic.find(:all, :order => 'id')
assert_equal(4, topics.size)
diff --git a/activerecord/test/cases/connection_adapters/connection_handler_test.rb b/activerecord/test/cases/connection_adapters/connection_handler_test.rb
index abf317768f..bd0d161838 100644
--- a/activerecord/test/cases/connection_adapters/connection_handler_test.rb
+++ b/activerecord/test/cases/connection_adapters/connection_handler_test.rb
@@ -6,7 +6,12 @@ module ActiveRecord
def setup
@handler = ConnectionHandler.new
@handler.establish_connection 'america', Base.connection_pool.spec
- @klass = Struct.new(:name).new('america')
+ @klass = Class.new do
+ def self.name; 'america'; end
+ end
+ @subklass = Class.new(@klass) do
+ def self.name; 'north america'; end
+ end
end
def test_retrieve_connection
@@ -28,6 +33,20 @@ module ActiveRecord
def test_retrieve_connection_pool
assert_not_nil @handler.retrieve_connection_pool(@klass)
end
+
+ def test_retrieve_connection_pool_uses_superclass_when_no_subclass_connection
+ assert_not_nil @handler.retrieve_connection_pool(@subklass)
+ end
+
+ def test_retrieve_connection_pool_uses_superclass_pool_after_subclass_establish_and_remove
+ @handler.establish_connection 'north america', Base.connection_pool.spec
+ assert_not_same @handler.retrieve_connection_pool(@klass),
+ @handler.retrieve_connection_pool(@subklass)
+
+ @handler.remove_connection @subklass
+ assert_same @handler.retrieve_connection_pool(@klass),
+ @handler.retrieve_connection_pool(@subklass)
+ end
end
end
end
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index d840d38678..3088ab012f 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -48,6 +48,15 @@ class FinderTest < ActiveRecord::TestCase
assert Topic.exists?
end
+ # exists? should handle nil for id's that come from URLs and always return false
+ # (example: Topic.exists?(params[:id])) where params[:id] is nil
+ def test_exists_with_nil_arg
+ assert !Topic.exists?(nil)
+ assert Topic.exists?
+ assert !Topic.first.replies.exists?(nil)
+ assert Topic.first.replies.exists?
+ end
+
def test_does_not_exist_with_empty_table_and_no_args_given
Topic.delete_all
assert !Topic.exists?
@@ -244,8 +253,8 @@ class FinderTest < ActiveRecord::TestCase
end
def test_first_and_last_with_integer_should_use_sql_limit
- assert_sql(/LIMIT 2/) { Topic.first(2).entries }
- assert_sql(/LIMIT 5/) { Topic.last(5).entries }
+ assert_sql(/LIMIT 2|ROWNUM <= 2/) { Topic.first(2).entries }
+ assert_sql(/LIMIT 5|ROWNUM <= 5/) { Topic.last(5).entries }
end
def test_last_with_integer_and_order_should_keep_the_order
diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb
index 9cd07fa8a5..adfd8e83a1 100644
--- a/activerecord/test/cases/persistence_test.rb
+++ b/activerecord/test/cases/persistence_test.rb
@@ -202,9 +202,12 @@ class PersistencesTest < ActiveRecord::TestCase
end
def test_create_columns_not_equal_attributes
- topic = Topic.new
- topic.title = 'Another New Topic'
- topic.send :write_attribute, 'does_not_exist', 'test'
+ topic = Topic.allocate.init_with(
+ 'attributes' => {
+ 'title' => 'Another New Topic',
+ 'does_not_exist' => 'test'
+ }
+ )
assert_nothing_raised { topic.save }
end
@@ -249,9 +252,11 @@ class PersistencesTest < ActiveRecord::TestCase
topic.title = "Still another topic"
topic.save
- topicReloaded = Topic.find(topic.id)
- topicReloaded.title = "A New Topic"
- topicReloaded.send :write_attribute, 'does_not_exist', 'test'
+ topicReloaded = Topic.allocate
+ topicReloaded.init_with(
+ 'attributes' => topic.attributes.merge('does_not_exist' => 'test')
+ )
+ topicReloaded.title = 'A New Topic'
assert_nothing_raised { topicReloaded.save }
end
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index da96afd718..0f50ac9a2b 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -863,6 +863,134 @@ class RelationTest < ActiveRecord::TestCase
assert_equal 'hen', hen.name
end
+ def test_first_or_create
+ parrot = Bird.where(:color => 'green').first_or_create(:name => 'parrot')
+ assert_kind_of Bird, parrot
+ assert parrot.persisted?
+ assert_equal 'parrot', parrot.name
+ assert_equal 'green', parrot.color
+
+ same_parrot = Bird.where(:color => 'green').first_or_create(:name => 'parakeet')
+ assert_kind_of Bird, same_parrot
+ assert same_parrot.persisted?
+ assert_equal parrot, same_parrot
+ end
+
+ def test_first_or_create_with_no_parameters
+ parrot = Bird.where(:color => 'green').first_or_create
+ assert_kind_of Bird, parrot
+ assert !parrot.persisted?
+ assert_equal 'green', parrot.color
+ end
+
+ def test_first_or_create_with_block
+ parrot = Bird.where(:color => 'green').first_or_create { |bird| bird.name = 'parrot' }
+ assert_kind_of Bird, parrot
+ assert parrot.persisted?
+ assert_equal 'green', parrot.color
+ assert_equal 'parrot', parrot.name
+
+ same_parrot = Bird.where(:color => 'green').first_or_create { |bird| bird.name = 'parakeet' }
+ assert_equal parrot, same_parrot
+ end
+
+ def test_first_or_create_with_array
+ several_green_birds = Bird.where(:color => 'green').first_or_create([{:name => 'parrot'}, {:name => 'parakeet'}])
+ assert_kind_of Array, several_green_birds
+ several_green_birds.each { |bird| assert bird.persisted? }
+
+ same_parrot = Bird.where(:color => 'green').first_or_create([{:name => 'hummingbird'}, {:name => 'macaw'}])
+ assert_kind_of Bird, same_parrot
+ assert_equal several_green_birds.first, same_parrot
+ end
+
+ def test_first_or_create_bang_with_valid_options
+ parrot = Bird.where(:color => 'green').first_or_create!(:name => 'parrot')
+ assert_kind_of Bird, parrot
+ assert parrot.persisted?
+ assert_equal 'parrot', parrot.name
+ assert_equal 'green', parrot.color
+
+ same_parrot = Bird.where(:color => 'green').first_or_create!(:name => 'parakeet')
+ assert_kind_of Bird, same_parrot
+ assert same_parrot.persisted?
+ assert_equal parrot, same_parrot
+ end
+
+ def test_first_or_create_bang_with_invalid_options
+ assert_raises(ActiveRecord::RecordInvalid) { Bird.where(:color => 'green').first_or_create!(:pirate_id => 1) }
+ end
+
+ def test_first_or_create_bang_with_no_parameters
+ assert_raises(ActiveRecord::RecordInvalid) { Bird.where(:color => 'green').first_or_create! }
+ end
+
+ def test_first_or_create_bang_with_valid_block
+ parrot = Bird.where(:color => 'green').first_or_create! { |bird| bird.name = 'parrot' }
+ assert_kind_of Bird, parrot
+ assert parrot.persisted?
+ assert_equal 'green', parrot.color
+ assert_equal 'parrot', parrot.name
+
+ same_parrot = Bird.where(:color => 'green').first_or_create! { |bird| bird.name = 'parakeet' }
+ assert_equal parrot, same_parrot
+ end
+
+ def test_first_or_create_bang_with_invalid_block
+ assert_raise(ActiveRecord::RecordInvalid) do
+ Bird.where(:color => 'green').first_or_create! { |bird| bird.pirate_id = 1 }
+ end
+ end
+
+ def test_first_or_create_with_valid_array
+ several_green_birds = Bird.where(:color => 'green').first_or_create!([{:name => 'parrot'}, {:name => 'parakeet'}])
+ assert_kind_of Array, several_green_birds
+ several_green_birds.each { |bird| assert bird.persisted? }
+
+ same_parrot = Bird.where(:color => 'green').first_or_create!([{:name => 'hummingbird'}, {:name => 'macaw'}])
+ assert_kind_of Bird, same_parrot
+ assert_equal several_green_birds.first, same_parrot
+ end
+
+ def test_first_or_create_with_invalid_array
+ assert_raises(ActiveRecord::RecordInvalid) { Bird.where(:color => 'green').first_or_create!([ {:name => 'parrot'}, {:pirate_id => 1} ]) }
+ end
+
+ def test_first_or_new
+ parrot = Bird.where(:color => 'green').first_or_new(:name => 'parrot')
+ assert_kind_of Bird, parrot
+ assert !parrot.persisted?
+ assert parrot.valid?
+ assert parrot.new_record?
+ assert_equal 'parrot', parrot.name
+ assert_equal 'green', parrot.color
+ end
+
+ def test_first_or_new_with_no_parameters
+ parrot = Bird.where(:color => 'green').first_or_new
+ assert_kind_of Bird, parrot
+ assert !parrot.persisted?
+ assert !parrot.valid?
+ assert parrot.new_record?
+ assert_equal 'green', parrot.color
+ end
+
+ def test_first_or_new_with_block
+ parrot = Bird.where(:color => 'green').first_or_new { |bird| bird.name = 'parrot' }
+ assert_kind_of Bird, parrot
+ assert !parrot.persisted?
+ assert parrot.valid?
+ assert parrot.new_record?
+ assert_equal 'green', parrot.color
+ assert_equal 'parrot', parrot.name
+ end
+
+ def test_first_or_build_is_alias_for_first_or_new
+ birds = Bird.scoped
+ assert birds.respond_to?(:first_or_build)
+ assert_equal birds.method(:first_or_new), birds.method(:first_or_build)
+ end
+
def test_explicit_create_scope
hens = Bird.where(:name => 'hen')
assert_equal 'hen', hens.new.name
diff --git a/activerecord/test/cases/serialization_test.rb b/activerecord/test/cases/serialization_test.rb
index 382d659289..61b04b3e37 100644
--- a/activerecord/test/cases/serialization_test.rb
+++ b/activerecord/test/cases/serialization_test.rb
@@ -7,12 +7,13 @@ class SerializationTest < ActiveRecord::TestCase
def setup
@contact_attributes = {
- :name => 'aaron stack',
- :age => 25,
- :avatar => 'binarydata',
- :created_at => Time.utc(2006, 8, 1),
- :awesome => false,
- :preferences => { :gem => '<strong>ruby</strong>' }
+ :name => 'aaron stack',
+ :age => 25,
+ :avatar => 'binarydata',
+ :created_at => Time.utc(2006, 8, 1),
+ :awesome => false,
+ :preferences => { :gem => '<strong>ruby</strong>' },
+ :alternative_id => nil
}
end
diff --git a/activerecord/test/cases/session_store/session_test.rb b/activerecord/test/cases/session_store/session_test.rb
index 669c0b7b4d..258cee7aba 100644
--- a/activerecord/test/cases/session_store/session_test.rb
+++ b/activerecord/test/cases/session_store/session_test.rb
@@ -36,6 +36,7 @@ module ActiveRecord
end
def test_find_by_sess_id_compat
+ Session.reset_column_information
klass = Class.new(Session) do
def self.session_id_column
'sessid'
@@ -53,6 +54,7 @@ module ActiveRecord
assert_equal session.sessid, found.session_id
ensure
klass.drop_table!
+ Session.reset_column_information
end
def test_find_by_session_id
diff --git a/activerecord/test/models/contact.rb b/activerecord/test/models/contact.rb
index e081eee661..3d15c7fbed 100644
--- a/activerecord/test/models/contact.rb
+++ b/activerecord/test/models/contact.rb
@@ -11,12 +11,13 @@ class Contact < ActiveRecord::Base
connection.merge_column('contacts', name, sql_type, options)
end
- column :name, :string
- column :age, :integer
- column :avatar, :binary
- column :created_at, :datetime
- column :awesome, :boolean
- column :preferences, :string
+ column :name, :string
+ column :age, :integer
+ column :avatar, :binary
+ column :created_at, :datetime
+ column :awesome, :boolean
+ column :preferences, :string
+ column :alternative_id, :integer
serialize :preferences
diff --git a/activerecord/test/models/topic.rb b/activerecord/test/models/topic.rb
index 6440dbe8ab..fe424e61b2 100644
--- a/activerecord/test/models/topic.rb
+++ b/activerecord/test/models/topic.rb
@@ -78,11 +78,12 @@ class Topic < ActiveRecord::Base
after_initialize :set_email_address
+ def approved=(val)
+ @custom_approved = val
+ write_attribute(:approved, val)
+ end
+
protected
- def approved=(val)
- @custom_approved = val
- write_attribute(:approved, val)
- end
def default_written_on
self.written_on = Time.now unless attribute_present?("written_on")
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 64e0452100..9d5ad16a3c 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -47,6 +47,7 @@ ActiveRecord::Schema.define do
create_table :audit_logs, :force => true do |t|
t.column :message, :string, :null=>false
t.column :developer_id, :integer, :null=>false
+ t.integer :unvalidated_developer_id
end
create_table :authors, :force => true do |t|
@@ -156,6 +157,7 @@ ActiveRecord::Schema.define do
t.string :type
t.integer :taggings_count, :default => 0
t.integer :children_count, :default => 0
+ t.integer :parent_id
end
create_table :companies, :force => true do |t|
@@ -461,6 +463,7 @@ ActiveRecord::Schema.define do
create_table :pirates, :force => true do |t|
t.column :catchphrase, :string
t.column :parrot_id, :integer
+ t.integer :non_validated_parrot_id
t.column :created_on, :datetime
t.column :updated_on, :datetime
end
@@ -529,6 +532,7 @@ ActiveRecord::Schema.define do
create_table :ships, :force => true do |t|
t.string :name
t.integer :pirate_id
+ t.integer :update_only_pirate_id
t.datetime :created_at
t.datetime :created_on
t.datetime :updated_at
@@ -663,7 +667,9 @@ ActiveRecord::Schema.define do
t.string :description
t.integer :man_id
t.integer :polymorphic_man_id
- t.string :polymorphic_man_type
+ t.string :polymorphic_man_type
+ t.integer :horrible_polymorphic_man_id
+ t.string :horrible_polymorphic_man_type
end
create_table :interests, :force => true do |t|