diff options
Diffstat (limited to 'activerecord')
33 files changed, 526 insertions, 432 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 67bec5f38e..9abfe2e6fd 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,5 +1,44 @@ ## Rails 4.0.0 (unreleased) ## +* Rename `ActiveRecord::Fixtures` class to `ActiveRecord::FixtureSet`. + Instances of this class normally hold a collection of fixtures (records) + loaded either from a single YAML file, or from a file and a folder + with the same name. This change make the class name singular and makes + the class easier to distinguish from the modules like + `ActiveRecord::TestFixtures`, which operates on multiple fixture sets, + or `DelegatingFixtures`, `::Fixtures`, etc., + and from the class `ActiveRecord::Fixture`, which corresponds to a single + fixture. + + *Alexey Muranov* + +* The postgres adapter now supports tables with capital letters. + Fix #5920 + + *Yves Senn* + +* `CollectionAssociation#count` returns `0` without querying if the + parent record is not persisted. + + Before: + + person.pets.count + # SELECT COUNT(*) FROM "pets" WHERE "pets"."person_id" IS NULL + # => 0 + + After: + + person.pets.count + # fires without sql query + # => 0 + + *Francesco Rodriguez* + +* Fix `reset_counters` crashing on `has_many :through` associations. + Fix #7822. + + *lulalala* + * Support for partial inserts. When inserting new records, only the fields which have been changed @@ -608,7 +647,7 @@ *Michael Pearson* * Added default order to `first` to assure consistent results among - diferent database engines. Introduced `take` as a replacement to + different database engines. Introduced `take` as a replacement to the old behavior of `first`. *Marcelo Silveira* diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb index fe3e5b00f7..7f39d3083e 100644 --- a/activerecord/lib/active_record/associations/collection_association.rb +++ b/activerecord/lib/active_record/associations/collection_association.rb @@ -174,6 +174,8 @@ module ActiveRecord # association, it will be used for the query. Otherwise, construct options and pass them with # scope to the target class's +count+. def count(column_name = nil, count_options = {}) + return 0 if owner.new_record? + column_name, count_options = nil, column_name if column_name.is_a?(Hash) if options[:counter_sql] || options[:finder_sql] @@ -412,7 +414,7 @@ module ActiveRecord persisted.map! do |record| if mem_record = memory.delete(record) - (record.attribute_names - mem_record.changes.keys).each do |name| + ((record.attribute_names & mem_record.attribute_names) - mem_record.changes.keys).each do |name| mem_record[name] = record[name] end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb index 8a073bf878..2264595751 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb @@ -314,7 +314,7 @@ module ActiveRecord INNER JOIN pg_depend dep ON attr.attrelid = dep.refobjid AND attr.attnum = dep.refobjsubid INNER JOIN pg_constraint cons ON attr.attrelid = cons.conrelid AND attr.attnum = cons.conkey[1] WHERE cons.contype = 'p' - AND dep.refobjid = '#{table}'::regclass + AND dep.refobjid = '#{quote_table_name(table)}'::regclass end_sql row && row.first diff --git a/activerecord/lib/active_record/counter_cache.rb b/activerecord/lib/active_record/counter_cache.rb index c877079b25..57838ff984 100644 --- a/activerecord/lib/active_record/counter_cache.rb +++ b/activerecord/lib/active_record/counter_cache.rb @@ -22,6 +22,10 @@ module ActiveRecord counters.each do |association| has_many_association = reflect_on_association(association.to_sym) + if has_many_association.is_a? ActiveRecord::Reflection::ThroughReflection + has_many_association = has_many_association.through_reflection + end + foreign_key = has_many_association.foreign_key.to_s child_class = has_many_association.klass belongs_to = child_class.reflect_on_all_associations(:belongs_to) diff --git a/activerecord/lib/active_record/fixtures/file.rb b/activerecord/lib/active_record/fixture_set/file.rb index 0f6ab3e396..11b53275e1 100644 --- a/activerecord/lib/active_record/fixtures/file.rb +++ b/activerecord/lib/active_record/fixture_set/file.rb @@ -2,7 +2,7 @@ require 'erb' require 'yaml' module ActiveRecord - class Fixtures + class FixtureSet class File # :nodoc: include Enumerable diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index 60fc653735..899e89a6f5 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -2,9 +2,11 @@ require 'erb' require 'yaml' require 'zlib' require 'active_support/dependencies' -require 'active_record/fixtures/file' +require 'active_record/fixture_set/file' require 'active_record/errors' +require 'active_support/deprecation' # temporary + module ActiveRecord class FixtureClassNotFound < ActiveRecord::ActiveRecordError #:nodoc: end @@ -350,8 +352,8 @@ module ActiveRecord # to the rescue: # # george_reginald: - # monkey_id: <%= ActiveRecord::Fixtures.identify(:reginald) %> - # pirate_id: <%= ActiveRecord::Fixtures.identify(:george) %> + # monkey_id: <%= ActiveRecord::FixtureSet.identify(:reginald) %> + # pirate_id: <%= ActiveRecord::FixtureSet.identify(:george) %> # # == Support for YAML defaults # @@ -370,10 +372,11 @@ module ActiveRecord # *DEFAULTS # # Any fixture labeled "DEFAULTS" is safely ignored. - class Fixtures + class FixtureSet #-- - # NOTE: an instance of Fixtures can be called fixture_set, it is normally stored in a single YAML file and possibly in a folder with the same name. + # An instance of FixtureSet is normally stored in a single YAML file and possibly in a folder with the same name. #++ + MAX_ID = 2 ** 30 - 1 @@all_cached_fixtures = Hash.new { |h,k| h[k] = {} } @@ -451,7 +454,7 @@ module ActiveRecord fixtures_map = {} fixture_sets = files_to_read.map do |fs_name| - fixtures_map[fs_name] = new( # ActiveRecord::Fixtures.new + fixtures_map[fs_name] = new( # ActiveRecord::FixtureSet.new connection, fs_name, class_names[fs_name] || default_fixture_model_name(fs_name), @@ -565,7 +568,7 @@ module ActiveRecord # generate a primary key if necessary if has_primary_key_column? && !row.include?(primary_key_name) - row[primary_key_name] = ActiveRecord::Fixtures.identify(label) + row[primary_key_name] = ActiveRecord::FixtureSet.identify(label) end # If STI is used, find the correct subclass for association reflection @@ -588,7 +591,7 @@ module ActiveRecord row[association.foreign_type] = $1 end - row[fk_name] = ActiveRecord::Fixtures.identify(value) + row[fk_name] = ActiveRecord::FixtureSet.identify(value) end when :has_and_belongs_to_many if (targets = row.delete(association.name.to_s)) @@ -596,7 +599,7 @@ module ActiveRecord table_name = association.join_table rows[table_name].concat targets.map { |target| { association.foreign_key => row[primary_key_name], - association.association_foreign_key => ActiveRecord::Fixtures.identify(target) } + association.association_foreign_key => ActiveRecord::FixtureSet.identify(target) } } end end @@ -637,7 +640,7 @@ module ActiveRecord } + [yaml_file_path] yaml_files.each do |file| - Fixtures::File.open(file) do |fh| + FixtureSet::File.open(file) do |fh| fh.each do |fixture_name, row| fixtures[fixture_name] = ActiveRecord::Fixture.new(row, model_class) end @@ -651,6 +654,11 @@ module ActiveRecord end + #-- + # Deprecate 'Fixtures' in favor of 'FixtureSet'. + #++ + Fixtures = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('ActiveRecord::Fixtures', '::ActiveRecord::FixtureSet') + class Fixture #:nodoc: include Enumerable @@ -712,7 +720,7 @@ module ActiveRecord self.pre_loaded_fixtures = false self.fixture_class_names = Hash.new do |h, fixture_set_name| - h[fixture_set_name] = ActiveRecord::Fixtures.default_fixture_model_name(fixture_set_name) + h[fixture_set_name] = ActiveRecord::FixtureSet.default_fixture_model_name(fixture_set_name) end end @@ -847,7 +855,7 @@ module ActiveRecord end # Load fixtures for every test. else - ActiveRecord::Fixtures.reset_cache + ActiveRecord::FixtureSet.reset_cache @@already_loaded_fixtures[self.class] = nil @loaded_fixtures = load_fixtures end @@ -860,7 +868,7 @@ module ActiveRecord return unless defined?(ActiveRecord) && !ActiveRecord::Base.configurations.blank? unless run_in_transaction? - ActiveRecord::Fixtures.reset_cache + ActiveRecord::FixtureSet.reset_cache end # Rollback changes if a transaction is active. @@ -879,7 +887,7 @@ module ActiveRecord private def load_fixtures - fixtures = ActiveRecord::Fixtures.create_fixtures(fixture_path, fixture_table_names, fixture_class_names) + fixtures = ActiveRecord::FixtureSet.create_fixtures(fixture_path, fixture_table_names, fixture_class_names) Hash[fixtures.map { |f| [f.name, f] }] end @@ -888,16 +896,16 @@ module ActiveRecord def instantiate_fixtures if pre_loaded_fixtures - raise RuntimeError, 'Load fixtures before instantiating them.' if ActiveRecord::Fixtures.all_loaded_fixtures.empty? + raise RuntimeError, 'Load fixtures before instantiating them.' if ActiveRecord::FixtureSet.all_loaded_fixtures.empty? unless @@required_fixture_classes - self.class.require_fixture_classes ActiveRecord::Fixtures.all_loaded_fixtures.keys + self.class.require_fixture_classes ActiveRecord::FixtureSet.all_loaded_fixtures.keys @@required_fixture_classes = true end - ActiveRecord::Fixtures.instantiate_all_loaded_fixtures(self, load_instances?) + ActiveRecord::FixtureSet.instantiate_all_loaded_fixtures(self, load_instances?) else raise RuntimeError, 'Load fixtures before instantiating them.' if @loaded_fixtures.nil? @loaded_fixtures.each_value do |fixture_set| - ActiveRecord::Fixtures.instantiate_fixtures(self, fixture_set, load_instances?) + ActiveRecord::FixtureSet.instantiate_fixtures(self, fixture_set, load_instances?) end end end diff --git a/activerecord/lib/active_record/log_subscriber.rb b/activerecord/lib/active_record/log_subscriber.rb index a25f2c7bca..ca79950049 100644 --- a/activerecord/lib/active_record/log_subscriber.rb +++ b/activerecord/lib/active_record/log_subscriber.rb @@ -1,11 +1,13 @@ module ActiveRecord class LogSubscriber < ActiveSupport::LogSubscriber + IGNORE_PAYLOAD_NAMES = ["SCHEMA", "EXPLAIN"] + def self.runtime=(value) - Thread.current["active_record_sql_runtime"] = value + Thread.current[:active_record_sql_runtime] = value end def self.runtime - Thread.current["active_record_sql_runtime"] ||= 0 + Thread.current[:active_record_sql_runtime] ||= 0 end def self.reset_runtime @@ -24,9 +26,9 @@ module ActiveRecord payload = event.payload - return if 'SCHEMA' == payload[:name] + return if IGNORE_PAYLOAD_NAMES.include?(payload[:name]) - name = '%s (%.1fms)' % [payload[:name], event.duration] + name = "#{payload[:name]} (#{event.duration.round(1)}ms)" sql = payload[:sql].squeeze(' ') binds = nil diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index b11483de8c..d7e35fb771 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -129,7 +129,8 @@ module ActiveRecord end initializer "active_record.add_watchable_files" do |app| - config.watchable_files.concat ["#{app.root}/db/schema.rb", "#{app.root}/db/structure.sql"] + path = app.paths["db"].first + config.watchable_files.concat ["#{path}/schema.rb", "#{path}/structure.sql"] end config.after_initialize do |app| diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index d134978128..69a9526fcc 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -196,7 +196,7 @@ db_namespace = namespace :db do fixtures_dir = File.join [base_dir, ENV['FIXTURES_DIR']].compact (ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/) : Dir["#{fixtures_dir}/**/*.yml"].map {|f| f[(fixtures_dir.size + 1)..-5] }).each do |fixture_file| - ActiveRecord::Fixtures.create_fixtures(fixtures_dir, fixture_file) + ActiveRecord::FixtureSet.create_fixtures(fixtures_dir, fixture_file) end end @@ -207,13 +207,13 @@ db_namespace = namespace :db do label, id = ENV['LABEL'], ENV['ID'] raise 'LABEL or ID required' if label.blank? && id.blank? - puts %Q(The fixture ID for "#{label}" is #{ActiveRecord::Fixtures.identify(label)}.) if label + puts %Q(The fixture ID for "#{label}" is #{ActiveRecord::FixtureSet.identify(label)}.) if label base_dir = ENV['FIXTURES_PATH'] ? File.join(Rails.root, ENV['FIXTURES_PATH']) : File.join(Rails.root, 'test', 'fixtures') Dir["#{base_dir}/**/*.yml"].each do |file| if data = YAML::load(ERB.new(IO.read(file)).result) data.keys.each do |key| - key_id = ActiveRecord::Fixtures.identify(key) + key_id = ActiveRecord::FixtureSet.identify(key) if key == label || key_id == id.to_i puts "#{file}: #{key} (#{key_id})" diff --git a/activerecord/lib/active_record/sanitization.rb b/activerecord/lib/active_record/sanitization.rb index 42b4cff4b8..f3e47a958e 100644 --- a/activerecord/lib/active_record/sanitization.rb +++ b/activerecord/lib/active_record/sanitization.rb @@ -141,23 +141,6 @@ module ActiveRecord end end - def expand_range_bind_variables(bind_vars) #:nodoc: - expanded = [] - - bind_vars.each do |var| - next if var.is_a?(Hash) - - if var.is_a?(Range) - expanded << var.first - expanded << var.last - else - expanded << var - end - end - - expanded - end - def quote_bound_value(value, c = connection) #:nodoc: if value.respond_to?(:map) && !value.acts_like?(:string) if value.respond_to?(:empty?) && value.empty? diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index 019290725d..3706885881 100644 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -10,7 +10,7 @@ module ActiveRecord # puts invalid.record.errors # end class RecordInvalid < ActiveRecordError - attr_reader :recordĀ # :nodoc: + attr_reader :record # :nodoc: def initialize(record) # :nodoc: @record = record errors = @record.errors.full_messages.join(", ") diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb index f3620c1324..5dece1cb36 100644 --- a/activerecord/lib/active_record/validations/uniqueness.rb +++ b/activerecord/lib/active_record/validations/uniqueness.rb @@ -64,14 +64,12 @@ module ActiveRecord end def build_relation(klass, table, attribute, value) #:nodoc: - reflection = klass.reflect_on_association(attribute) - if reflection - column = klass.columns_hash[reflection.foreign_key] + if reflection = klass.reflect_on_association(attribute) attribute = reflection.foreign_key value = value.attributes[reflection.primary_key_column.name] - else - column = klass.columns_hash[attribute.to_s] end + + column = klass.columns_hash[attribute.to_s] value = column.limit ? value.to_s[0, column.limit] : value.to_s if !value.nil? && column.text? if !options[:case_sensitive] && value && column.text? diff --git a/activerecord/test/cases/adapters/mysql/reserved_word_test.rb b/activerecord/test/cases/adapters/mysql/reserved_word_test.rb index aff971a955..5164acf77f 100644 --- a/activerecord/test/cases/adapters/mysql/reserved_word_test.rb +++ b/activerecord/test/cases/adapters/mysql/reserved_word_test.rb @@ -136,9 +136,9 @@ class MysqlReservedWordTest < ActiveRecord::TestCase #the following functions were added to DRY test cases private - # custom fixture loader, uses Fixtures#create_fixtures and appends base_path to the current file's path + # custom fixture loader, uses FixtureSet#create_fixtures and appends base_path to the current file's path def create_test_fixtures(*fixture_names) - ActiveRecord::Fixtures.create_fixtures(FIXTURES_ROOT + "/reserved_words", fixture_names) + ActiveRecord::FixtureSet.create_fixtures(FIXTURES_ROOT + "/reserved_words", fixture_names) end # custom drop table, uses execute on connection to drop a table if it exists. note: escapes table_name diff --git a/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb b/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb index 9fd07f014e..1017b0758d 100644 --- a/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb +++ b/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb @@ -136,9 +136,9 @@ class MysqlReservedWordTest < ActiveRecord::TestCase #the following functions were added to DRY test cases private - # custom fixture loader, uses Fixtures#create_fixtures and appends base_path to the current file's path + # custom fixture loader, uses FixtureSet#create_fixtures and appends base_path to the current file's path def create_test_fixtures(*fixture_names) - ActiveRecord::Fixtures.create_fixtures(FIXTURES_ROOT + "/reserved_words", fixture_names) + ActiveRecord::FixtureSet.create_fixtures(FIXTURES_ROOT + "/reserved_words", fixture_names) end # custom drop table, uses execute on connection to drop a table if it exists. note: escapes table_name diff --git a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb index 92e31a3e44..f1362dd15f 100644 --- a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb +++ b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb @@ -14,6 +14,10 @@ module ActiveRecord assert_equal 'id', @connection.primary_key('ex') end + def test_primary_key_works_tables_containing_capital_letters + assert_equal 'id', @connection.primary_key('CamelCase') + end + def test_non_standard_primary_key @connection.exec_query('drop table if exists ex') @connection.exec_query('create table ex(data character varying(255) primary key)') diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb index f3520d43e0..42f5b69d4e 100644 --- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb @@ -799,6 +799,12 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase assert_equal 1, developer.projects.count end + def test_counting_should_not_fire_sql_if_parent_is_unsaved + assert_no_queries do + assert_equal 0, Developer.new.projects.count + end + end + unless current_adapter?(:PostgreSQLAdapter) def test_count_with_finder_sql assert_equal 3, projects(:active_record).developers_with_finder_sql.count diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 4b56037a08..50c23c863f 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -262,6 +262,12 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert_equal firm.limited_clients.length, firm.limited_clients.count end + def test_counting_should_not_fire_sql_if_parent_is_unsaved + assert_no_queries do + assert_equal 0, Person.new.readers.count + end + end + def test_finding assert_equal 2, Firm.all.merge!(:order => "id").first.clients.length end diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index d4ceae6f80..f0582a3090 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -767,6 +767,12 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase assert_equal 1, authors(:mary).categories.general.count end + def test_counting_should_not_fire_sql_if_parent_is_unsaved + assert_no_queries do + assert_equal 0, Person.new.posts.count + end + end + def test_has_many_through_belongs_to_should_update_when_the_through_foreign_key_changes post = posts(:eager_other) diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb index 86893ec4b3..9b00c21b52 100644 --- a/activerecord/test/cases/associations/join_model_test.rb +++ b/activerecord/test/cases/associations/join_model_test.rb @@ -231,6 +231,14 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase assert_equal "2", categories(:sti_test).authors_with_select.first.post_id.to_s end + def test_create_through_has_many_with_piggyback + category = categories(:sti_test) + ernie = category.authors_with_select.create(:name => 'Ernie') + assert_nothing_raised do + assert_equal ernie, category.authors_with_select.detect {|a| a.name == 'Ernie'} + end + end + def test_include_has_many_through posts = Post.all.merge!(:order => 'posts.id').to_a posts_with_authors = Post.all.merge!(:includes => :authors, :order => 'posts.id').to_a diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index fbfdd0f07a..0f859bf452 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -617,343 +617,6 @@ class BasicsTest < ActiveRecord::TestCase assert_equal 'value2', weird.read_attribute('a$b') end - def test_multiparameter_attributes_on_date - attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "24" } - topic = Topic.find(1) - topic.attributes = attributes - # note that extra #to_date call allows test to pass for Oracle, which - # treats dates/times the same - assert_date_from_db Date.new(2004, 6, 24), topic.last_read.to_date - end - - def test_multiparameter_attributes_on_date_with_empty_year - attributes = { "last_read(1i)" => "", "last_read(2i)" => "6", "last_read(3i)" => "24" } - topic = Topic.find(1) - topic.attributes = attributes - # note that extra #to_date call allows test to pass for Oracle, which - # treats dates/times the same - assert_nil topic.last_read - end - - def test_multiparameter_attributes_on_date_with_empty_month - attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "", "last_read(3i)" => "24" } - topic = Topic.find(1) - topic.attributes = attributes - # note that extra #to_date call allows test to pass for Oracle, which - # treats dates/times the same - assert_nil topic.last_read - end - - def test_multiparameter_attributes_on_date_with_empty_day - attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "" } - topic = Topic.find(1) - topic.attributes = attributes - # note that extra #to_date call allows test to pass for Oracle, which - # treats dates/times the same - assert_nil topic.last_read - end - - def test_multiparameter_attributes_on_date_with_empty_day_and_year - attributes = { "last_read(1i)" => "", "last_read(2i)" => "6", "last_read(3i)" => "" } - topic = Topic.find(1) - topic.attributes = attributes - # note that extra #to_date call allows test to pass for Oracle, which - # treats dates/times the same - assert_nil topic.last_read - end - - def test_multiparameter_attributes_on_date_with_empty_day_and_month - attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "", "last_read(3i)" => "" } - topic = Topic.find(1) - topic.attributes = attributes - # note that extra #to_date call allows test to pass for Oracle, which - # treats dates/times the same - assert_nil topic.last_read - end - - def test_multiparameter_attributes_on_date_with_empty_year_and_month - attributes = { "last_read(1i)" => "", "last_read(2i)" => "", "last_read(3i)" => "24" } - topic = Topic.find(1) - topic.attributes = attributes - # note that extra #to_date call allows test to pass for Oracle, which - # treats dates/times the same - assert_nil topic.last_read - end - - def test_multiparameter_attributes_on_date_with_all_empty - attributes = { "last_read(1i)" => "", "last_read(2i)" => "", "last_read(3i)" => "" } - topic = Topic.find(1) - topic.attributes = attributes - assert_nil topic.last_read - end - - def test_multiparameter_attributes_on_time - attributes = { - "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24", - "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00" - } - topic = Topic.find(1) - topic.attributes = attributes - assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on - end - - def test_multiparameter_attributes_on_time_with_no_date - ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do - attributes = { - "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00" - } - topic = Topic.find(1) - topic.attributes = attributes - end - assert_equal("written_on", ex.errors[0].attribute) - end - - def test_multiparameter_attributes_on_time_with_invalid_time_params - ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do - attributes = { - "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24", - "written_on(4i)" => "2004", "written_on(5i)" => "36", "written_on(6i)" => "64", - } - topic = Topic.find(1) - topic.attributes = attributes - end - assert_equal("written_on", ex.errors[0].attribute) - end - - def test_multiparameter_attributes_on_time_with_old_date - attributes = { - "written_on(1i)" => "1850", "written_on(2i)" => "6", "written_on(3i)" => "24", - "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00" - } - topic = Topic.find(1) - topic.attributes = attributes - # testing against to_s(:db) representation because either a Time or a DateTime might be returned, depending on platform - assert_equal "1850-06-24 16:24:00", topic.written_on.to_s(:db) - end - - def test_multiparameter_attributes_on_time_will_raise_on_big_time_if_missing_date_parts - ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do - attributes = { - "written_on(4i)" => "16", "written_on(5i)" => "24" - } - topic = Topic.find(1) - topic.attributes = attributes - end - assert_equal("written_on", ex.errors[0].attribute) - end - - def test_multiparameter_attributes_on_time_with_raise_on_small_time_if_missing_date_parts - ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do - attributes = { - "written_on(4i)" => "16", "written_on(5i)" => "12", "written_on(6i)" => "02" - } - topic = Topic.find(1) - topic.attributes = attributes - end - assert_equal("written_on", ex.errors[0].attribute) - end - - def test_multiparameter_attributes_on_time_will_ignore_hour_if_missing - attributes = { - "written_on(1i)" => "2004", "written_on(2i)" => "12", "written_on(3i)" => "12", - "written_on(5i)" => "12", "written_on(6i)" => "02" - } - topic = Topic.find(1) - topic.attributes = attributes - assert_equal Time.local(2004, 12, 12, 0, 12, 2), topic.written_on - end - - def test_multiparameter_attributes_on_time_will_ignore_hour_if_blank - attributes = { - "written_on(1i)" => "", "written_on(2i)" => "", "written_on(3i)" => "", - "written_on(4i)" => "", "written_on(5i)" => "12", "written_on(6i)" => "02" - } - topic = Topic.find(1) - topic.attributes = attributes - assert_nil topic.written_on - end - - def test_multiparameter_attributes_on_time_will_ignore_date_if_empty - attributes = { - "written_on(1i)" => "", "written_on(2i)" => "", "written_on(3i)" => "", - "written_on(4i)" => "16", "written_on(5i)" => "24" - } - topic = Topic.find(1) - topic.attributes = attributes - assert_nil topic.written_on - end - def test_multiparameter_attributes_on_time_with_seconds_will_ignore_date_if_empty - attributes = { - "written_on(1i)" => "", "written_on(2i)" => "", "written_on(3i)" => "", - "written_on(4i)" => "16", "written_on(5i)" => "12", "written_on(6i)" => "02" - } - topic = Topic.find(1) - topic.attributes = attributes - assert_nil topic.written_on - end - - def test_multiparameter_attributes_on_time_with_utc - ActiveRecord::Base.default_timezone = :utc - attributes = { - "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24", - "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00" - } - topic = Topic.find(1) - topic.attributes = attributes - assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on - end - - def test_multiparameter_attributes_on_time_with_time_zone_aware_attributes - ActiveRecord::Base.time_zone_aware_attributes = true - ActiveRecord::Base.default_timezone = :utc - Time.zone = ActiveSupport::TimeZone[-28800] - attributes = { - "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24", - "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00" - } - topic = Topic.find(1) - topic.attributes = attributes - assert_equal Time.utc(2004, 6, 24, 23, 24, 0), topic.written_on - assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on.time - assert_equal Time.zone, topic.written_on.time_zone - end - - def test_multiparameter_attributes_on_time_with_time_zone_aware_attributes_false - Time.zone = ActiveSupport::TimeZone[-28800] - attributes = { - "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24", - "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00" - } - topic = Topic.find(1) - topic.attributes = attributes - assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on - assert_equal false, topic.written_on.respond_to?(:time_zone) - end - - def test_multiparameter_attributes_on_time_with_skip_time_zone_conversion_for_attributes - ActiveRecord::Base.time_zone_aware_attributes = true - ActiveRecord::Base.default_timezone = :utc - Time.zone = ActiveSupport::TimeZone[-28800] - Topic.skip_time_zone_conversion_for_attributes = [:written_on] - attributes = { - "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24", - "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00" - } - topic = Topic.find(1) - topic.attributes = attributes - assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on - assert_equal false, topic.written_on.respond_to?(:time_zone) - ensure - Topic.skip_time_zone_conversion_for_attributes = [] - end - - # Oracle, and Sybase do not have a TIME datatype. - unless current_adapter?(:OracleAdapter, :SybaseAdapter) - def test_multiparameter_attributes_on_time_only_column_with_time_zone_aware_attributes_does_not_do_time_zone_conversion - ActiveRecord::Base.time_zone_aware_attributes = true - ActiveRecord::Base.default_timezone = :utc - Time.zone = ActiveSupport::TimeZone[-28800] - attributes = { - "bonus_time(1i)" => "2000", "bonus_time(2i)" => "1", "bonus_time(3i)" => "1", - "bonus_time(4i)" => "16", "bonus_time(5i)" => "24" - } - topic = Topic.find(1) - topic.attributes = attributes - assert_equal Time.utc(2000, 1, 1, 16, 24, 0), topic.bonus_time - assert topic.bonus_time.utc? - end - end - - def test_multiparameter_attributes_on_time_with_empty_seconds - attributes = { - "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24", - "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "" - } - topic = Topic.find(1) - topic.attributes = attributes - assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on - end - - def test_multiparameter_attributes_setting_time_attribute - return skip "Oracle does not have TIME data type" if current_adapter? :OracleAdapter - - topic = Topic.new( "bonus_time(4i)"=> "01", "bonus_time(5i)" => "05" ) - assert_equal 1, topic.bonus_time.hour - assert_equal 5, topic.bonus_time.min - end - - def test_multiparameter_attributes_setting_date_attribute - topic = Topic.new( "written_on(1i)" => "1952", "written_on(2i)" => "3", "written_on(3i)" => "11" ) - assert_equal 1952, topic.written_on.year - assert_equal 3, topic.written_on.month - assert_equal 11, topic.written_on.day - end - - def test_multiparameter_attributes_setting_date_and_time_attribute - topic = Topic.new( - "written_on(1i)" => "1952", - "written_on(2i)" => "3", - "written_on(3i)" => "11", - "written_on(4i)" => "13", - "written_on(5i)" => "55") - assert_equal 1952, topic.written_on.year - assert_equal 3, topic.written_on.month - assert_equal 11, topic.written_on.day - assert_equal 13, topic.written_on.hour - assert_equal 55, topic.written_on.min - end - - def test_multiparameter_attributes_setting_time_but_not_date_on_date_field - assert_raise( ActiveRecord::MultiparameterAssignmentErrors ) do - Topic.new( "written_on(4i)" => "13", "written_on(5i)" => "55" ) - end - end - - def test_multiparameter_assignment_of_aggregation - customer = Customer.new - address = Address.new("The Street", "The City", "The Country") - attributes = { "address(1)" => address.street, "address(2)" => address.city, "address(3)" => address.country } - customer.attributes = attributes - assert_equal address, customer.address - end - - def test_multiparameter_assignment_of_aggregation_out_of_order - customer = Customer.new - address = Address.new("The Street", "The City", "The Country") - attributes = { "address(3)" => address.country, "address(2)" => address.city, "address(1)" => address.street } - customer.attributes = attributes - assert_equal address, customer.address - end - - def test_multiparameter_assignment_of_aggregation_with_missing_values - ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do - customer = Customer.new - address = Address.new("The Street", "The City", "The Country") - attributes = { "address(2)" => address.city, "address(3)" => address.country } - customer.attributes = attributes - end - assert_equal("address", ex.errors[0].attribute) - end - - def test_multiparameter_assignment_of_aggregation_with_blank_values - customer = Customer.new - address = Address.new("The Street", "The City", "The Country") - attributes = { "address(1)" => "", "address(2)" => address.city, "address(3)" => address.country } - customer.attributes = attributes - assert_equal Address.new(nil, "The City", "The Country"), customer.address - end - - def test_multiparameter_assignment_of_aggregation_with_large_index - ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do - customer = Customer.new - address = Address.new("The Street", "The City", "The Country") - attributes = { "address(1)" => "The Street", "address(2)" => address.city, "address(3000)" => address.country } - customer.attributes = attributes - end - - assert_equal("address", ex.errors[0].attribute) - end - def test_attributes_on_dummy_time # Oracle, and Sybase do not have a TIME datatype. return true if current_adapter?(:OracleAdapter, :SybaseAdapter) diff --git a/activerecord/test/cases/counter_cache_test.rb b/activerecord/test/cases/counter_cache_test.rb index ee443741ca..fc46a249c8 100644 --- a/activerecord/test/cases/counter_cache_test.rb +++ b/activerecord/test/cases/counter_cache_test.rb @@ -10,9 +10,12 @@ require 'models/dog' require 'models/dog_lover' require 'models/person' require 'models/friendship' +require 'models/subscriber' +require 'models/subscription' +require 'models/book' class CounterCacheTest < ActiveRecord::TestCase - fixtures :topics, :categories, :categorizations, :cars, :dogs, :dog_lovers, :people, :friendships + fixtures :topics, :categories, :categorizations, :cars, :dogs, :dog_lovers, :people, :friendships, :subscribers, :subscriptions, :books class ::SpecialTopic < ::Topic has_many :special_replies, :foreign_key => 'parent_id' @@ -118,4 +121,14 @@ class CounterCacheTest < ActiveRecord::TestCase Person.reset_counters(michael.id, :followers) end end + + test "reset counter of has_many :through association" do + subscriber = subscribers('second') + Subscriber.reset_counters(subscriber.id, 'books') + Subscriber.increment_counter('books_count', subscriber.id) + + assert_difference 'subscriber.reload.books_count', -1 do + Subscriber.reset_counters(subscriber.id, 'books') + end + end end diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb index 75a52544d3..7334514f9a 100644 --- a/activerecord/test/cases/dirty_test.rb +++ b/activerecord/test/cases/dirty_test.rb @@ -554,7 +554,7 @@ class DirtyTest < ActiveRecord::TestCase test "partial insert" do with_partial_updates Person do jon = nil - assert_sql(/first_name/) do + assert_sql(/first_name/i) do jon = Person.create! first_name: 'Jon' end diff --git a/activerecord/test/cases/fixtures/file_test.rb b/activerecord/test/cases/fixture_set/file_test.rb index e623fbe4d1..a029fedbd3 100644 --- a/activerecord/test/cases/fixtures/file_test.rb +++ b/activerecord/test/cases/fixture_set/file_test.rb @@ -2,7 +2,7 @@ require 'cases/helper' require 'tempfile' module ActiveRecord - class Fixtures + class FixtureSet class FileTest < ActiveRecord::TestCase def test_open fh = File.open(::File.join(FIXTURES_ROOT, "accounts.yml")) diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb index c28f8de682..b0b29f5f42 100644 --- a/activerecord/test/cases/fixtures_test.rb +++ b/activerecord/test/cases/fixtures_test.rb @@ -40,7 +40,7 @@ class FixturesTest < ActiveRecord::TestCase FIXTURES.each do |name| fixtures = nil assert_nothing_raised { fixtures = create_fixtures(name).first } - assert_kind_of(ActiveRecord::Fixtures, fixtures) + assert_kind_of(ActiveRecord::FixtureSet, fixtures) fixtures.each { |_name, fixture| fixture.each { |key, value| assert_match(MATCH_ATTRIBUTE_NAME, key) @@ -57,7 +57,7 @@ class FixturesTest < ActiveRecord::TestCase dir = File.dirname badyaml.path name = File.basename badyaml.path, '.yml' assert_raises(ActiveRecord::Fixture::FormatError) do - ActiveRecord::Fixtures.create_fixtures(dir, name) + ActiveRecord::FixtureSet.create_fixtures(dir, name) end ensure badyaml.close @@ -65,7 +65,7 @@ class FixturesTest < ActiveRecord::TestCase end def test_create_fixtures - fixtures = ActiveRecord::Fixtures.create_fixtures(FIXTURES_ROOT, "parrots") + fixtures = ActiveRecord::FixtureSet.create_fixtures(FIXTURES_ROOT, "parrots") assert Parrot.find_by_name('Curious George'), 'George is not in the database' assert fixtures.detect { |f| f.name == 'parrots' }, "no fixtures named 'parrots' in #{fixtures.map(&:name).inspect}" end @@ -74,11 +74,11 @@ class FixturesTest < ActiveRecord::TestCase fixtures_array = nil assert_nothing_raised { fixtures_array = create_fixtures(*FIXTURES) } assert_kind_of(Array, fixtures_array) - fixtures_array.each { |fixtures| assert_kind_of(ActiveRecord::Fixtures, fixtures) } + fixtures_array.each { |fixtures| assert_kind_of(ActiveRecord::FixtureSet, fixtures) } end def test_create_symbol_fixtures - fixtures = ActiveRecord::Fixtures.create_fixtures(FIXTURES_ROOT, :collections, :collections => Course) { Course.connection } + fixtures = ActiveRecord::FixtureSet.create_fixtures(FIXTURES_ROOT, :collections, :collections => Course) { Course.connection } assert Course.find_by_name('Collection'), 'course is not in the database' assert fixtures.detect { |f| f.name == 'collections' }, "no fixtures named 'collections' in #{fixtures.map(&:name).inspect}" @@ -102,7 +102,7 @@ class FixturesTest < ActiveRecord::TestCase if ActiveRecord::Base.connection.supports_migrations? def test_inserts_with_pre_and_suffix # Reset cache to make finds on the new table work - ActiveRecord::Fixtures.reset_cache + ActiveRecord::FixtureSet.reset_cache ActiveRecord::Base.connection.create_table :prefix_other_topics_suffix do |t| t.column :title, :string @@ -190,11 +190,11 @@ class FixturesTest < ActiveRecord::TestCase end def test_empty_yaml_fixture - assert_not_nil ActiveRecord::Fixtures.new( Account.connection, "accounts", 'Account', FIXTURES_ROOT + "/naked/yml/accounts") + assert_not_nil ActiveRecord::FixtureSet.new( Account.connection, "accounts", 'Account', FIXTURES_ROOT + "/naked/yml/accounts") end def test_empty_yaml_fixture_with_a_comment_in_it - assert_not_nil ActiveRecord::Fixtures.new( Account.connection, "companies", 'Company', FIXTURES_ROOT + "/naked/yml/companies") + assert_not_nil ActiveRecord::FixtureSet.new( Account.connection, "companies", 'Company', FIXTURES_ROOT + "/naked/yml/companies") end def test_nonexistent_fixture_file @@ -204,19 +204,19 @@ class FixturesTest < ActiveRecord::TestCase assert Dir[nonexistent_fixture_path+"*"].empty? assert_raise(Errno::ENOENT) do - ActiveRecord::Fixtures.new( Account.connection, "companies", 'Company', nonexistent_fixture_path) + ActiveRecord::FixtureSet.new( Account.connection, "companies", 'Company', nonexistent_fixture_path) end end def test_dirty_dirty_yaml_file assert_raise(ActiveRecord::Fixture::FormatError) do - ActiveRecord::Fixtures.new( Account.connection, "courses", 'Course', FIXTURES_ROOT + "/naked/yml/courses") + ActiveRecord::FixtureSet.new( Account.connection, "courses", 'Course', FIXTURES_ROOT + "/naked/yml/courses") end end def test_omap_fixtures assert_nothing_raised do - fixtures = ActiveRecord::Fixtures.new(Account.connection, 'categories', 'Category', FIXTURES_ROOT + "/categories_ordered") + fixtures = ActiveRecord::FixtureSet.new(Account.connection, 'categories', 'Category', FIXTURES_ROOT + "/categories_ordered") fixtures.each.with_index do |(name, fixture), i| assert_equal "fixture_no_#{i}", name @@ -254,7 +254,7 @@ if Account.connection.respond_to?(:reset_pk_sequence!) def setup @instances = [Account.new(:credit_limit => 50), Company.new(:name => 'RoR Consulting')] - ActiveRecord::Fixtures.reset_cache # make sure tables get reinitialized + ActiveRecord::FixtureSet.reset_cache # make sure tables get reinitialized end def test_resets_to_min_pk_with_specified_pk_and_sequence @@ -582,13 +582,13 @@ class FasterFixturesTest < ActiveRecord::TestCase def load_extra_fixture(name) fixture = create_fixtures(name).first - assert fixture.is_a?(ActiveRecord::Fixtures) + assert fixture.is_a?(ActiveRecord::FixtureSet) @loaded_fixtures[fixture.table_name] = fixture end def test_cache - assert ActiveRecord::Fixtures.fixture_is_cached?(ActiveRecord::Base.connection, 'categories') - assert ActiveRecord::Fixtures.fixture_is_cached?(ActiveRecord::Base.connection, 'authors') + assert ActiveRecord::FixtureSet.fixture_is_cached?(ActiveRecord::Base.connection, 'categories') + assert ActiveRecord::FixtureSet.fixture_is_cached?(ActiveRecord::Base.connection, 'authors') assert_no_queries do create_fixtures('categories') @@ -596,7 +596,7 @@ class FasterFixturesTest < ActiveRecord::TestCase end load_extra_fixture('posts') - assert ActiveRecord::Fixtures.fixture_is_cached?(ActiveRecord::Base.connection, 'posts') + assert ActiveRecord::FixtureSet.fixture_is_cached?(ActiveRecord::Base.connection, 'posts') self.class.setup_fixture_accessors :posts assert_equal 'Welcome to the weblog', posts(:welcome).title end @@ -606,17 +606,17 @@ class FoxyFixturesTest < ActiveRecord::TestCase fixtures :parrots, :parrots_pirates, :pirates, :treasures, :mateys, :ships, :computers, :developers, :"admin/accounts", :"admin/users" def test_identifies_strings - assert_equal(ActiveRecord::Fixtures.identify("foo"), ActiveRecord::Fixtures.identify("foo")) - assert_not_equal(ActiveRecord::Fixtures.identify("foo"), ActiveRecord::Fixtures.identify("FOO")) + assert_equal(ActiveRecord::FixtureSet.identify("foo"), ActiveRecord::FixtureSet.identify("foo")) + assert_not_equal(ActiveRecord::FixtureSet.identify("foo"), ActiveRecord::FixtureSet.identify("FOO")) end def test_identifies_symbols - assert_equal(ActiveRecord::Fixtures.identify(:foo), ActiveRecord::Fixtures.identify(:foo)) + assert_equal(ActiveRecord::FixtureSet.identify(:foo), ActiveRecord::FixtureSet.identify(:foo)) end def test_identifies_consistently - assert_equal 207281424, ActiveRecord::Fixtures.identify(:ruby) - assert_equal 1066363776, ActiveRecord::Fixtures.identify(:sapphire_2) + assert_equal 207281424, ActiveRecord::FixtureSet.identify(:ruby) + assert_equal 1066363776, ActiveRecord::FixtureSet.identify(:sapphire_2) end TIMESTAMP_COLUMNS = %w(created_at created_on updated_at updated_on) @@ -757,7 +757,7 @@ class FixtureLoadingTest < ActiveRecord::TestCase end class CustomNameForFixtureOrModelTest < ActiveRecord::TestCase - ActiveRecord::Fixtures.reset_cache + ActiveRecord::FixtureSet.reset_cache set_fixture_class :randomly_named_a9 => ClassNameThatDoesNotFollowCONVENTIONS, @@ -782,7 +782,7 @@ class CustomNameForFixtureOrModelTest < ActiveRecord::TestCase end def test_table_name_is_defined_in_the_model - assert_equal 'randomly_named_table', ActiveRecord::Fixtures::all_loaded_fixtures["admin/randomly_named_a9"].table_name + assert_equal 'randomly_named_table', ActiveRecord::FixtureSet::all_loaded_fixtures["admin/randomly_named_a9"].table_name assert_equal 'randomly_named_table', Admin::ClassNameThatDoesNotFollowCONVENTIONS.table_name end end diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb index f39111ba77..cff6689c15 100644 --- a/activerecord/test/cases/helper.rb +++ b/activerecord/test/cases/helper.rb @@ -80,7 +80,7 @@ class ActiveSupport::TestCase self.use_transactional_fixtures = true def create_fixtures(*fixture_set_names, &block) - ActiveRecord::Fixtures.create_fixtures(ActiveSupport::TestCase.fixture_path, fixture_set_names, fixture_class_names, &block) + ActiveRecord::FixtureSet.create_fixtures(ActiveSupport::TestCase.fixture_path, fixture_set_names, fixture_class_names, &block) end end diff --git a/activerecord/test/cases/multiparameter_attributes_test.rb b/activerecord/test/cases/multiparameter_attributes_test.rb new file mode 100644 index 0000000000..1209f5460f --- /dev/null +++ b/activerecord/test/cases/multiparameter_attributes_test.rb @@ -0,0 +1,350 @@ +require "cases/helper" +require 'models/topic' +require 'models/customer' + +class MultiParameterAttributeTest < ActiveRecord::TestCase + fixtures :topics + + def setup + ActiveRecord::Base.time_zone_aware_attributes = false + ActiveRecord::Base.default_timezone = :local + Time.zone = nil + end + + def test_multiparameter_attributes_on_date + attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "24" } + topic = Topic.find(1) + topic.attributes = attributes + # note that extra #to_date call allows test to pass for Oracle, which + # treats dates/times the same + assert_date_from_db Date.new(2004, 6, 24), topic.last_read.to_date + end + + def test_multiparameter_attributes_on_date_with_empty_year + attributes = { "last_read(1i)" => "", "last_read(2i)" => "6", "last_read(3i)" => "24" } + topic = Topic.find(1) + topic.attributes = attributes + # note that extra #to_date call allows test to pass for Oracle, which + # treats dates/times the same + assert_nil topic.last_read + end + + def test_multiparameter_attributes_on_date_with_empty_month + attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "", "last_read(3i)" => "24" } + topic = Topic.find(1) + topic.attributes = attributes + # note that extra #to_date call allows test to pass for Oracle, which + # treats dates/times the same + assert_nil topic.last_read + end + + def test_multiparameter_attributes_on_date_with_empty_day + attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "" } + topic = Topic.find(1) + topic.attributes = attributes + # note that extra #to_date call allows test to pass for Oracle, which + # treats dates/times the same + assert_nil topic.last_read + end + + def test_multiparameter_attributes_on_date_with_empty_day_and_year + attributes = { "last_read(1i)" => "", "last_read(2i)" => "6", "last_read(3i)" => "" } + topic = Topic.find(1) + topic.attributes = attributes + # note that extra #to_date call allows test to pass for Oracle, which + # treats dates/times the same + assert_nil topic.last_read + end + + def test_multiparameter_attributes_on_date_with_empty_day_and_month + attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "", "last_read(3i)" => "" } + topic = Topic.find(1) + topic.attributes = attributes + # note that extra #to_date call allows test to pass for Oracle, which + # treats dates/times the same + assert_nil topic.last_read + end + + def test_multiparameter_attributes_on_date_with_empty_year_and_month + attributes = { "last_read(1i)" => "", "last_read(2i)" => "", "last_read(3i)" => "24" } + topic = Topic.find(1) + topic.attributes = attributes + # note that extra #to_date call allows test to pass for Oracle, which + # treats dates/times the same + assert_nil topic.last_read + end + + def test_multiparameter_attributes_on_date_with_all_empty + attributes = { "last_read(1i)" => "", "last_read(2i)" => "", "last_read(3i)" => "" } + topic = Topic.find(1) + topic.attributes = attributes + assert_nil topic.last_read + end + + def test_multiparameter_attributes_on_time + attributes = { + "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24", + "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00" + } + topic = Topic.find(1) + topic.attributes = attributes + assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on + end + + def test_multiparameter_attributes_on_time_with_no_date + ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do + attributes = { + "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00" + } + topic = Topic.find(1) + topic.attributes = attributes + end + assert_equal("written_on", ex.errors[0].attribute) + end + + def test_multiparameter_attributes_on_time_with_invalid_time_params + ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do + attributes = { + "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24", + "written_on(4i)" => "2004", "written_on(5i)" => "36", "written_on(6i)" => "64", + } + topic = Topic.find(1) + topic.attributes = attributes + end + assert_equal("written_on", ex.errors[0].attribute) + end + + def test_multiparameter_attributes_on_time_with_old_date + attributes = { + "written_on(1i)" => "1850", "written_on(2i)" => "6", "written_on(3i)" => "24", + "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00" + } + topic = Topic.find(1) + topic.attributes = attributes + # testing against to_s(:db) representation because either a Time or a DateTime might be returned, depending on platform + assert_equal "1850-06-24 16:24:00", topic.written_on.to_s(:db) + end + + def test_multiparameter_attributes_on_time_will_raise_on_big_time_if_missing_date_parts + ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do + attributes = { + "written_on(4i)" => "16", "written_on(5i)" => "24" + } + topic = Topic.find(1) + topic.attributes = attributes + end + assert_equal("written_on", ex.errors[0].attribute) + end + + def test_multiparameter_attributes_on_time_with_raise_on_small_time_if_missing_date_parts + ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do + attributes = { + "written_on(4i)" => "16", "written_on(5i)" => "12", "written_on(6i)" => "02" + } + topic = Topic.find(1) + topic.attributes = attributes + end + assert_equal("written_on", ex.errors[0].attribute) + end + + def test_multiparameter_attributes_on_time_will_ignore_hour_if_missing + attributes = { + "written_on(1i)" => "2004", "written_on(2i)" => "12", "written_on(3i)" => "12", + "written_on(5i)" => "12", "written_on(6i)" => "02" + } + topic = Topic.find(1) + topic.attributes = attributes + assert_equal Time.local(2004, 12, 12, 0, 12, 2), topic.written_on + end + + def test_multiparameter_attributes_on_time_will_ignore_hour_if_blank + attributes = { + "written_on(1i)" => "", "written_on(2i)" => "", "written_on(3i)" => "", + "written_on(4i)" => "", "written_on(5i)" => "12", "written_on(6i)" => "02" + } + topic = Topic.find(1) + topic.attributes = attributes + assert_nil topic.written_on + end + + def test_multiparameter_attributes_on_time_will_ignore_date_if_empty + attributes = { + "written_on(1i)" => "", "written_on(2i)" => "", "written_on(3i)" => "", + "written_on(4i)" => "16", "written_on(5i)" => "24" + } + topic = Topic.find(1) + topic.attributes = attributes + assert_nil topic.written_on + end + def test_multiparameter_attributes_on_time_with_seconds_will_ignore_date_if_empty + attributes = { + "written_on(1i)" => "", "written_on(2i)" => "", "written_on(3i)" => "", + "written_on(4i)" => "16", "written_on(5i)" => "12", "written_on(6i)" => "02" + } + topic = Topic.find(1) + topic.attributes = attributes + assert_nil topic.written_on + end + + def test_multiparameter_attributes_on_time_with_utc + ActiveRecord::Base.default_timezone = :utc + attributes = { + "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24", + "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00" + } + topic = Topic.find(1) + topic.attributes = attributes + assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on + end + + def test_multiparameter_attributes_on_time_with_time_zone_aware_attributes + ActiveRecord::Base.time_zone_aware_attributes = true + ActiveRecord::Base.default_timezone = :utc + Time.zone = ActiveSupport::TimeZone[-28800] + attributes = { + "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24", + "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00" + } + topic = Topic.find(1) + topic.attributes = attributes + assert_equal Time.utc(2004, 6, 24, 23, 24, 0), topic.written_on + assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on.time + assert_equal Time.zone, topic.written_on.time_zone + end + + def test_multiparameter_attributes_on_time_with_time_zone_aware_attributes_false + Time.zone = ActiveSupport::TimeZone[-28800] + attributes = { + "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24", + "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00" + } + topic = Topic.find(1) + topic.attributes = attributes + assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on + assert_equal false, topic.written_on.respond_to?(:time_zone) + end + + def test_multiparameter_attributes_on_time_with_skip_time_zone_conversion_for_attributes + ActiveRecord::Base.time_zone_aware_attributes = true + ActiveRecord::Base.default_timezone = :utc + Time.zone = ActiveSupport::TimeZone[-28800] + Topic.skip_time_zone_conversion_for_attributes = [:written_on] + attributes = { + "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24", + "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00" + } + topic = Topic.find(1) + topic.attributes = attributes + assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on + assert_equal false, topic.written_on.respond_to?(:time_zone) + ensure + Topic.skip_time_zone_conversion_for_attributes = [] + end + + # Oracle, and Sybase do not have a TIME datatype. + unless current_adapter?(:OracleAdapter, :SybaseAdapter) + def test_multiparameter_attributes_on_time_only_column_with_time_zone_aware_attributes_does_not_do_time_zone_conversion + ActiveRecord::Base.time_zone_aware_attributes = true + ActiveRecord::Base.default_timezone = :utc + Time.zone = ActiveSupport::TimeZone[-28800] + attributes = { + "bonus_time(1i)" => "2000", "bonus_time(2i)" => "1", "bonus_time(3i)" => "1", + "bonus_time(4i)" => "16", "bonus_time(5i)" => "24" + } + topic = Topic.find(1) + topic.attributes = attributes + assert_equal Time.utc(2000, 1, 1, 16, 24, 0), topic.bonus_time + assert topic.bonus_time.utc? + end + end + + def test_multiparameter_attributes_on_time_with_empty_seconds + attributes = { + "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24", + "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "" + } + topic = Topic.find(1) + topic.attributes = attributes + assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on + end + + def test_multiparameter_attributes_setting_time_attribute + return skip "Oracle does not have TIME data type" if current_adapter? :OracleAdapter + + topic = Topic.new( "bonus_time(4i)"=> "01", "bonus_time(5i)" => "05" ) + assert_equal 1, topic.bonus_time.hour + assert_equal 5, topic.bonus_time.min + end + + def test_multiparameter_attributes_setting_date_attribute + topic = Topic.new( "written_on(1i)" => "1952", "written_on(2i)" => "3", "written_on(3i)" => "11" ) + assert_equal 1952, topic.written_on.year + assert_equal 3, topic.written_on.month + assert_equal 11, topic.written_on.day + end + + def test_multiparameter_attributes_setting_date_and_time_attribute + topic = Topic.new( + "written_on(1i)" => "1952", + "written_on(2i)" => "3", + "written_on(3i)" => "11", + "written_on(4i)" => "13", + "written_on(5i)" => "55") + assert_equal 1952, topic.written_on.year + assert_equal 3, topic.written_on.month + assert_equal 11, topic.written_on.day + assert_equal 13, topic.written_on.hour + assert_equal 55, topic.written_on.min + end + + def test_multiparameter_attributes_setting_time_but_not_date_on_date_field + assert_raise( ActiveRecord::MultiparameterAssignmentErrors ) do + Topic.new( "written_on(4i)" => "13", "written_on(5i)" => "55" ) + end + end + + def test_multiparameter_assignment_of_aggregation + customer = Customer.new + address = Address.new("The Street", "The City", "The Country") + attributes = { "address(1)" => address.street, "address(2)" => address.city, "address(3)" => address.country } + customer.attributes = attributes + assert_equal address, customer.address + end + + def test_multiparameter_assignment_of_aggregation_out_of_order + customer = Customer.new + address = Address.new("The Street", "The City", "The Country") + attributes = { "address(3)" => address.country, "address(2)" => address.city, "address(1)" => address.street } + customer.attributes = attributes + assert_equal address, customer.address + end + + def test_multiparameter_assignment_of_aggregation_with_missing_values + ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do + customer = Customer.new + address = Address.new("The Street", "The City", "The Country") + attributes = { "address(2)" => address.city, "address(3)" => address.country } + customer.attributes = attributes + end + assert_equal("address", ex.errors[0].attribute) + end + + def test_multiparameter_assignment_of_aggregation_with_blank_values + customer = Customer.new + address = Address.new("The Street", "The City", "The Country") + attributes = { "address(1)" => "", "address(2)" => address.city, "address(3)" => address.country } + customer.attributes = attributes + assert_equal Address.new(nil, "The City", "The Country"), customer.address + end + + def test_multiparameter_assignment_of_aggregation_with_large_index + ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do + customer = Customer.new + address = Address.new("The Street", "The City", "The Country") + attributes = { "address(1)" => "The Street", "address(2)" => address.city, "address(3000)" => address.country } + customer.attributes = attributes + end + + assert_equal("address", ex.errors[0].attribute) + end +end diff --git a/activerecord/test/cases/store_test.rb b/activerecord/test/cases/store_test.rb index dc47d40f41..562ca8d9ff 100644 --- a/activerecord/test/cases/store_test.rb +++ b/activerecord/test/cases/store_test.rb @@ -136,7 +136,7 @@ class StoreTest < ActiveRecord::TestCase end test "all stored attributes are returned" do - assert_equal [:color, :homepage, :favorite_food, :phone_number], Admin::User.stored_attributes[:settings] + assert_equal [:color, :homepage, :favorite_food], Admin::User.stored_attributes[:settings] end test "stores_attributes are class level settings" do diff --git a/activerecord/test/fixtures/mateys.yml b/activerecord/test/fixtures/mateys.yml index d3690955fc..3f0405aaf8 100644 --- a/activerecord/test/fixtures/mateys.yml +++ b/activerecord/test/fixtures/mateys.yml @@ -1,4 +1,4 @@ blackbeard_to_redbeard: - pirate_id: <%= ActiveRecord::Fixtures.identify(:blackbeard) %> - target_id: <%= ActiveRecord::Fixtures.identify(:redbeard) %> + pirate_id: <%= ActiveRecord::FixtureSet.identify(:blackbeard) %> + target_id: <%= ActiveRecord::FixtureSet.identify(:redbeard) %> weight: 10 diff --git a/activerecord/test/fixtures/parrots_pirates.yml b/activerecord/test/fixtures/parrots_pirates.yml index 66472243c7..e1a301b91a 100644 --- a/activerecord/test/fixtures/parrots_pirates.yml +++ b/activerecord/test/fixtures/parrots_pirates.yml @@ -1,7 +1,7 @@ george_blackbeard: - parrot_id: <%= ActiveRecord::Fixtures.identify(:george) %> - pirate_id: <%= ActiveRecord::Fixtures.identify(:blackbeard) %> + parrot_id: <%= ActiveRecord::FixtureSet.identify(:george) %> + pirate_id: <%= ActiveRecord::FixtureSet.identify(:blackbeard) %> louis_blackbeard: - parrot_id: <%= ActiveRecord::Fixtures.identify(:louis) %> - pirate_id: <%= ActiveRecord::Fixtures.identify(:blackbeard) %> + parrot_id: <%= ActiveRecord::FixtureSet.identify(:louis) %> + pirate_id: <%= ActiveRecord::FixtureSet.identify(:blackbeard) %> diff --git a/activerecord/test/fixtures/peoples_treasures.yml b/activerecord/test/fixtures/peoples_treasures.yml index a72b190d0c..46abe50e6c 100644 --- a/activerecord/test/fixtures/peoples_treasures.yml +++ b/activerecord/test/fixtures/peoples_treasures.yml @@ -1,3 +1,3 @@ michael_diamond: - rich_person_id: <%= ActiveRecord::Fixtures.identify(:michael) %> - treasure_id: <%= ActiveRecord::Fixtures.identify(:diamond) %> + rich_person_id: <%= ActiveRecord::FixtureSet.identify(:michael) %> + treasure_id: <%= ActiveRecord::FixtureSet.identify(:diamond) %> diff --git a/activerecord/test/models/admin/user.rb b/activerecord/test/models/admin/user.rb index 35170faa76..467f3ccd39 100644 --- a/activerecord/test/models/admin/user.rb +++ b/activerecord/test/models/admin/user.rb @@ -1,7 +1,7 @@ class Admin::User < ActiveRecord::Base belongs_to :account store :settings, :accessors => [ :color, :homepage ] - store_accessor :settings, :favorite_food, :phone_number + store_accessor :settings, :favorite_food store :preferences, :accessors => [ :remember_login ] store :json_data, :accessors => [ :height, :weight ], :coder => JSON store :json_data_empty, :accessors => [ :is_a_good_guy ], :coder => JSON diff --git a/activerecord/test/models/subscription.rb b/activerecord/test/models/subscription.rb index 4bdb36ea46..bcac4738a3 100644 --- a/activerecord/test/models/subscription.rb +++ b/activerecord/test/models/subscription.rb @@ -1,4 +1,4 @@ class Subscription < ActiveRecord::Base - belongs_to :subscriber + belongs_to :subscriber, :counter_cache => :books_count belongs_to :book end diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index 798ea20efc..2e4ec96933 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -621,6 +621,7 @@ ActiveRecord::Schema.define do create_table :subscribers, :force => true, :id => false do |t| t.string :nick, :null => false t.string :name + t.column :books_count, :integer, :null => false, :default => 0 end add_index :subscribers, :nick, :unique => true |