aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/CHANGELOG.md98
-rw-r--r--activerecord/lib/active_record/associations/builder/belongs_to.rb2
-rw-r--r--activerecord/lib/active_record/associations/collection_proxy.rb13
-rw-r--r--activerecord/lib/active_record/attribute_methods/dirty.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/quoting.rb10
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb11
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb12
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/connection_specification.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid.rb5
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb18
-rw-r--r--activerecord/lib/active_record/railtie.rb2
-rw-r--r--activerecord/lib/active_record/railties/controller_runtime.rb3
-rw-r--r--activerecord/lib/active_record/railties/databases.rake2
-rw-r--r--activerecord/lib/active_record/relation.rb3
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder.rb5
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb94
-rw-r--r--activerecord/lib/active_record/serialization.rb2
-rw-r--r--activerecord/lib/rails/generators/active_record/migration/migration_generator.rb11
-rw-r--r--activerecord/lib/rails/generators/active_record/migration/templates/create_table_migration.rb (renamed from activerecord/lib/rails/generators/active_record/model/templates/migration.rb)0
-rw-r--r--activerecord/lib/rails/generators/active_record/model/model_generator.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/datatype_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/hstore_test.rb15
-rw-r--r--activerecord/test/cases/adapters/postgresql/json_test.rb15
-rw-r--r--activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb6
-rw-r--r--activerecord/test/cases/associations_test.rb14
-rw-r--r--activerecord/test/cases/dirty_test.rb15
-rw-r--r--activerecord/test/cases/json_serialization_test.rb88
-rw-r--r--activerecord/test/cases/quoting_test.rb14
-rw-r--r--activerecord/test/cases/relation/where_test.rb25
-rw-r--r--activerecord/test/cases/relation_scoping_test.rb150
-rw-r--r--activerecord/test/cases/schema_dumper_test.rb12
-rw-r--r--activerecord/test/cases/serialization_test.rb4
-rw-r--r--activerecord/test/cases/timestamp_test.rb12
-rw-r--r--activerecord/test/schema/postgresql_specific_schema.rb6
-rw-r--r--activerecord/test/schema/schema.rb2
37 files changed, 566 insertions, 125 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 3fbf043c7c..97616ffc58 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,8 +1,88 @@
-## Rails 4.0.0.beta1 (February 25, 2013) ##
+## Rails 4.0.0 (unreleased) ##
-* Fix overriding of attributes by default_scope on `ActiveRecord::Base#dup`.
+* Assigning "0.0" to a nullable numeric column does not make it dirty.
+ Fix #9034.
- *Hiroshige UMINO*
+ 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.
@@ -13,6 +93,17 @@
# 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`.
+
+ *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:
@@ -1507,4 +1598,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/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/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/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 cc289d4a14..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,3 +1,5 @@
+require 'ipaddr'
+
module ActiveRecord
module ConnectionAdapters # :nodoc:
# The goal of this module is to move Adapter specific column
@@ -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..0cce8c7596 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
@@ -727,9 +727,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..9826b18053 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
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/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..efbae108b9 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -236,8 +236,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 5076ae8a76..b7960936cf 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -317,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)
@@ -762,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/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/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_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/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/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/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/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/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 d789b6cb7a..cd9835259a 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -540,8 +540,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