diff options
Diffstat (limited to 'activerecord')
60 files changed, 751 insertions, 198 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index d494d41aac..3b140b9ec3 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,5 @@ +## Rails 4.0.0 (unreleased) ## + * Expand `#cache_key` to consult all relevant updated timestamps. Previously only `updated_at` column was checked, now it will @@ -8,12 +10,137 @@ *Brendon Murphy* +* Throw `NotImplementedError` when trying to instantiate `ActiveRecord::Base` or an abstract class. + + *Aaron Weiner* + +* Warn when `rake db:structure:dump` with a mysl database and + `mysqldump` is not in the PATH or fails. + Fixes #9518. + + *Yves Senn* + +* Remove `connection#structure_dump`, which is no longer used. *Yves Senn* + +* Make it possible to execute migrations without a transaction even + if the database adapter supports DDL transactions. + Fixes #9483. + + Example: + + class ChangeEnum < ActiveRecord::Migration + self.disable_ddl_transaction! + def up + execute "ALTER TYPE model_size ADD VALUE 'new_value'" + end + end + + *Yves Senn* + +* Assigning "0.0" to a nullable numeric column does not make it dirty. + Fix #9034. + + Example: + + product = Product.create price: 0.0 + product.price = '0.0' + product.changed? # => false (this used to return true) + product.changes # => {} (this used to return { price: [0.0, 0.0] }) + + *Yves Senn* + +* Added functionality to unscope relations in a relations chain. For + instance, if you are passed in a chain of relations as follows: + + User.where(name: "John").order('id DESC') + + but you want to get rid of order, then this feature allows you to do: + + User.where(name: "John").order('id DESC').unscope(:order) + == User.where(name: "John") + + The .unscope() function is more general than the .except() method because + .except() only works on the relation it is acting on. However, .unscope() + works for any relation in the entire relation chain. + + *John Wang* + +* Postgresql timestamp with time zone (timestamptz) datatype now returns a + ActiveSupport::TimeWithZone instance instead of a string + + *Troy Kruthoff* + +* The `#append` method for collection associations behaves like`<<`. + `#prepend` is not defined and `<<` or `#append` should be used. + Fixes #7364. + + *Yves Senn* + +* Added support for creating a table via Rails migration generator. + For example, + + rails g migration create_books title:string content:text + + will generate a migration that creates a table called books with + the listed attributes, without creating a model. + + *Sammy Larbi* + +* Fix bug that raises the wrong exception when the exception handled by PostgreSQL adapter + doesn't respond to `#result`. + Fixes #8617. + + *kennyj* + +* Support PostgreSQL specific column types when using `change_table`. + Fixes #9480. + + Example: + + change_table :authors do |t| + t.hstore :books + t.json :metadata + end + + *Yves Senn* + +* Revert 408227d9c5ed7d, 'quote numeric'. This introduced some regressions. + + *Steve Klabnik* + +* Fix calculation of `db_runtime` property in + `ActiveRecord::Railties::ControllerRuntime#cleanup_view_runtime`. + Previously, after raising `ActionView::MissingTemplate`, `db_runtime` was + not populated. + Fixes #9215. + + *Igor Fedoronchuk* + +* Do not try to touch invalid (and thus not persisted) parent record + for a `belongs_to :parent, touch: true` association + + *Olek Janiszewski* + +* Fix when performing an ordered join query. The bug only + affected queries where the order was given with a symbol. + Fixes #9275. + + Example: + + # This will expand the order :name to "authors".name. + Author.joins(:books).where('books.published = 1').order(:name) + + ## Rails 4.0.0.beta1 (February 25, 2013) ## -* Fix overriding of attributes by default_scope on `ActiveRecord::Base#dup`. +* Fix overriding of attributes by `default_scope` on `ActiveRecord::Base#dup`. *Hiroshige UMINO* +* Update queries now use prepared statements. + + *Olli Rissanen* + * Fixing issue #8345. Now throwing an error when one attempts to touch a new object that has not yet been persisted. For instance: @@ -1508,4 +1635,5 @@ *Aaron Patterson* + Please check [3-2-stable](https://github.com/rails/rails/blob/3-2-stable/activerecord/CHANGELOG.md) for previous changes. diff --git a/activerecord/README.rdoc b/activerecord/README.rdoc index 9fc6785d99..ed1e171d58 100644 --- a/activerecord/README.rdoc +++ b/activerecord/README.rdoc @@ -130,7 +130,7 @@ A short rundown of some of the major features: SQLite3[link:classes/ActiveRecord/ConnectionAdapters/SQLite3Adapter.html]. -* Logging support for Log4r[http://log4r.sourceforge.net] and Logger[http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc]. +* Logging support for Log4r[http://log4r.rubyforge.org] and Logger[http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc]. ActiveRecord::Base.logger = ActiveSupport::Logger.new(STDOUT) ActiveRecord::Base.logger = Log4r::Logger.new('Application Log') diff --git a/activerecord/RUNNING_UNIT_TESTS.rdoc b/activerecord/RUNNING_UNIT_TESTS.rdoc index bdd8834dcb..2f3d516c43 100644 --- a/activerecord/RUNNING_UNIT_TESTS.rdoc +++ b/activerecord/RUNNING_UNIT_TESTS.rdoc @@ -2,7 +2,7 @@ If you don't have the environment set make sure to read - http://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html#testing-active-record. + http://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html#testing-active-record == Running the Tests diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 513d1012ba..35e4eb19a4 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1115,7 +1115,7 @@ module ActiveRecord # :dependent behavior may affect other callbacks. # # * <tt>:destroy</tt> causes all the associated objects to also be destroyed - # * <tt>:delete_all</tt> causes all the asssociated objects to be deleted directly from the database (so callbacks will not execute) + # * <tt>:delete_all</tt> causes all the associated objects to be deleted directly from the database (so callbacks will not execute) # * <tt>:nullify</tt> causes the foreign keys to be set to +NULL+. Callbacks are not executed. # * <tt>:restrict_with_exception</tt> causes an exception to be raised if there are any associated records # * <tt>:restrict_with_error</tt> causes an error to be added to the owner if there are any associated objects diff --git a/activerecord/lib/active_record/associations/association.rb b/activerecord/lib/active_record/associations/association.rb index 3f0e4ca999..868095f068 100644 --- a/activerecord/lib/active_record/associations/association.rb +++ b/activerecord/lib/active_record/associations/association.rb @@ -146,7 +146,7 @@ module ActiveRecord def interpolate(sql, record = nil) if sql.respond_to?(:to_proc) - owner.send(:instance_exec, record, &sql) + owner.instance_exec(record, &sql) else sql end diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb index 532af3e83e..54b1a69774 100644 --- a/activerecord/lib/active_record/associations/belongs_to_association.rb +++ b/activerecord/lib/active_record/associations/belongs_to_association.rb @@ -1,5 +1,5 @@ module ActiveRecord - # = Active Record Belongs To Associations + # = Active Record Belongs To Association module Associations class BelongsToAssociation < SingularAssociation #:nodoc: diff --git a/activerecord/lib/active_record/associations/builder/belongs_to.rb b/activerecord/lib/active_record/associations/builder/belongs_to.rb index 2f2600b7fb..97b1ff18e2 100644 --- a/activerecord/lib/active_record/associations/builder/belongs_to.rb +++ b/activerecord/lib/active_record/associations/builder/belongs_to.rb @@ -48,7 +48,7 @@ module ActiveRecord::Associations::Builder def belongs_to_touch_after_save_or_destroy_for_#{name} record = #{name} - unless record.nil? + unless record.nil? || record.new_record? record.touch #{options[:touch].inspect if options[:touch] != true} end end diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index e93e700c93..543204abac 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -83,9 +83,9 @@ module ActiveRecord # # #<Pet id: 3, name: "Choo-Choo"> # # ] # - # Be careful because this also means you’re initializing a model - # object with only the fields that you’ve selected. If you attempt - # to access a field that is not in the initialized record you’ll + # Be careful because this also means you're initializing a model + # object with only the fields that you've selected. If you attempt + # to access a field that is not in the initialized record you'll # receive: # # person.pets.select(:name).first.person_id @@ -924,7 +924,7 @@ module ActiveRecord alias_method :to_a, :to_ary # Adds one or more +records+ to the collection by setting their foreign keys - # to the association‘s primary key. Returns +self+, so several appends may be + # to the association's primary key. Returns +self+, so several appends may be # chained together. # # class Person < ActiveRecord::Base @@ -947,6 +947,11 @@ module ActiveRecord proxy_association.concat(records) && self end alias_method :push, :<< + alias_method :append, :<< + + def prepend(*args) + raise NoMethodError, "prepend on association is not defined. Please use << or append" + end # Equivalent to +delete_all+. The difference is that returns +self+, instead # of an array with the deleted objects, so methods can be chained. See diff --git a/activerecord/lib/active_record/associations/join_dependency/join_part.rb b/activerecord/lib/active_record/associations/join_dependency/join_part.rb index 711f7b3ce1..5604687b57 100644 --- a/activerecord/lib/active_record/associations/join_dependency/join_part.rb +++ b/activerecord/lib/active_record/associations/join_dependency/join_part.rb @@ -70,7 +70,7 @@ module ActiveRecord end def instantiate(row) - @cached_record[record_id(row)] ||= active_record.send(:instantiate, extract_record(row)) + @cached_record[record_id(row)] ||= active_record.instantiate(extract_record(row)) end end end diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb index 616ae1631f..6315dd9549 100644 --- a/activerecord/lib/active_record/attribute_methods/dirty.rb +++ b/activerecord/lib/active_record/attribute_methods/dirty.rb @@ -107,7 +107,11 @@ module ActiveRecord def changes_from_zero_to_string?(old, value) # For columns with old 0 and value non-empty string - old == 0 && value.is_a?(String) && value.present? && value != '0' + old == 0 && value.is_a?(String) && value.present? && non_zero?(value) + end + + def non_zero?(value) + value !~ /\A0+(\.0+)?\z/ end end end diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index 704998301c..55542262b0 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -347,7 +347,7 @@ module ActiveRecord end # reconstruct the scope now that we know the owner's id - association.send(:reset_scope) if association.respond_to?(:reset_scope) + association.reset_scope if association.respond_to?(:reset_scope) end end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb index aec4654eee..d18b9c991f 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb @@ -25,19 +25,13 @@ module ActiveRecord when true, false if column && column.type == :integer value ? '1' : '0' - elsif column && [:text, :string, :binary].include?(column.type) - value ? "'1'" : "'0'" else value ? quoted_true : quoted_false end # BigDecimals need to be put in a non-normalized form and quoted. when nil then "NULL" - when Numeric, ActiveSupport::Duration - value = BigDecimal === value ? value.to_s('F') : value.to_s - if column && ![:integer, :float, :decimal].include?(column.type) - value = "'#{value}'" - end - value + when BigDecimal then value.to_s('F') + when Numeric, ActiveSupport::Duration then value.to_s when Date, Time then "'#{quoted_date(value)}'" when Symbol then "'#{quote_string(value.to_s)}'" when Class then "'#{value.to_s}'" diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb index f758e19a4f..42206de8fc 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -44,8 +44,8 @@ module ActiveRecord # Represents the schema of an SQL table in an abstract way. This class # provides methods for manipulating the schema representation. # - # Inside migration files, the +t+ object in +create_table+ and - # +change_table+ is actually of this type: + # Inside migration files, the +t+ object in +create_table+ + # is actually of this type: # # class SomeMigration < ActiveRecord::Migration # def up @@ -489,7 +489,7 @@ module ActiveRecord args.each do |name| @base.add_column(@table_name, name, column_type, options) end - end + end end private diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb index fd5eaab9c9..f587bf8140 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb @@ -1,10 +1,12 @@ +require 'ipaddr' + module ActiveRecord module ConnectionAdapters # :nodoc: # The goal of this module is to move Adapter specific column # definitions to the Adapter instead of having it in the schema # dumper itself. This code represents the normal case. # We can then redefine how certain data types may be handled in the schema dumper on the - # Adapter level by over-writing this code inside the database spececific adapters + # Adapter level by over-writing this code inside the database specific adapters module ColumnDumper def column_spec(column, types) spec = prepare_column_options(column, types) @@ -50,6 +52,15 @@ module ActiveRecord when Range # infinity dumps as Infinity, which causes uninitialized constant error value.inspect.gsub('Infinity', '::Float::INFINITY') + when IPAddr + subnet_mask = value.instance_variable_get(:@mask_addr) + + # If the subnet mask is equal to /32, don't output it + if subnet_mask == (2**32 - 1) + "\"#{value.to_s}\"" + else + "\"#{value.to_s}/#{subnet_mask.to_s(2).count('1')}\"" + end else value.inspect end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index 9bae880024..5b8de184fe 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -156,7 +156,7 @@ module ActiveRecord # # See also TableDefinition#column for details on how to create columns. def create_table(table_name, options = {}) - td = table_definition + td = create_table_definition td.primary_key(options[:primary_key] || Base.get_primary_key(table_name.to_s.singularize)) unless options[:id] == false yield td if block_given? @@ -298,10 +298,10 @@ module ActiveRecord def change_table(table_name, options = {}) if supports_bulk_alter? && options[:bulk] recorder = ActiveRecord::Migration::CommandRecorder.new(self) - yield Table.new(table_name, recorder) + yield update_table_definition(table_name, recorder) bulk_change_table(table_name, recorder.commands) else - yield Table.new(table_name, self) + yield update_table_definition(table_name, self) end end @@ -516,11 +516,6 @@ module ActiveRecord end alias :remove_belongs_to :remove_reference - # Returns a string of <tt>CREATE TABLE</tt> SQL statement(s) for recreating the - # entire structure of the database. - def structure_dump - end - def dump_schema_information #:nodoc: sm_table = ActiveRecord::Migrator.schema_migrations_table_name @@ -727,9 +722,13 @@ module ActiveRecord end private - def table_definition + def create_table_definition TableDefinition.new(self) end + + def update_table_definition(table_name, base) + Table.new(table_name, base) + end end end end diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb index 5480204511..be4a30aed9 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -212,6 +212,8 @@ module ActiveRecord if value.kind_of?(String) && column && column.type == :binary && column.class.respond_to?(:string_to_binary) s = column.class.string_to_binary(value).unpack("H*")[0] "x'#{s}'" + elsif value.kind_of?(BigDecimal) + value.to_s("F") else super end @@ -330,20 +332,6 @@ module ActiveRecord # SCHEMA STATEMENTS ======================================== - def structure_dump #:nodoc: - if supports_views? - sql = "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'" - else - sql = "SHOW TABLES" - end - - select_all(sql, 'SCHEMA').map { |table| - table.delete('Table_type') - sql = "SHOW CREATE TABLE #{quote_table_name(table.to_a.first.last)}" - exec_query(sql, 'SCHEMA').first['Create Table'] + ";\n\n" - }.join - end - # Drops the database specified on the +name+ attribute # and creates it again using the provided +options+. def recreate_database(name, options = {}) diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb index 2c683fc3ac..8bad7d0cf5 100644 --- a/activerecord/lib/active_record/connection_adapters/connection_specification.rb +++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb @@ -38,7 +38,7 @@ module ActiveRecord private def resolve_string_connection(spec) # :nodoc: hash = configurations.fetch(spec) do |k| - self.class.connection_url_to_hash(k) + connection_url_to_hash(k) end raise(AdapterNotSpecified, "#{spec} database is not configured") unless hash @@ -65,7 +65,7 @@ module ActiveRecord ConnectionSpecification.new(spec, adapter_method) end - def self.connection_url_to_hash(url) # :nodoc: + def connection_url_to_hash(url) # :nodoc: config = URI.parse url adapter = config.scheme adapter = "postgresql" if adapter == "postgres" diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb index e09319890a..68f2f2ca7b 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb @@ -317,10 +317,6 @@ module ActiveRecord alias_type 'macaddr', 'text' alias_type 'uuid', 'text' - # FIXME: I don't think this is correct. We should probably be returning a parsed date, - # but the tests pass with a string returned. - register_type 'timestamptz', OID::Identity.new - register_type 'money', OID::Money.new register_type 'bytea', OID::Bytea.new register_type 'bool', OID::Boolean.new @@ -329,6 +325,7 @@ module ActiveRecord alias_type 'float8', 'float4' register_type 'timestamp', OID::Timestamp.new + register_type 'timestamptz', OID::Timestamp.new register_type 'date', OID::Date.new register_type 'time', OID::Time.new diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 2bb2557efd..c91e1b3fb9 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -263,7 +263,7 @@ module ActiveRecord attr_accessor :array end - class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition + module ColumnMethods def xml(*args) options = args.extract_options! column(args[0], 'xml', options) @@ -325,6 +325,10 @@ module ActiveRecord def json(name, options = {}) column(name, 'json', options) end + end + + class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition + include ColumnMethods def column(name, type = nil, options = {}) super @@ -344,6 +348,10 @@ module ActiveRecord end end + class Table < ActiveRecord::ConnectionAdapters::Table + include ColumnMethods + end + ADAPTER_NAME = 'PostgreSQL' NATIVE_DATABASE_TYPES = { @@ -667,6 +675,8 @@ module ActiveRecord UNIQUE_VIOLATION = "23505" def translate_exception(exception, message) + return exception unless exception.respond_to?(:result) + case exception.result.try(:error_field, PGresult::PG_DIAG_SQLSTATE) when UNIQUE_VIOLATION RecordNotUnique.new(message, exception) @@ -884,9 +894,13 @@ module ActiveRecord $1.strip if $1 end - def table_definition + def create_table_definition TableDefinition.new(self) end + + def update_table_definition(table_name, base) + Table.new(table_name, base) + end end end end diff --git a/activerecord/lib/active_record/explain.rb b/activerecord/lib/active_record/explain.rb index b2a9a54af1..3135465dfe 100644 --- a/activerecord/lib/active_record/explain.rb +++ b/activerecord/lib/active_record/explain.rb @@ -6,7 +6,8 @@ module ActiveRecord def collecting_queries_for_explain # :nodoc: current = Thread.current original, current[:available_queries_for_explain] = current[:available_queries_for_explain], [] - return yield, current[:available_queries_for_explain] + yield + return current[:available_queries_for_explain] ensure # Note that the return value above does not depend on this assigment. current[:available_queries_for_explain] = original diff --git a/activerecord/lib/active_record/inheritance.rb b/activerecord/lib/active_record/inheritance.rb index e630897a4b..13a8352f13 100644 --- a/activerecord/lib/active_record/inheritance.rb +++ b/activerecord/lib/active_record/inheritance.rb @@ -15,6 +15,9 @@ module ActiveRecord # and if the inheritance column is attr accessible, it initializes an # instance of the given subclass instead of the base class def new(*args, &block) + if abstract_class? || self == Base + raise NotImplementedError, "#{self} is an abstract class and can not be instantiated." + end if (attrs = args.first).is_a?(Hash) if subclass = subclass_from_attrs(attrs) return subclass.new(*args, &block) diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 823595a128..62e8881c4c 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -330,6 +330,23 @@ module ActiveRecord # # For a list of commands that are reversible, please see # <tt>ActiveRecord::Migration::CommandRecorder</tt>. + # + # == Transactional Migrations + # + # If the database adapter supports DDL transactions, all migrations will + # automatically be wrapped in a transaction. There are queries that you + # can't execute inside a transaction though, and for these situations + # you can turn the automatic transactions off. + # + # class ChangeEnum < ActiveRecord::Migration + # self.disable_ddl_transaction! + # def up + # execute "ALTER TYPE model_size ADD VALUE 'new_value'" + # end + # end + # + # Remember that you can still open your own transactions, even if you + # are in a Migration with <tt>self.disable_ddl_transaction!</tt>. class Migration autoload :CommandRecorder, 'active_record/migration/command_recorder' @@ -351,6 +368,7 @@ module ActiveRecord class << self attr_accessor :delegate # :nodoc: + attr_accessor :disable_ddl_transaction # :nodoc: end def self.check_pending! @@ -365,8 +383,16 @@ module ActiveRecord new.migrate direction end - cattr_accessor :verbose + # Disable DDL transactions for this migration. + def self.disable_ddl_transaction! + @disable_ddl_transaction = true + end + + def disable_ddl_transaction # :nodoc: + self.class.disable_ddl_transaction + end + cattr_accessor :verbose attr_accessor :name, :version def initialize(name = self.class.name, version = nil) @@ -375,8 +401,8 @@ module ActiveRecord @connection = nil end + self.verbose = true # instantiate the delegate object after initialize is defined - self.verbose = true self.delegate = new # Reverses the migration commands for the given block and @@ -663,7 +689,7 @@ module ActiveRecord File.basename(filename) end - delegate :migrate, :announce, :write, :to => :migration + delegate :migrate, :announce, :write, :disable_ddl_transaction, to: :migration private @@ -856,12 +882,12 @@ module ActiveRecord Base.logger.info "Migrating to #{migration.name} (#{migration.version})" if Base.logger begin - ddl_transaction do + ddl_transaction(migration) do migration.migrate(@direction) record_version_state_after_migrating(migration.version) end rescue => e - canceled_msg = Base.connection.supports_ddl_transactions? ? "this and " : "" + canceled_msg = use_transaction?(migration) ? "this and " : "" raise StandardError, "An error has occurred, #{canceled_msg}all later migrations canceled:\n\n#{e}", e.backtrace end end @@ -935,12 +961,16 @@ module ActiveRecord end # Wrap the migration in a transaction only if supported by the adapter. - def ddl_transaction - if Base.connection.supports_ddl_transactions? + def ddl_transaction(migration) + if use_transaction?(migration) Base.transaction { yield } else yield end end + + def use_transaction?(migration) + !migration.disable_ddl_transaction && Base.connection.supports_ddl_transactions? + end end end diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index 64eac3aca7..13f3bf7085 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -152,7 +152,7 @@ module ActiveRecord # and then establishes the connection. initializer "active_record.initialize_database" do |app| ActiveSupport.on_load(:active_record) do - self.configurations = app.config.database_configuration + self.configurations = app.config.database_configuration || {} establish_connection end end diff --git a/activerecord/lib/active_record/railties/controller_runtime.rb b/activerecord/lib/active_record/railties/controller_runtime.rb index 7695eacbff..af4840476c 100644 --- a/activerecord/lib/active_record/railties/controller_runtime.rb +++ b/activerecord/lib/active_record/railties/controller_runtime.rb @@ -21,9 +21,10 @@ module ActiveRecord def cleanup_view_runtime if ActiveRecord::Base.connected? db_rt_before_render = ActiveRecord::LogSubscriber.reset_runtime + self.db_runtime = (db_runtime || 0) + db_rt_before_render runtime = super db_rt_after_render = ActiveRecord::LogSubscriber.reset_runtime - self.db_runtime = db_rt_before_render + db_rt_after_render + self.db_runtime += db_rt_after_render runtime - db_rt_after_render else super diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index f36af7182f..d92e268109 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -2,7 +2,7 @@ require 'active_record' db_namespace = namespace :db do task :load_config do - ActiveRecord::Base.configurations = Rails.application.config.database_configuration + ActiveRecord::Base.configurations = Rails.application.config.database_configuration || {} ActiveRecord::Migrator.migrations_paths = Rails.application.paths['db/migrate'].to_a if defined?(ENGINE_PATH) && engine = Rails::Engine.find(ENGINE_PATH) diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index bc50802c4a..ad54ba55b6 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -188,8 +188,7 @@ module ActiveRecord # Please see further details in the # {Active Record Query Interface guide}[http://guides.rubyonrails.org/active_record_querying.html#running-explain]. def explain - _, queries = collecting_queries_for_explain { exec_queries } - exec_explain(queries) + exec_explain(collecting_queries_for_explain { exec_queries }) end # Converts relation objects to Array. @@ -236,8 +235,9 @@ module ActiveRecord # Scope all queries to the current scope. # # Comment.where(post_id: 1).scoping do - # Comment.first # SELECT * FROM comments WHERE post_id = 1 + # Comment.first # end + # # => SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = 1 ORDER BY "comments"."id" ASC LIMIT 1 # # Please check unscoped if you want to remove all previous scopes (including # the default_scope) during the execution of a block. diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index 5cd015eba7..bd783a94cf 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -98,11 +98,6 @@ module ActiveRecord when Class # FIXME: I think we need to deprecate this behavior attribute.eq(value.name) - when Integer, ActiveSupport::Duration - # Arel treats integers as literals, but they should be quoted when compared with strings - table = attribute.relation - column = table.engine.connection.schema_cache.columns_hash(table.name)[attribute.name.to_s] - attribute.eq(Arel::Nodes::SqlLiteral.new(table.engine.connection.quote(value, column))) else attribute.eq(value) end diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 225677085f..b7960936cf 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -5,7 +5,7 @@ module ActiveRecord extend ActiveSupport::Concern # WhereChain objects act as placeholder for queries in which #where does not have any parameter. - # In this case, #where must be chained with either #not, #like, or #not_like to return a new relation. + # In this case, #where must be chained with #not to return a new relation. class WhereChain def initialize(scope) @scope = scope @@ -285,6 +285,11 @@ module ActiveRecord references.map! { |arg| arg =~ /^([a-zA-Z]\w*)\.(\w+)/ && $1 }.compact! references!(references) if references.any? + # if a symbol is given we prepend the quoted table name + args = args.map { |arg| + arg.is_a?(Symbol) ? "#{quoted_table_name}.#{arg} ASC" : arg + } + self.order_values = args + self.order_values self end @@ -312,6 +317,67 @@ module ActiveRecord self end + VALID_UNSCOPING_VALUES = Set.new([:where, :select, :group, :order, :lock, + :limit, :offset, :joins, :includes, :from, + :readonly, :having]) + + # Removes an unwanted relation that is already defined on a chain of relations. + # This is useful when passing around chains of relations and would like to + # modify the relations without reconstructing the entire chain. + # + # User.order('email DESC').unscope(:order) == User.all + # + # The method arguments are symbols which correspond to the names of the methods + # which should be unscoped. The valid arguments are given in VALID_UNSCOPING_VALUES. + # The method can also be called with multiple arguments. For example: + # + # User.order('email DESC').select('id').where(name: "John") + # .unscope(:order, :select, :where) == User.all + # + # One can additionally pass a hash as an argument to unscope specific :where values. + # This is done by passing a hash with a single key-value pair. The key should be + # :where and the value should be the where value to unscope. For example: + # + # User.where(name: "John", active: true).unscope(where: :name) + # == User.where(active: true) + # + # Note that this method is more generalized than ActiveRecord::SpawnMethods#except + # because #except will only affect a particular relation's values. It won't wipe + # the order, grouping, etc. when that relation is merged. For example: + # + # Post.comments.except(:order) + # + # will still have an order if it comes from the default_scope on Comment. + def unscope(*args) + check_if_method_has_arguments!("unscope", args) + spawn.unscope!(*args) + end + + def unscope!(*args) + args.flatten! + + args.each do |scope| + case scope + when Symbol + symbol_unscoping(scope) + when Hash + scope.each do |key, target_value| + if key != :where + raise ArgumentError, "Hash arguments in .unscope(*args) must have :where as the key." + end + + Array(target_value).each do |val| + where_unscoping(val) + end + end + else + raise ArgumentError, "Unrecognized scoping: #{args.inspect}. Use .unscope(where: :attribute_name) or .unscope(:order), for example." + end + end + + self + end + # Performs a joins on +args+: # # User.joins(:posts) @@ -757,6 +823,39 @@ module ActiveRecord private + def symbol_unscoping(scope) + if !VALID_UNSCOPING_VALUES.include?(scope) + raise ArgumentError, "Called unscope() with invalid unscoping argument ':#{scope}'. Valid arguments are :#{VALID_UNSCOPING_VALUES.to_a.join(", :")}." + end + + single_val_method = Relation::SINGLE_VALUE_METHODS.include?(scope) + unscope_code = :"#{scope}_value#{'s' unless single_val_method}=" + + case scope + when :order + self.send(:reverse_order_value=, false) + result = [] + else + result = [] unless single_val_method + end + + self.send(unscope_code, result) + end + + def where_unscoping(target_value) + target_value_sym = target_value.to_sym + + where_values.reject! do |rel| + case rel + when Arel::Nodes::In, Arel::Nodes::Equality + subrelation = (rel.left.kind_of?(Arel::Attributes::Attribute) ? rel.left : rel.right) + subrelation.name.to_sym == target_value_sym + else + raise "unscope(where: #{target_value.inspect}) failed: unscoping #{rel.class} is unimplemented." + end + end + end + def custom_join_ast(table, joins) joins = joins.reject { |join| join.blank? } diff --git a/activerecord/lib/active_record/serialization.rb b/activerecord/lib/active_record/serialization.rb index 6b55af4205..bd9079b596 100644 --- a/activerecord/lib/active_record/serialization.rb +++ b/activerecord/lib/active_record/serialization.rb @@ -5,7 +5,7 @@ module ActiveRecord #:nodoc: include ActiveModel::Serializers::JSON included do - self.include_root_in_json = true + self.include_root_in_json = false end def serializable_hash(options = nil) diff --git a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb index 17378969a5..10696258c9 100644 --- a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb +++ b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb @@ -57,7 +57,10 @@ module ActiveRecord args.concat(["--result-file", "#{filename}"]) args.concat(["--no-data"]) args.concat(["#{configuration['database']}"]) - Kernel.system(*args) + unless Kernel.system(*args) + $stderr.puts "Could not dump the database structure. "\ + "Make sure `mysqldump` is in your PATH and check the command output for warnings." + end end def structure_load(filename) diff --git a/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb b/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb index 5f1dbe36d6..b967bb6e0f 100644 --- a/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb +++ b/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb @@ -8,13 +8,14 @@ module ActiveRecord def create_migration_file set_local_assigns! validate_file_name! - migration_template "migration.rb", "db/migrate/#{file_name}.rb" + migration_template @migration_template, "db/migrate/#{file_name}.rb" end protected attr_reader :migration_action, :join_tables def set_local_assigns! + @migration_template = "migration.rb" case file_name when /^(add|remove)_.*_(?:to|from)_(.*)/ @migration_action = $1 @@ -26,6 +27,9 @@ module ActiveRecord set_index_names end + when /^create_(.+)/ + @table_name = $1.pluralize + @migration_template = "create_table_migration.rb" end end @@ -44,7 +48,10 @@ module ActiveRecord end private - + def attributes_with_index + attributes.select { |a| !a.reference? && a.has_index? } + end + def validate_file_name! unless file_name =~ /^[_a-z0-9]+$/ raise IllegalMigrationNameError.new(file_name) diff --git a/activerecord/lib/rails/generators/active_record/model/templates/migration.rb b/activerecord/lib/rails/generators/active_record/migration/templates/create_table_migration.rb index 3a3cf86d73..3a3cf86d73 100644 --- a/activerecord/lib/rails/generators/active_record/model/templates/migration.rb +++ b/activerecord/lib/rails/generators/active_record/migration/templates/create_table_migration.rb diff --git a/activerecord/lib/rails/generators/active_record/model/model_generator.rb b/activerecord/lib/rails/generators/active_record/model/model_generator.rb index 5f36181694..40e134e626 100644 --- a/activerecord/lib/rails/generators/active_record/model/model_generator.rb +++ b/activerecord/lib/rails/generators/active_record/model/model_generator.rb @@ -15,7 +15,7 @@ module ActiveRecord def create_migration_file return unless options[:migration] && options[:parent].nil? attributes.each { |a| a.attr_options.delete(:index) if a.reference? && !a.has_index? } if options[:indexes] == false - migration_template "migration.rb", "db/migrate/create_#{table_name}.rb" + migration_template "../../migration/templates/create_table_migration.rb", "db/migrate/create_#{table_name}.rb" end def create_model_file diff --git a/activerecord/test/cases/adapters/mysql/reserved_word_test.rb b/activerecord/test/cases/adapters/mysql/reserved_word_test.rb index 5164acf77f..4cf4bc4c61 100644 --- a/activerecord/test/cases/adapters/mysql/reserved_word_test.rb +++ b/activerecord/test/cases/adapters/mysql/reserved_word_test.rb @@ -63,11 +63,6 @@ class MysqlReservedWordTest < ActiveRecord::TestCase assert_nothing_raised { @connection.rename_column(:group, :order, :values) } end - # dump structure of table with reserved word name - def test_structure_dump - assert_nothing_raised { @connection.structure_dump } - end - # introspect table with reserved word name def test_introspect assert_nothing_raised { @connection.columns(:group) } diff --git a/activerecord/test/cases/adapters/mysql2/connection_test.rb b/activerecord/test/cases/adapters/mysql2/connection_test.rb index 1265cb927e..fedd9f603c 100644 --- a/activerecord/test/cases/adapters/mysql2/connection_test.rb +++ b/activerecord/test/cases/adapters/mysql2/connection_test.rb @@ -70,12 +70,6 @@ class MysqlConnectionTest < ActiveRecord::TestCase end end - def test_logs_name_structure_dump - @connection.structure_dump - assert_equal "SCHEMA", @connection.logged[0][1] - assert_equal "SCHEMA", @connection.logged[2][1] - end - def test_logs_name_show_variable @connection.show_variable 'foo' assert_equal "SCHEMA", @connection.logged[0][1] diff --git a/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb b/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb index 1017b0758d..98596a7f62 100644 --- a/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb +++ b/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb @@ -63,11 +63,6 @@ class MysqlReservedWordTest < ActiveRecord::TestCase assert_nothing_raised { @connection.rename_column(:group, :order, :values) } end - # dump structure of table with reserved word name - def test_structure_dump - assert_nothing_raised { @connection.structure_dump } - end - # introspect table with reserved word name def test_introspect assert_nothing_raised { @connection.columns(:group) } diff --git a/activerecord/test/cases/adapters/postgresql/datatype_test.rb b/activerecord/test/cases/adapters/postgresql/datatype_test.rb index 33c796191e..1e6ae85a25 100644 --- a/activerecord/test/cases/adapters/postgresql/datatype_test.rb +++ b/activerecord/test/cases/adapters/postgresql/datatype_test.rb @@ -573,6 +573,7 @@ _SQL @first_timestamp_with_zone = PostgresqlTimestampWithZone.find(1) assert_equal Time.utc(2010,1,1, 11,0,0), @first_timestamp_with_zone.time + assert_instance_of Time, @first_timestamp_with_zone.time ensure ActiveRecord::Base.default_timezone = old_default_tz ActiveRecord::Base.time_zone_aware_attributes = old_tz @@ -590,6 +591,7 @@ _SQL @first_timestamp_with_zone = PostgresqlTimestampWithZone.find(1) assert_equal Time.utc(2010,1,1, 11,0,0), @first_timestamp_with_zone.time + assert_instance_of Time, @first_timestamp_with_zone.time ensure ActiveRecord::Base.default_timezone = old_default_tz ActiveRecord::Base.time_zone_aware_attributes = old_tz diff --git a/activerecord/test/cases/adapters/postgresql/hstore_test.rb b/activerecord/test/cases/adapters/postgresql/hstore_test.rb index 6640f9b497..ad98d7c8ce 100644 --- a/activerecord/test/cases/adapters/postgresql/hstore_test.rb +++ b/activerecord/test/cases/adapters/postgresql/hstore_test.rb @@ -65,6 +65,21 @@ class PostgresqlHstoreTest < ActiveRecord::TestCase assert_equal :hstore, @column.type end + def test_change_table_supports_hstore + @connection.transaction do + @connection.change_table('hstores') do |t| + t.hstore 'users', default: '' + end + Hstore.reset_column_information + column = Hstore.columns.find { |c| c.name == 'users' } + assert_equal :hstore, column.type + + raise ActiveRecord::Rollback # reset the schema change + end + ensure + Hstore.reset_column_information + end + def test_type_cast_hstore assert @column diff --git a/activerecord/test/cases/adapters/postgresql/json_test.rb b/activerecord/test/cases/adapters/postgresql/json_test.rb index d64037eec0..6fc08ae4f0 100644 --- a/activerecord/test/cases/adapters/postgresql/json_test.rb +++ b/activerecord/test/cases/adapters/postgresql/json_test.rb @@ -31,6 +31,21 @@ class PostgresqlJSONTest < ActiveRecord::TestCase assert_equal :json, @column.type end + def test_change_table_supports_json + @connection.transaction do + @connection.change_table('json_data_type') do |t| + t.json 'users', default: '{}' + end + JsonDataType.reset_column_information + column = JsonDataType.columns.find { |c| c.name == 'users' } + assert_equal :json, column.type + + raise ActiveRecord::Rollback # reset the schema change + end + ensure + JsonDataType.reset_column_information + end + def test_type_cast_json assert @column diff --git a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb index 872204c644..05e0f0e192 100644 --- a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb +++ b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb @@ -250,6 +250,12 @@ module ActiveRecord assert_equal "DISTINCT posts.title, posts.updater_id AS alias_0", @connection.distinct("posts.title", ["posts.updater_id desc nulls last"]) end + def test_raise_error_when_cannot_translate_exception + assert_raise TypeError do + @connection.send(:log, nil) { @connection.execute(nil) } + end + end + private def insert(ctx, data) binds = data.map { |name, value| diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb index 3a6da0e59f..8664ee99d2 100644 --- a/activerecord/test/cases/associations/belongs_to_associations_test.rb +++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb @@ -21,9 +21,9 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase :posts, :tags, :taggings, :comments, :sponsors, :members def test_belongs_to - Client.find(3).firm.name - assert_equal companies(:first_firm).name, Client.find(3).firm.name - assert_not_nil Client.find(3).firm, "Microsoft should have a firm" + firm = Client.find(3).firm + assert_not_nil firm + assert_equal companies(:first_firm).name, firm.name end def test_belongs_to_with_primary_key diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index 6ed3cb7b58..1de7ee0846 100644 --- a/activerecord/test/cases/associations/eager_test.rb +++ b/activerecord/test/cases/associations/eager_test.rb @@ -1173,4 +1173,9 @@ class EagerAssociationTest < ActiveRecord::TestCase assert_no_queries { assert_equal 2, author.comments_with_order_and_conditions.size } assert_no_queries { assert_equal 5, author.posts.size, "should not cache a subset of the association" } end + + test "works in combination with order(:symbol)" do + author = Author.includes(:posts).references(:posts).order(:name).where('posts.title IS NOT NULL').first + assert_equal authors(:bob), author + end end diff --git a/activerecord/test/cases/associations_test.rb b/activerecord/test/cases/associations_test.rb index d7f25f760e..201fa5d5a9 100644 --- a/activerecord/test/cases/associations_test.rb +++ b/activerecord/test/cases/associations_test.rb @@ -172,6 +172,18 @@ class AssociationProxyTest < ActiveRecord::TestCase assert_equal 1, josh.posts.size end + def test_append_behaves_like_push + josh = Author.new(:name => "Josh") + josh.posts.append Post.new(:title => "New on Edge", :body => "More cool stuff!") + assert josh.posts.loaded? + assert_equal 1, josh.posts.size + end + + def test_prepend_is_not_defined + josh = Author.new(:name => "Josh") + assert_raises(NoMethodError) { josh.posts.prepend Post.new } + end + def test_save_on_parent_does_not_load_target david = developers(:david) @@ -291,7 +303,7 @@ class OverridingAssociationsTest < ActiveRecord::TestCase end def test_requires_symbol_argument - assert_raises ArgumentError do + assert_raises ArgumentError do Class.new(Post) do belongs_to "author" end diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 5d558d5093..e5880a6b7b 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -307,6 +307,20 @@ class BasicsTest < ActiveRecord::TestCase assert_equal("last_read", ex.errors[0].attribute) end + def test_initialize_abstract_class + e = assert_raises(NotImplementedError) do + FirstAbstractClass.new + end + assert_equal("FirstAbstractClass is an abstract class and can not be instantiated.", e.message) + end + + def test_initialize_base + e = assert_raises(NotImplementedError) do + ActiveRecord::Base.new + end + assert_equal("ActiveRecord::Base is an abstract class and can not be instantiated.", e.message) + end + def test_create_after_initialize_without_block cb = CustomBulb.create(:name => 'Dude') assert_equal('Dude', cb.name) diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb index c7d2ba6073..7b2034dadf 100644 --- a/activerecord/test/cases/dirty_test.rb +++ b/activerecord/test/cases/dirty_test.rb @@ -243,6 +243,21 @@ class DirtyTest < ActiveRecord::TestCase assert !pirate.changed? end + def test_float_zero_to_string_zero_not_marked_as_changed + data = NumericData.new :temperature => 0.0 + data.save! + + assert_not data.changed? + + data.temperature = '0' + assert_empty data.changes + + data.temperature = '0.0' + assert_empty data.changes + + data.temperature = '0.00' + assert_empty data.changes + end def test_zero_to_blank_marked_as_changed pirate = Pirate.new diff --git a/activerecord/test/cases/explain_test.rb b/activerecord/test/cases/explain_test.rb index b1d276f9eb..6dac5db111 100644 --- a/activerecord/test/cases/explain_test.rb +++ b/activerecord/test/cases/explain_test.rb @@ -20,7 +20,7 @@ if ActiveRecord::Base.connection.supports_explain? end def test_collecting_queries_for_explain - result, queries = ActiveRecord::Base.collecting_queries_for_explain do + queries = ActiveRecord::Base.collecting_queries_for_explain do Car.where(:name => 'honda').to_a end @@ -28,7 +28,6 @@ if ActiveRecord::Base.connection.supports_explain? assert_match "SELECT", sql assert_match "honda", sql assert_equal [], binds - assert_equal [cars(:honda)], result end def test_exec_explain_with_no_binds diff --git a/activerecord/test/cases/json_serialization_test.rb b/activerecord/test/cases/json_serialization_test.rb index a86b165c78..a222675918 100644 --- a/activerecord/test/cases/json_serialization_test.rb +++ b/activerecord/test/cases/json_serialization_test.rb @@ -6,7 +6,21 @@ require 'models/tagging' require 'models/tag' require 'models/comment' +module JsonSerializationHelpers + private + + def set_include_root_in_json(value) + original_root_in_json = ActiveRecord::Base.include_root_in_json + ActiveRecord::Base.include_root_in_json = value + yield + ensure + ActiveRecord::Base.include_root_in_json = original_root_in_json + end +end + class JsonSerializationTest < ActiveRecord::TestCase + include JsonSerializationHelpers + class NamespacedContact < Contact column :name, :string end @@ -23,20 +37,24 @@ class JsonSerializationTest < ActiveRecord::TestCase end def test_should_demodulize_root_in_json - @contact = NamespacedContact.new :name => 'whatever' - json = @contact.to_json - assert_match %r{^\{"namespaced_contact":\{}, json + set_include_root_in_json(true) do + @contact = NamespacedContact.new name: 'whatever' + json = @contact.to_json + assert_match %r{^\{"namespaced_contact":\{}, json + end end def test_should_include_root_in_json - json = @contact.to_json - - assert_match %r{^\{"contact":\{}, json - assert_match %r{"name":"Konata Izumi"}, json - assert_match %r{"age":16}, json - assert json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))})) - assert_match %r{"awesome":true}, json - assert_match %r{"preferences":\{"shows":"anime"\}}, json + set_include_root_in_json(true) do + json = @contact.to_json + + assert_match %r{^\{"contact":\{}, json + assert_match %r{"name":"Konata Izumi"}, json + assert_match %r{"age":16}, json + assert json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))})) + assert_match %r{"awesome":true}, json + assert_match %r{"preferences":\{"shows":"anime"\}}, json + end end def test_should_encode_all_encodable_attributes @@ -141,6 +159,8 @@ end class DatabaseConnectedJsonEncodingTest < ActiveRecord::TestCase fixtures :authors, :posts, :comments, :tags, :taggings + include JsonSerializationHelpers + def setup @david = authors(:david) @mary = authors(:mary) @@ -227,23 +247,21 @@ class DatabaseConnectedJsonEncodingTest < ActiveRecord::TestCase end def test_should_allow_only_option_for_list_of_authors - ActiveRecord::Base.include_root_in_json = false - authors = [@david, @mary] - assert_equal %([{"name":"David"},{"name":"Mary"}]), ActiveSupport::JSON.encode(authors, :only => :name) - ensure - ActiveRecord::Base.include_root_in_json = true + set_include_root_in_json(false) do + authors = [@david, @mary] + assert_equal %([{"name":"David"},{"name":"Mary"}]), ActiveSupport::JSON.encode(authors, only: :name) + end end def test_should_allow_except_option_for_list_of_authors - ActiveRecord::Base.include_root_in_json = false - authors = [@david, @mary] - encoded = ActiveSupport::JSON.encode(authors, :except => [ - :name, :author_address_id, :author_address_extra_id, - :organization_id, :owned_essay_id - ]) - assert_equal %([{"id":1},{"id":2}]), encoded - ensure - ActiveRecord::Base.include_root_in_json = true + set_include_root_in_json(false) do + authors = [@david, @mary] + encoded = ActiveSupport::JSON.encode(authors, except: [ + :name, :author_address_id, :author_address_extra_id, + :organization_id, :owned_essay_id + ]) + assert_equal %([{"id":1},{"id":2}]), encoded + end end def test_should_allow_includes_for_list_of_authors @@ -262,17 +280,21 @@ class DatabaseConnectedJsonEncodingTest < ActiveRecord::TestCase end def test_should_allow_options_for_hash_of_authors - authors_hash = { - 1 => @david, - 2 => @mary - } - assert_equal %({"1":{"author":{"name":"David"}}}), ActiveSupport::JSON.encode(authors_hash, :only => [1, :name]) + set_include_root_in_json(true) do + authors_hash = { + 1 => @david, + 2 => @mary + } + assert_equal %({"1":{"author":{"name":"David"}}}), ActiveSupport::JSON.encode(authors_hash, only: [1, :name]) + end end def test_should_be_able_to_encode_relation - authors_relation = Author.where(:id => [@david.id, @mary.id]) + set_include_root_in_json(true) do + authors_relation = Author.where(id: [@david.id, @mary.id]) - json = ActiveSupport::JSON.encode authors_relation, :only => :name - assert_equal '[{"author":{"name":"David"}},{"author":{"name":"Mary"}}]', json + json = ActiveSupport::JSON.encode authors_relation, only: :name + assert_equal '[{"author":{"name":"David"}},{"author":{"name":"Mary"}}]', json + end end end diff --git a/activerecord/test/cases/migration/logger_test.rb b/activerecord/test/cases/migration/logger_test.rb index ee0c20747e..97efb94b66 100644 --- a/activerecord/test/cases/migration/logger_test.rb +++ b/activerecord/test/cases/migration/logger_test.rb @@ -7,6 +7,7 @@ module ActiveRecord self.use_transactional_fixtures = false Migration = Struct.new(:name, :version) do + def disable_ddl_transaction; false end def migrate direction # do nothing end @@ -34,4 +35,3 @@ module ActiveRecord end end end - diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index fa8dec0e15..960d28fcf5 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -239,9 +239,13 @@ class MigrationTest < ActiveRecord::TestCase assert_not Person.column_methods_hash.include?(:last_name) - migration = Struct.new(:name, :version) { - def migrate(x); raise 'Something broke'; end - }.new('zomg', 100) + migration = Class.new(ActiveRecord::Migration) { + def version; 100 end + def migrate(x) + add_column "people", "last_name", :string + raise 'Something broke' + end + }.new migrator = ActiveRecord::Migrator.new(:up, [migration], 100) @@ -250,7 +254,39 @@ class MigrationTest < ActiveRecord::TestCase assert_equal "An error has occurred, this and all later migrations canceled:\n\nSomething broke", e.message Person.reset_column_information + assert_not Person.column_methods_hash.include?(:last_name), + "On error, the Migrator should revert schema changes but it did not." + end + + def test_migration_without_transaction + unless ActiveRecord::Base.connection.supports_ddl_transactions? + skip "not supported on #{ActiveRecord::Base.connection.class}" + end + assert_not Person.column_methods_hash.include?(:last_name) + + migration = Class.new(ActiveRecord::Migration) { + self.disable_ddl_transaction! + + def version; 101 end + def migrate(x) + add_column "people", "last_name", :string + raise 'Something broke' + end + }.new + + migrator = ActiveRecord::Migrator.new(:up, [migration], 101) + e = assert_raise(StandardError) { migrator.migrate } + assert_equal "An error has occurred, all later migrations canceled:\n\nSomething broke", e.message + + Person.reset_column_information + assert Person.column_methods_hash.include?(:last_name), + "without ddl transactions, the Migrator should not rollback on error but it did." + ensure + Person.reset_column_information + if Person.column_methods_hash.include?(:last_name) + Person.connection.remove_column('people', 'last_name') + end end def test_schema_migrations_table_name diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb index 8156f99037..b936cca875 100644 --- a/activerecord/test/cases/persistence_test.rb +++ b/activerecord/test/cases/persistence_test.rb @@ -399,14 +399,6 @@ class PersistencesTest < ActiveRecord::TestCase assert_raises(ActiveRecord::ActiveRecordError) { minivan.update_attribute(:color, 'black') } end - def test_string_ids - # FIXME: Fix this failing test - skip "Failing test. We need this fixed before 4.0.0" - mv = Minivan.where(:minivan_id => 1234).first_or_initialize - assert mv.new_record? - assert_equal '1234', mv.minivan_id - end - def test_update_attribute_with_one_updated t = Topic.first t.update_attribute(:title, 'super_title') diff --git a/activerecord/test/cases/quoting_test.rb b/activerecord/test/cases/quoting_test.rb index 0ad05223d4..3dd11ae89d 100644 --- a/activerecord/test/cases/quoting_test.rb +++ b/activerecord/test/cases/quoting_test.rb @@ -122,35 +122,35 @@ module ActiveRecord def test_quote_float float = 1.2 assert_equal float.to_s, @quoter.quote(float, nil) - assert_equal float.to_s, @quoter.quote(float, FakeColumn.new(:float)) + assert_equal float.to_s, @quoter.quote(float, Object.new) end def test_quote_fixnum fixnum = 1 assert_equal fixnum.to_s, @quoter.quote(fixnum, nil) - assert_equal fixnum.to_s, @quoter.quote(fixnum, FakeColumn.new(:integer)) + assert_equal fixnum.to_s, @quoter.quote(fixnum, Object.new) end def test_quote_bignum bignum = 1 << 100 assert_equal bignum.to_s, @quoter.quote(bignum, nil) - assert_equal bignum.to_s, @quoter.quote(bignum, FakeColumn.new(:integer)) + assert_equal bignum.to_s, @quoter.quote(bignum, Object.new) end def test_quote_bigdecimal bigdec = BigDecimal.new((1 << 100).to_s) assert_equal bigdec.to_s('F'), @quoter.quote(bigdec, nil) - assert_equal bigdec.to_s('F'), @quoter.quote(bigdec, FakeColumn.new(:decimal)) + assert_equal bigdec.to_s('F'), @quoter.quote(bigdec, Object.new) end def test_dates_and_times @quoter.extend(Module.new { def quoted_date(value) 'lol' end }) assert_equal "'lol'", @quoter.quote(Date.today, nil) - assert_equal "'lol'", @quoter.quote(Date.today, FakeColumn.new(:date)) + assert_equal "'lol'", @quoter.quote(Date.today, Object.new) assert_equal "'lol'", @quoter.quote(Time.now, nil) - assert_equal "'lol'", @quoter.quote(Time.now, FakeColumn.new(:time)) + assert_equal "'lol'", @quoter.quote(Time.now, Object.new) assert_equal "'lol'", @quoter.quote(DateTime.now, nil) - assert_equal "'lol'", @quoter.quote(DateTime.now, FakeColumn.new(:datetime)) + assert_equal "'lol'", @quoter.quote(DateTime.now, Object.new) end def test_crazy_object diff --git a/activerecord/test/cases/relation/where_test.rb b/activerecord/test/cases/relation/where_test.rb index 53cdf89b1f..c43c7601a2 100644 --- a/activerecord/test/cases/relation/where_test.rb +++ b/activerecord/test/cases/relation/where_test.rb @@ -108,30 +108,5 @@ module ActiveRecord assert_equal 4, Edge.where(blank).order("sink_id").to_a.size end end - - def test_where_with_integer_for_string_column - count = Post.where(:title => 0).count - assert_equal 0, count - end - - def test_where_with_float_for_string_column - count = Post.where(:title => 0.0).count - assert_equal 0, count - end - - def test_where_with_boolean_for_string_column - count = Post.where(:title => false).count - assert_equal 0, count - end - - def test_where_with_decimal_for_string_column - count = Post.where(:title => BigDecimal.new(0)).count - assert_equal 0, count - end - - def test_where_with_duration_for_string_column - count = Post.where(:title => 0.seconds).count - assert_equal 0, count - end end end diff --git a/activerecord/test/cases/relation_scoping_test.rb b/activerecord/test/cases/relation_scoping_test.rb index 8e6c38706f..2b4aadc7ed 100644 --- a/activerecord/test/cases/relation_scoping_test.rb +++ b/activerecord/test/cases/relation_scoping_test.rb @@ -391,19 +391,19 @@ class DefaultScopingTest < ActiveRecord::TestCase def test_default_scope_with_inheritance wheres = InheritedPoorDeveloperCalledJamis.all.where_values_hash assert_equal "Jamis", wheres[:name] - assert_equal Arel.sql("50000"), wheres[:salary] + assert_equal 50000, wheres[:salary] end def test_default_scope_with_module_includes wheres = ModuleIncludedPoorDeveloperCalledJamis.all.where_values_hash assert_equal "Jamis", wheres[:name] - assert_equal Arel.sql("50000"), wheres[:salary] + assert_equal 50000, wheres[:salary] end def test_default_scope_with_multiple_calls wheres = MultiplePoorDeveloperCalledJamis.all.where_values_hash assert_equal "Jamis", wheres[:name] - assert_equal Arel.sql("50000"), wheres[:salary] + assert_equal 50000, wheres[:salary] end def test_scope_overwrites_default @@ -424,6 +424,150 @@ class DefaultScopingTest < ActiveRecord::TestCase assert_equal expected, received end + def test_unscope_overrides_default_scope + expected = Developer.all.collect { |dev| [dev.name, dev.id] } + received = Developer.order('name ASC, id DESC').unscope(:order).collect { |dev| [dev.name, dev.id] } + assert_equal expected, received + end + + def test_unscope_after_reordering_and_combining + expected = Developer.order('id DESC, name DESC').collect { |dev| [dev.name, dev.id] } + received = DeveloperOrderedBySalary.reorder('name DESC').unscope(:order).order('id DESC, name DESC').collect { |dev| [dev.name, dev.id] } + assert_equal expected, received + + expected_2 = Developer.all.collect { |dev| [dev.name, dev.id] } + received_2 = Developer.order('id DESC, name DESC').unscope(:order).collect { |dev| [dev.name, dev.id] } + assert_equal expected_2, received_2 + + expected_3 = Developer.all.collect { |dev| [dev.name, dev.id] } + received_3 = Developer.reorder('name DESC').unscope(:order).collect { |dev| [dev.name, dev.id] } + assert_equal expected_3, received_3 + end + + def test_unscope_with_where_attributes + expected = Developer.order('salary DESC').collect { |dev| dev.name } + received = DeveloperOrderedBySalary.where(name: 'David').unscope(where: :name).collect { |dev| dev.name } + assert_equal expected, received + + expected_2 = Developer.order('salary DESC').collect { |dev| dev.name } + received_2 = DeveloperOrderedBySalary.select("id").where("name" => "Jamis").unscope({:where => :name}, :select).collect { |dev| dev.name } + assert_equal expected_2, received_2 + + expected_3 = Developer.order('salary DESC').collect { |dev| dev.name } + received_3 = DeveloperOrderedBySalary.select("id").where("name" => "Jamis").unscope(:select, :where).collect { |dev| dev.name } + assert_equal expected_3, received_3 + end + + def test_unscope_multiple_where_clauses + expected = Developer.order('salary DESC').collect { |dev| dev.name } + received = DeveloperOrderedBySalary.where(name: 'Jamis').where(id: 1).unscope(where: [:name, :id]).collect { |dev| dev.name } + assert_equal expected, received + end + + def test_unscope_with_grouping_attributes + expected = Developer.order('salary DESC').collect { |dev| dev.name } + received = DeveloperOrderedBySalary.group(:name).unscope(:group).collect { |dev| dev.name } + assert_equal expected, received + + expected_2 = Developer.order('salary DESC').collect { |dev| dev.name } + received_2 = DeveloperOrderedBySalary.group("name").unscope(:group).collect { |dev| dev.name } + assert_equal expected_2, received_2 + end + + def test_unscope_with_limit_in_query + expected = Developer.order('salary DESC').collect { |dev| dev.name } + received = DeveloperOrderedBySalary.limit(1).unscope(:limit).collect { |dev| dev.name } + assert_equal expected, received + end + + def test_order_to_unscope_reordering + expected = DeveloperOrderedBySalary.all.collect { |dev| [dev.name, dev.id] } + received = DeveloperOrderedBySalary.order('salary DESC, name ASC').reverse_order.unscope(:order).collect { |dev| [dev.name, dev.id] } + assert_equal expected, received + end + + def test_unscope_reverse_order + expected = Developer.all.collect { |dev| dev.name } + received = Developer.order('salary DESC').reverse_order.unscope(:order).collect { |dev| dev.name } + assert_equal expected, received + end + + def test_unscope_select + expected = Developer.order('salary ASC').collect { |dev| dev.name } + received = Developer.order('salary DESC').reverse_order.select(:name => "Jamis").unscope(:select).collect { |dev| dev.name } + assert_equal expected, received + + expected_2 = Developer.all.collect { |dev| dev.id } + received_2 = Developer.select(:name).unscope(:select).collect { |dev| dev.id } + assert_equal expected_2, received_2 + end + + def test_unscope_offset + expected = Developer.all.collect { |dev| dev.name } + received = Developer.offset(5).unscope(:offset).collect { |dev| dev.name } + assert_equal expected, received + end + + def test_unscope_joins_and_select_on_developers_projects + expected = Developer.all.collect { |dev| dev.name } + received = Developer.joins('JOIN developers_projects ON id = developer_id').select(:id).unscope(:joins, :select).collect { |dev| dev.name } + assert_equal expected, received + end + + def test_unscope_includes + expected = Developer.all.collect { |dev| dev.name } + received = Developer.includes(:projects).select(:id).unscope(:includes, :select).collect { |dev| dev.name } + assert_equal expected, received + end + + def test_unscope_having + expected = DeveloperOrderedBySalary.all.collect { |dev| dev.name } + received = DeveloperOrderedBySalary.having("name IN ('Jamis', 'David')").unscope(:having).collect { |dev| dev.name } + assert_equal expected, received + end + + def test_unscope_errors_with_invalid_value + assert_raises(ArgumentError) do + Developer.includes(:projects).where(name: "Jamis").unscope(:stupidly_incorrect_value) + end + + assert_raises(ArgumentError) do + Developer.all.unscope(:includes, :select, :some_broken_value) + end + + assert_raises(ArgumentError) do + Developer.order('name DESC').reverse_order.unscope(:reverse_order) + end + + assert_raises(ArgumentError) do + Developer.order('name DESC').where(name: "Jamis").unscope() + end + end + + def test_unscope_errors_with_non_where_hash_keys + assert_raises(ArgumentError) do + Developer.where(name: "Jamis").limit(4).unscope(limit: 4) + end + + assert_raises(ArgumentError) do + Developer.where(name: "Jamis").unscope("where" => :name) + end + end + + def test_unscope_errors_with_non_symbol_or_hash_arguments + assert_raises(ArgumentError) do + Developer.where(name: "Jamis").limit(3).unscope("limit") + end + + assert_raises(ArgumentError) do + Developer.select("id").unscope("select") + end + + assert_raises(ArgumentError) do + Developer.select("id").unscope(5) + end + end + def test_order_in_default_scope_should_not_prevail expected = Developer.all.merge!(:order => 'salary').to_a.collect { |dev| dev.salary } received = DeveloperOrderedBySalary.all.merge!(:order => 'salary').to_a.collect { |dev| dev.salary } diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb index 92dc575d37..fd0b05cb77 100644 --- a/activerecord/test/cases/relation_test.rb +++ b/activerecord/test/cases/relation_test.rb @@ -180,19 +180,32 @@ module ActiveRecord class RelationMutationTest < ActiveSupport::TestCase class FakeKlass < Struct.new(:table_name, :name) + def quoted_table_name + %{"#{table_name}"} + end end def relation - @relation ||= Relation.new FakeKlass, :b + @relation ||= Relation.new FakeKlass.new('posts'), :b end - (Relation::MULTI_VALUE_METHODS - [:references, :extending]).each do |method| + (Relation::MULTI_VALUE_METHODS - [:references, :extending, :order]).each do |method| test "##{method}!" do assert relation.public_send("#{method}!", :foo).equal?(relation) assert_equal [:foo], relation.public_send("#{method}_values") end end + test "#order!" do + assert relation.order!('name ASC').equal?(relation) + assert_equal ['name ASC'], relation.order_values + end + + test "#order! with symbol prepends the table name" do + assert relation.order!(:name).equal?(relation) + assert_equal ['"posts".name ASC'], relation.order_values + end + test '#references!' do assert relation.references!(:foo).equal?(relation) assert relation.references_values.include?('foo') diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb index bfecc0d1e9..1147b9a09e 100644 --- a/activerecord/test/cases/schema_dumper_test.rb +++ b/activerecord/test/cases/schema_dumper_test.rb @@ -261,22 +261,22 @@ class SchemaDumperTest < ActiveRecord::TestCase def test_schema_dump_includes_inet_shorthand_definition output = standard_dump - if %r{create_table "postgresql_network_address"} =~ output - assert_match %r{t.inet "inet_address"}, output + if %r{create_table "postgresql_network_addresses"} =~ output + assert_match %r{t.inet\s+"inet_address",\s+default: "192.168.1.1"}, output end end def test_schema_dump_includes_cidr_shorthand_definition output = standard_dump - if %r{create_table "postgresql_network_address"} =~ output - assert_match %r{t.cidr "cidr_address"}, output + if %r{create_table "postgresql_network_addresses"} =~ output + assert_match %r{t.cidr\s+"cidr_address",\s+default: "192.168.1.0/24"}, output end end def test_schema_dump_includes_macaddr_shorthand_definition output = standard_dump - if %r{create_table "postgresql_network_address"} =~ output - assert_match %r{t.macaddr "macaddr_address"}, output + if %r{create_table "postgresql_network_addresses"} =~ output + assert_match %r{t.macaddr\s+"mac_address",\s+default: "ff:ff:ff:ff:ff:ff"}, output end end diff --git a/activerecord/test/cases/serialization_test.rb b/activerecord/test/cases/serialization_test.rb index eb9cb91e32..c46060a646 100644 --- a/activerecord/test/cases/serialization_test.rb +++ b/activerecord/test/cases/serialization_test.rb @@ -18,6 +18,10 @@ class SerializationTest < ActiveRecord::TestCase } end + def test_include_root_in_json_is_false_by_default + assert_equal false, ActiveRecord::Base.include_root_in_json, "include_root_in_json should be false by default but was not" + end + def test_serialize_should_be_reversible FORMATS.each do |format| @serialized = Contact.new.send("to_#{format}") diff --git a/activerecord/test/cases/tasks/mysql_rake_test.rb b/activerecord/test/cases/tasks/mysql_rake_test.rb index 38b9dd02f0..dadcca5b7f 100644 --- a/activerecord/test/cases/tasks/mysql_rake_test.rb +++ b/activerecord/test/cases/tasks/mysql_rake_test.rb @@ -249,10 +249,21 @@ module ActiveRecord def test_structure_dump filename = "awesome-file.sql" - Kernel.expects(:system).with("mysqldump", "--result-file", filename, "--no-data", "test-db") + Kernel.expects(:system).with("mysqldump", "--result-file", filename, "--no-data", "test-db").returns(true) ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, filename) end + + def test_warn_when_external_structure_dump_fails + filename = "awesome-file.sql" + Kernel.expects(:system).with("mysqldump", "--result-file", filename, "--no-data", "test-db").returns(false) + + warnings = capture(:stderr) do + ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, filename) + end + + assert_match(/Could not dump the database structure/, warnings) + end end class MySQLStructureLoadTest < ActiveRecord::TestCase diff --git a/activerecord/test/cases/timestamp_test.rb b/activerecord/test/cases/timestamp_test.rb index bb034848e1..777a2b70dd 100644 --- a/activerecord/test/cases/timestamp_test.rb +++ b/activerecord/test/cases/timestamp_test.rb @@ -113,6 +113,18 @@ class TimestampTest < ActiveRecord::TestCase assert_not_equal previously_owner_updated_at, pet.owner.updated_at end + def test_saving_a_new_record_belonging_to_invalid_parent_with_touch_should_not_raise_exception + klass = Class.new(Owner) do + def self.name; 'Owner'; end + validate { errors.add(:base, :invalid) } + end + + pet = Pet.new(owner: klass.new) + pet.save! + + assert pet.owner.new_record? + end + def test_saving_a_record_with_a_belongs_to_that_specifies_touching_a_specific_attribute_the_parent_should_update_that_attribute klass = Class.new(ActiveRecord::Base) do def self.name; 'Pet'; end diff --git a/activerecord/test/schema/postgresql_specific_schema.rb b/activerecord/test/schema/postgresql_specific_schema.rb index 83b50030bd..d8271ac8d1 100644 --- a/activerecord/test/schema/postgresql_specific_schema.rb +++ b/activerecord/test/schema/postgresql_specific_schema.rb @@ -145,9 +145,9 @@ _SQL execute <<_SQL CREATE TABLE postgresql_network_addresses ( id SERIAL PRIMARY KEY, - cidr_address CIDR, - inet_address INET, - mac_address MACADDR + cidr_address CIDR default '192.168.1.0/24', + inet_address INET default '192.168.1.1', + mac_address MACADDR default 'ff:ff:ff:ff:ff:ff' ); _SQL diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index a323b1e722..118271b594 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -542,8 +542,6 @@ ActiveRecord::Schema.define do create_table :price_estimates, :force => true do |t| t.string :estimate_of_type t.integer :estimate_of_id - t.string :thing_type - t.integer :thing_id t.integer :price end |