aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
authorwycats <wycats@gmail.com>2010-04-10 17:22:52 -0400
committerwycats <wycats@gmail.com>2010-04-10 17:22:52 -0400
commit87f7093ee3306f417e1136d947eba200d40ff8e7 (patch)
treeba70dbdaf67e12fc067bb5d8343d7681932452ef /activerecord
parentee8e9d548472fb8cb8792a569e579c6513be77d6 (diff)
parent381f877bbbbf81d679f5be3b7ac7e961d41502bd (diff)
downloadrails-87f7093ee3306f417e1136d947eba200d40ff8e7.tar.gz
rails-87f7093ee3306f417e1136d947eba200d40ff8e7.tar.bz2
rails-87f7093ee3306f417e1136d947eba200d40ff8e7.zip
Merge branch 'master' into docrails_master
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/Rakefile1
-rw-r--r--activerecord/lib/active_record/associations/association_collection.rb13
-rw-r--r--activerecord/lib/active_record/attribute_methods/dirty.rb6
-rw-r--r--activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb8
-rwxr-xr-xactiverecord/lib/active_record/base.rb76
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb21
-rw-r--r--activerecord/lib/active_record/railties/databases.rake10
-rw-r--r--activerecord/lib/active_record/version.rb2
-rw-r--r--activerecord/test/cases/active_schema_test_postgresql.rb7
-rw-r--r--activerecord/test/cases/adapter_test.rb6
-rw-r--r--activerecord/test/cases/associations/belongs_to_associations_test.rb8
-rw-r--r--activerecord/test/cases/dirty_test.rb95
-rw-r--r--activerecord/test/cases/migration_test.rb64
-rw-r--r--activerecord/test/cases/named_scope_test.rb10
-rw-r--r--activerecord/test/cases/xml_serialization_test.rb12
16 files changed, 221 insertions, 120 deletions
diff --git a/activerecord/Rakefile b/activerecord/Rakefile
index e638da0f3e..bf05389eae 100644
--- a/activerecord/Rakefile
+++ b/activerecord/Rakefile
@@ -50,6 +50,7 @@ end
t.libs << "test" << connection_path
t.test_files=Dir.glob( "test/cases/**/*_test{,_#{adapter_short}}.rb" ).sort
t.verbose = true
+ t.warning = true
}
task "isolated_test_#{adapter}" do
diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb
index 5ecdf1ac8d..b808f8c306 100644
--- a/activerecord/lib/active_record/associations/association_collection.rb
+++ b/activerecord/lib/active_record/associations/association_collection.rb
@@ -105,6 +105,7 @@ module ActiveRecord
def reset
reset_target!
+ reset_named_scopes_cache!
@loaded = false
end
@@ -146,7 +147,7 @@ module ActiveRecord
# has_many :books
# end
#
- # Author.find(:first).books.transaction do
+ # Author.first.books.transaction do
# # same effect as calling Book.transaction
# end
def transaction(*args)
@@ -162,6 +163,7 @@ module ActiveRecord
load_target
delete(@target)
reset_target!
+ reset_named_scopes_cache!
end
# Calculate sum using SQL, not Enumerable
@@ -250,6 +252,7 @@ module ActiveRecord
load_target
destroy(@target)
reset_target!
+ reset_named_scopes_cache!
end
def create(attrs = {})
@@ -406,8 +409,8 @@ module ActiveRecord
super
end
elsif @reflection.klass.scopes[method]
- @_scopes ||= {}
- @_scopes[method] ||= with_scope(construct_scope) { @reflection.klass.send(method, *args) }
+ @_named_scopes_cache ||= {}
+ @_named_scopes_cache[method] ||= with_scope(construct_scope) { @reflection.klass.send(method, *args) }
else
with_scope(construct_scope) do
if block_given?
@@ -428,6 +431,10 @@ module ActiveRecord
@target = Array.new
end
+ def reset_named_scopes_cache!
+ @_named_scopes_cache = {}
+ end
+
def find_target
records =
if @reflection.options[:finder_sql]
diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb
index 435aea9b09..36f2a9777c 100644
--- a/activerecord/lib/active_record/attribute_methods/dirty.rb
+++ b/activerecord/lib/active_record/attribute_methods/dirty.rb
@@ -52,6 +52,8 @@ module ActiveRecord
@changed_attributes.delete(attr) unless field_changed?(attr, old, value)
else
old = clone_attribute_value(:read_attribute, attr)
+ # Save Time objects as TimeWithZone if time_zone_aware_attributes == true
+ old = old.in_time_zone if clone_with_time_zone_conversion_attribute?(attr, old)
@changed_attributes[attr] = old if field_changed?(attr, old, value)
end
@@ -84,6 +86,10 @@ module ActiveRecord
old != value
end
+
+ def clone_with_time_zone_conversion_attribute?(attr, old)
+ old.class.name == "Time" && time_zone_aware_attributes && !skip_time_zone_conversion_for_attributes.include?(attr.to_sym)
+ end
end
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
index a8e3e28a7a..783d61383b 100644
--- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
+++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
@@ -17,7 +17,7 @@ module ActiveRecord
# This enhanced read method automatically converts the UTC time stored in the database to the time zone stored in Time.zone.
def define_method_attribute(attr_name)
if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name])
- method_body = <<-EOV
+ method_body, line = <<-EOV, __LINE__ + 1
def #{attr_name}(reload = false)
cached = @attributes_cache['#{attr_name}']
return cached if cached && !reload
@@ -25,7 +25,7 @@ module ActiveRecord
@attributes_cache['#{attr_name}'] = time.acts_like?(:time) ? time.in_time_zone : time
end
EOV
- generated_attribute_methods.module_eval(method_body, __FILE__, __LINE__)
+ generated_attribute_methods.module_eval(method_body, __FILE__, line)
else
super
end
@@ -35,7 +35,7 @@ module ActiveRecord
# This enhanced write method will automatically convert the time passed to it to the zone stored in Time.zone.
def define_method_attribute=(attr_name)
if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name])
- method_body = <<-EOV
+ method_body, line = <<-EOV, __LINE__ + 1
def #{attr_name}=(time)
unless time.acts_like?(:time)
time = time.is_a?(String) ? Time.zone.parse(time) : time.to_time rescue time
@@ -44,7 +44,7 @@ module ActiveRecord
write_attribute(:#{attr_name}, time)
end
EOV
- generated_attribute_methods.module_eval(method_body, __FILE__, __LINE__)
+ generated_attribute_methods.module_eval(method_body, __FILE__, line)
else
super
end
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index f0b107255c..d544c48a4c 100755
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -56,15 +56,15 @@ module ActiveRecord #:nodoc:
#
# class User < ActiveRecord::Base
# def self.authenticate_unsafely(user_name, password)
- # find(:first, :conditions => "user_name = '#{user_name}' AND password = '#{password}'")
+ # where("user_name = '#{user_name}' AND password = '#{password}'").first
# end
#
# def self.authenticate_safely(user_name, password)
- # find(:first, :conditions => [ "user_name = ? AND password = ?", user_name, password ])
+ # where("user_name = ? AND password = ?", user_name, password).first
# end
#
# def self.authenticate_safely_simply(user_name, password)
- # find(:first, :conditions => { :user_name => user_name, :password => password })
+ # where(:user_name => user_name, :password => password).first
# end
# end
#
@@ -77,30 +77,30 @@ module ActiveRecord #:nodoc:
# question mark is supposed to represent. In those cases, you can resort to named bind variables instead. That's done by replacing
# the question marks with symbols and supplying a hash with values for the matching symbol keys:
#
- # Company.find(:first, :conditions => [
+ # Company.where(
# "id = :id AND name = :name AND division = :division AND created_at > :accounting_date",
# { :id => 3, :name => "37signals", :division => "First", :accounting_date => '2005-01-01' }
- # ])
+ # ).first
#
# Similarly, a simple hash without a statement will generate conditions based on equality with the SQL AND
# operator. For instance:
#
- # Student.find(:all, :conditions => { :first_name => "Harvey", :status => 1 })
- # Student.find(:all, :conditions => params[:student])
+ # Student.where(:first_name => "Harvey", :status => 1)
+ # Student.where(params[:student])
#
# A range may be used in the hash to use the SQL BETWEEN operator:
#
- # Student.find(:all, :conditions => { :grade => 9..12 })
+ # Student.where(:grade => 9..12)
#
# An array may be used in the hash to use the SQL IN operator:
#
- # Student.find(:all, :conditions => { :grade => [9,11,12] })
+ # Student.where(:grade => [9,11,12])
#
# When joining tables, nested hashes or keys written in the form 'table_name.column_name' can be used to qualify the table name of a
# particular condition. For instance:
#
- # Student.find(:all, :conditions => { :schools => { :type => 'public' }}, :joins => :schools)
- # Student.find(:all, :conditions => { 'schools.type' => 'public' }, :joins => :schools)
+ # Student.joins(:schools).where(:schools => { :type => 'public' })
+ # Student.joins(:schools).where('schools.type' => 'public' )
#
# == Overwriting default accessors
#
@@ -153,18 +153,18 @@ module ActiveRecord #:nodoc:
# Dynamic attribute-based finders are a cleaner way of getting (and/or creating) objects by simple queries without turning to SQL. They work by
# appending the name of an attribute to <tt>find_by_</tt>, <tt>find_last_by_</tt>, or <tt>find_all_by_</tt>, so you get finders like <tt>Person.find_by_user_name</tt>,
# <tt>Person.find_all_by_last_name</tt>, and <tt>Payment.find_by_transaction_id</tt>. So instead of writing
- # <tt>Person.find(:first, :conditions => ["user_name = ?", user_name])</tt>, you just do <tt>Person.find_by_user_name(user_name)</tt>.
- # And instead of writing <tt>Person.find(:all, :conditions => ["last_name = ?", last_name])</tt>, you just do <tt>Person.find_all_by_last_name(last_name)</tt>.
+ # <tt>Person.where(:user_name => user_name).first</tt>, you just do <tt>Person.find_by_user_name(user_name)</tt>.
+ # And instead of writing <tt>Person.where(:last_name => last_name).all</tt>, you just do <tt>Person.find_all_by_last_name(last_name)</tt>.
#
# It's also possible to use multiple attributes in the same find by separating them with "_and_", so you get finders like
# <tt>Person.find_by_user_name_and_password</tt> or even <tt>Payment.find_by_purchaser_and_state_and_country</tt>. So instead of writing
- # <tt>Person.find(:first, :conditions => ["user_name = ? AND password = ?", user_name, password])</tt>, you just do
+ # <tt>Person.where(:user_name => user_name, :password => password).first</tt>, you just do
# <tt>Person.find_by_user_name_and_password(user_name, password)</tt>.
#
- # It's even possible to use all the additional parameters to find. For example, the full interface for <tt>Payment.find_all_by_amount</tt>
- # is actually <tt>Payment.find_all_by_amount(amount, options)</tt>. And the full interface to <tt>Person.find_by_user_name</tt> is
- # actually <tt>Person.find_by_user_name(user_name, options)</tt>. So you could call <tt>Payment.find_all_by_amount(50, :order => "created_on")</tt>.
- # Also you may call <tt>Payment.find_last_by_amount(amount, options)</tt> returning the last record matching that amount and options.
+ # It's even possible to call these dynamic finder methods on relations and named scopes. For example :
+ #
+ # Payment.order("created_on").find_all_by_amount(50)
+ # Payment.pending.find_last_by_amount(100)
#
# The same dynamic finder style can be used to create the object if it doesn't already exist. This dynamic finder is called with
# <tt>find_or_create_by_</tt> and will return the object if it already exists and otherwise creates it, then returns it. Protected attributes won't be set unless they are given in a block. For example:
@@ -224,7 +224,7 @@ module ActiveRecord #:nodoc:
# class PriorityClient < Client; end
#
# When you do <tt>Firm.create(:name => "37signals")</tt>, this record will be saved in the companies table with type = "Firm". You can then
- # fetch this row again using <tt>Company.find(:first, "name = '37signals'")</tt> and it will return a Firm object.
+ # fetch this row again using <tt>Company.where(:name => '37signals').first</tt> and it will return a Firm object.
#
# If you don't have a type column defined in your table, single-table inheritance won't be triggered. In that case, it'll work just
# like normal subclasses with no special magic for differentiating between them or reloading the right type with find.
@@ -1117,10 +1117,6 @@ module ActiveRecord #:nodoc:
# It's even possible to use all the additional parameters to +find+. For example, the full interface for +find_all_by_amount+
# is actually <tt>find_all_by_amount(amount, options)</tt>.
#
- # Also enables dynamic scopes like scoped_by_user_name(user_name) and scoped_by_user_name_and_password(user_name, password) that
- # are turned into scoped(:conditions => ["user_name = ?", user_name]) and scoped(:conditions => ["user_name = ? AND password = ?", user_name, password])
- # respectively.
- #
# Each dynamic finder, scope or initializer/creator is also defined in the class after it is first invoked, so that future
# attempts to use it do not run through method_missing.
def method_missing(method_id, *arguments, &block)
@@ -1138,7 +1134,7 @@ module ActiveRecord #:nodoc:
attribute_names = match.attribute_names
super unless all_attributes_exists?(attribute_names)
if match.scope?
- self.class_eval %{
+ self.class_eval <<-METHOD, __FILE__, __LINE__ + 1
def self.#{method_id}(*args) # def self.scoped_by_user_name_and_password(*args)
options = args.extract_options! # options = args.extract_options!
attributes = construct_attributes_from_arguments( # attributes = construct_attributes_from_arguments(
@@ -1147,7 +1143,7 @@ module ActiveRecord #:nodoc:
#
scoped(:conditions => attributes) # scoped(:conditions => attributes)
end # end
- }, __FILE__, __LINE__
+ METHOD
send(method_id, *arguments)
end
else
@@ -1196,12 +1192,12 @@ module ActiveRecord #:nodoc:
protected
# Scope parameters to method calls within the block. Takes a hash of method_name => parameters hash.
- # method_name may be <tt>:find</tt> or <tt>:create</tt>. <tt>:find</tt> parameters may include the <tt>:conditions</tt>, <tt>:joins</tt>,
- # <tt>:include</tt>, <tt>:offset</tt>, <tt>:limit</tt>, and <tt>:readonly</tt> options. <tt>:create</tt> parameters are an attributes hash.
+ # method_name may be <tt>:find</tt> or <tt>:create</tt>. <tt>:find</tt> parameter is <tt>Relation</tt> while
+ # <tt>:create</tt> parameters are an attributes hash.
#
# class Article < ActiveRecord::Base
# def self.create_with_scope
- # with_scope(:find => { :conditions => "blog_id = 1" }, :create => { :blog_id => 1 }) do
+ # with_scope(:find => where(:blog_id => 1), :create => { :blog_id => 1 }) do
# find(1) # => SELECT * from articles WHERE blog_id = 1 AND id = 1
# a = create(1)
# a.blog_id # => 1
@@ -1210,20 +1206,20 @@ module ActiveRecord #:nodoc:
# end
#
# In nested scopings, all previous parameters are overwritten by the innermost rule, with the exception of
- # <tt>:conditions</tt>, <tt>:include</tt>, and <tt>:joins</tt> options in <tt>:find</tt>, which are merged.
+ # <tt>where</tt>, <tt>includes</tt>, and <tt>joins</tt> operations in <tt>Relation</tt>, which are merged.
#
- # <tt>:joins</tt> options are uniqued so multiple scopes can join in the same table without table aliasing
+ # <tt>joins</tt> operations are uniqued so multiple scopes can join in the same table without table aliasing
# problems. If you need to join multiple tables, but still want one of the tables to be uniqued, use the
# array of strings format for your joins.
#
# class Article < ActiveRecord::Base
# def self.find_with_scope
- # with_scope(:find => { :conditions => "blog_id = 1", :limit => 1 }, :create => { :blog_id => 1 }) do
- # with_scope(:find => { :limit => 10 }) do
- # find(:all) # => SELECT * from articles WHERE blog_id = 1 LIMIT 10
+ # with_scope(:find => where(:blog_id => 1).limit(1), :create => { :blog_id => 1 }) do
+ # with_scope(:find => limit(10)) do
+ # all # => SELECT * from articles WHERE blog_id = 1 LIMIT 10
# end
- # with_scope(:find => { :conditions => "author_id = 3" }) do
- # find(:all) # => SELECT * from articles WHERE blog_id = 1 AND author_id = 3 LIMIT 1
+ # with_scope(:find => where(:author_id => 3)) do
+ # all # => SELECT * from articles WHERE blog_id = 1 AND author_id = 3 LIMIT 1
# end
# end
# end
@@ -1233,9 +1229,9 @@ module ActiveRecord #:nodoc:
#
# class Article < ActiveRecord::Base
# def self.find_with_exclusive_scope
- # with_scope(:find => { :conditions => "blog_id = 1", :limit => 1 }) do
- # with_exclusive_scope(:find => { :limit => 10 })
- # find(:all) # => SELECT * from articles LIMIT 10
+ # with_scope(:find => where(:blog_id => 1).limit(1)) do
+ # with_exclusive_scope(:find => limit(10))
+ # all # => SELECT * from articles LIMIT 10
# end
# end
# end
@@ -1318,9 +1314,9 @@ module ActiveRecord #:nodoc:
modularized_name = type_name_with_module(type_name)
silence_warnings do
begin
- class_eval(modularized_name, __FILE__, __LINE__)
+ class_eval(modularized_name, __FILE__)
rescue NameError
- class_eval(type_name, __FILE__, __LINE__)
+ class_eval(type_name, __FILE__)
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
index 1e095110f2..e5d100b51b 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
@@ -15,7 +15,7 @@ module ActiveRecord
def dirties_query_cache(base, *method_names)
method_names.each do |method_name|
- base.class_eval <<-end_code, __FILE__, __LINE__
+ base.class_eval <<-end_code, __FILE__, __LINE__ + 1
def #{method_name}_with_query_dirty(*args) # def update_with_query_dirty(*args)
clear_query_cache if @query_cache_enabled # clear_query_cache if @query_cache_enabled
#{method_name}_without_query_dirty(*args) # update_without_query_dirty(*args)
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 2395a744a3..68ee88bba4 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -658,27 +658,6 @@ module ActiveRecord
end
end
- # Creates a schema for the given user
- #
- # Example:
- # create_schema('products', 'postgres')
- def create_schema(schema_name, pg_username)
- execute("CREATE SCHEMA \"#{schema_name}\" AUTHORIZATION \"#{pg_username}\"")
- end
-
- # Drops a schema
- #
- # Example:
- # drop_schema('products')
- def drop_schema(schema_name)
- execute("DROP SCHEMA \"#{schema_name}\"")
- end
-
- # Returns an array of all schemas in the database
- def all_schemas
- query('SELECT schema_name FROM information_schema.schemata').flatten
- end
-
# Returns the list of all tables in the schema search path or a specified schema.
def tables(name = nil)
query(<<-SQL, name).map { |row| row[0] }
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index a107befef3..2b53afc68b 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -84,15 +84,9 @@ namespace :db do
end
end
when 'postgresql'
- @encoding = config['encoding'] || ENV['CHARSET'] || 'utf8'
- schema_search_path = config['schema_search_path'] || 'public'
- first_in_schema_search_path = schema_search_path.split(',').first.strip
+ @encoding = config[:encoding] || ENV['CHARSET'] || 'utf8'
begin
ActiveRecord::Base.establish_connection(config.merge('database' => 'postgres', 'schema_search_path' => 'public'))
- unless ActiveRecord::Base.connection.all_schemas.include?(first_in_schema_search_path)
- ActiveRecord::Base.connection.create_schema(first_in_schema_search_path, config['username'])
- $stderr.puts "Schema #{first_in_schema_search_path} has been created."
- end
ActiveRecord::Base.connection.create_database(config['database'], config.merge('encoding' => @encoding))
ActiveRecord::Base.establish_connection(config)
rescue
@@ -452,6 +446,8 @@ namespace :db do
end
end
+task 'test:prepare' => 'db:test:prepare'
+
def drop_database(config)
case config['adapter']
when 'mysql'
diff --git a/activerecord/lib/active_record/version.rb b/activerecord/lib/active_record/version.rb
index cf78ec6e12..bf6b995a37 100644
--- a/activerecord/lib/active_record/version.rb
+++ b/activerecord/lib/active_record/version.rb
@@ -3,7 +3,7 @@ module ActiveRecord
MAJOR = 3
MINOR = 0
TINY = 0
- BUILD = "beta2"
+ BUILD = "beta3"
STRING = [MAJOR, MINOR, TINY, BUILD].join('.')
end
diff --git a/activerecord/test/cases/active_schema_test_postgresql.rb b/activerecord/test/cases/active_schema_test_postgresql.rb
index 67c662d694..af80f724f2 100644
--- a/activerecord/test/cases/active_schema_test_postgresql.rb
+++ b/activerecord/test/cases/active_schema_test_postgresql.rb
@@ -17,13 +17,6 @@ class PostgresqlActiveSchemaTest < Test::Unit::TestCase
assert_equal %(CREATE DATABASE "aimonetti" ENCODING = 'latin1'), create_database(:aimonetti, :encoding => :latin1)
end
- def test_create_schema
- assert_equal %(CREATE SCHEMA "rizwan" AUTHORIZATION "postgres"), create_schema(:rizwan, :postgres)
- end
-
- def test_drop_schema
- assert_equal %(DROP SCHEMA "rizwan"), drop_schema(:rizwan)
- end
private
def method_missing(method_symbol, *arguments)
ActiveRecord::Base.connection.send(method_symbol, *arguments)
diff --git a/activerecord/test/cases/adapter_test.rb b/activerecord/test/cases/adapter_test.rb
index 9f78ae008c..9b28766405 100644
--- a/activerecord/test/cases/adapter_test.rb
+++ b/activerecord/test/cases/adapter_test.rb
@@ -81,12 +81,6 @@ class AdapterTest < ActiveRecord::TestCase
def test_encoding
assert_not_nil @connection.encoding
end
-
- def test_all_schemas
- @connection.create_schema(:test_schema, :postgres)
- assert @connection.all_schemas.include?('test_schema')
- @connection.drop_schema(:test_schema)
- end
end
def test_table_alias
diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb
index 62121b93cb..be77ee4bf3 100644
--- a/activerecord/test/cases/associations/belongs_to_associations_test.rb
+++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb
@@ -35,11 +35,11 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
def test_belongs_to_with_primary_key_joins_on_correct_column
sql = Client.joins(:firm_with_primary_key).to_sql
if current_adapter?(:MysqlAdapter)
- assert_no_match /`firm_with_primary_keys_companies`\.`id`/, sql
- assert_match /`firm_with_primary_keys_companies`\.`name`/, sql
+ assert_no_match(/`firm_with_primary_keys_companies`\.`id`/, sql)
+ assert_match(/`firm_with_primary_keys_companies`\.`name`/, sql)
else
- assert_no_match /"firm_with_primary_keys_companies"\."id"/, sql
- assert_match /"firm_with_primary_keys_companies"\."name"/, sql
+ assert_no_match(/"firm_with_primary_keys_companies"\."id"/, sql)
+ assert_match(/"firm_with_primary_keys_companies"\."name"/, sql)
end
end
diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb
index a64ccb2120..7a17ef1ee0 100644
--- a/activerecord/test/cases/dirty_test.rb
+++ b/activerecord/test/cases/dirty_test.rb
@@ -54,6 +54,89 @@ class DirtyTest < ActiveRecord::TestCase
assert_nil pirate.catchphrase_change
end
+ def test_time_attributes_changes_with_time_zone
+ in_time_zone 'Paris' do
+ target = Class.new(ActiveRecord::Base)
+ target.table_name = 'pirates'
+
+ # New record - no changes.
+ pirate = target.new
+ assert !pirate.created_on_changed?
+ assert_nil pirate.created_on_change
+
+ # Saved - no changes.
+ pirate.catchphrase = 'arrrr, time zone!!'
+ pirate.save!
+ assert !pirate.created_on_changed?
+ assert_nil pirate.created_on_change
+
+ # Change created_on.
+ old_created_on = pirate.created_on
+ pirate.created_on = Time.now - 1.day
+ assert pirate.created_on_changed?
+ assert_kind_of ActiveSupport::TimeWithZone, pirate.created_on_was
+ assert_equal old_created_on, pirate.created_on_was
+ end
+ end
+
+ def test_time_attributes_changes_without_time_zone_by_skip
+ in_time_zone 'Paris' do
+ target = Class.new(ActiveRecord::Base)
+ target.table_name = 'pirates'
+
+ target.skip_time_zone_conversion_for_attributes = [:created_on]
+
+ # New record - no changes.
+ pirate = target.new
+ assert !pirate.created_on_changed?
+ assert_nil pirate.created_on_change
+
+ # Saved - no changes.
+ pirate.catchphrase = 'arrrr, time zone!!'
+ pirate.save!
+ assert !pirate.created_on_changed?
+ assert_nil pirate.created_on_change
+
+ # Change created_on.
+ old_created_on = pirate.created_on
+ pirate.created_on = Time.now + 1.day
+ assert pirate.created_on_changed?
+ # kind_of does not work because
+ # ActiveSupport::TimeWithZone.name == 'Time'
+ assert_equal Time, pirate.created_on_was.class
+ assert_equal old_created_on, pirate.created_on_was
+ end
+ end
+
+ def test_time_attributes_changes_without_time_zone
+
+ target = Class.new(ActiveRecord::Base)
+ target.table_name = 'pirates'
+
+ target.time_zone_aware_attributes = false
+
+ # New record - no changes.
+ pirate = target.new
+ assert !pirate.created_on_changed?
+ assert_nil pirate.created_on_change
+
+ # Saved - no changes.
+ pirate.catchphrase = 'arrrr, time zone!!'
+ pirate.save!
+ assert !pirate.created_on_changed?
+ assert_nil pirate.created_on_change
+
+ # Change created_on.
+ old_created_on = pirate.created_on
+ pirate.created_on = Time.now + 1.day
+ assert pirate.created_on_changed?
+ # kind_of does not work because
+ # ActiveSupport::TimeWithZone.name == 'Time'
+ assert_equal Time, pirate.created_on_was.class
+ assert_equal old_created_on, pirate.created_on_was
+ end
+
+
def test_aliased_attribute_changes
# the actual attribute here is name, title is an
# alias setup via alias_attribute
@@ -406,4 +489,16 @@ class DirtyTest < ActiveRecord::TestCase
assert_equal %w(parrot_id), pirate.changed
assert_nil pirate.parrot_id_was
end
+
+ def in_time_zone(zone)
+ old_zone = Time.zone
+ old_tz = ActiveRecord::Base.time_zone_aware_attributes
+
+ Time.zone = zone ? ActiveSupport::TimeZone[zone] : nil
+ ActiveRecord::Base.time_zone_aware_attributes = !zone.nil?
+ yield
+ ensure
+ Time.zone = old_zone
+ ActiveRecord::Base.time_zone_aware_attributes = old_tz
+ end
end
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index e213986ede..7a26ee072d 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -1136,21 +1136,6 @@ if ActiveRecord::Base.connection.supports_migrations?
load(MIGRATIONS_ROOT + "/valid/1_people_have_last_names.rb")
end
- def test_migrator_interleaved_migrations
- ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/interleaved/pass_1")
-
- assert_nothing_raised do
- ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/interleaved/pass_2")
- end
-
- Person.reset_column_information
- assert Person.column_methods_hash.include?(:last_name)
-
- assert_nothing_raised do
- ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/interleaved/pass_3")
- end
- end
-
def test_migrator_db_has_no_schema_migrations_table
# Oracle adapter raises error if semicolon is present as last character
if current_adapter?(:OracleAdapter)
@@ -1362,16 +1347,6 @@ if ActiveRecord::Base.connection.supports_migrations?
end
end
- def test_migration_should_be_run_without_logger
- previous_logger = ActiveRecord::Base.logger
- ActiveRecord::Base.logger = nil
- assert_nothing_raised do
- ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid")
- end
- ensure
- ActiveRecord::Base.logger = previous_logger
- end
-
protected
def with_env_tz(new_tz = 'US/Eastern')
old_tz, ENV['TZ'] = ENV['TZ'], new_tz
@@ -1457,6 +1432,45 @@ if ActiveRecord::Base.connection.supports_migrations?
end # SexyMigrationsTest
+ class MigrationLoggerTest < ActiveRecord::TestCase
+ def setup
+ Object.send(:remove_const, :InnocentJointable)
+ end
+
+ def test_migration_should_be_run_without_logger
+ previous_logger = ActiveRecord::Base.logger
+ ActiveRecord::Base.logger = nil
+ assert_nothing_raised do
+ ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid")
+ end
+ ensure
+ ActiveRecord::Base.logger = previous_logger
+ end
+ end
+
+ class InterleavedMigrationsTest < ActiveRecord::TestCase
+ def setup
+ Object.send(:remove_const, :PeopleHaveLastNames)
+ end
+
+ def test_migrator_interleaved_migrations
+ ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/interleaved/pass_1")
+
+ assert_nothing_raised do
+ ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/interleaved/pass_2")
+ end
+
+ Person.reset_column_information
+ assert Person.column_methods_hash.include?(:last_name)
+
+ Object.send(:remove_const, :PeopleHaveLastNames)
+ Object.send(:remove_const, :InnocentJointable)
+ assert_nothing_raised do
+ ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/interleaved/pass_3")
+ end
+ end
+ end
+
class ChangeTableMigrationsTest < ActiveRecord::TestCase
def setup
@connection = Person.connection
diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb
index 3d5ebb6cb8..e4cafad11e 100644
--- a/activerecord/test/cases/named_scope_test.rb
+++ b/activerecord/test/cases/named_scope_test.rb
@@ -422,6 +422,16 @@ class NamedScopeTest < ActiveRecord::TestCase
post.comments.containing_the_letter_e.all # force load
assert_no_queries { post.comments.containing_the_letter_e.all }
end
+
+ def test_named_scopes_are_reset_on_association_reload
+ post = posts(:welcome)
+
+ [:destroy_all, :reset, :delete_all].each do |method|
+ before = post.comments.containing_the_letter_e
+ post.comments.send(method)
+ assert before.object_id != post.comments.containing_the_letter_e.object_id, "AssociationCollection##{method} should reset the named scopes cache"
+ end
+ end
end
class DynamicScopeMatchTest < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/xml_serialization_test.rb b/activerecord/test/cases/xml_serialization_test.rb
index e1ad5c1685..2849ff11b7 100644
--- a/activerecord/test/cases/xml_serialization_test.rb
+++ b/activerecord/test/cases/xml_serialization_test.rb
@@ -130,10 +130,20 @@ class NilXmlSerializationTest < ActiveRecord::TestCase
end
class DatabaseConnectedXmlSerializationTest < ActiveRecord::TestCase
- fixtures :authors, :posts
+ fixtures :authors, :posts, :projects
+
# to_xml used to mess with the hash the user provided which
# caused the builder to be reused. This meant the document kept
# getting appended to.
+
+ def test_modules
+ projects = MyApplication::Business::Project.all
+ xml = projects.to_xml
+ root = projects.first.class.to_s.underscore.pluralize.tr('/','_').dasherize
+ assert_match "<#{root} type=\"array\">", xml
+ assert_match "</#{root}>", xml
+ end
+
def test_passing_hash_shouldnt_reuse_builder
options = {:include=>:posts}
david = authors(:david)