aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Gemfile.lock8
-rw-r--r--actioncable/Rakefile3
-rw-r--r--actionpack/lib/action_dispatch/middleware/ssl.rb2
-rw-r--r--actionpack/lib/action_dispatch/testing/integration.rb2
-rw-r--r--actionpack/test/controller/integration_test.rb12
-rw-r--r--actionpack/test/controller/parameters/serialization_test.rb10
-rw-r--r--actionpack/test/controller/test_case_test.rb32
-rw-r--r--actionpack/test/dispatch/request/multipart_params_parsing_test.rb2
-rw-r--r--actionpack/test/fixtures/multipart/mona_lisa.jpgbin159528 -> 0 bytes
-rw-r--r--actionpack/test/fixtures/multipart/ruby_on_rails.jpgbin0 -> 45142 bytes
-rw-r--r--actionview/lib/action_view/helpers/date_helper.rb2
-rw-r--r--actionview/lib/action_view/helpers/url_helper.rb11
-rw-r--r--actionview/test/template/url_helper_test.rb27
-rw-r--r--activejob/lib/active_job/test_helper.rb24
-rw-r--r--activejob/test/cases/test_helper_test.rb4
-rw-r--r--activemodel/lib/active_model/validations/with.rb2
-rw-r--r--activemodel/test/cases/type/big_integer_test.rb24
-rw-r--r--activemodel/test/cases/type/binary_test.rb15
-rw-r--r--activemodel/test/cases/type/boolean_test.rb39
-rw-r--r--activemodel/test/cases/type/date_test.rb19
-rw-r--r--activemodel/test/cases/type/date_time_test.rb38
-rw-r--r--activemodel/test/cases/type/float_test.rb22
-rw-r--r--activemodel/test/cases/type/immutable_string_test.rb21
-rw-r--r--activemodel/test/cases/type/integer_test.rb7
-rw-r--r--activemodel/test/cases/type/registry_test.rb52
-rw-r--r--activemodel/test/cases/type/string_test.rb37
-rw-r--r--activemodel/test/cases/type/time_test.rb21
-rw-r--r--activemodel/test/cases/type/value_test.rb14
-rw-r--r--activemodel/test/cases/types_test.rb125
-rw-r--r--activerecord/CHANGELOG.md20
-rw-r--r--activerecord/lib/active_record/associations/collection_proxy.rb6
-rw-r--r--activerecord/lib/active_record/attribute_methods/query.rb2
-rw-r--r--activerecord/lib/active_record/locking/optimistic.rb22
-rw-r--r--activerecord/lib/active_record/model_schema.rb167
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb11
-rw-r--r--activerecord/lib/active_record/type.rb4
-rw-r--r--activerecord/lib/active_record/type/helpers.rb5
-rw-r--r--activerecord/lib/active_record/type/internal/abstract_json.rb4
-rw-r--r--activerecord/lib/active_record/type/serialized.rb4
-rw-r--r--activerecord/lib/active_record/type/value.rb5
-rw-r--r--activerecord/test/cases/adapters/mysql2/connection_test.rb8
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb7
-rw-r--r--activerecord/test/cases/dirty_test.rb2
-rw-r--r--activerecord/test/cases/locking_test.rb142
-rw-r--r--activerecord/test/cases/serialized_attribute_test.rb4
-rw-r--r--activerecord/test/schema/schema.rb2
-rw-r--r--activesupport/CHANGELOG.md22
-rw-r--r--activesupport/lib/active_support/callbacks.rb12
-rw-r--r--activesupport/lib/active_support/core_ext/date_and_time/calculations.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/hash/compact.rb40
-rw-r--r--activesupport/lib/active_support/hash_with_indifferent_access.rb6
-rw-r--r--activesupport/lib/active_support/test_case.rb2
-rw-r--r--activesupport/lib/active_support/values/time_zone.rb26
-rw-r--r--activesupport/test/core_ext/date_and_time_behavior.rb4
-rw-r--r--activesupport/test/executor_test.rb2
-rw-r--r--activesupport/test/time_zone_test.rb18
-rw-r--r--guides/rails_guides/markdown.rb2
-rw-r--r--guides/rails_guides/markdown/renderer.rb2
-rw-r--r--guides/source/5_0_release_notes.md11
-rw-r--r--guides/source/layout.html.erb2
-rw-r--r--guides/source/upgrading_ruby_on_rails.md2
-rw-r--r--railties/lib/rails/generators/rails/app/app_generator.rb1
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults.rb.tt2
-rw-r--r--railties/lib/rails/test_unit/railtie.rb4
-rw-r--r--railties/test/application/runner_test.rb9
-rw-r--r--railties/test/generators/app_generator_test.rb4
66 files changed, 804 insertions, 359 deletions
diff --git a/Gemfile.lock b/Gemfile.lock
index 63850eee90..4257685c46 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -235,9 +235,9 @@ GEM
multi_json (1.12.1)
multipart-post (2.0.0)
mustache (1.0.3)
- mysql2 (0.4.4)
- mysql2 (0.4.4-x64-mingw32)
- mysql2 (0.4.4-x86-mingw32)
+ mysql2 (0.4.5)
+ mysql2 (0.4.5-x64-mingw32)
+ mysql2 (0.4.5-x86-mingw32)
nio4r (1.2.1)
nokogiri (1.6.8.1)
mini_portile2 (~> 2.1.0)
@@ -417,4 +417,4 @@ DEPENDENCIES
websocket-client-simple!
BUNDLED WITH
- 1.13.2
+ 1.13.6
diff --git a/actioncable/Rakefile b/actioncable/Rakefile
index 648de57004..87d443919c 100644
--- a/actioncable/Rakefile
+++ b/actioncable/Rakefile
@@ -1,7 +1,6 @@
require "rake/testtask"
require "pathname"
require "action_cable"
-require "blade"
dir = File.dirname(__FILE__)
@@ -25,6 +24,7 @@ namespace :test do
end
task :integration do
+ require "blade"
if ENV["CI"]
Blade.start(interface: :ci)
else
@@ -36,6 +36,7 @@ end
namespace :assets do
desc "Compile Action Cable assets"
task :compile do
+ require "blade"
Blade.build
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/ssl.rb b/actionpack/lib/action_dispatch/middleware/ssl.rb
index c9bd417aa2..557721c301 100644
--- a/actionpack/lib/action_dispatch/middleware/ssl.rb
+++ b/actionpack/lib/action_dispatch/middleware/ssl.rb
@@ -23,7 +23,7 @@ module ActionDispatch
# `180.days` (recommended).
# * `subdomains`: Set to `true` to tell the browser to apply these settings
# to all subdomains. This protects your cookies from interception by a
- # vulnerable site on a subdomain. Defaults to `false`.
+ # vulnerable site on a subdomain. Defaults to `true`.
# * `preload`: Advertise that this site may be included in browsers'
# preloaded HSTS lists. HSTS protects your site on every visit *except the
# first visit* since it hasn't seen your HSTS header yet. To close this
diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb
index e24b943026..e2ea96513c 100644
--- a/actionpack/lib/action_dispatch/testing/integration.rb
+++ b/actionpack/lib/action_dispatch/testing/integration.rb
@@ -599,8 +599,6 @@ module ActionDispatch
class IntegrationTest < ActiveSupport::TestCase
include TestProcess
- undef :assigns
-
module UrlOptions
extend ActiveSupport::Concern
def url_options
diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb
index 652c0f0dd2..67a42989c2 100644
--- a/actionpack/test/controller/integration_test.rb
+++ b/actionpack/test/controller/integration_test.rb
@@ -153,14 +153,6 @@ class IntegrationTestTest < ActiveSupport::TestCase
mixin.__send__(:remove_method, :method_missing)
end
end
-
- def test_assigns_is_undefined_and_not_point_to_the_gem
- e = assert_raises(NoMethodError) do
- @test.assigns(:foo)
- end
-
- assert_match(/undefined method/, e.message)
- end
end
# Tests that integration tests don't call Controller test methods for processing.
@@ -1078,8 +1070,8 @@ class IntegrationFileUploadTest < ActionDispatch::IntegrationTest
def test_fixture_file_upload
post "/test_file_upload",
params: {
- file: fixture_file_upload("/mona_lisa.jpg", "image/jpg")
+ file: fixture_file_upload("/ruby_on_rails.jpg", "image/jpg")
}
- assert_equal "159528", @response.body
+ assert_equal "45142", @response.body
end
end
diff --git a/actionpack/test/controller/parameters/serialization_test.rb b/actionpack/test/controller/parameters/serialization_test.rb
index 4fb1564c68..6fba2fde91 100644
--- a/actionpack/test/controller/parameters/serialization_test.rb
+++ b/actionpack/test/controller/parameters/serialization_test.rb
@@ -14,12 +14,10 @@ class ParametersSerializationTest < ActiveSupport::TestCase
test "yaml serialization" do
params = ActionController::Parameters.new(key: :value)
- assert_equal <<-end_of_yaml.strip_heredoc, YAML.dump(params)
- --- !ruby/object:ActionController::Parameters
- parameters: !ruby/hash:ActiveSupport::HashWithIndifferentAccess
- key: :value
- permitted: false
- end_of_yaml
+ yaml_dump = YAML.dump(params)
+ assert_match("--- !ruby/object:ActionController::Parameters", yaml_dump)
+ assert_match(/parameters: !ruby\/hash:ActiveSupport::HashWithIndifferentAccess\n\s+key: :value/, yaml_dump)
+ assert_match("permitted: false", yaml_dump)
end
test "yaml deserialization" do
diff --git a/actionpack/test/controller/test_case_test.rb b/actionpack/test/controller/test_case_test.rb
index 82f42e306f..33f11e67ef 100644
--- a/actionpack/test/controller/test_case_test.rb
+++ b/actionpack/test/controller/test_case_test.rb
@@ -781,7 +781,7 @@ XML
READ_PLAIN = "r:binary"
def test_test_uploaded_file
- filename = "mona_lisa.jpg"
+ filename = "ruby_on_rails.jpg"
path = "#{FILES_DIR}/#{filename}"
content_type = "image/png"
expected = File.read(path)
@@ -801,13 +801,13 @@ XML
def test_fixture_path_is_accessed_from_self_instead_of_active_support_test_case
TestCaseTest.stub :fixture_path, FILES_DIR do
- uploaded_file = fixture_file_upload("/mona_lisa.jpg", "image/png")
- assert_equal File.open("#{FILES_DIR}/mona_lisa.jpg", READ_PLAIN).read, uploaded_file.read
+ uploaded_file = fixture_file_upload("/ruby_on_rails.jpg", "image/png")
+ assert_equal File.open("#{FILES_DIR}/ruby_on_rails.jpg", READ_PLAIN).read, uploaded_file.read
end
end
def test_test_uploaded_file_with_binary
- filename = "mona_lisa.jpg"
+ filename = "ruby_on_rails.jpg"
path = "#{FILES_DIR}/#{filename}"
content_type = "image/png"
@@ -819,7 +819,7 @@ XML
end
def test_fixture_file_upload_with_binary
- filename = "mona_lisa.jpg"
+ filename = "ruby_on_rails.jpg"
path = "#{FILES_DIR}/#{filename}"
content_type = "image/jpg"
@@ -831,44 +831,44 @@ XML
end
def test_fixture_file_upload_should_be_able_access_to_tempfile
- file = fixture_file_upload(FILES_DIR + "/mona_lisa.jpg", "image/jpg")
+ file = fixture_file_upload(FILES_DIR + "/ruby_on_rails.jpg", "image/jpg")
assert file.respond_to?(:tempfile), "expected tempfile should respond on fixture file object, got nothing"
end
def test_fixture_file_upload
post :test_file_upload,
params: {
- file: fixture_file_upload(FILES_DIR + "/mona_lisa.jpg", "image/jpg")
+ file: fixture_file_upload(FILES_DIR + "/ruby_on_rails.jpg", "image/jpg")
}
- assert_equal "159528", @response.body
+ assert_equal "45142", @response.body
end
def test_fixture_file_upload_relative_to_fixture_path
TestCaseTest.stub :fixture_path, FILES_DIR do
- uploaded_file = fixture_file_upload("mona_lisa.jpg", "image/jpg")
- assert_equal File.open("#{FILES_DIR}/mona_lisa.jpg", READ_PLAIN).read, uploaded_file.read
+ uploaded_file = fixture_file_upload("ruby_on_rails.jpg", "image/jpg")
+ assert_equal File.open("#{FILES_DIR}/ruby_on_rails.jpg", READ_PLAIN).read, uploaded_file.read
end
end
def test_fixture_file_upload_ignores_fixture_path_given_full_path
TestCaseTest.stub :fixture_path, File.dirname(__FILE__) do
- uploaded_file = fixture_file_upload("#{FILES_DIR}/mona_lisa.jpg", "image/jpg")
- assert_equal File.open("#{FILES_DIR}/mona_lisa.jpg", READ_PLAIN).read, uploaded_file.read
+ uploaded_file = fixture_file_upload("#{FILES_DIR}/ruby_on_rails.jpg", "image/jpg")
+ assert_equal File.open("#{FILES_DIR}/ruby_on_rails.jpg", READ_PLAIN).read, uploaded_file.read
end
end
def test_fixture_file_upload_ignores_nil_fixture_path
- uploaded_file = fixture_file_upload("#{FILES_DIR}/mona_lisa.jpg", "image/jpg")
- assert_equal File.open("#{FILES_DIR}/mona_lisa.jpg", READ_PLAIN).read, uploaded_file.read
+ uploaded_file = fixture_file_upload("#{FILES_DIR}/ruby_on_rails.jpg", "image/jpg")
+ assert_equal File.open("#{FILES_DIR}/ruby_on_rails.jpg", READ_PLAIN).read, uploaded_file.read
end
def test_action_dispatch_uploaded_file_upload
- filename = "mona_lisa.jpg"
+ filename = "ruby_on_rails.jpg"
path = "#{FILES_DIR}/#{filename}"
post :test_file_upload, params: {
file: Rack::Test::UploadedFile.new(path, "image/jpg", true)
}
- assert_equal "159528", @response.body
+ assert_equal "45142", @response.body
end
def test_test_uploaded_file_exception_when_file_doesnt_exist
diff --git a/actionpack/test/dispatch/request/multipart_params_parsing_test.rb b/actionpack/test/dispatch/request/multipart_params_parsing_test.rb
index e572c722a0..eb4bb14ed1 100644
--- a/actionpack/test/dispatch/request/multipart_params_parsing_test.rb
+++ b/actionpack/test/dispatch/request/multipart_params_parsing_test.rb
@@ -142,7 +142,7 @@ class MultipartParamsParsingTest < ActionDispatch::IntegrationTest
test "uploads and reads binary file" do
with_test_routing do
- fixture = FIXTURE_PATH + "/mona_lisa.jpg"
+ fixture = FIXTURE_PATH + "/ruby_on_rails.jpg"
params = { uploaded_data: fixture_file_upload(fixture, "image/jpg") }
post "/read", params: params
end
diff --git a/actionpack/test/fixtures/multipart/mona_lisa.jpg b/actionpack/test/fixtures/multipart/mona_lisa.jpg
deleted file mode 100644
index 5cf3bef3d0..0000000000
--- a/actionpack/test/fixtures/multipart/mona_lisa.jpg
+++ /dev/null
Binary files differ
diff --git a/actionpack/test/fixtures/multipart/ruby_on_rails.jpg b/actionpack/test/fixtures/multipart/ruby_on_rails.jpg
new file mode 100644
index 0000000000..ed284ea0ba
--- /dev/null
+++ b/actionpack/test/fixtures/multipart/ruby_on_rails.jpg
Binary files differ
diff --git a/actionview/lib/action_view/helpers/date_helper.rb b/actionview/lib/action_view/helpers/date_helper.rb
index 04c5fd4218..ea7259a25d 100644
--- a/actionview/lib/action_view/helpers/date_helper.rb
+++ b/actionview/lib/action_view/helpers/date_helper.rb
@@ -267,7 +267,7 @@ module ActionView
# date_select("article", "written_on", default: 3.days.from_now)
#
# # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute
- # # which is set in the form with todays date, regardless of the value in the Active Record object.
+ # # which is set in the form with today's date, regardless of the value in the Active Record object.
# date_select("article", "written_on", selected: Date.today)
#
# # Generates a date select that when POSTed is stored in the credit_card variable, in the bill_due attribute
diff --git a/actionview/lib/action_view/helpers/url_helper.rb b/actionview/lib/action_view/helpers/url_helper.rb
index dad0e9dac3..d410976bef 100644
--- a/actionview/lib/action_view/helpers/url_helper.rb
+++ b/actionview/lib/action_view/helpers/url_helper.rb
@@ -617,6 +617,17 @@ module ActionView
# to_form_params({ name: 'Denmark' }, 'country')
# # => [{name: 'country[name]', value: 'Denmark'}]
def to_form_params(attribute, namespace = nil) # :nodoc:
+ attribute = if attribute.respond_to?(:permitted?)
+ unless attribute.permitted?
+ raise ArgumentError, "Attempting to generate a buttom from non-sanitized request parameters!" \
+ " Whitelist and sanitize passed parameters to be secure."
+ end
+
+ attribute.to_h
+ else
+ attribute
+ end
+
params = []
case attribute
when Hash
diff --git a/actionview/test/template/url_helper_test.rb b/actionview/test/template/url_helper_test.rb
index 2ef2be65d2..5a2319fe96 100644
--- a/actionview/test/template/url_helper_test.rb
+++ b/actionview/test/template/url_helper_test.rb
@@ -221,6 +221,33 @@ class UrlHelperTest < ActiveSupport::TestCase
)
end
+ class FakeParams
+ def initialize(permitted = true)
+ @permitted = permitted
+ end
+
+ def permitted?
+ @permitted
+ end
+
+ def to_h
+ { foo: :bar, baz: "quux" }
+ end
+ end
+
+ def test_button_to_with_permited_strong_params
+ assert_dom_equal(
+ %{<form action="http://www.example.com" class="button_to" method="post"><input type="submit" value="Hello" /><input type="hidden" name="baz" value="quux" /><input type="hidden" name="foo" value="bar" /></form>},
+ button_to("Hello", "http://www.example.com", params: FakeParams.new)
+ )
+ end
+
+ def test_button_to_with_unpermited_strong_params
+ assert_raises(ArgumentError) do
+ button_to("Hello", "http://www.example.com", params: FakeParams.new(false))
+ end
+ end
+
def test_button_to_with_nested_hash_params
assert_dom_equal(
%{<form action="http://www.example.com" class="button_to" method="post"><input type="submit" value="Hello" /><input type="hidden" name="foo[bar]" value="baz" /></form>},
diff --git a/activejob/lib/active_job/test_helper.rb b/activejob/lib/active_job/test_helper.rb
index bbd2a0c06c..1a8b3375ae 100644
--- a/activejob/lib/active_job/test_helper.rb
+++ b/activejob/lib/active_job/test_helper.rb
@@ -269,6 +269,25 @@ module ActiveJob
instantiate_job(matching_job)
end
+ # Performs all enqueued jobs in the duration of the block.
+ #
+ # def test_perform_enqueued_jobs
+ # perform_enqueued_jobs do
+ # MyJob.perform_later(1, 2, 3)
+ # end
+ # assert_performed_jobs 1
+ # end
+ #
+ # This method also supports filtering. If the +:only+ option is specified,
+ # then only the listed job(s) will be performed.
+ #
+ # def test_perform_enqueued_jobs_with_only
+ # perform_enqueued_jobs(only: MyJob) do
+ # MyJob.perform_later(1, 2, 3) # will be performed
+ # HelloJob.perform_later(1, 2, 3) # will not be perfomed
+ # end
+ # assert_performed_jobs 1
+ # end
def perform_enqueued_jobs(only: nil)
old_perform_enqueued_jobs = queue_adapter.perform_enqueued_jobs
old_perform_enqueued_at_jobs = queue_adapter.perform_enqueued_at_jobs
@@ -286,6 +305,11 @@ module ActiveJob
end
end
+ # Accesses the queue_adapter set by ActiveJob::Base.
+ #
+ # def test_assert_job_has_custom_queue_adapter_set
+ # assert_instance_of CustomQueueAdapter, HelloJob.queue_adapter
+ # end
def queue_adapter
ActiveJob::Base.queue_adapter
end
diff --git a/activejob/test/cases/test_helper_test.rb b/activejob/test/cases/test_helper_test.rb
index 253c557cc5..685c93da2d 100644
--- a/activejob/test/cases/test_helper_test.rb
+++ b/activejob/test/cases/test_helper_test.rb
@@ -250,7 +250,7 @@ class EnqueuedJobsTest < ActiveJob::TestCase
HelloJob.perform_later
end
- assert_equal 2, ActiveJob::Base.queue_adapter.enqueued_jobs.count
+ assert_equal 2, queue_adapter.enqueued_jobs.count
end
end
@@ -507,7 +507,7 @@ class PerformedJobsTest < ActiveJob::TestCase
HelloJob.perform_later
end
- assert_equal 2, ActiveJob::Base.queue_adapter.performed_jobs.count
+ assert_equal 2, queue_adapter.performed_jobs.count
end
end
diff --git a/activemodel/lib/active_model/validations/with.rb b/activemodel/lib/active_model/validations/with.rb
index 6de01b3392..6ae9630d82 100644
--- a/activemodel/lib/active_model/validations/with.rb
+++ b/activemodel/lib/active_model/validations/with.rb
@@ -1,3 +1,5 @@
+require "active_support/core_ext/array/extract_options"
+
module ActiveModel
module Validations
class WithValidator < EachValidator # :nodoc:
diff --git a/activemodel/test/cases/type/big_integer_test.rb b/activemodel/test/cases/type/big_integer_test.rb
new file mode 100644
index 0000000000..db1d215083
--- /dev/null
+++ b/activemodel/test/cases/type/big_integer_test.rb
@@ -0,0 +1,24 @@
+require "cases/helper"
+require "active_model/type"
+
+module ActiveModel
+ module Type
+ class BigIntegerTest < ActiveModel::TestCase
+ def test_type_cast_big_integer
+ type = Type::BigInteger.new
+ assert_equal 1, type.cast(1)
+ assert_equal 1, type.cast("1")
+ end
+
+ def test_small_values
+ type = Type::BigInteger.new
+ assert_equal -9999999999999999999999999999999, type.serialize(-9999999999999999999999999999999)
+ end
+
+ def test_large_values
+ type = Type::BigInteger.new
+ assert_equal 9999999999999999999999999999999, type.serialize(9999999999999999999999999999999)
+ end
+ end
+ end
+end
diff --git a/activemodel/test/cases/type/binary_test.rb b/activemodel/test/cases/type/binary_test.rb
new file mode 100644
index 0000000000..e6a32dbeec
--- /dev/null
+++ b/activemodel/test/cases/type/binary_test.rb
@@ -0,0 +1,15 @@
+require "cases/helper"
+require "active_model/type"
+
+module ActiveModel
+ module Type
+ class BinaryTest < ActiveModel::TestCase
+ def test_type_cast_binary
+ type = Type::Binary.new
+ assert_equal nil, type.cast(nil)
+ assert_equal "1", type.cast("1")
+ assert_equal 1, type.cast(1)
+ end
+ end
+ end
+end
diff --git a/activemodel/test/cases/type/boolean_test.rb b/activemodel/test/cases/type/boolean_test.rb
new file mode 100644
index 0000000000..92e5aebfb7
--- /dev/null
+++ b/activemodel/test/cases/type/boolean_test.rb
@@ -0,0 +1,39 @@
+require "cases/helper"
+require "active_model/type"
+
+module ActiveModel
+ module Type
+ class BooleanTest < ActiveModel::TestCase
+ def test_type_cast_boolean
+ type = Type::Boolean.new
+ assert type.cast("").nil?
+ assert type.cast(nil).nil?
+
+ assert type.cast(true)
+ assert type.cast(1)
+ assert type.cast("1")
+ assert type.cast("t")
+ assert type.cast("T")
+ assert type.cast("true")
+ assert type.cast("TRUE")
+ assert type.cast("on")
+ assert type.cast("ON")
+ assert type.cast(" ")
+ assert type.cast("\u3000\r\n")
+ assert type.cast("\u0000")
+ assert type.cast("SOMETHING RANDOM")
+
+ # explicitly check for false vs nil
+ assert_equal false, type.cast(false)
+ assert_equal false, type.cast(0)
+ assert_equal false, type.cast("0")
+ assert_equal false, type.cast("f")
+ assert_equal false, type.cast("F")
+ assert_equal false, type.cast("false")
+ assert_equal false, type.cast("FALSE")
+ assert_equal false, type.cast("off")
+ assert_equal false, type.cast("OFF")
+ end
+ end
+ end
+end
diff --git a/activemodel/test/cases/type/date_test.rb b/activemodel/test/cases/type/date_test.rb
new file mode 100644
index 0000000000..44e20a327b
--- /dev/null
+++ b/activemodel/test/cases/type/date_test.rb
@@ -0,0 +1,19 @@
+require "cases/helper"
+require "active_model/type"
+
+module ActiveModel
+ module Type
+ class DateTest < ActiveModel::TestCase
+ def test_type_cast_date
+ type = Type::Date.new
+ assert_equal nil, type.cast(nil)
+ assert_equal nil, type.cast("")
+ assert_equal nil, type.cast(" ")
+ assert_equal nil, type.cast("ABC")
+
+ date_string = ::Time.now.utc.strftime("%F")
+ assert_equal date_string, type.cast(date_string).strftime("%F")
+ end
+ end
+ end
+end
diff --git a/activemodel/test/cases/type/date_time_test.rb b/activemodel/test/cases/type/date_time_test.rb
new file mode 100644
index 0000000000..fb82260d2b
--- /dev/null
+++ b/activemodel/test/cases/type/date_time_test.rb
@@ -0,0 +1,38 @@
+require "cases/helper"
+require "active_model/type"
+
+module ActiveModel
+ module Type
+ class DateTimeTest < ActiveModel::TestCase
+ def test_type_cast_datetime_and_timestamp
+ type = Type::DateTime.new
+ assert_equal nil, type.cast(nil)
+ assert_equal nil, type.cast("")
+ assert_equal nil, type.cast(" ")
+ assert_equal nil, type.cast("ABC")
+
+ datetime_string = ::Time.now.utc.strftime("%FT%T")
+ assert_equal datetime_string, type.cast(datetime_string).strftime("%FT%T")
+ end
+
+ def test_string_to_time_with_timezone
+ ["UTC", "US/Eastern"].each do |zone|
+ with_timezone_config default: zone do
+ type = Type::DateTime.new
+ assert_equal ::Time.utc(2013, 9, 4, 0, 0, 0), type.cast("Wed, 04 Sep 2013 03:00:00 EAT")
+ end
+ end
+ end
+
+ private
+
+ def with_timezone_config(default:)
+ old_zone_default = ::Time.zone_default
+ ::Time.zone_default = ::Time.find_zone(default)
+ yield
+ ensure
+ ::Time.zone_default = old_zone_default
+ end
+ end
+ end
+end
diff --git a/activemodel/test/cases/type/float_test.rb b/activemodel/test/cases/type/float_test.rb
new file mode 100644
index 0000000000..2e34f57f7e
--- /dev/null
+++ b/activemodel/test/cases/type/float_test.rb
@@ -0,0 +1,22 @@
+require "cases/helper"
+require "active_model/type"
+
+module ActiveModel
+ module Type
+ class FloatTest < ActiveModel::TestCase
+ def test_type_cast_float
+ type = Type::Float.new
+ assert_equal 1.0, type.cast("1")
+ end
+
+ def test_changing_float
+ type = Type::Float.new
+
+ assert type.changed?(5.0, 5.0, "5wibble")
+ assert_not type.changed?(5.0, 5.0, "5")
+ assert_not type.changed?(5.0, 5.0, "5.0")
+ assert_not type.changed?(nil, nil, nil)
+ end
+ end
+ end
+end
diff --git a/activemodel/test/cases/type/immutable_string_test.rb b/activemodel/test/cases/type/immutable_string_test.rb
new file mode 100644
index 0000000000..23e58974fb
--- /dev/null
+++ b/activemodel/test/cases/type/immutable_string_test.rb
@@ -0,0 +1,21 @@
+require "cases/helper"
+require "active_model/type"
+
+module ActiveModel
+ module Type
+ class ImmutableStringTest < ActiveModel::TestCase
+ test "cast strings are frozen" do
+ s = "foo"
+ type = Type::ImmutableString.new
+ assert_equal true, type.cast(s).frozen?
+ end
+
+ test "immutable strings are not duped coming out" do
+ s = "foo"
+ type = Type::ImmutableString.new
+ assert_same s, type.cast(s)
+ assert_same s, type.deserialize(s)
+ end
+ end
+ end
+end
diff --git a/activemodel/test/cases/type/integer_test.rb b/activemodel/test/cases/type/integer_test.rb
index e00246b602..d81ce189e6 100644
--- a/activemodel/test/cases/type/integer_test.rb
+++ b/activemodel/test/cases/type/integer_test.rb
@@ -1,5 +1,6 @@
require "cases/helper"
require "active_model/type"
+require "active_support/core_ext/numeric/time"
module ActiveModel
module Type
@@ -41,6 +42,12 @@ module ActiveModel
assert_equal 0, type.serialize(false)
end
+ test "casting duration" do
+ type = Type::Integer.new
+ assert_equal 1800, type.cast(30.minutes)
+ assert_equal 7200, type.cast(2.hours)
+ end
+
test "changed?" do
type = Type::Integer.new
diff --git a/activemodel/test/cases/type/registry_test.rb b/activemodel/test/cases/type/registry_test.rb
index 2a48998a62..927b6d0307 100644
--- a/activemodel/test/cases/type/registry_test.rb
+++ b/activemodel/test/cases/type/registry_test.rb
@@ -2,38 +2,40 @@ require "cases/helper"
require "active_model/type"
module ActiveModel
- class RegistryTest < ActiveModel::TestCase
- test "a class can be registered for a symbol" do
- registry = Type::Registry.new
- registry.register(:foo, ::String)
- registry.register(:bar, ::Array)
+ module Type
+ class RegistryTest < ActiveModel::TestCase
+ test "a class can be registered for a symbol" do
+ registry = Type::Registry.new
+ registry.register(:foo, ::String)
+ registry.register(:bar, ::Array)
- assert_equal "", registry.lookup(:foo)
- assert_equal [], registry.lookup(:bar)
- end
-
- test "a block can be registered" do
- registry = Type::Registry.new
- registry.register(:foo) do |*args|
- [*args, "block for foo"]
+ assert_equal "", registry.lookup(:foo)
+ assert_equal [], registry.lookup(:bar)
end
- registry.register(:bar) do |*args|
- [*args, "block for bar"]
+
+ test "a block can be registered" do
+ registry = Type::Registry.new
+ registry.register(:foo) do |*args|
+ [*args, "block for foo"]
+ end
+ registry.register(:bar) do |*args|
+ [*args, "block for bar"]
+ end
+
+ assert_equal [:foo, 1, "block for foo"], registry.lookup(:foo, 1)
+ assert_equal [:foo, 2, "block for foo"], registry.lookup(:foo, 2)
+ assert_equal [:bar, 1, 2, 3, "block for bar"], registry.lookup(:bar, 1, 2, 3)
end
- assert_equal [:foo, 1, "block for foo"], registry.lookup(:foo, 1)
- assert_equal [:foo, 2, "block for foo"], registry.lookup(:foo, 2)
- assert_equal [:bar, 1, 2, 3, "block for bar"], registry.lookup(:bar, 1, 2, 3)
- end
+ test "a reasonable error is given when no type is found" do
+ registry = Type::Registry.new
- test "a reasonable error is given when no type is found" do
- registry = Type::Registry.new
+ e = assert_raises(ArgumentError) do
+ registry.lookup(:foo)
+ end
- e = assert_raises(ArgumentError) do
- registry.lookup(:foo)
+ assert_equal "Unknown type :foo", e.message
end
-
- assert_equal "Unknown type :foo", e.message
end
end
end
diff --git a/activemodel/test/cases/type/string_test.rb b/activemodel/test/cases/type/string_test.rb
index 7b25a1ef74..222083817e 100644
--- a/activemodel/test/cases/type/string_test.rb
+++ b/activemodel/test/cases/type/string_test.rb
@@ -2,26 +2,27 @@ require "cases/helper"
require "active_model/type"
module ActiveModel
- class StringTypeTest < ActiveModel::TestCase
- test "type casting" do
- type = Type::String.new
- assert_equal "t", type.cast(true)
- assert_equal "f", type.cast(false)
- assert_equal "123", type.cast(123)
- end
+ module Type
+ class StringTest < ActiveModel::TestCase
+ test "type casting" do
+ type = Type::String.new
+ assert_equal "t", type.cast(true)
+ assert_equal "f", type.cast(false)
+ assert_equal "123", type.cast(123)
+ end
- test "immutable strings are not duped coming out" do
- s = "foo"
- type = Type::ImmutableString.new
- assert_same s, type.cast(s)
- assert_same s, type.deserialize(s)
- end
+ test "cast strings are mutable" do
+ s = "foo"
+ type = Type::String.new
+ assert_equal false, type.cast(s).frozen?
+ end
- test "values are duped coming out" do
- s = "foo"
- type = Type::String.new
- assert_not_same s, type.cast(s)
- assert_not_same s, type.deserialize(s)
+ test "values are duped coming out" do
+ s = "foo"
+ type = Type::String.new
+ assert_not_same s, type.cast(s)
+ assert_not_same s, type.deserialize(s)
+ end
end
end
end
diff --git a/activemodel/test/cases/type/time_test.rb b/activemodel/test/cases/type/time_test.rb
new file mode 100644
index 0000000000..a6a79833e6
--- /dev/null
+++ b/activemodel/test/cases/type/time_test.rb
@@ -0,0 +1,21 @@
+require "cases/helper"
+require "active_model/type"
+
+module ActiveModel
+ module Type
+ class TimeTest < ActiveModel::TestCase
+ def test_type_cast_time
+ type = Type::Time.new
+ assert_equal nil, type.cast(nil)
+ assert_equal nil, type.cast("")
+ assert_equal nil, type.cast("ABC")
+
+ time_string = ::Time.now.utc.strftime("%T")
+ assert_equal time_string, type.cast(time_string).strftime("%T")
+
+ assert_equal ::Time.utc(2000, 1, 1, 16, 45, 54), type.cast("2015-06-13T19:45:54+03:00")
+ assert_equal ::Time.utc(1999, 12, 31, 21, 7, 8), type.cast("06:07:08+09:00")
+ end
+ end
+ end
+end
diff --git a/activemodel/test/cases/type/value_test.rb b/activemodel/test/cases/type/value_test.rb
new file mode 100644
index 0000000000..d8b3e7f164
--- /dev/null
+++ b/activemodel/test/cases/type/value_test.rb
@@ -0,0 +1,14 @@
+require "cases/helper"
+require "active_model/type"
+
+module ActiveModel
+ module Type
+ class ValueTest < ActiveModel::TestCase
+ def test_type_equality
+ assert_equal Type::Value.new, Type::Value.new
+ assert_not_equal Type::Value.new, Type::Integer.new
+ assert_not_equal Type::Value.new(precision: 1), Type::Value.new(precision: 2)
+ end
+ end
+ end
+end
diff --git a/activemodel/test/cases/types_test.rb b/activemodel/test/cases/types_test.rb
deleted file mode 100644
index c46775cb41..0000000000
--- a/activemodel/test/cases/types_test.rb
+++ /dev/null
@@ -1,125 +0,0 @@
-require "cases/helper"
-require "active_model/type"
-require "active_support/core_ext/numeric/time"
-
-module ActiveModel
- class TypesTest < ActiveModel::TestCase
- def test_type_cast_boolean
- type = Type::Boolean.new
- assert type.cast("").nil?
- assert type.cast(nil).nil?
-
- assert type.cast(true)
- assert type.cast(1)
- assert type.cast("1")
- assert type.cast("t")
- assert type.cast("T")
- assert type.cast("true")
- assert type.cast("TRUE")
- assert type.cast("on")
- assert type.cast("ON")
- assert type.cast(" ")
- assert type.cast("\u3000\r\n")
- assert type.cast("\u0000")
- assert type.cast("SOMETHING RANDOM")
-
- # explicitly check for false vs nil
- assert_equal false, type.cast(false)
- assert_equal false, type.cast(0)
- assert_equal false, type.cast("0")
- assert_equal false, type.cast("f")
- assert_equal false, type.cast("F")
- assert_equal false, type.cast("false")
- assert_equal false, type.cast("FALSE")
- assert_equal false, type.cast("off")
- assert_equal false, type.cast("OFF")
- end
-
- def test_type_cast_float
- type = Type::Float.new
- assert_equal 1.0, type.cast("1")
- end
-
- def test_changing_float
- type = Type::Float.new
-
- assert type.changed?(5.0, 5.0, "5wibble")
- assert_not type.changed?(5.0, 5.0, "5")
- assert_not type.changed?(5.0, 5.0, "5.0")
- assert_not type.changed?(nil, nil, nil)
- end
-
- def test_type_cast_binary
- type = Type::Binary.new
- assert_equal nil, type.cast(nil)
- assert_equal "1", type.cast("1")
- assert_equal 1, type.cast(1)
- end
-
- def test_type_cast_time
- type = Type::Time.new
- assert_equal nil, type.cast(nil)
- assert_equal nil, type.cast("")
- assert_equal nil, type.cast("ABC")
-
- time_string = Time.now.utc.strftime("%T")
- assert_equal time_string, type.cast(time_string).strftime("%T")
-
- assert_equal ::Time.utc(2000, 1, 1, 16, 45, 54), type.cast("2015-06-13T19:45:54+03:00")
- assert_equal ::Time.utc(1999, 12, 31, 21, 7, 8), type.cast("06:07:08+09:00")
- end
-
- def test_type_cast_datetime_and_timestamp
- type = Type::DateTime.new
- assert_equal nil, type.cast(nil)
- assert_equal nil, type.cast("")
- assert_equal nil, type.cast(" ")
- assert_equal nil, type.cast("ABC")
-
- datetime_string = Time.now.utc.strftime("%FT%T")
- assert_equal datetime_string, type.cast(datetime_string).strftime("%FT%T")
- end
-
- def test_type_cast_date
- type = Type::Date.new
- assert_equal nil, type.cast(nil)
- assert_equal nil, type.cast("")
- assert_equal nil, type.cast(" ")
- assert_equal nil, type.cast("ABC")
-
- date_string = Time.now.utc.strftime("%F")
- assert_equal date_string, type.cast(date_string).strftime("%F")
- end
-
- def test_type_cast_duration_to_integer
- type = Type::Integer.new
- assert_equal 1800, type.cast(30.minutes)
- assert_equal 7200, type.cast(2.hours)
- end
-
- def test_string_to_time_with_timezone
- ["UTC", "US/Eastern"].each do |zone|
- with_timezone_config default: zone do
- type = Type::DateTime.new
- assert_equal Time.utc(2013, 9, 4, 0, 0, 0), type.cast("Wed, 04 Sep 2013 03:00:00 EAT")
- end
- end
- end
-
- def test_type_equality
- assert_equal Type::Value.new, Type::Value.new
- assert_not_equal Type::Value.new, Type::Integer.new
- assert_not_equal Type::Value.new(precision: 1), Type::Value.new(precision: 2)
- end
-
- private
-
- def with_timezone_config(default:)
- old_zone_default = ::Time.zone_default
- ::Time.zone_default = ::Time.find_zone(default)
- yield
- ensure
- ::Time.zone_default = old_zone_default
- end
- end
-end
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 15b49e0a0b..487195a2e0 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,12 +1,24 @@
+* Deprecate passing arguments and block at the same time to
+ `ActiveRecord::QueryMethods#select`.
+
+ *Prathamesh Sonpatki*
+
+* Optimistic locking: Added ability update locking_column value.
+ Ignore optimistic locking if update with new locking_column value.
+
+ *bogdanvlviv*
+
+* Fixed: Optimistic locking does not work well with null in the database.
+
+ Fixes #26024
+
+ *bogdanvlviv*
+
* Fixed support for case insensitive comparisons of `text` columns in
PostgreSQL.
*Edho Arief*
-* Made ActiveRecord consistently use `ActiveRecord::Type` (not `ActiveModel::Type`)
-
- *Iain Beeston*
-
* Serialize JSON attribute value `nil` as SQL `NULL`, not JSON `null`
*Trung Duc Tran*
diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb
index dda240585e..0800639c24 100644
--- a/activerecord/lib/active_record/associations/collection_proxy.rb
+++ b/activerecord/lib/active_record/associations/collection_proxy.rb
@@ -106,12 +106,6 @@ module ActiveRecord
# # #<Pet id: 2, name: "Spook", person_id: 1>,
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
# # ]
- #
- # person.pets.select(:name) { |pet| pet.name =~ /oo/ }
- # # => [
- # # #<Pet id: 2, name: "Spook">,
- # # #<Pet id: 3, name: "Choo-Choo">
- # # ]
# Finds an object in the collection responding to the +id+. Uses the same
# rules as ActiveRecord::Base.find. Returns ActiveRecord::RecordNotFound
diff --git a/activerecord/lib/active_record/attribute_methods/query.rb b/activerecord/lib/active_record/attribute_methods/query.rb
index 05f0e974b6..10498f4322 100644
--- a/activerecord/lib/active_record/attribute_methods/query.rb
+++ b/activerecord/lib/active_record/attribute_methods/query.rb
@@ -19,7 +19,7 @@ module ActiveRecord
if Numeric === value || value !~ /[^0-9]/
!value.to_i.zero?
else
- return false if ActiveRecord::Type::Boolean::FALSE_VALUES.include?(value)
+ return false if ActiveModel::Type::Boolean::FALSE_VALUES.include?(value)
!value.blank?
end
elsif value.respond_to?(:zero?)
diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb
index 8e8a97990a..82882469e3 100644
--- a/activerecord/lib/active_record/locking/optimistic.rb
+++ b/activerecord/lib/active_record/locking/optimistic.rb
@@ -47,6 +47,8 @@ module ActiveRecord
# self.locking_column = :lock_person
# end
#
+ # Please note that the optimistic locking will be ignored if you update the
+ # locking column's value.
module Optimistic
extend ActiveSupport::Concern
@@ -60,6 +62,7 @@ module ActiveRecord
end
private
+
def increment_lock
lock_col = self.class.locking_column
previous_lock_value = send(lock_col).to_i
@@ -77,21 +80,24 @@ module ActiveRecord
def _update_record(attribute_names = self.attribute_names) #:nodoc:
return super unless locking_enabled?
- return 0 if attribute_names.empty?
lock_col = self.class.locking_column
- previous_lock_value = send(lock_col).to_i
- increment_lock
- attribute_names += [lock_col]
- attribute_names.uniq!
+ return super if attribute_names.include?(lock_col)
+ return 0 if attribute_names.empty?
begin
+ previous_lock_value = read_attribute_before_type_cast(lock_col)
+
+ increment_lock
+
+ attribute_names.push(lock_col)
+
relation = self.class.unscoped
affected_rows = relation.where(
self.class.primary_key => id,
- lock_col => previous_lock_value,
+ lock_col => previous_lock_value
).update_all(
attributes_for_update(attribute_names).map do |name|
[name, _read_attribute(name)]
@@ -104,9 +110,9 @@ module ActiveRecord
affected_rows
- # If something went wrong, revert the version.
+ # If something went wrong, revert the locking_column value.
rescue Exception
- send(lock_col + "=", previous_lock_value)
+ send(lock_col + "=", previous_lock_value.to_i)
raise
end
end
diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb
index 76b3169411..525f7444a5 100644
--- a/activerecord/lib/active_record/model_schema.rb
+++ b/activerecord/lib/active_record/model_schema.rb
@@ -2,71 +2,150 @@ module ActiveRecord
module ModelSchema
extend ActiveSupport::Concern
+ ##
+ # :singleton-method: primary_key_prefix_type
+ # :call-seq: primary_key_prefix_type
+ #
+ # The prefix type that will be prepended to every primary key column name.
+ # The options are +:table_name+ and +:table_name_with_underscore+. If the first is specified,
+ # the Product class will look for "productid" instead of "id" as the primary column. If the
+ # latter is specified, the Product class will look for "product_id" instead of "id". Remember
+ # that this is a global setting for all Active Records.
+
+ ##
+ # :singleton-method: primary_key_prefix_type=
+ # :call-seq: primary_key_prefix_type=(prefix_type)
+ #
+ # Sets the prefix type that will be prepended to every primary key column name.
+ # The options are +:table_name+ and +:table_name_with_underscore+. If the first is specified,
+ # the Product class will look for "productid" instead of "id" as the primary column. If the
+ # latter is specified, the Product class will look for "product_id" instead of "id". Remember
+ # that this is a global setting for all Active Records.
+
+ ##
+ # :singleton-method: table_name_prefix
+ # :call-seq: table_name_prefix
+ #
+ # The prefix string to prepend to every table name.
+
+ ##
+ # :singleton-method: table_name_prefix=
+ # :call-seq: table_name_prefix=(prefix)
+ #
+ # Sets the prefix string to prepend to every table name. So if set to "basecamp_", all table
+ # names will be named like "basecamp_projects", "basecamp_people", etc. This is a convenient
+ # way of creating a namespace for tables in a shared database. By default, the prefix is the
+ # empty string.
+ #
+ # If you are organising your models within modules you can add a prefix to the models within
+ # a namespace by defining a singleton method in the parent module called table_name_prefix which
+ # returns your chosen prefix.
+
+ ##
+ # :singleton-method: table_name_suffix
+ # :call-seq: table_name_suffix
+ #
+ # The suffix string to append to every table name.
+
+ ##
+ # :singleton-method: table_name_suffix=
+ # :call-seq: table_name_suffix=(suffix)
+ #
+ # Works like +table_name_prefix=+, but appends instead of prepends (set to "_basecamp" gives "projects_basecamp",
+ # "people_basecamp"). By default, the suffix is the empty string.
+ #
+ # If you are organising your models within modules, you can add a suffix to the models within
+ # a namespace by defining a singleton method in the parent module called table_name_suffix which
+ # returns your chosen suffix.
+
+ ##
+ # :singleton-method: schema_migrations_table_name
+ # :call-seq: schema_migrations_table_name
+ #
+ # The name of the schema migrations table. By default, the value is <tt>"schema_migrations"</tt>.
+
+ ##
+ # :singleton-method: schema_migrations_table_name=
+ # :call-seq: schema_migrations_table_name=(table_name)
+ #
+ # Sets the name of the schema migrations table.
+
+ ##
+ # :singleton-method: internal_metadata_table_name
+ # :call-seq: internal_metadata_table_name
+ #
+ # The name of the internal metadata table. By default, the value is <tt>"ar_internal_metadata"</tt>.
+
+ ##
+ # :singleton-method: internal_metadata_table_name=
+ # :call-seq: internal_metadata_table_name=(table_name)
+ #
+ # Sets the name of the internal metadata table.
+
+ ##
+ # :singleton-method: protected_environments
+ # :call-seq: protected_environments
+ #
+ # The array of names of environments where destructive actions should be prohibited. By default,
+ # the value is <tt>["production"]</tt>.
+
+ ##
+ # :singleton-method: protected_environments=
+ # :call-seq: protected_environments=(environments)
+ #
+ # Sets an array of names of environments where destructive actions should be prohibited.
+
+ ##
+ # :singleton-method: pluralize_table_names
+ # :call-seq: pluralize_table_names
+ #
+ # Indicates whether table names should be the pluralized versions of the corresponding class names.
+ # If true, the default table name for a Product class will be "products". If false, it would just be "product".
+ # See table_name for the full rules on table/class naming. This is true, by default.
+
+ ##
+ # :singleton-method: pluralize_table_names=
+ # :call-seq: pluralize_table_names=(value)
+ #
+ # Set whether table names should be the pluralized versions of the corresponding class names.
+ # If true, the default table name for a Product class will be "products". If false, it would just be "product".
+ # See table_name for the full rules on table/class naming. This is true, by default.
+
+ ##
+ # :singleton-method: ignored_columns
+ # :call-seq: ignored_columns
+ #
+ # The list of columns names the model should ignore. Ignored columns won't have attribute
+ # accessors defined, and won't be referenced in SQL queries.
+
+ ##
+ # :singleton-method: ignored_columns=
+ # :call-seq: ignored_columns=(columns)
+ #
+ # Sets the columns names the model should ignore. Ignored columns won't have attribute
+ # accessors defined, and won't be referenced in SQL queries.
+
included do
- ##
- # :singleton-method:
- # Accessor for the prefix type that will be prepended to every primary key column name.
- # The options are :table_name and :table_name_with_underscore. If the first is specified,
- # the Product class will look for "productid" instead of "id" as the primary column. If the
- # latter is specified, the Product class will look for "product_id" instead of "id". Remember
- # that this is a global setting for all Active Records.
mattr_accessor :primary_key_prefix_type, instance_writer: false
- ##
- # :singleton-method:
- # Accessor for the name of the prefix string to prepend to every table name. So if set
- # to "basecamp_", all table names will be named like "basecamp_projects", "basecamp_people",
- # etc. This is a convenient way of creating a namespace for tables in a shared database.
- # By default, the prefix is the empty string.
- #
- # If you are organising your models within modules you can add a prefix to the models within
- # a namespace by defining a singleton method in the parent module called table_name_prefix which
- # returns your chosen prefix.
class_attribute :table_name_prefix, instance_writer: false
self.table_name_prefix = ""
- ##
- # :singleton-method:
- # Works like +table_name_prefix+, but appends instead of prepends (set to "_basecamp" gives "projects_basecamp",
- # "people_basecamp"). By default, the suffix is the empty string.
- #
- # If you are organising your models within modules, you can add a suffix to the models within
- # a namespace by defining a singleton method in the parent module called table_name_suffix which
- # returns your chosen suffix.
class_attribute :table_name_suffix, instance_writer: false
self.table_name_suffix = ""
- ##
- # :singleton-method:
- # Accessor for the name of the schema migrations table. By default, the value is "schema_migrations"
class_attribute :schema_migrations_table_name, instance_accessor: false
self.schema_migrations_table_name = "schema_migrations"
- ##
- # :singleton-method:
- # Accessor for the name of the internal metadata table. By default, the value is "ar_internal_metadata"
class_attribute :internal_metadata_table_name, instance_accessor: false
self.internal_metadata_table_name = "ar_internal_metadata"
- ##
- # :singleton-method:
- # Accessor for an array of names of environments where destructive actions should be prohibited. By default,
- # the value is ["production"]
class_attribute :protected_environments, instance_accessor: false
self.protected_environments = ["production"]
- ##
- # :singleton-method:
- # Indicates whether table names should be the pluralized versions of the corresponding class names.
- # If true, the default table name for a Product class will be +products+. If false, it would just be +product+.
- # See table_name for the full rules on table/class naming. This is true, by default.
class_attribute :pluralize_table_names, instance_writer: false
self.pluralize_table_names = true
- ##
- # :singleton-method:
- # Accessor for the list of columns names the model should ignore. Ignored columns won't have attribute
- # accessors defined, and won't be referenced in SQL queries.
class_attribute :ignored_columns, instance_accessor: false
self.ignored_columns = [].freeze
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 9fbbe32e7f..c6d0902e0d 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -242,7 +242,16 @@ module ActiveRecord
# Model.select(:field).first.other_field
# # => ActiveModel::MissingAttributeError: missing attribute: other_field
def select(*fields)
- return super if block_given?
+ if block_given?
+ if fields.any?
+ ActiveSupport::Deprecation.warn(<<-WARNING.squish)
+ When select is called with a block, it ignores other arguments. This behavior is now deprecated and will result in an ArgumentError in Rails 5.1. You can safely remove the arguments to resolve the deprecation warning because they do not have any effect on the output of the call to the select method with a block.
+ WARNING
+ end
+
+ return super()
+ end
+
raise ArgumentError, "Call this with at least one field" if fields.empty?
spawn._select!(*fields)
end
diff --git a/activerecord/lib/active_record/type.rb b/activerecord/lib/active_record/type.rb
index 84373dddf2..0b48d2186a 100644
--- a/activerecord/lib/active_record/type.rb
+++ b/activerecord/lib/active_record/type.rb
@@ -1,6 +1,4 @@
require "active_model/type"
-require "active_record/type/helpers"
-require "active_record/type/value"
require "active_record/type/internal/abstract_json"
require "active_record/type/internal/timezone"
@@ -50,6 +48,7 @@ module ActiveRecord
end
end
+ Helpers = ActiveModel::Type::Helpers
BigInteger = ActiveModel::Type::BigInteger
Binary = ActiveModel::Type::Binary
Boolean = ActiveModel::Type::Boolean
@@ -60,6 +59,7 @@ module ActiveRecord
String = ActiveModel::Type::String
Text = ActiveModel::Type::Text
UnsignedInteger = ActiveModel::Type::UnsignedInteger
+ Value = ActiveModel::Type::Value
register(:big_integer, Type::BigInteger, override: false)
register(:binary, Type::Binary, override: false)
diff --git a/activerecord/lib/active_record/type/helpers.rb b/activerecord/lib/active_record/type/helpers.rb
deleted file mode 100644
index a32ccd4bc3..0000000000
--- a/activerecord/lib/active_record/type/helpers.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-module ActiveRecord
- module Type
- Helpers = ActiveModel::Type::Helpers
- end
-end
diff --git a/activerecord/lib/active_record/type/internal/abstract_json.rb b/activerecord/lib/active_record/type/internal/abstract_json.rb
index 67028546e4..e19c5a14da 100644
--- a/activerecord/lib/active_record/type/internal/abstract_json.rb
+++ b/activerecord/lib/active_record/type/internal/abstract_json.rb
@@ -1,8 +1,8 @@
module ActiveRecord
module Type
module Internal # :nodoc:
- class AbstractJson < Type::Value # :nodoc:
- include Type::Helpers::Mutable
+ class AbstractJson < ActiveModel::Type::Value # :nodoc:
+ include ActiveModel::Type::Helpers::Mutable
def type
:json
diff --git a/activerecord/lib/active_record/type/serialized.rb b/activerecord/lib/active_record/type/serialized.rb
index ca12c83b1a..ac9134bfcb 100644
--- a/activerecord/lib/active_record/type/serialized.rb
+++ b/activerecord/lib/active_record/type/serialized.rb
@@ -1,7 +1,7 @@
module ActiveRecord
module Type
- class Serialized < DelegateClass(Type::Value) # :nodoc:
- include Type::Helpers::Mutable
+ class Serialized < DelegateClass(ActiveModel::Type::Value) # :nodoc:
+ include ActiveModel::Type::Helpers::Mutable
attr_reader :subtype, :coder
diff --git a/activerecord/lib/active_record/type/value.rb b/activerecord/lib/active_record/type/value.rb
deleted file mode 100644
index 89ef29106b..0000000000
--- a/activerecord/lib/active_record/type/value.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-module ActiveRecord
- module Type
- class Value < ActiveModel::Type::Value; end
- end
-end
diff --git a/activerecord/test/cases/adapters/mysql2/connection_test.rb b/activerecord/test/cases/adapters/mysql2/connection_test.rb
index 8d8955e5c9..ff012efc5f 100644
--- a/activerecord/test/cases/adapters/mysql2/connection_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/connection_test.rb
@@ -65,18 +65,18 @@ class Mysql2ConnectionTest < ActiveRecord::Mysql2TestCase
def test_execute_after_disconnect
@connection.disconnect!
- error = assert_raise(ActiveRecord::StatementInvalid) do
+
+ assert_raise(ActiveRecord::StatementInvalid) do
@connection.execute("SELECT 1")
end
- assert_match(/closed MySQL connection/, error.message)
end
def test_quote_after_disconnect
@connection.disconnect!
- error = assert_raise(Mysql2::Error) do
+
+ assert_raise(Mysql2::Error) do
@connection.quote("string")
end
- assert_match(/closed MySQL connection/, error.message)
end
def test_active_after_disconnect
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index 0ce67f971b..c1e04714fb 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -788,6 +788,13 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal [1], posts(:welcome).comments.select { |c| c.id == 1 }.map(&:id)
end
+ def test_select_with_block_and_specific_attributes
+ assert_deprecated do
+ comments = posts(:welcome).comments.select(:id, :body) { |c| c.id == 1 }
+ assert_equal [1], comments.map(&:id)
+ end
+ end
+
def test_select_without_foreign_key
assert_equal companies(:first_firm).accounts.first.credit_limit, companies(:first_firm).accounts.select(:credit_limit).first.credit_limit
end
diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb
index 09bd00291d..3bd8475bb0 100644
--- a/activerecord/test/cases/dirty_test.rb
+++ b/activerecord/test/cases/dirty_test.rb
@@ -341,7 +341,7 @@ class DirtyTest < ActiveRecord::TestCase
def test_partial_update_with_optimistic_locking
person = Person.new(first_name: "foo")
- old_lock_version = 1
+ old_lock_version = person.lock_version
with_partial_writes Person, false do
assert_queries(2) { 2.times { person.save! } }
diff --git a/activerecord/test/cases/locking_test.rb b/activerecord/test/cases/locking_test.rb
index 13b6f6daaf..0579df0a07 100644
--- a/activerecord/test/cases/locking_test.rb
+++ b/activerecord/test/cases/locking_test.rb
@@ -161,14 +161,6 @@ class OptimisticLockingTest < ActiveRecord::TestCase
assert_equal(error.record.object_id, p2.object_id)
end
- def test_lock_new_with_nil
- p1 = Person.new(first_name: "anika")
- p1.save!
- p1.lock_version = nil # simulate bad fixture or column with no default
- p1.save!
- assert_equal 1, p1.lock_version
- end
-
def test_lock_new_when_explicitly_passing_nil
p1 = Person.new(first_name: "anika", lock_version: nil)
p1.save!
@@ -222,22 +214,149 @@ class OptimisticLockingTest < ActiveRecord::TestCase
def test_lock_without_default_sets_version_to_zero
t1 = LockWithoutDefault.new
+
+ assert_equal 0, t1.lock_version
+ assert_nil t1.lock_version_before_type_cast
+
+ t1.save!
+ t1.reload
+
assert_equal 0, t1.lock_version
+ assert_equal 0, t1.lock_version_before_type_cast
+ end
+
+ def test_lock_without_default_should_work_with_null_in_the_database
+ ActiveRecord::Base.connection.execute("INSERT INTO lock_without_defaults(title) VALUES('title1')")
+ t1 = LockWithoutDefault.last
+ t2 = LockWithoutDefault.last
+
+ assert_equal 0, t1.lock_version
+ assert_nil t1.lock_version_before_type_cast
+ assert_equal 0, t2.lock_version
+ assert_nil t2.lock_version_before_type_cast
+
+ t1.title = "new title1"
+ t2.title = "new title2"
+
+ assert_nothing_raised { t1.save! }
+ assert_equal 1, t1.lock_version
+ assert_equal "new title1", t1.title
+
+ assert_raise(ActiveRecord::StaleObjectError) { t2.save! }
+ assert_equal 0, t2.lock_version
+ assert_equal "new title2", t2.title
+ end
+
+ def test_lock_without_default_should_update_with_lock_col
+ t1 = LockWithoutDefault.create(title: "title1", lock_version: 6)
+
+ assert_equal 6, t1.lock_version
+
+ t1.update(lock_version: 0)
+ t1.reload
- t1.save
- t1 = LockWithoutDefault.find(t1.id)
assert_equal 0, t1.lock_version
end
+ def test_lock_without_default_queries_count
+ t1 = LockWithoutDefault.create(title: "title1")
+
+ assert_equal "title1", t1.title
+ assert_equal 0, t1.lock_version
+
+ assert_queries(1) { t1.update(title: "title2") }
+
+ t1.reload
+ assert_equal "title2", t1.title
+ assert_equal 1, t1.lock_version
+
+ assert_queries(1) { t1.update(title: "title3", lock_version: 6) }
+
+ t1.reload
+ assert_equal "title3", t1.title
+ assert_equal 6, t1.lock_version
+
+ t2 = LockWithoutDefault.new(title: "title1")
+
+ assert_queries(1) { t2.save! }
+
+ t2.reload
+ assert_equal "title1", t2.title
+ assert_equal 0, t2.lock_version
+ end
+
def test_lock_with_custom_column_without_default_sets_version_to_zero
t1 = LockWithCustomColumnWithoutDefault.new
+
assert_equal 0, t1.custom_lock_version
assert_nil t1.custom_lock_version_before_type_cast
t1.save!
t1.reload
+
+ assert_equal 0, t1.custom_lock_version
+ assert_equal 0, t1.custom_lock_version_before_type_cast
+ end
+
+ def test_lock_with_custom_column_without_default_should_work_with_null_in_the_database
+ ActiveRecord::Base.connection.execute("INSERT INTO lock_without_defaults_cust(title) VALUES('title1')")
+
+ t1 = LockWithCustomColumnWithoutDefault.last
+ t2 = LockWithCustomColumnWithoutDefault.last
+
assert_equal 0, t1.custom_lock_version
- assert [0, "0"].include?(t1.custom_lock_version_before_type_cast)
+ assert_nil t1.custom_lock_version_before_type_cast
+ assert_equal 0, t2.custom_lock_version
+ assert_nil t2.custom_lock_version_before_type_cast
+
+ t1.title = "new title1"
+ t2.title = "new title2"
+
+ assert_nothing_raised { t1.save! }
+ assert_equal 1, t1.custom_lock_version
+ assert_equal "new title1", t1.title
+
+ assert_raise(ActiveRecord::StaleObjectError) { t2.save! }
+ assert_equal 0, t2.custom_lock_version
+ assert_equal "new title2", t2.title
+ end
+
+ def test_lock_with_custom_column_without_default_should_update_with_lock_col
+ t1 = LockWithCustomColumnWithoutDefault.create(title: "title1", custom_lock_version: 6)
+
+ assert_equal 6, t1.custom_lock_version
+
+ t1.update(custom_lock_version: 0)
+ t1.reload
+
+ assert_equal 0, t1.custom_lock_version
+ end
+
+ def test_lock_with_custom_column_without_default_queries_count
+ t1 = LockWithCustomColumnWithoutDefault.create(title: "title1")
+
+ assert_equal "title1", t1.title
+ assert_equal 0, t1.custom_lock_version
+
+ assert_queries(1) { t1.update(title: "title2") }
+
+ t1.reload
+ assert_equal "title2", t1.title
+ assert_equal 1, t1.custom_lock_version
+
+ assert_queries(1) { t1.update(title: "title3", custom_lock_version: 6) }
+
+ t1.reload
+ assert_equal "title3", t1.title
+ assert_equal 6, t1.custom_lock_version
+
+ t2 = LockWithCustomColumnWithoutDefault.new(title: "title1")
+
+ assert_queries(1) { t2.save! }
+
+ t2.reload
+ assert_equal "title1", t2.title
+ assert_equal 0, t2.custom_lock_version
end
def test_readonly_attributes
@@ -461,6 +580,7 @@ unless in_memory_db?
end
protected
+
def duel(zzz = 5)
t0, t1, t2, t3 = nil, nil, nil, nil
diff --git a/activerecord/test/cases/serialized_attribute_test.rb b/activerecord/test/cases/serialized_attribute_test.rb
index 8e9514de7c..bebd856faf 100644
--- a/activerecord/test/cases/serialized_attribute_test.rb
+++ b/activerecord/test/cases/serialized_attribute_test.rb
@@ -313,8 +313,8 @@ class SerializedAttributeTest < ActiveRecord::TestCase
return if value.nil?
value.gsub(" encoded", "")
end
- type = Class.new(ActiveRecord::Type::Value) do
- include ActiveRecord::Type::Helpers::Mutable
+ type = Class.new(ActiveModel::Type::Value) do
+ include ActiveModel::Type::Helpers::Mutable
def serialize(value)
return if value.nil?
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index d2fb090118..983ac076a9 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -436,10 +436,12 @@ ActiveRecord::Schema.define do
end
create_table :lock_without_defaults, force: true do |t|
+ t.column :title, :string
t.column :lock_version, :integer
end
create_table :lock_without_defaults_cust, force: true do |t|
+ t.column :title, :string
t.column :custom_lock_version, :integer
end
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index f840783059..83ff80e31a 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,3 +1,21 @@
+* Use `Hash#compact` and `Hash#compact!` from Ruby 2.4. Old Ruby versions
+ will continue to get these methods from Active Support as before.
+
+ *Prathamesh Sonpatki*
+
+* Fix `ActiveSupport::TimeZone#strptime`.
+ Support for timestamps in format of seconds (%s) and milliseconds (%Q).
+
+ Fixes #26840.
+
+ *Lev Denisov*
+
+* Fix `DateAndTime::Calculations#copy_time_to`. Copy `nsec` instead of `usec`.
+
+ Jumping forward or backward between weeks now preserves nanosecond digits.
+
+ *Josua Schmid*
+
* Fix `ActiveSupport::TimeWithZone#in` across DST boundaries.
Previously calls to `in` were being sent to the non-DST aware
@@ -8,10 +26,10 @@
Time.zone = "US/Eastern"
t = Time.zone.local(2016,11,6,1)
- # => Sun, 06 Nov 2016 01:00:00 EDT -05:00
+ # => Sun, 06 Nov 2016 01:00:00 EDT -05:00
t.in(1.hour)
- # => Sun, 06 Nov 2016 01:00:00 EST -05:00
+ # => Sun, 06 Nov 2016 01:00:00 EST -05:00
Fixes #26580.
diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb
index 890b1cd73b..2ad1145fae 100644
--- a/activesupport/lib/active_support/callbacks.rb
+++ b/activesupport/lib/active_support/callbacks.rb
@@ -117,8 +117,8 @@ module ActiveSupport
(skipped ||= []) << current
next
else
- expanded = current.expand_call_template(env, invoke_sequence)
- expanded.shift.send(*expanded, &expanded.shift)
+ target, block, method, *arguments = current.expand_call_template(env, invoke_sequence)
+ target.send(method, *arguments, &block)
end
current.invoke_after(env)
skipped.pop.invoke_after(env) while skipped && skipped.first
@@ -410,8 +410,8 @@ module ActiveSupport
# values.
def make_lambda
lambda do |target, value, &block|
- c = expand(target, value, block)
- c.shift.send(*c, &c.shift)
+ target, block, method, *arguments = expand(target, value, block)
+ target.send(method, *arguments, &block)
end
end
@@ -419,8 +419,8 @@ module ActiveSupport
# values, but then return the boolean inverse of that result.
def inverted_lambda
lambda do |target, value, &block|
- c = expand(target, value, block)
- ! c.shift.send(*c, &c.shift)
+ target, block, method, *arguments = expand(target, value, block)
+ ! target.send(method, *arguments, &block)
end
end
diff --git a/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb b/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb
index c614f14289..f2ba7fdda5 100644
--- a/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb
@@ -334,7 +334,7 @@ module DateAndTime
end
def copy_time_to(other)
- other.change(hour: hour, min: min, sec: sec, usec: try(:usec))
+ other.change(hour: hour, min: min, sec: sec, nsec: try(:nsec))
end
end
end
diff --git a/activesupport/lib/active_support/core_ext/hash/compact.rb b/activesupport/lib/active_support/core_ext/hash/compact.rb
index 78b3387c3b..5cae495bda 100644
--- a/activesupport/lib/active_support/core_ext/hash/compact.rb
+++ b/activesupport/lib/active_support/core_ext/hash/compact.rb
@@ -1,23 +1,27 @@
class Hash
- # Returns a hash with non +nil+ values.
- #
- # hash = { a: true, b: false, c: nil }
- # hash.compact # => { a: true, b: false }
- # hash # => { a: true, b: false, c: nil }
- # { c: nil }.compact # => {}
- # { c: true }.compact # => { c: true }
- def compact
- select { |_, value| !value.nil? }
+ unless Hash.instance_methods(false).include?(:compact)
+ # Returns a hash with non +nil+ values.
+ #
+ # hash = { a: true, b: false, c: nil }
+ # hash.compact # => { a: true, b: false }
+ # hash # => { a: true, b: false, c: nil }
+ # { c: nil }.compact # => {}
+ # { c: true }.compact # => { c: true }
+ def compact
+ select { |_, value| !value.nil? }
+ end
end
- # Replaces current hash with non +nil+ values.
- # Returns nil if no changes were made, otherwise returns the hash.
- #
- # hash = { a: true, b: false, c: nil }
- # hash.compact! # => { a: true, b: false }
- # hash # => { a: true, b: false }
- # { c: true }.compact! # => nil
- def compact!
- reject! { |_, value| value.nil? }
+ unless Hash.instance_methods(false).include?(:compact!)
+ # Replaces current hash with non +nil+ values.
+ # Returns nil if no changes were made, otherwise returns the hash.
+ #
+ # hash = { a: true, b: false, c: nil }
+ # hash.compact! # => { a: true, b: false }
+ # hash # => { a: true, b: false }
+ # { c: true }.compact! # => nil
+ def compact!
+ reject! { |_, value| value.nil? }
+ end
end
end
diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb
index 74a603c05d..7ecc5c19bd 100644
--- a/activesupport/lib/active_support/hash_with_indifferent_access.rb
+++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb
@@ -40,6 +40,12 @@ module ActiveSupport
# rgb = { black: '#000000', white: '#FFFFFF' }.with_indifferent_access
#
# which may be handy.
+ #
+ # To access this class outside of Rails, require the core extension with:
+ #
+ # require "active_support/core_ext/hash/indifferent_access"
+ #
+ # which will, in turn, require this file.
class HashWithIndifferentAccess < Hash
# Returns +true+ so that <tt>Array#extract_options!</tt> finds members of
# this class.
diff --git a/activesupport/lib/active_support/test_case.rb b/activesupport/lib/active_support/test_case.rb
index 1c599b8851..3de4ccc1da 100644
--- a/activesupport/lib/active_support/test_case.rb
+++ b/activesupport/lib/active_support/test_case.rb
@@ -65,5 +65,7 @@ module ActiveSupport
alias :assert_not_predicate :refute_predicate
alias :assert_not_respond_to :refute_respond_to
alias :assert_not_same :refute_same
+
+ ActiveSupport.run_load_hooks(:active_support_test_case, self)
end
end
diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb
index cb97a0e135..a562f33420 100644
--- a/activesupport/lib/active_support/values/time_zone.rb
+++ b/activesupport/lib/active_support/values/time_zone.rb
@@ -450,17 +450,21 @@ module ActiveSupport
raise ArgumentError, "invalid date" if parts.nil?
return if parts.empty?
- time = Time.new(
- parts.fetch(:year, now.year),
- parts.fetch(:mon, now.month),
- parts.fetch(:mday, parts[:year] || parts[:mon] ? 1 : now.day),
- parts.fetch(:hour, 0),
- parts.fetch(:min, 0),
- parts.fetch(:sec, 0) + parts.fetch(:sec_fraction, 0),
- parts.fetch(:offset, 0)
- )
-
- if parts[:offset]
+ if parts[:seconds]
+ time = Time.at(parts[:seconds])
+ else
+ time = Time.new(
+ parts.fetch(:year, now.year),
+ parts.fetch(:mon, now.month),
+ parts.fetch(:mday, parts[:year] || parts[:mon] ? 1 : now.day),
+ parts.fetch(:hour, 0),
+ parts.fetch(:min, 0),
+ parts.fetch(:sec, 0) + parts.fetch(:sec_fraction, 0),
+ parts.fetch(:offset, 0)
+ )
+ end
+
+ if parts[:offset] || parts[:seconds]
TimeWithZone.new(time.utc, self)
else
TimeWithZone.new(nil, self, time)
diff --git a/activesupport/test/core_ext/date_and_time_behavior.rb b/activesupport/test/core_ext/date_and_time_behavior.rb
index bf83ac602f..6cc9781e02 100644
--- a/activesupport/test/core_ext/date_and_time_behavior.rb
+++ b/activesupport/test/core_ext/date_and_time_behavior.rb
@@ -126,7 +126,9 @@ module DateAndTimeBehavior
end
def test_next_week_at_same_time
- assert_equal date_time_init(2005,2,28,15,15,10), date_time_init(2005,2,22,15,15,10).next_week(:monday, same_time: true)
+ assert_equal date_time_init(2005,2,28,15,15,10), date_time_init(2005,2,22,15,15,10).next_week(:monday, same_time: true)
+ assert_equal date_time_init(2005,2,28,15,15,10,999999), date_time_init(2005,2,22,15,15,10,999999).next_week(:monday, same_time: true)
+ assert_equal date_time_init(2005,2,28,15,15,10,Rational(999999999, 1000)), date_time_init(2005,2,22,15,15,10,Rational(999999999, 1000)).next_week(:monday, same_time: true)
assert_equal date_time_init(2005,3,4,15,15,10), date_time_init(2005,2,22,15,15,10).next_week(:friday, same_time: true)
assert_equal date_time_init(2006,10,30,0,0,0), date_time_init(2006,10,23,0,0,0).next_week(:monday, same_time: true)
assert_equal date_time_init(2006,11,1,0,0,0), date_time_init(2006,10,23,0,0,0).next_week(:wednesday, same_time: true)
diff --git a/activesupport/test/executor_test.rb b/activesupport/test/executor_test.rb
index d409216206..03ec6020c3 100644
--- a/activesupport/test/executor_test.rb
+++ b/activesupport/test/executor_test.rb
@@ -192,6 +192,8 @@ class ExecutorTest < ActiveSupport::TestCase
end
def test_class_serial_is_unaffected
+ skip if !defined?(RubyVM)
+
hook = Class.new do
define_method(:run) do
nil
diff --git a/activesupport/test/time_zone_test.rb b/activesupport/test/time_zone_test.rb
index 76fee1fdd4..bd29584478 100644
--- a/activesupport/test/time_zone_test.rb
+++ b/activesupport/test/time_zone_test.rb
@@ -395,6 +395,24 @@ class TimeZoneTest < ActiveSupport::TestCase
end
end
+ def test_strptime_with_timestamp_seconds
+ with_env_tz "US/Eastern" do
+ zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
+ time_str = "1470272280"
+ time = zone.strptime(time_str, "%s")
+ assert_equal Time.at(1470272280), time
+ end
+ end
+
+ def test_strptime_with_timestamp_milliseconds
+ with_env_tz "US/Eastern" do
+ zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
+ time_str = "1470272280000"
+ time = zone.strptime(time_str, "%Q")
+ assert_equal Time.at(1470272280), time
+ end
+ end
+
def test_utc_offset_lazy_loaded_from_tzinfo_when_not_passed_in_to_initialize
tzinfo = TZInfo::Timezone.get("America/New_York")
zone = ActiveSupport::TimeZone.create(tzinfo.name, nil, tzinfo)
diff --git a/guides/rails_guides/markdown.rb b/guides/rails_guides/markdown.rb
index 33563d669c..009e5aff99 100644
--- a/guides/rails_guides/markdown.rb
+++ b/guides/rails_guides/markdown.rb
@@ -158,7 +158,7 @@ module RailsGuides
@view.content_for(:header_section) { @header }
@view.content_for(:page_title) { @title }
@view.content_for(:index_section) { @index }
- @view.render(layout: @layout, text: @body)
+ @view.render(layout: @layout, html: @body.html_safe)
end
end
end
diff --git a/guides/rails_guides/markdown/renderer.rb b/guides/rails_guides/markdown/renderer.rb
index f8e32fc498..eebd9ac730 100644
--- a/guides/rails_guides/markdown/renderer.rb
+++ b/guides/rails_guides/markdown/renderer.rb
@@ -52,7 +52,7 @@ HTML
"ruby; html-script: true"
when "html"
"xml" # HTML is understood, but there are .xml rules in the CSS
- else
+ else
"plain"
end
end
diff --git a/guides/source/5_0_release_notes.md b/guides/source/5_0_release_notes.md
index cc332cbf97..a98f7be067 100644
--- a/guides/source/5_0_release_notes.md
+++ b/guides/source/5_0_release_notes.md
@@ -417,7 +417,7 @@ Please refer to the [Changelog][action-pack] for detailed changes.
`ActionDispatch::IntegrationTest` instead.
([commit](https://github.com/rails/rails/commit/4414c5d1795e815b102571425974a8b1d46d932d))
-* Rails will only generate "weak", instead of strong ETags.
+* Rails generates weak ETags by default.
([Pull Request](https://github.com/rails/rails/pull/17573))
* Controller actions without an explicit `render` call and with no
@@ -453,6 +453,9 @@ Please refer to the [Changelog][action-pack] for detailed changes.
`ActionController::Live`.
([More details in this issue](https://github.com/rails/rails/issues/25581))
+* Introduce `Response#strong_etag=` and `#weak_etag=` and analogous
+ options for `fresh_when` and `stale?`.
+ ([Pull Request](https://github.com/rails/rails/pull/24387))
Action View
-------------
@@ -1000,7 +1003,8 @@ Please refer to the [Changelog][active-support] for detailed changes.
* Added `#on_weekend?`, `#on_weekday?`, `#next_weekday`, `#prev_weekday` methods to `Date`,
`Time`, and `DateTime`.
- ([Pull Request](https://github.com/rails/rails/pull/18335))
+ ([Pull Request](https://github.com/rails/rails/pull/18335),
+ [Pull Request](https://github.com/rails/rails/pull/23687))
* Added `same_time` option to `#next_week` and `#prev_week` for `Date`, `Time`,
and `DateTime`.
@@ -1051,9 +1055,6 @@ Please refer to the [Changelog][active-support] for detailed changes.
* Added `Array#second_to_last` and `Array#third_to_last` methods.
([Pull Request](https://github.com/rails/rails/pull/23583))
-* Added `#on_weekday?` method to `Date`, `Time`, and `DateTime`.
- ([Pull Request](https://github.com/rails/rails/pull/23687))
-
* Publish `ActiveSupport::Executor` and `ActiveSupport::Reloader` APIs to allow
components and libraries to manage, and participate in, the execution of
application code, and the application reloading process.
diff --git a/guides/source/layout.html.erb b/guides/source/layout.html.erb
index 943fd3fd7f..bb50761b30 100644
--- a/guides/source/layout.html.erb
+++ b/guides/source/layout.html.erb
@@ -88,7 +88,7 @@
<div id="container">
<div class="wrapper">
<div id="mainCol">
- <%= yield.html_safe %>
+ <%= yield %>
<h3>Feedback</h3>
<p>
diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md
index 2372590cec..dda2b12a3a 100644
--- a/guides/source/upgrading_ruby_on_rails.md
+++ b/guides/source/upgrading_ruby_on_rails.md
@@ -325,7 +325,7 @@ should support caching.
#### Configure the Output of `db:structure:dump`
-If you're using `schema_search_path` or other PostgreSQL extentions, you can control how the schema is
+If you're using `schema_search_path` or other PostgreSQL extensions, you can control how the schema is
dumped. Set to `:all` to generate all dumps, or to `:schema_search_path` to generate from schema search path.
config.active_record.dump_schemas = :all
diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb
index 9f9c50ca10..03573b274d 100644
--- a/railties/lib/rails/generators/rails/app/app_generator.rb
+++ b/railties/lib/rails/generators/rails/app/app_generator.rb
@@ -241,6 +241,7 @@ module Rails
end
def create_db_files
+ return if options[:skip_active_record]
build(:db)
end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults.rb.tt
index 5ad18cc5ad..3ad3eba98a 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults.rb.tt
@@ -32,7 +32,9 @@ ActiveSupport.halt_callback_chains_on_return_false = <%= options[:update] ? true
# Configure SSL options to enable HSTS with subdomains. Previous versions had false.
Rails.application.config.ssl_options = { hsts: { subdomains: true } }
<%- end -%>
+<%- unless options[:skip_sprockets] -%>
# Unknown asset fallback will return the path passed in when the given
# asset is not present in the asset pipeline.
Rails.application.config.assets.unknown_asset_fallback = <%= options[:update] ? true : false %>
+<%- end -%>
diff --git a/railties/lib/rails/test_unit/railtie.rb b/railties/lib/rails/test_unit/railtie.rb
index d0fc795515..746120e6a1 100644
--- a/railties/lib/rails/test_unit/railtie.rb
+++ b/railties/lib/rails/test_unit/railtie.rb
@@ -14,7 +14,9 @@ module Rails
end
initializer "test_unit.line_filtering" do
- ActiveSupport::TestCase.extend Rails::LineFiltering
+ ActiveSupport.on_load(:active_support_test_case) {
+ ActiveSupport::TestCase.extend Rails::LineFiltering
+ }
end
rake_tasks do
diff --git a/railties/test/application/runner_test.rb b/railties/test/application/runner_test.rb
index 77e7a2cca5..8769703f66 100644
--- a/railties/test/application/runner_test.rb
+++ b/railties/test/application/runner_test.rb
@@ -43,6 +43,15 @@ module ApplicationTests
assert_match "42", Dir.chdir(app_path) { `bin/rails runner "bin/count_users.rb"` }
end
+ def test_no_minitest_loaded_in_production_mode
+ app_file "bin/print_features.rb", <<-SCRIPT
+ p $LOADED_FEATURES.grep(/minitest/)
+ SCRIPT
+ assert_match "[]", Dir.chdir(app_path) {
+ `RAILS_ENV=production bin/rails runner "bin/print_features.rb"`
+ }
+ end
+
def test_should_set_dollar_0_to_file
app_file "bin/dollar0.rb", <<-SCRIPT
puts $0
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index 8f7fa1155f..9299b9ebea 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -356,6 +356,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
def test_generator_if_skip_active_record_is_given
run_generator [destination_root, "--skip-active-record"]
+ assert_no_directory "db/"
assert_no_file "config/database.yml"
assert_no_file "app/models/application_record.rb"
assert_file "config/application.rb", /#\s+require\s+["']active_record\/railtie["']/
@@ -411,6 +412,9 @@ class AppGeneratorTest < Rails::Generators::TestCase
assert_no_match(/config\.assets\.js_compressor = :uglifier/, content)
assert_no_match(/config\.assets\.css_compressor = :sass/, content)
end
+ assert_file "config/initializers/new_framework_defaults.rb" do |content|
+ assert_no_match(/unknown_asset_fallback/, content)
+ end
end
def test_generator_if_skip_action_cable_is_given