diff options
author | wycats <wycats@gmail.com> | 2010-04-10 17:22:52 -0400 |
---|---|---|
committer | wycats <wycats@gmail.com> | 2010-04-10 17:22:52 -0400 |
commit | 87f7093ee3306f417e1136d947eba200d40ff8e7 (patch) | |
tree | ba70dbdaf67e12fc067bb5d8343d7681932452ef /activerecord | |
parent | ee8e9d548472fb8cb8792a569e579c6513be77d6 (diff) | |
parent | 381f877bbbbf81d679f5be3b7ac7e961d41502bd (diff) | |
download | rails-87f7093ee3306f417e1136d947eba200d40ff8e7.tar.gz rails-87f7093ee3306f417e1136d947eba200d40ff8e7.tar.bz2 rails-87f7093ee3306f417e1136d947eba200d40ff8e7.zip |
Merge branch 'master' into docrails_master
Diffstat (limited to 'activerecord')
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) |