aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Gemfile2
-rw-r--r--actionpack/CHANGELOG.md18
-rw-r--r--actionpack/lib/action_controller/metal/conditional_get.rb60
-rw-r--r--actionpack/test/controller/render_test.rb51
-rw-r--r--actionview/lib/action_view/helpers/tags/base.rb49
-rw-r--r--actionview/lib/action_view/helpers/tags/collection_check_boxes.rb9
-rw-r--r--activerecord/CHANGELOG.md24
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb10
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/quoting.rb5
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb7
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/date.rb11
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/date_time.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/float.rb21
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/infinity.rb13
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/integer.rb11
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/money.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb14
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/time.rb11
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb9
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb12
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb44
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb18
-rw-r--r--activerecord/lib/active_record/enum.rb81
-rw-r--r--activerecord/lib/active_record/railties/databases.rake2
-rw-r--r--activerecord/lib/active_record/scoping.rb4
-rw-r--r--activerecord/lib/active_record/scoping/named.rb17
-rw-r--r--activerecord/lib/active_record/type/float.rb8
-rw-r--r--activerecord/test/cases/adapters/postgresql/json_test.rb13
-rw-r--r--activerecord/test/cases/adapters/postgresql/timestamp_test.rb61
-rw-r--r--activerecord/test/cases/enum_test.rb17
-rw-r--r--activerecord/test/cases/helper.rb2
-rw-r--r--activerecord/test/cases/migration/command_recorder_test.rb5
-rw-r--r--activerecord/test/cases/migration/foreign_key_test.rb1
-rw-r--r--activerecord/test/cases/migration/references_foreign_key_test.rb14
-rw-r--r--activerecord/test/cases/migration_test.rb11
-rw-r--r--activerecord/test/cases/scoping/relation_scoping_test.rb6
-rw-r--r--activerecord/test/cases/types_test.rb10
-rw-r--r--activesupport/CHANGELOG.md21
-rw-r--r--activesupport/lib/active_support/core_ext/module/delegation.rb36
-rw-r--r--activesupport/lib/active_support/core_ext/string/output_safety.rb2
-rw-r--r--activesupport/lib/active_support/subscriber.rb2
-rw-r--r--activesupport/test/core_ext/module_test.rb2
-rw-r--r--activesupport/test/safe_buffer_test.rb7
-rw-r--r--activesupport/test/xml_mini_test.rb2
-rw-r--r--guides/source/testing.md22
-rw-r--r--guides/source/upgrading_ruby_on_rails.md16
-rw-r--r--railties/lib/rails/application.rb4
-rw-r--r--railties/lib/rails/engine.rb8
53 files changed, 481 insertions, 318 deletions
diff --git a/Gemfile b/Gemfile
index 4299ab337d..08aa842d2f 100644
--- a/Gemfile
+++ b/Gemfile
@@ -85,7 +85,7 @@ platforms :ruby do
gem 'sqlite3', '~> 1.3.6'
group :db do
- gem 'pg', '>= 0.15.0'
+ gem 'pg', '>= 0.18.0'
gem 'mysql', '>= 2.9.0'
gem 'mysql2', '>= 0.3.13'
end
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index 8298a199d8..220946329b 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,3 +1,21 @@
+* Expand `ActionController::ConditionalGet#fresh_when` and `stale?` to also
+ accept a collection of records as the first argument, so that the
+ following code can be written in a shorter form.
+
+ # Before
+ def index
+ @articles = Article.all
+ fresh_when(etag: @articles, last_modified: @articles.maximum(:updated_at))
+ end
+
+ # After
+ def index
+ @articles = Article.all
+ fresh_when(@articles)
+ end
+
+ *claudiob*
+
* Explicitly ignored wildcard verbs when searching for HEAD routes before fallback
Fixes an issue where a mounted rack app at root would intercept the HEAD
diff --git a/actionpack/lib/action_controller/metal/conditional_get.rb b/actionpack/lib/action_controller/metal/conditional_get.rb
index febbc72861..28f0b6e349 100644
--- a/actionpack/lib/action_controller/metal/conditional_get.rb
+++ b/actionpack/lib/action_controller/metal/conditional_get.rb
@@ -57,15 +57,25 @@ module ActionController
# This will render the show template if the request isn't sending a matching ETag or
# If-Modified-Since header and just a <tt>304 Not Modified</tt> response if there's a match.
#
- # You can also just pass a record where +last_modified+ will be set by calling
- # +updated_at+ and the +etag+ by passing the object itself.
+ # You can also just pass a record. In this case +last_modified+ will be set
+ # by calling +updated_at+ and +etag+ by passing the object itself.
#
# def show
# @article = Article.find(params[:id])
# fresh_when(@article)
# end
#
- # When passing a record, you can still set whether the public header:
+ # You can also pass an object that responds to +maximum+, such as a
+ # collection of active records. In this case +last_modified+ will be set by
+ # calling +maximum(:updated_at)+ on the collection (the timestamp of the
+ # most recently updated record) and the +etag+ by passing the object itself.
+ #
+ # def index
+ # @articles = Article.all
+ # fresh_when(@articles)
+ # end
+ #
+ # When passing a record or a collection, you can still set the public header:
#
# def show
# @article = Article.find(params[:id])
@@ -77,18 +87,16 @@ module ActionController
#
# before_action { fresh_when @article, template: 'widgets/show' }
#
- def fresh_when(record_or_options, additional_options = {})
- if record_or_options.is_a? Hash
- options = record_or_options
- options.assert_valid_keys(:etag, :last_modified, :public, :template)
- else
- record = record_or_options
- options = { etag: record, last_modified: record.try(:updated_at) }.merge!(additional_options)
+ def fresh_when(object = nil, etag: object, last_modified: nil, public: false, template: nil)
+ last_modified ||= object.try(:updated_at) || object.try(:maximum, :updated_at)
+
+ if etag || template
+ response.etag = combine_etags(etag: etag, last_modified: last_modified,
+ public: public, template: template)
end
- response.etag = combine_etags(options) if options[:etag] || options[:template]
- response.last_modified = options[:last_modified] if options[:last_modified]
- response.cache_control[:public] = true if options[:public]
+ response.last_modified = last_modified if last_modified
+ response.cache_control[:public] = true if public
head :not_modified if request.fresh?(response)
end
@@ -123,8 +131,8 @@ module ActionController
# end
# end
#
- # You can also just pass a record where +last_modified+ will be set by calling
- # +updated_at+ and the +etag+ by passing the object itself.
+ # You can also just pass a record. In this case +last_modified+ will be set
+ # by calling +updated_at+ and +etag+ by passing the object itself.
#
# def show
# @article = Article.find(params[:id])
@@ -137,7 +145,23 @@ module ActionController
# end
# end
#
- # When passing a record, you can still set whether the public header:
+ # You can also pass an object that responds to +maximum+, such as a
+ # collection of active records. In this case +last_modified+ will be set by
+ # calling +maximum(:updated_at)+ on the collection (the timestamp of the
+ # most recently updated record) and the +etag+ by passing the object itself.
+ #
+ # def index
+ # @articles = Article.all
+ #
+ # if stale?(@articles)
+ # @statistics = @articles.really_expensive_call
+ # respond_to do |format|
+ # # all the supported formats
+ # end
+ # end
+ # end
+ #
+ # When passing a record or a collection, you can still set the public header:
#
# def show
# @article = Article.find(params[:id])
@@ -157,8 +181,8 @@ module ActionController
# super if stale? @article, template: 'widgets/show'
# end
#
- def stale?(record_or_options, additional_options = {})
- fresh_when(record_or_options, additional_options)
+ def stale?(object = nil, etag: object, last_modified: nil, public: nil, template: nil)
+ fresh_when(object, etag: etag, last_modified: last_modified, public: public, template: template)
!request.fresh?(response)
end
diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb
index 1c3f436cfa..8d6b62f2bf 100644
--- a/actionpack/test/controller/render_test.rb
+++ b/actionpack/test/controller/render_test.rb
@@ -58,6 +58,27 @@ class TestController < ActionController::Base
end
end
+ class Collection
+ def initialize(records)
+ @records = records
+ end
+
+ def maximum(attribute)
+ @records.max_by(&attribute).public_send(attribute)
+ end
+ end
+
+ def conditional_hello_with_collection_of_records
+ ts = Time.now.utc.beginning_of_day
+
+ record = Struct.new(:updated_at, :cache_key).new(ts, "foo/123")
+ old_record = Struct.new(:updated_at, :cache_key).new(ts - 1.day, "bar/123")
+
+ if stale?(Collection.new([record, old_record]))
+ render action: 'hello_world'
+ end
+ end
+
def conditional_hello_with_expires_in
expires_in 60.1.seconds
render :action => 'hello_world'
@@ -288,7 +309,6 @@ class LastModifiedRenderTest < ActionController::TestCase
assert_equal @last_modified, @response.headers['Last-Modified']
end
-
def test_responds_with_last_modified_with_record
get :conditional_hello_with_record
assert_equal @last_modified, @response.headers['Last-Modified']
@@ -299,6 +319,7 @@ class LastModifiedRenderTest < ActionController::TestCase
get :conditional_hello_with_record
assert_equal 304, @response.status.to_i
assert @response.body.blank?
+ assert_not_nil @response.etag
assert_equal @last_modified, @response.headers['Last-Modified']
end
@@ -317,6 +338,34 @@ class LastModifiedRenderTest < ActionController::TestCase
assert_equal @last_modified, @response.headers['Last-Modified']
end
+ def test_responds_with_last_modified_with_collection_of_records
+ get :conditional_hello_with_collection_of_records
+ assert_equal @last_modified, @response.headers['Last-Modified']
+ end
+
+ def test_request_not_modified_with_collection_of_records
+ @request.if_modified_since = @last_modified
+ get :conditional_hello_with_collection_of_records
+ assert_equal 304, @response.status.to_i
+ assert @response.body.blank?
+ assert_equal @last_modified, @response.headers['Last-Modified']
+ end
+
+ def test_request_not_modified_but_etag_differs_with_collection_of_records
+ @request.if_modified_since = @last_modified
+ @request.if_none_match = "234"
+ get :conditional_hello_with_collection_of_records
+ assert_response :success
+ end
+
+ def test_request_modified_with_collection_of_records
+ @request.if_modified_since = 'Thu, 16 Jul 2008 00:00:00 GMT'
+ get :conditional_hello_with_collection_of_records
+ assert_equal 200, @response.status.to_i
+ assert @response.body.present?
+ assert_equal @last_modified, @response.headers['Last-Modified']
+ end
+
def test_request_with_bang_gets_last_modified
get :conditional_hello_with_bangs
assert_equal @last_modified, @response.headers['Last-Modified']
diff --git a/actionview/lib/action_view/helpers/tags/base.rb b/actionview/lib/action_view/helpers/tags/base.rb
index 7740c60eac..acc6443a96 100644
--- a/actionview/lib/action_view/helpers/tags/base.rb
+++ b/actionview/lib/action_view/helpers/tags/base.rb
@@ -14,7 +14,7 @@ module ActionView
@object_name.sub!(/\[\]$/,"") || @object_name.sub!(/\[\]\]$/,"]")
@object = retrieve_object(options.delete(:object))
@options = options
- @auto_index = retrieve_autoindex(Regexp.last_match.pre_match) if Regexp.last_match
+ @auto_index = Regexp.last_match ? retrieve_autoindex(Regexp.last_match.pre_match) : nil
end
# This is what child classes implement.
@@ -79,35 +79,30 @@ module ActionView
end
def add_default_name_and_id(options)
- if options.has_key?("index")
- options["name"] ||= options.fetch("name"){ tag_name_with_index(options["index"], options["multiple"]) }
- options["id"] = options.fetch("id"){ tag_id_with_index(options["index"]) }
- options.delete("index")
- elsif defined?(@auto_index)
- options["name"] ||= options.fetch("name"){ tag_name_with_index(@auto_index, options["multiple"]) }
- options["id"] = options.fetch("id"){ tag_id_with_index(@auto_index) }
- else
- options["name"] ||= options.fetch("name"){ tag_name(options["multiple"]) }
- options["id"] = options.fetch("id"){ tag_id }
+ index = name_and_id_index(options)
+ options["name"] = options.fetch("name"){ tag_name(options["multiple"], index) }
+ options["id"] = options.fetch("id"){ tag_id(index) }
+ if namespace = options.delete("namespace")
+ options['id'] = options['id'] ? "#{namespace}_#{options['id']}" : namespace
end
-
- options["id"] = [options.delete('namespace'), options["id"]].compact.join("_").presence
- end
-
- def tag_name(multiple = false)
- "#{@object_name}[#{sanitized_method_name}]#{"[]" if multiple}"
- end
-
- def tag_name_with_index(index, multiple = false)
- "#{@object_name}[#{index}][#{sanitized_method_name}]#{"[]" if multiple}"
end
- def tag_id
- "#{sanitized_object_name}_#{sanitized_method_name}"
+ def tag_name(multiple = false, index = nil)
+ # a little duplication to construct less strings
+ if index
+ "#{@object_name}[#{index}][#{sanitized_method_name}]#{"[]" if multiple}"
+ else
+ "#{@object_name}[#{sanitized_method_name}]#{"[]" if multiple}"
+ end
end
- def tag_id_with_index(index)
- "#{sanitized_object_name}_#{index}_#{sanitized_method_name}"
+ def tag_id(index = nil)
+ # a little duplication to construct less strings
+ if index
+ "#{sanitized_object_name}_#{index}_#{sanitized_method_name}"
+ else
+ "#{sanitized_object_name}_#{sanitized_method_name}"
+ end
end
def sanitized_object_name
@@ -149,6 +144,10 @@ module ActionView
end
option_tags
end
+
+ def name_and_id_index(options)
+ options.key?("index") ? options.delete("index") || "" : @auto_index
+ end
end
end
end
diff --git a/actionview/lib/action_view/helpers/tags/collection_check_boxes.rb b/actionview/lib/action_view/helpers/tags/collection_check_boxes.rb
index 6242a2a085..1765fa6558 100644
--- a/actionview/lib/action_view/helpers/tags/collection_check_boxes.rb
+++ b/actionview/lib/action_view/helpers/tags/collection_check_boxes.rb
@@ -41,14 +41,7 @@ module ActionView
end
def hidden_field
- hidden_name = @html_options[:name]
-
- hidden_name ||= if @options.has_key?(:index)
- "#{tag_name_with_index(@options[:index])}[]"
- else
- "#{tag_name}[]"
- end
-
+ hidden_name = @html_options[:name] || "#{tag_name(false, @options[:index])}[]"
@template_object.hidden_field_tag(hidden_name, "", id: nil)
end
end
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index c1b803c7f8..2ee3c90f89 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,27 @@
+* Have `enum` perform type casting consistently with the rest of Active
+ Record, such as `where`.
+
+ *Sean Griffin*
+
+* `scoping` no longer pollutes the current scope of sibling classes when using
+ STI. e.x.
+
+ StiOne.none.scoping do
+ StiTwo.all
+ end
+
+ Fixes #18806.
+
+ *Sean Griffin*
+
+* `remove_reference` with `foreign_key: true` removes the foreign key before
+ removing the column. This fixes a bug where it was not possible to remove
+ the column on MySQL.
+
+ Fixes #18664.
+
+ *Yves Senn*
+
* `find_in_batches` now accepts an `:end_at` parameter that complements the `:start`
parameter to specify where to stop batch processing.
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
index 3b1e321f4b..42ad285340 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
@@ -201,16 +201,14 @@ module ActiveRecord
# isolation level. However, support is disabled for MySQL versions below 5,
# because they are affected by a bug[http://bugs.mysql.com/bug.php?id=39170]
# which means the isolation level gets persisted outside the transaction.
- def transaction(options = {})
- options.assert_valid_keys :requires_new, :joinable, :isolation
-
- if !options[:requires_new] && current_transaction.joinable?
- if options[:isolation]
+ def transaction(requires_new: nil, isolation: nil, joinable: true)
+ if !requires_new && current_transaction.joinable?
+ if isolation
raise ActiveRecord::TransactionIsolationError, "cannot set isolation when joining a transaction"
end
yield
else
- transaction_manager.within_new_transaction(options) { yield }
+ transaction_manager.within_new_transaction(isolation: isolation, joinable: joinable) { yield }
end
rescue ActiveRecord::Rollback
# rollbacks are silently swallowed
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
index 29d85d82cb..947e11c7bf 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
@@ -102,6 +102,11 @@ module ActiveRecord
quote_table_name("#{table}.#{attr}")
end
+ def quote_default_expression(value, column) #:nodoc:
+ value = lookup_cast_type(column.sql_type).type_cast_for_database(value)
+ quote(value)
+ end
+
def quoted_true
"'t'"
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb
index 81f8615976..bc8fa9b6cf 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb
@@ -98,8 +98,7 @@ module ActiveRecord
end
def quote_default_expression(value, column)
- value = type_for_column(column).type_cast_for_database(value)
- @conn.quote(value)
+ @conn.quote_default_expression(value, column)
end
def options_include_default?(options)
@@ -118,10 +117,6 @@ module ActiveRecord
MSG
end
end
-
- def type_for_column(column)
- @conn.lookup_cast_type(column.sql_type)
- end
end
end
end
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 7eaa89c9a7..8cd4b8e5b2 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -15,7 +15,7 @@ module ActiveRecord
# are typically created by methods in TableDefinition, and added to the
# +columns+ attribute of said TableDefinition object, in order to be used
# for generating a number of table creation or table changing SQL statements.
- class ColumnDefinition < Struct.new(:name, :type, :limit, :precision, :scale, :default, :null, :first, :after, :auto_increment, :primary_key, :sql_type, :cast_type) #:nodoc:
+ class ColumnDefinition < Struct.new(:name, :type, :limit, :precision, :scale, :default, :null, :first, :after, :auto_increment, :primary_key, :sql_type) #:nodoc:
def primary_key?
primary_key || type.to_sym == :primary_key
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 4f6a1ec67b..932aaf7aa7 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
@@ -29,7 +29,7 @@ module ActiveRecord
limit = column.limit || native_database_types[column.type][:limit]
spec[:limit] = limit.inspect if limit
- spec[:precision] = column.precision.inspect if column.precision && column.precision != 0
+ spec[:precision] = column.precision.inspect if column.precision
spec[:scale] = column.scale.inspect if column.scale
default = schema_default(column) if column.has_default?
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 ed32997d25..3ca1c3c49a 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -662,7 +662,13 @@ module ActiveRecord
#
# remove_reference(:products, :supplier, polymorphic: true)
#
+ # ====== Remove the reference with a foreign key
+ #
+ # remove_reference(:products, :user, index: true, foreign_key: true)
+ #
def remove_reference(table_name, ref_name, options = {})
+ remove_foreign_key table_name, ref_name if options[:foreign_key]
+
remove_column(table_name, "#{ref_name}_id")
remove_column(table_name, "#{ref_name}_type") if options[:polymorphic]
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 054c59ef5c..17d38da129 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -73,6 +73,12 @@ module ActiveRecord
spec
end
+ def prepare_column_options(column)
+ spec = super
+ spec.delete(:precision) if column.type == :datetime && column.precision == 0
+ spec
+ end
+
class Column < ConnectionAdapters::Column # :nodoc:
delegate :strict, :collation, :extra, to: :sql_type_metadata, allow_nil: true
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb
index d28a2b4fa0..92349e2f9b 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb
@@ -1,25 +1,19 @@
-require 'active_record/connection_adapters/postgresql/oid/infinity'
-
require 'active_record/connection_adapters/postgresql/oid/array'
require 'active_record/connection_adapters/postgresql/oid/bit'
require 'active_record/connection_adapters/postgresql/oid/bit_varying'
require 'active_record/connection_adapters/postgresql/oid/bytea'
require 'active_record/connection_adapters/postgresql/oid/cidr'
-require 'active_record/connection_adapters/postgresql/oid/date'
require 'active_record/connection_adapters/postgresql/oid/date_time'
require 'active_record/connection_adapters/postgresql/oid/decimal'
require 'active_record/connection_adapters/postgresql/oid/enum'
-require 'active_record/connection_adapters/postgresql/oid/float'
require 'active_record/connection_adapters/postgresql/oid/hstore'
require 'active_record/connection_adapters/postgresql/oid/inet'
-require 'active_record/connection_adapters/postgresql/oid/integer'
require 'active_record/connection_adapters/postgresql/oid/json'
require 'active_record/connection_adapters/postgresql/oid/jsonb'
require 'active_record/connection_adapters/postgresql/oid/money'
require 'active_record/connection_adapters/postgresql/oid/point'
require 'active_record/connection_adapters/postgresql/oid/range'
require 'active_record/connection_adapters/postgresql/oid/specialized_string'
-require 'active_record/connection_adapters/postgresql/oid/time'
require 'active_record/connection_adapters/postgresql/oid/uuid'
require 'active_record/connection_adapters/postgresql/oid/vector'
require 'active_record/connection_adapters/postgresql/oid/xml'
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/date.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/date.rb
deleted file mode 100644
index 1d8d264530..0000000000
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/date.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-module ActiveRecord
- module ConnectionAdapters
- module PostgreSQL
- module OID # :nodoc:
- class Date < Type::Date # :nodoc:
- include Infinity
- end
- end
- end
- end
-end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/date_time.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/date_time.rb
index b9e7894e5c..2c04c46131 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/date_time.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/date_time.rb
@@ -3,8 +3,6 @@ module ActiveRecord
module PostgreSQL
module OID # :nodoc:
class DateTime < Type::DateTime # :nodoc:
- include Infinity
-
def cast_value(value)
if value.is_a?(::String)
case value
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/float.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/float.rb
deleted file mode 100644
index 78ef94b912..0000000000
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/float.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-module ActiveRecord
- module ConnectionAdapters
- module PostgreSQL
- module OID # :nodoc:
- class Float < Type::Float # :nodoc:
- include Infinity
-
- def cast_value(value)
- case value
- when ::Float then value
- when 'Infinity' then ::Float::INFINITY
- when '-Infinity' then -::Float::INFINITY
- when 'NaN' then ::Float::NAN
- else value.to_f
- end
- end
- end
- end
- end
- end
-end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/infinity.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/infinity.rb
deleted file mode 100644
index e47780399a..0000000000
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/infinity.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-module ActiveRecord
- module ConnectionAdapters
- module PostgreSQL
- module OID # :nodoc:
- module Infinity # :nodoc:
- def infinity(options = {})
- options[:negative] ? -::Float::INFINITY : ::Float::INFINITY
- end
- end
- end
- end
- end
-end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/integer.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/integer.rb
deleted file mode 100644
index 59abdc0009..0000000000
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/integer.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-module ActiveRecord
- module ConnectionAdapters
- module PostgreSQL
- module OID # :nodoc:
- class Integer < Type::Integer # :nodoc:
- include Infinity
- end
- end
- end
- end
-end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/money.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/money.rb
index df890c2ed6..2163674019 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/money.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/money.rb
@@ -3,8 +3,6 @@ module ActiveRecord
module PostgreSQL
module OID # :nodoc:
class Money < Type::Decimal # :nodoc:
- include Infinity
-
class_attribute :precision
def type
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb
index 2a5a59fbc6..9d3633d109 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb
@@ -59,13 +59,23 @@ module ActiveRecord
def extract_bounds(value)
from, to = value[1..-2].split(',')
{
- from: (value[1] == ',' || from == '-infinity') ? @subtype.infinity(negative: true) : from,
- to: (value[-2] == ',' || to == 'infinity') ? @subtype.infinity : to,
+ from: (value[1] == ',' || from == '-infinity') ? infinity(negative: true) : from,
+ to: (value[-2] == ',' || to == 'infinity') ? infinity : to,
exclude_start: (value[0] == '('),
exclude_end: (value[-1] == ')')
}
end
+ def infinity(negative: false)
+ if subtype.respond_to?(:infinity)
+ subtype.infinity(negative: negative)
+ elsif negative
+ -::Float::INFINITY
+ else
+ ::Float::INFINITY
+ end
+ end
+
def infinity?(value)
value.respond_to?(:infinite?) && value.infinite?
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/time.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/time.rb
deleted file mode 100644
index 8f0246eddb..0000000000
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/time.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-module ActiveRecord
- module ConnectionAdapters
- module PostgreSQL
- module OID # :nodoc:
- class Time < Type::Time # :nodoc:
- include Infinity
- end
- end
- end
- end
-end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
index 11114f32fe..b8d0e26f85 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
@@ -52,12 +52,14 @@ module ActiveRecord
end
# Does not quote function default values for UUID columns
- def quote_default_value(value, column) #:nodoc:
+ def quote_default_expression(value, column) #:nodoc:
if column.type == :uuid && value =~ /\(\)/
value
- else
+ elsif column.respond_to?(:array?)
value = type_cast_from_column(column, value)
quote(value)
+ else
+ super
end
end
@@ -127,18 +129,15 @@ module ActiveRecord
bit_varying: OID::BitVarying,
binary: OID::Bytea,
cidr: OID::Cidr,
- date: OID::Date,
date_time: OID::DateTime,
decimal: OID::Decimal,
enum: OID::Enum,
- float: OID::Float,
hstore: OID::Hstore,
inet: OID::Inet,
json: OID::Json,
jsonb: OID::Jsonb,
money: OID::Money,
point: OID::Point,
- time: OID::Time,
uuid: OID::Uuid,
vector: OID::Vector,
xml: OID::Xml,
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
index 750eaeca92..81fde18f64 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
@@ -9,14 +9,6 @@ module ActiveRecord
o.sql_type << '[]' if o.array
super
end
-
- def quote_default_expression(value, column)
- if column.type == :uuid && value =~ /\(\)/
- value
- else
- super
- end
- end
end
module SchemaStatements
@@ -440,7 +432,7 @@ module ActiveRecord
# cast the default to the columns type, which leaves us with a default like "default NULL::character varying".
execute alter_column_query % "DROP DEFAULT"
else
- execute alter_column_query % "SET DEFAULT #{quote_default_value(default, column)}"
+ execute alter_column_query % "SET DEFAULT #{quote_default_expression(default, column)}"
end
end
@@ -448,7 +440,7 @@ module ActiveRecord
clear_cache!
unless null || default.nil?
column = column_for(table_name, column_name)
- execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_value(default, column)} WHERE #{quote_column_name(column_name)} IS NULL") if column
+ execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_expression(default, column)} WHERE #{quote_column_name(column_name)} IS NULL") if column
end
execute("ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL")
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index e8ecaffcab..65d604ecac 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -255,6 +255,8 @@ module ActiveRecord
@table_alias_length = nil
connect
+ add_pg_decoders
+
@statements = StatementPool.new @connection,
self.class.type_cast_config_to_integer(config.fetch(:statement_limit) { 1000 })
@@ -459,11 +461,11 @@ module ActiveRecord
end
def initialize_type_map(m) # :nodoc:
- register_class_with_limit m, 'int2', OID::Integer
- register_class_with_limit m, 'int4', OID::Integer
- register_class_with_limit m, 'int8', OID::Integer
+ register_class_with_limit m, 'int2', Type::Integer
+ register_class_with_limit m, 'int4', Type::Integer
+ register_class_with_limit m, 'int8', Type::Integer
m.alias_type 'oid', 'int2'
- m.register_type 'float4', OID::Float.new
+ m.register_type 'float4', Type::Float.new
m.alias_type 'float8', 'float4'
m.register_type 'text', Type::Text.new
register_class_with_limit m, 'varchar', Type::String
@@ -474,8 +476,8 @@ module ActiveRecord
register_class_with_limit m, 'bit', OID::Bit
register_class_with_limit m, 'varbit', OID::BitVarying
m.alias_type 'timestamptz', 'timestamp'
- m.register_type 'date', OID::Date.new
- m.register_type 'time', OID::Time.new
+ m.register_type 'date', Type::Date.new
+ m.register_type 'time', Type::Time.new
m.register_type 'money', OID::Money.new
m.register_type 'bytea', OID::Bytea.new
@@ -780,6 +782,36 @@ module ActiveRecord
end
end
end
+
+ def add_pg_decoders
+ coders_by_name = {
+ 'int2' => PG::TextDecoder::Integer,
+ 'int4' => PG::TextDecoder::Integer,
+ 'int8' => PG::TextDecoder::Integer,
+ 'oid' => PG::TextDecoder::Integer,
+ 'float4' => PG::TextDecoder::Float,
+ 'float8' => PG::TextDecoder::Float,
+ 'bool' => PG::TextDecoder::Boolean,
+ }
+ query = <<-SQL
+ SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, t.typtype, t.typbasetype
+ FROM pg_type as t
+ SQL
+ coders = execute_and_clear(query, "SCHEMA", []) do |result|
+ result
+ .map { |row| construct_coder(row, coders_by_name['typname']) }
+ .compact
+ end
+
+ map = PG::TypeMapByOid.new
+ coders.each { |coder| map.add_coder(coder) }
+ @connection.type_map_for_results = map
+ end
+
+ def construct_coder(row, coder_class)
+ return unless coder_class
+ coder_class.new(oid: row['oid'], name: row['typname'])
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index edd060248f..400b586c95 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -41,15 +41,6 @@ module ActiveRecord
end
module ConnectionAdapters #:nodoc:
- class SQLite3Binary < Type::Binary # :nodoc:
- def cast_value(value)
- if value.encoding != Encoding::ASCII_8BIT
- value = value.force_encoding(Encoding::ASCII_8BIT)
- end
- value
- end
- end
-
# The SQLite3 adapter works SQLite 3.6.16 or newer
# with the sqlite3-ruby drivers (available as gem from https://rubygems.org/gems/sqlite3).
#
@@ -240,10 +231,6 @@ module ActiveRecord
end
end
- def type_classes_with_standard_constructor
- super.merge(binary: SQLite3Binary)
- end
-
def quote_string(s) #:nodoc:
@connection.class.quote(s)
end
@@ -493,11 +480,6 @@ module ActiveRecord
protected
- def initialize_type_map(m)
- super
- m.register_type(/binary/i, SQLite3Binary.new)
- end
-
def table_structure(table_name)
structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", 'SCHEMA').to_hash
raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
diff --git a/activerecord/lib/active_record/enum.rb b/activerecord/lib/active_record/enum.rb
index f053372cfb..3086f13f96 100644
--- a/activerecord/lib/active_record/enum.rb
+++ b/activerecord/lib/active_record/enum.rb
@@ -79,6 +79,37 @@ module ActiveRecord
super
end
+ class EnumType < Type::Value
+ def initialize(name, mapping)
+ @name = name
+ @mapping = mapping
+ end
+
+ def type_cast_from_user(value)
+ return if value.blank?
+
+ if mapping.has_key?(value)
+ value.to_s
+ elsif mapping.has_value?(value)
+ mapping.key(value)
+ else
+ raise ArgumentError, "'#{value}' is not a valid #{name}"
+ end
+ end
+
+ def type_cast_from_database(value)
+ mapping.key(value.to_i)
+ end
+
+ def type_cast_for_database(value)
+ mapping.fetch(value, value)
+ end
+
+ protected
+
+ attr_reader :name, :mapping
+ end
+
def enum(definitions)
klass = self
definitions.each do |name, values|
@@ -90,37 +121,19 @@ module ActiveRecord
detect_enum_conflict!(name, name.to_s.pluralize, true)
klass.singleton_class.send(:define_method, name.to_s.pluralize) { enum_values }
- _enum_methods_module.module_eval do
- # def status=(value) self[:status] = statuses[value] end
- klass.send(:detect_enum_conflict!, name, "#{name}=")
- define_method("#{name}=") { |value|
- if enum_values.has_key?(value) || value.blank?
- self[name] = enum_values[value]
- elsif enum_values.has_value?(value)
- # Assigning a value directly is not a end-user feature, hence it's not documented.
- # This is used internally to make building objects from the generated scopes work
- # as expected, i.e. +Conversation.archived.build.archived?+ should be true.
- self[name] = value
- else
- raise ArgumentError, "'#{value}' is not a valid #{name}"
- end
- }
-
- # def status() statuses.key self[:status] end
- klass.send(:detect_enum_conflict!, name, name)
- define_method(name) { enum_values.key self[name] }
+ detect_enum_conflict!(name, name)
+ detect_enum_conflict!(name, "#{name}=")
- # def status_before_type_cast() statuses.key self[:status] end
- klass.send(:detect_enum_conflict!, name, "#{name}_before_type_cast")
- define_method("#{name}_before_type_cast") { enum_values.key self[name] }
+ attribute name, EnumType.new(name, enum_values)
+ _enum_methods_module.module_eval do
pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
pairs.each do |value, i|
enum_values[value] = i
# def active?() status == 0 end
klass.send(:detect_enum_conflict!, name, "#{value}?")
- define_method("#{value}?") { self[name] == i }
+ define_method("#{value}?") { self[name] == value.to_s }
# def active!() update! status: :active end
klass.send(:detect_enum_conflict!, name, "#{value}!")
@@ -128,7 +141,7 @@ module ActiveRecord
# scope :active, -> { where status: 0 }
klass.send(:detect_enum_conflict!, name, value, true)
- klass.scope value, -> { klass.where name => i }
+ klass.scope value, -> { klass.where name => value }
end
end
defined_enums[name.to_s] = enum_values
@@ -138,25 +151,7 @@ module ActiveRecord
private
def _enum_methods_module
@_enum_methods_module ||= begin
- mod = Module.new do
- private
- def save_changed_attribute(attr_name, old)
- if (mapping = self.class.defined_enums[attr_name.to_s])
- value = _read_attribute(attr_name)
- if attribute_changed?(attr_name)
- if mapping[old] == value
- clear_attribute_changes([attr_name])
- end
- else
- if old != value
- set_attribute_was(attr_name, mapping.key(old))
- end
- end
- else
- super
- end
- end
- end
+ mod = Module.new
include mod
mod
end
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index dde4dfa83c..2591e7492d 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -286,7 +286,7 @@ db_namespace = namespace :db do
end
desc "Recreate the databases from the structure.sql file"
- task :load => [:environment, :load_config] do
+ task :load => [:load_config] do
ActiveRecord::Tasks::DatabaseTasks.load_schema_current(:sql, ENV['SCHEMA'])
end
diff --git a/activerecord/lib/active_record/scoping.rb b/activerecord/lib/active_record/scoping.rb
index 3e43591672..fca4f1c9d3 100644
--- a/activerecord/lib/active_record/scoping.rb
+++ b/activerecord/lib/active_record/scoping.rb
@@ -11,11 +11,11 @@ module ActiveRecord
module ClassMethods
def current_scope #:nodoc:
- ScopeRegistry.value_for(:current_scope, base_class.to_s)
+ ScopeRegistry.value_for(:current_scope, self.to_s)
end
def current_scope=(scope) #:nodoc:
- ScopeRegistry.set_value_for(:current_scope, base_class.to_s, scope)
+ ScopeRegistry.set_value_for(:current_scope, self.to_s, scope)
end
end
diff --git a/activerecord/lib/active_record/scoping/named.rb b/activerecord/lib/active_record/scoping/named.rb
index a083deafe1..43c7b1c574 100644
--- a/activerecord/lib/active_record/scoping/named.rb
+++ b/activerecord/lib/active_record/scoping/named.rb
@@ -157,11 +157,20 @@ module ActiveRecord
extension = Module.new(&block) if block
- singleton_class.send(:define_method, name) do |*args|
- scope = all.scoping { body.call(*args) }
- scope = scope.extending(extension) if extension
+ if body.respond_to?(:to_proc)
+ singleton_class.send(:define_method, name) do |*args|
+ scope = all.scoping { instance_exec(*args, &body) }
+ scope = scope.extending(extension) if extension
- scope || all
+ scope || all
+ end
+ else
+ singleton_class.send(:define_method, name) do |*args|
+ scope = all.scoping { body.call(*args) }
+ scope = scope.extending(extension) if extension
+
+ scope || all
+ end
end
end
end
diff --git a/activerecord/lib/active_record/type/float.rb b/activerecord/lib/active_record/type/float.rb
index 0a9088e0a1..b3928242b7 100644
--- a/activerecord/lib/active_record/type/float.rb
+++ b/activerecord/lib/active_record/type/float.rb
@@ -12,7 +12,13 @@ module ActiveRecord
private
def cast_value(value)
- value.to_f
+ case value
+ when ::Float then value
+ when "Infinity" then ::Float::INFINITY
+ when "-Infinity" then -::Float::INFINITY
+ when "NaN" then ::Float::NAN
+ else value.to_f
+ end
end
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/json_test.rb b/activerecord/test/cases/adapters/postgresql/json_test.rb
index cbe7e62870..3b123f979e 100644
--- a/activerecord/test/cases/adapters/postgresql/json_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/json_test.rb
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
require "cases/helper"
require 'support/schema_dumping_helper'
@@ -13,20 +14,18 @@ module PostgresqlJSONSharedTestCases
def setup
@connection = ActiveRecord::Base.connection
begin
- @connection.transaction do
- @connection.create_table('json_data_type') do |t|
- t.public_send column_type, 'payload', default: {} # t.json 'payload', default: {}
- t.public_send column_type, 'settings' # t.json 'settings'
- end
+ @connection.create_table('json_data_type') do |t|
+ t.public_send column_type, 'payload', default: {} # t.json 'payload', default: {}
+ t.public_send column_type, 'settings' # t.json 'settings'
end
rescue ActiveRecord::StatementInvalid
- skip "do not test on PG without json"
+ skip "do not test on PostgreSQL without #{column_type} type."
end
@column = JsonDataType.columns_hash['payload']
end
def teardown
- @connection.execute 'drop table if exists json_data_type'
+ @connection.drop_table :json_data_type, if_exists: true
end
def test_column
diff --git a/activerecord/test/cases/adapters/postgresql/timestamp_test.rb b/activerecord/test/cases/adapters/postgresql/timestamp_test.rb
index 9e631fb4ca..3091ee136f 100644
--- a/activerecord/test/cases/adapters/postgresql/timestamp_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/timestamp_test.rb
@@ -1,4 +1,5 @@
require 'cases/helper'
+require 'support/schema_dumping_helper'
require 'models/developer'
require 'models/topic'
@@ -46,8 +47,6 @@ end
class TimestampTest < ActiveRecord::TestCase
fixtures :topics
- class Foo < ActiveRecord::Base; end
-
def test_group_by_date
keys = Topic.group("date_trunc('month', created_at)").count.keys
assert_operator keys.length, :>, 0
@@ -72,6 +71,35 @@ class TimestampTest < ActiveRecord::TestCase
assert_equal(-1.0 / 0.0, d.updated_at)
end
+ def test_bc_timestamp
+ date = Date.new(0) - 1.week
+ Developer.create!(:name => "aaron", :updated_at => date)
+ assert_equal date, Developer.find_by_name("aaron").updated_at
+ end
+
+ def test_bc_timestamp_leap_year
+ date = Time.utc(-4, 2, 29)
+ Developer.create!(:name => "taihou", :updated_at => date)
+ assert_equal date, Developer.find_by_name("taihou").updated_at
+ end
+
+ def test_bc_timestamp_year_zero
+ date = Time.utc(0, 4, 7)
+ Developer.create!(:name => "yahagi", :updated_at => date)
+ assert_equal date, Developer.find_by_name("yahagi").updated_at
+ end
+end
+
+class TimestampPrecisionTest < ActiveRecord::TestCase
+ include SchemaDumpingHelper
+ self.use_transactional_fixtures = false
+
+ class Foo < ActiveRecord::Base; end
+
+ teardown do
+ ActiveRecord::Base.connection.drop_table(:foos, if_exists: true)
+ end
+
def test_default_datetime_precision
ActiveRecord::Base.connection.create_table(:foos)
ActiveRecord::Base.connection.add_column :foos, :created_at, :datetime
@@ -119,24 +147,6 @@ class TimestampTest < ActiveRecord::TestCase
assert_equal '4', pg_datetime_precision('foos', 'updated_at')
end
- def test_bc_timestamp
- date = Date.new(0) - 1.week
- Developer.create!(:name => "aaron", :updated_at => date)
- assert_equal date, Developer.find_by_name("aaron").updated_at
- end
-
- def test_bc_timestamp_leap_year
- date = Time.utc(-4, 2, 29)
- Developer.create!(:name => "taihou", :updated_at => date)
- assert_equal date, Developer.find_by_name("taihou").updated_at
- end
-
- def test_bc_timestamp_year_zero
- date = Time.utc(0, 4, 7)
- Developer.create!(:name => "yahagi", :updated_at => date)
- assert_equal date, Developer.find_by_name("yahagi").updated_at
- end
-
def test_formatting_timestamp_according_to_precision
ActiveRecord::Base.connection.create_table(:foos, force: true) do |t|
t.datetime :created_at, precision: 0
@@ -150,8 +160,15 @@ class TimestampTest < ActiveRecord::TestCase
assert_equal date.to_s, foo.updated_at.to_s
assert_equal 000000, foo.created_at.usec
assert_equal 999900, foo.updated_at.usec
- ensure
- ActiveRecord::Base.connection.drop_table(:foos, if_exists: true)
+ end
+
+ def test_datetime_precision_with_zero_should_be_dumped
+ ActiveRecord::Base.connection.create_table(:foos) do |t|
+ t.timestamps precision: 0
+ end
+ output = dump_table_schema("foos")
+ assert_match %r{t\.datetime\s+"created_at",\s+precision: 0,\s+null: false$}, output
+ assert_match %r{t\.datetime\s+"updated_at",\s+precision: 0,\s+null: false$}, output
end
private
diff --git a/activerecord/test/cases/enum_test.rb b/activerecord/test/cases/enum_test.rb
index 346fcab6ea..ed568413a2 100644
--- a/activerecord/test/cases/enum_test.rb
+++ b/activerecord/test/cases/enum_test.rb
@@ -26,6 +26,17 @@ class EnumTest < ActiveRecord::TestCase
assert_equal @book, Book.unread.first
end
+ test "build from scope" do
+ assert Book.proposed.build.proposed?
+ refute Book.proposed.build.written?
+ assert Book.where(status: Book.statuses[:proposed]).build.proposed?
+ end
+
+ test "find via where" do
+ assert_equal @book, Book.where(status: "proposed").first
+ refute_equal @book, Book.where(status: "written").first
+ end
+
test "update by declaration" do
@book.written!
assert @book.written?
@@ -161,7 +172,11 @@ class EnumTest < ActiveRecord::TestCase
end
test "_before_type_cast returns the enum label (required for form fields)" do
- assert_equal "proposed", @book.status_before_type_cast
+ if @book.status_came_from_user?
+ assert_equal "proposed", @book.status_before_type_cast
+ else
+ assert_equal "proposed", @book.status
+ end
end
test "reserved enum names" do
diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb
index 0a577fa2f5..f1f927852c 100644
--- a/activerecord/test/cases/helper.rb
+++ b/activerecord/test/cases/helper.rb
@@ -124,7 +124,7 @@ def enable_extension!(extension, connection)
return connection.reconnect! if connection.extension_enabled?(extension)
connection.enable_extension extension
- connection.commit_db_transaction
+ connection.commit_db_transaction if connection.transaction_open?
connection.reconnect!
end
diff --git a/activerecord/test/cases/migration/command_recorder_test.rb b/activerecord/test/cases/migration/command_recorder_test.rb
index 8cba777fe2..3844b1a92e 100644
--- a/activerecord/test/cases/migration/command_recorder_test.rb
+++ b/activerecord/test/cases/migration/command_recorder_test.rb
@@ -256,6 +256,11 @@ module ActiveRecord
assert_equal [:add_reference, [:table, :taggable, { polymorphic: true }], nil], add
end
+ def test_invert_remove_reference_with_index_and_foreign_key
+ add = @recorder.inverse_of :remove_reference, [:table, :taggable, { index: true, foreign_key: true }]
+ assert_equal [:add_reference, [:table, :taggable, { index: true, foreign_key: true }], nil], add
+ end
+
def test_invert_remove_belongs_to_alias
add = @recorder.inverse_of :remove_belongs_to, [:table, :user]
assert_equal [:add_reference, [:table, :user], nil], add
diff --git a/activerecord/test/cases/migration/foreign_key_test.rb b/activerecord/test/cases/migration/foreign_key_test.rb
index 66e2175c48..b2f2d077eb 100644
--- a/activerecord/test/cases/migration/foreign_key_test.rb
+++ b/activerecord/test/cases/migration/foreign_key_test.rb
@@ -1,5 +1,4 @@
require 'cases/helper'
-require 'active_support/testing/stream'
require 'support/ddl_helper'
require 'support/schema_dumping_helper'
diff --git a/activerecord/test/cases/migration/references_foreign_key_test.rb b/activerecord/test/cases/migration/references_foreign_key_test.rb
index 99de7db70c..17ac72a109 100644
--- a/activerecord/test/cases/migration/references_foreign_key_test.rb
+++ b/activerecord/test/cases/migration/references_foreign_key_test.rb
@@ -10,8 +10,8 @@ module ActiveRecord
end
teardown do
- @connection.drop_table("testings") if @connection.table_exists? "testings"
- @connection.drop_table("testing_parents") if @connection.table_exists? "testing_parents"
+ @connection.drop_table "testings", if_exists: true
+ @connection.drop_table "testing_parents", if_exists: true
end
test "foreign keys can be created with the table" do
@@ -95,6 +95,16 @@ module ActiveRecord
end
end
end
+
+ test "foreign key column can be removed" do
+ @connection.create_table :testings do |t|
+ t.references :testing_parent, index: true, foreign_key: true
+ end
+
+ assert_difference "@connection.foreign_keys('testings').size", -1 do
+ @connection.remove_reference :testings, :testing_parent, foreign_key: true
+ end
+ end
end
end
end
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index 51b0034755..5f9fd5d527 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -1,4 +1,3 @@
-require 'active_support/testing/stream'
require "cases/helper"
require "cases/migration/helper"
require 'bigdecimal/util'
@@ -120,10 +119,6 @@ class MigrationTest < ActiveRecord::TestCase
end
def test_create_table_with_force_true_does_not_drop_nonexisting_table
- if Person.connection.table_exists?(:testings2)
- Person.connection.drop_table :testings2
- end
-
# using a copy as we need the drop_table method to
# continue to work for the ensure block of the test
temp_conn = Person.connection.dup
@@ -134,7 +129,7 @@ class MigrationTest < ActiveRecord::TestCase
t.column :foo, :string
end
ensure
- Person.connection.drop_table :testings2 rescue nil
+ Person.connection.drop_table :testings2, if_exists: true
end
def connection
@@ -431,8 +426,6 @@ class MigrationTest < ActiveRecord::TestCase
end
def test_create_table_with_binary_column
- Person.connection.drop_table :binary_testings rescue nil
-
assert_nothing_raised {
Person.connection.create_table :binary_testings do |t|
t.column "data", :binary, :null => false
@@ -444,7 +437,7 @@ class MigrationTest < ActiveRecord::TestCase
assert_nil data_column.default
- Person.connection.drop_table :binary_testings rescue nil
+ Person.connection.drop_table :binary_testings, if_exists: true
end
unless mysql_enforcing_gtid_consistency?
diff --git a/activerecord/test/cases/scoping/relation_scoping_test.rb b/activerecord/test/cases/scoping/relation_scoping_test.rb
index 02b32abebf..4bfffbe9c6 100644
--- a/activerecord/test/cases/scoping/relation_scoping_test.rb
+++ b/activerecord/test/cases/scoping/relation_scoping_test.rb
@@ -208,6 +208,12 @@ class RelationScopingTest < ActiveRecord::TestCase
assert_equal [], DeveloperFilteredOnJoins.all
assert_not_equal [], Developer.all
end
+
+ def test_current_scope_does_not_pollute_other_subclasses
+ Post.none.scoping do
+ assert StiPost.all.any?
+ end
+ end
end
class NestedRelationScopingTest < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/types_test.rb b/activerecord/test/cases/types_test.rb
index d35d34ff2d..34b6f2e8a5 100644
--- a/activerecord/test/cases/types_test.rb
+++ b/activerecord/test/cases/types_test.rb
@@ -108,16 +108,6 @@ module ActiveRecord
assert_not_equal Type::Value.new(precision: 1), Type::Value.new(precision: 2)
end
- if current_adapter?(:SQLite3Adapter)
- def test_binary_encoding
- type = SQLite3Binary.new
- utf8_string = "a string".encode(Encoding::UTF_8)
- type_cast = type.type_cast_from_user(utf8_string)
-
- assert_equal Encoding::ASCII_8BIT, type_cast.encoding
- end
- end
-
def test_attributes_which_are_invalid_for_database_can_still_be_reassigned
type_which_cannot_go_to_the_database = Type::Value.new
def type_which_cannot_go_to_the_database.type_cast_for_database(*)
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index 13d5fe3411..44735e4b75 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,3 +1,24 @@
+* Fixed a roundtrip problem with AS::SafeBuffer where primitive-like strings
+ will be dumped as primitives:
+
+ Before:
+
+ YAML.load ActiveSupport::SafeBuffer.new("Hello").to_yaml # => "Hello"
+ YAML.load ActiveSupport::SafeBuffer.new("true").to_yaml # => true
+ YAML.load ActiveSupport::SafeBuffer.new("false").to_yaml # => false
+ YAML.load ActiveSupport::SafeBuffer.new("1").to_yaml # => 1
+ YAML.load ActiveSupport::SafeBuffer.new("1.1").to_yaml # => 1.1
+
+ After:
+
+ YAML.load ActiveSupport::SafeBuffer.new("Hello").to_yaml # => "Hello"
+ YAML.load ActiveSupport::SafeBuffer.new("true").to_yaml # => "true"
+ YAML.load ActiveSupport::SafeBuffer.new("false").to_yaml # => "false"
+ YAML.load ActiveSupport::SafeBuffer.new("1").to_yaml # => "1"
+ YAML.load ActiveSupport::SafeBuffer.new("1.1").to_yaml # => "1.1"
+
+ *Godfrey Chan*
+
* Enable number_to_percentage to keep the number's precision by allowing :precision to be nil
*Jack Xu*
diff --git a/activesupport/lib/active_support/core_ext/module/delegation.rb b/activesupport/lib/active_support/core_ext/module/delegation.rb
index 24df83800b..a5f4d03256 100644
--- a/activesupport/lib/active_support/core_ext/module/delegation.rb
+++ b/activesupport/lib/active_support/core_ext/module/delegation.rb
@@ -185,19 +185,31 @@ class Module
# On the other hand it could be that the target has side-effects,
# whereas conceptually, from the user point of view, the delegator should
# be doing one call.
-
- exception = %(raise DelegationError, "#{self}##{method_prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")
-
- method_def = [
- "def #{method_prefix}#{method}(#{definition})",
- " _ = #{to}",
- " if !_.nil? || nil.respond_to?(:#{method})",
- " _.#{method}(#{definition})",
- " else",
- " #{exception unless allow_nil}",
- " end",
+ if allow_nil
+ method_def = [
+ "def #{method_prefix}#{method}(#{definition})",
+ "_ = #{to}",
+ "if !_.nil? || nil.respond_to?(:#{method})",
+ " _.#{method}(#{definition})",
+ "end",
"end"
- ].join ';'
+ ].join ';'
+ else
+ exception = %(raise DelegationError, "#{self}##{method_prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")
+
+ method_def = [
+ "def #{method_prefix}#{method}(#{definition})",
+ " _ = #{to}",
+ " _.#{method}(#{definition})",
+ "rescue NoMethodError => e",
+ " if _.nil? && e.name == :#{method}",
+ " #{exception}",
+ " else",
+ " raise",
+ " end",
+ "end"
+ ].join ';'
+ end
module_eval(method_def, file, line)
end
diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb
index ff5712ed5d..bae4e206e6 100644
--- a/activesupport/lib/active_support/core_ext/string/output_safety.rb
+++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb
@@ -222,7 +222,7 @@ module ActiveSupport #:nodoc:
end
def encode_with(coder)
- coder.represent_scalar nil, to_str
+ coder.represent_object nil, to_str
end
UNSAFE_STRING_METHODS.each do |unsafe_method|
diff --git a/activesupport/lib/active_support/subscriber.rb b/activesupport/lib/active_support/subscriber.rb
index 98be78b41b..cd40284660 100644
--- a/activesupport/lib/active_support/subscriber.rb
+++ b/activesupport/lib/active_support/subscriber.rb
@@ -96,7 +96,7 @@ module ActiveSupport
event.end = finished
event.payload.merge!(payload)
- method = name.split('.').first
+ method = name.split('.'.freeze).first
send(method, event)
end
diff --git a/activesupport/test/core_ext/module_test.rb b/activesupport/test/core_ext/module_test.rb
index 3c49c4d14f..c9c9b66a6c 100644
--- a/activesupport/test/core_ext/module_test.rb
+++ b/activesupport/test/core_ext/module_test.rb
@@ -78,7 +78,7 @@ Product = Struct.new(:name) do
def type
@type ||= begin
- :thing_without_same_method_name_as_delegated.name
+ nil.type_name
end
end
end
diff --git a/activesupport/test/safe_buffer_test.rb b/activesupport/test/safe_buffer_test.rb
index 4532152996..18fb6d2fbf 100644
--- a/activesupport/test/safe_buffer_test.rb
+++ b/activesupport/test/safe_buffer_test.rb
@@ -61,6 +61,13 @@ class SafeBufferTest < ActiveSupport::TestCase
assert_equal({'str' => str}, YAML.load(yaml))
end
+ test "Should work with primitive-like-strings in to_yaml conversion" do
+ assert_equal 'true', YAML.load(ActiveSupport::SafeBuffer.new('true').to_yaml)
+ assert_equal 'false', YAML.load(ActiveSupport::SafeBuffer.new('false').to_yaml)
+ assert_equal '1', YAML.load(ActiveSupport::SafeBuffer.new('1').to_yaml)
+ assert_equal '1.1', YAML.load(ActiveSupport::SafeBuffer.new('1.1').to_yaml)
+ end
+
test "Should work with underscore" do
str = "MyTest".html_safe.underscore
assert_equal "my_test", str
diff --git a/activesupport/test/xml_mini_test.rb b/activesupport/test/xml_mini_test.rb
index f49431cbbf..bcd6997b06 100644
--- a/activesupport/test/xml_mini_test.rb
+++ b/activesupport/test/xml_mini_test.rb
@@ -11,7 +11,7 @@ module XmlMiniTest
assert_equal "my-key", ActiveSupport::XmlMini.rename_key("my_key")
end
- def test_rename_key_does_nothing_with_dasherize_true
+ def test_rename_key_dasherizes_with_dasherize_true
assert_equal "my-key", ActiveSupport::XmlMini.rename_key("my_key", :dasherize => true)
end
diff --git a/guides/source/testing.md b/guides/source/testing.md
index 7345c7f522..625f366db1 100644
--- a/guides/source/testing.md
+++ b/guides/source/testing.md
@@ -120,9 +120,9 @@ user_<%= n %>:
Rails by default automatically loads all fixtures from the `test/fixtures` directory for your models and controllers test. Loading involves three steps:
-* Remove any existing data from the table corresponding to the fixture
-* Load the fixture data into the table
-* Dump the fixture data into a method in case you want to access it directly
+1. Remove any existing data from the table corresponding to the fixture
+2. Load the fixture data into the table
+3. Dump the fixture data into a method in case you want to access it directly
TIP: In order to remove existing data from the database, Rails tries to disable referential integrity triggers (like foreign keys and check constraints). If you are getting annoying permission errors on running tests, make sure the database user has privilege to disable these triggers in testing environment. (In PostgreSQL, only superusers can disable all triggers. Read more about PostgreSQL permissions [here](http://blog.endpoint.com/2012/10/postgres-system-triggers-error.html))
@@ -270,6 +270,8 @@ Finished tests in 0.009262s, 107.9680 tests/s, 107.9680 assertions/s.
1 tests, 1 assertions, 0 failures, 0 errors, 0 skips
```
+This will run all test methods from the test case.
+
You can also run a particular test method from the test case by running the test and providing the `test method name`.
```bash
@@ -281,8 +283,6 @@ Finished tests in 0.009064s, 110.3266 tests/s, 110.3266 assertions/s.
1 tests, 1 assertions, 0 failures, 0 errors, 0 skips
```
-This will run all test methods from the test case.
-
The `.` (dot) above indicates a passing test. When a test fails you see an `F`; when a test throws an error you see an `E` in its place. The last line of the output is the summary.
#### Your first failing test
@@ -347,7 +347,11 @@ Finished tests in 0.047721s, 20.9551 tests/s, 20.9551 assertions/s.
1 tests, 1 assertions, 0 failures, 0 errors, 0 skips
```
-Now, if you noticed, we first wrote a test which fails for a desired functionality, then we wrote some code which adds the functionality and finally we ensured that our test passes. This approach to software development is referred to as _Test-Driven Development_ (TDD).
+Now, if you noticed, we first wrote a test which fails for a desired
+functionality, then we wrote some code which adds the functionality and finally
+we ensured that our test passes. This approach to software development is
+referred to as
+[_Test-Driven Development_ (TDD)](http://c2.com/cgi/wiki?TestDrivenDevelopment).
#### What an error looks like
@@ -379,7 +383,11 @@ NameError: undefined local variable or method `some_undefined_variable' for #<Ar
Notice the 'E' in the output. It denotes a test with error.
-NOTE: The execution of each test method stops as soon as any error or an assertion failure is encountered, and the test suite continues with the next method. All test methods are executed in alphabetical order.
+NOTE: The execution of each test method stops as soon as any error or an
+assertion failure is encountered, and the test suite continues with the next
+method. All test methods are executed in random order. The
+[`config.active_support.test_order` option](http://edgeguides.rubyonrails.org/configuring.html#configuring-active-support)
+can be used to configure test order.
When a test fails you are presented with the corresponding backtrace. By default
Rails filters that backtrace and will only print lines relevant to your
diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md
index 909a92b9dd..20b90bdba0 100644
--- a/guides/source/upgrading_ruby_on_rails.md
+++ b/guides/source/upgrading_ruby_on_rails.md
@@ -276,6 +276,22 @@ class Notifier < ActionMailer::Base
end
```
+### Foreign Key Support
+
+The migration DSL has been expanded to support foreign key definitions. If
+you've been using the Foreigner gem, you might want to consider removing it.
+Note that the foreign key support of Rails is a subset of Foreigner. This means
+that not every Foreigner definition can be fully replaced by it's Rails
+migration DSL counterpart.
+
+The migration procedure is as follows:
+
+1. remove `gem "foreigner"` from the Gemfile.
+2. run `bundle install`.
+3. run `bin/rake db:schema:dump`.
+4. make sure that `db/schema.rb` contains every foreign key definition with
+the necessary options.
+
Upgrading from Rails 4.0 to Rails 4.1
-------------------------------------
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index 8da73db821..b11815e013 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -428,8 +428,8 @@ module Rails
# and the order specified by the +railties_order+ config.
#
# While when running initializers we need engines in reverse
- # order here when copying migrations from railties we need then in the same
- # order as given by +railties_order+
+ # order here when copying migrations from railties we need them in the same
+ # order as given by +railties_order+.
def migration_railties # :nodoc:
ordered_railties.flatten - [self]
end
diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb
index dc8785c1c1..e1d5caf790 100644
--- a/railties/lib/rails/engine.rb
+++ b/railties/lib/rails/engine.rb
@@ -217,7 +217,7 @@ module Rails
# <tt>url_helpers</tt> from <tt>MyEngine::Engine.routes</tt>.
#
# The next thing that changes in isolated engines is the behavior of routes. Normally, when you namespace
- # your controllers, you also need to do namespace all your routes. With an isolated engine,
+ # your controllers, you also need to namespace all your routes. With an isolated engine,
# the namespace is applied by default, so you can ignore it in routes:
#
# MyEngine::Engine.routes.draw do
@@ -296,7 +296,7 @@ module Rails
# helper MyEngine::SharedEngineHelper
# end
#
- # If you want to include all of the engine's helpers, you can use #helper method on an engine's
+ # If you want to include all of the engine's helpers, you can use the #helper method on an engine's
# instance:
#
# class ApplicationController < ActionController::Base
@@ -312,7 +312,7 @@ module Rails
# Engines can have their own migrations. The default path for migrations is exactly the same
# as in application: <tt>db/migrate</tt>
#
- # To use engine's migrations in application you can use rake task, which copies them to
+ # To use engine's migrations in application you can use the rake task below, which copies them to
# application's dir:
#
# rake ENGINE_NAME:install:migrations
@@ -328,7 +328,7 @@ module Rails
#
# == Loading priority
#
- # In order to change engine's priority you can use +config.railties_order+ in main application.
+ # In order to change engine's priority you can use +config.railties_order+ in the main application.
# It will affect the priority of loading views, helpers, assets and all the other files
# related to engine or application.
#