aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Gemfile12
-rw-r--r--Gemfile.lock28
-rw-r--r--actionpack/lib/action_dispatch/http/response.rb20
-rw-r--r--actionpack/lib/action_dispatch/middleware/cookies.rb4
-rw-r--r--actionpack/test/dispatch/cookies_test.rb167
-rw-r--r--actionpack/test/dispatch/response_test.rb71
-rw-r--r--actionpack/test/journey/router_test.rb11
-rw-r--r--actionview/lib/action_view/helpers/form_tag_helper.rb2
-rw-r--r--actionview/lib/action_view/layouts.rb2
-rw-r--r--actionview/test/template/form_tag_helper_test.rb34
-rw-r--r--activemodel/CHANGELOG.md6
-rw-r--r--activemodel/lib/active_model/forbidden_attributes_protection.rb5
-rw-r--r--activerecord/CHANGELOG.md15
-rw-r--r--activerecord/lib/active_record/attribute.rb54
-rw-r--r--activerecord/lib/active_record/attribute/user_provided_default.rb13
-rw-r--r--activerecord/lib/active_record/attribute_methods/dirty.rb35
-rw-r--r--activerecord/lib/active_record/attribute_mutation_tracker.rb30
-rw-r--r--activerecord/lib/active_record/connection_handling.rb2
-rw-r--r--activerecord/lib/active_record/fixture_set/file.rb23
-rw-r--r--activerecord/lib/active_record/fixtures.rb39
-rw-r--r--activerecord/lib/active_record/persistence.rb2
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb8
-rw-r--r--activerecord/test/cases/attribute_methods_test.rb5
-rw-r--r--activerecord/test/cases/attribute_test.rb39
-rw-r--r--activerecord/test/cases/calculations_test.rb32
-rw-r--r--activerecord/test/cases/fixture_set/file_test.rb12
-rw-r--r--activerecord/test/cases/fixtures_test.rb35
-rw-r--r--activerecord/test/cases/integration_test.rb4
-rw-r--r--activerecord/test/cases/relation/where_test.rb26
-rw-r--r--activerecord/test/fixtures/bad_posts.yml9
-rw-r--r--activerecord/test/fixtures/other_comments.yml6
-rw-r--r--activerecord/test/fixtures/other_posts.yml7
-rw-r--r--activerecord/test/models/author.rb3
-rw-r--r--activerecord/test/models/parrot.rb6
-rw-r--r--activerecord/test/models/person.rb1
-rw-r--r--activesupport/CHANGELOG.md9
-rw-r--r--activesupport/activesupport.gemspec2
-rw-r--r--activesupport/lib/active_support.rb4
-rw-r--r--activesupport/lib/active_support/callbacks.rb15
-rw-r--r--activesupport/lib/active_support/core_ext/date/calculations.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/hash/keys.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/module/attribute_accessors.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/time/zones.rb2
-rw-r--r--activesupport/lib/active_support/testing/file_fixtures.rb2
-rw-r--r--activesupport/lib/active_support/testing/isolation.rb18
-rw-r--r--activesupport/lib/active_support/time_with_zone.rb9
-rw-r--r--activesupport/test/callbacks_test.rb4
-rw-r--r--activesupport/test/core_ext/module/attribute_accessor_test.rb14
-rw-r--r--activesupport/test/core_ext/time_with_zone_test.rb4
-rw-r--r--guides/source/association_basics.md5
-rw-r--r--guides/source/configuring.md2
-rw-r--r--guides/source/upgrading_ruby_on_rails.md25
-rw-r--r--railties/CHANGELOG.md69
-rw-r--r--railties/lib/rails/application.rb22
-rw-r--r--railties/lib/rails/application/configuration.rb1
-rw-r--r--railties/lib/rails/configuration.rb19
-rw-r--r--railties/lib/rails/engine.rb34
-rw-r--r--railties/lib/rails/engine/configuration.rb9
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/assets/config/manifest.js.tt8
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/assets/manifest.js.tt8
-rw-r--r--railties/lib/rails/generators/rails/plugin/plugin_generator.rb4
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/rails/dummy_manifest.js6
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/rails/engine_manifest.js6
-rw-r--r--railties/lib/rails/test_unit/minitest_plugin.rb12
-rw-r--r--railties/lib/rails/test_unit/reporter.rb38
-rw-r--r--railties/test/application/asset_debugging_test.rb18
-rw-r--r--railties/test/application/assets_test.rb111
-rw-r--r--railties/test/application/configuration_test.rb232
-rw-r--r--railties/test/application/test_runner_test.rb30
-rw-r--r--railties/test/isolation/abstract_unit.rb32
-rw-r--r--railties/test/test_unit/reporter_test.rb61
71 files changed, 1141 insertions, 470 deletions
diff --git a/Gemfile b/Gemfile
index 8bb4c7d7a9..97406be742 100644
--- a/Gemfile
+++ b/Gemfile
@@ -5,6 +5,10 @@ gemspec
# We need a newish Rake since Active Job sets its test tasks' descriptions.
gem 'rake', '>= 10.3'
+# Active Support depends on a prerelease concurrent-ruby 1.0.0, so track
+# latest master as it approaches release.
+gem 'concurrent-ruby', '~> 1.0.0.pre3', github: 'ruby-concurrency/concurrent-ruby'
+
# Active Job depends on the URI::GID::MissingModelIDError, which isn't released yet.
gem 'globalid', github: 'rails/globalid', branch: 'master'
gem 'rack', github: 'rack/rack', branch: 'master'
@@ -21,8 +25,8 @@ gem 'turbolinks', github: 'rails/turbolinks', branch: 'master'
gem 'arel', github: 'rails/arel', branch: 'master'
gem 'mail', github: 'mikel/mail', branch: 'master'
-gem 'sprockets', github: 'rails/sprockets', branch: 'master'
-gem 'sprockets-rails', github: 'rails/sprockets-rails', branch: 'master'
+gem 'sprockets', '~> 4.0', github: 'rails/sprockets', branch: 'master'
+gem 'sprockets-rails', '~> 3.0.0.beta3', github: 'rails/sprockets-rails', branch: 'master'
gem 'sass-rails', github: 'rails/sass-rails', branch: 'master'
# require: false so bcrypt is loaded only when has_secure_password is used.
@@ -33,7 +37,9 @@ gem 'bcrypt', '~> 3.1.10', require: false
# This needs to be with require false to avoid
# it being automatically loaded by sprockets
gem 'uglifier', '>= 1.3.0', require: false
-gem 'sass', '>= 3.3', require: false
+
+# Track stable branch of sass because it doesn't have circular require warnings
+gem 'sass', github: 'sass/sass', branch: 'stable', require: false
group :doc do
gem 'sdoc', '~> 0.4.0'
diff --git a/Gemfile.lock b/Gemfile.lock
index 563a9f56be..7d6db0474e 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -73,10 +73,10 @@ GIT
GIT
remote: git://github.com/rails/sprockets-rails.git
- revision: 600f981fe79ff2d4179baf84bd18fac5acb58b5e
+ revision: 77098c5acd9f27613875097ce6587aff9d871d7f
branch: master
specs:
- sprockets-rails (3.0.0.beta2)
+ sprockets-rails (3.0.0.beta3)
actionpack (>= 4.0)
activesupport (>= 4.0)
sprockets (>= 3.0.0)
@@ -97,6 +97,19 @@ GIT
turbolinks (3.0.0)
coffee-rails
+GIT
+ remote: git://github.com/ruby-concurrency/concurrent-ruby.git
+ revision: 10ceb96c146d2937b66e7a444445e5ab9edbeb7c
+ specs:
+ concurrent-ruby (1.0.0.pre3)
+
+GIT
+ remote: git://github.com/sass/sass.git
+ revision: 4ef8e3167985ace91b2105916756bd93c5d7bba6
+ branch: stable
+ specs:
+ sass (3.4.18)
+
PATH
remote: .
specs:
@@ -130,7 +143,7 @@ PATH
activesupport (= 5.0.0.alpha)
arel (= 7.0.0.alpha)
activesupport (5.0.0.alpha)
- concurrent-ruby (~> 1.0.0.pre2, < 2.0.0)
+ concurrent-ruby (~> 1.0.0.pre3, < 2.0.0)
i18n (~> 0.7)
json (~> 1.7, >= 1.7.7)
method_source
@@ -179,7 +192,6 @@ GEM
coffee-script-source
execjs
coffee-script-source (1.9.1.1)
- concurrent-ruby (1.0.0.pre2)
connection_pool (2.2.0)
dalli (2.7.4)
dante (0.2.0)
@@ -251,7 +263,6 @@ GEM
resque (~> 1.25)
rufus-scheduler (~> 3.0)
rufus-scheduler (3.1.4)
- sass (3.4.18)
sdoc (0.4.1)
json (~> 1.7, >= 1.7.7)
rdoc (~> 4.0)
@@ -309,6 +320,7 @@ DEPENDENCIES
benchmark-ips
byebug
coffee-rails (~> 4.1.0)
+ concurrent-ruby (~> 1.0.0.pre3)!
dalli (>= 2.2.1)
delayed_job
delayed_job_active_record
@@ -336,14 +348,14 @@ DEPENDENCIES
redcarpet (~> 3.2.3)
resque
resque-scheduler
- sass (>= 3.3)
+ sass!
sass-rails!
sdoc (~> 0.4.0)
sequel
sidekiq
sneakers
- sprockets!
- sprockets-rails!
+ sprockets (~> 4.0)!
+ sprockets-rails (~> 3.0.0.beta3)!
sqlite3 (~> 1.3.6)
stackprof
sucker_punch
diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb
index 85d9c3be00..f6f63f1f32 100644
--- a/actionpack/lib/action_dispatch/http/response.rb
+++ b/actionpack/lib/action_dispatch/http/response.rb
@@ -161,6 +161,26 @@ module ActionDispatch # :nodoc:
def set_header(key, v); headers[key] = v; end
def delete_header(key); headers.delete key; end
+ # Add a header that may have multiple values.
+ #
+ # Example:
+ # response.add_header 'Vary', 'Accept'
+ # response.add_header 'Vary', 'Accept-Encoding'
+ # response.add_header 'Vary', 'Cookie'
+ #
+ # assert_equal 'Accept,Accept-Encoding,Cookie', response.get_header 'Vary'
+ #
+ # http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
+ def add_header(key, v)
+ if v.nil?
+ get_header key
+ elsif have_header? key
+ set_header key, "#{get_header key},#{v}"
+ else
+ set_header key, v
+ end
+ end
+
def await_commit
synchronize do
@cv.wait_until { @committed }
diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb
index 7ed77352ae..2889acaeb8 100644
--- a/actionpack/lib/action_dispatch/middleware/cookies.rb
+++ b/actionpack/lib/action_dispatch/middleware/cookies.rb
@@ -396,7 +396,9 @@ module ActionDispatch
end
def write(headers)
- headers[HTTP_HEADER] = make_set_cookie_header headers[HTTP_HEADER]
+ if header = make_set_cookie_header(headers[HTTP_HEADER])
+ headers[HTTP_HEADER] = header
+ end
end
mattr_accessor :always_write_cookie
diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb
index e9b2fe3214..84c244c72a 100644
--- a/actionpack/test/dispatch/cookies_test.rb
+++ b/actionpack/test/dispatch/cookies_test.rb
@@ -3,6 +3,75 @@ require 'openssl'
require 'active_support/key_generator'
require 'active_support/message_verifier'
+class CookieJarTest < ActiveSupport::TestCase
+ attr_reader :request
+
+ def setup
+ @request = ActionDispatch::Request.new({})
+ end
+
+ def test_fetch
+ x = Object.new
+ assert_not request.cookie_jar.key?('zzzzzz')
+ assert_equal x, request.cookie_jar.fetch('zzzzzz', x)
+ assert_not request.cookie_jar.key?('zzzzzz')
+ end
+
+ def test_fetch_exists
+ x = Object.new
+ request.cookie_jar['foo'] = 'bar'
+ assert_equal 'bar', request.cookie_jar.fetch('foo', x)
+ end
+
+ def test_fetch_block
+ x = Object.new
+ assert_not request.cookie_jar.key?('zzzzzz')
+ assert_equal x, request.cookie_jar.fetch('zzzzzz') { x }
+ end
+
+ def test_key_is_to_s
+ request.cookie_jar['foo'] = 'bar'
+ assert_equal 'bar', request.cookie_jar.fetch(:foo)
+ end
+
+ def test_fetch_type_error
+ assert_raises(KeyError) do
+ request.cookie_jar.fetch(:omglolwut)
+ end
+ end
+
+ def test_each
+ request.cookie_jar['foo'] = :bar
+ list = []
+ request.cookie_jar.each do |k,v|
+ list << [k, v]
+ end
+
+ assert_equal [['foo', :bar]], list
+ end
+
+ def test_enumerable
+ request.cookie_jar['foo'] = :bar
+ actual = request.cookie_jar.map { |k,v| [k.to_s, v.to_s] }
+ assert_equal [['foo', 'bar']], actual
+ end
+
+ def test_key_methods
+ assert !request.cookie_jar.key?(:foo)
+ assert !request.cookie_jar.has_key?("foo")
+
+ request.cookie_jar[:foo] = :bar
+ assert request.cookie_jar.key?(:foo)
+ assert request.cookie_jar.has_key?("foo")
+ end
+
+ def test_write_doesnt_set_a_nil_header
+ headers = {}
+ request.cookie_jar.write(headers)
+ assert !headers.include?('Set-Cookie')
+ end
+end
+
class CookiesTest < ActionController::TestCase
class CustomSerializer
def self.load(value)
@@ -14,16 +83,6 @@ class CookiesTest < ActionController::TestCase
end
end
- class JSONWrapper
- def initialize(obj)
- @obj = obj
- end
-
- def as_json(options = nil)
- "wrapped: #{@obj.as_json(options)}"
- end
- end
-
class TestController < ActionController::Base
def authenticate
cookies["user_name"] = "david"
@@ -88,11 +147,6 @@ class CookiesTest < ActionController::TestCase
head :ok
end
- def set_wrapped_signed_cookie
- cookies.signed[:user_id] = JSONWrapper.new(45)
- head :ok
- end
-
def get_signed_cookie
cookies.signed[:user_id]
head :ok
@@ -103,6 +157,21 @@ class CookiesTest < ActionController::TestCase
head :ok
end
+ class JSONWrapper
+ def initialize(obj)
+ @obj = obj
+ end
+
+ def as_json(options = nil)
+ "wrapped: #{@obj.as_json(options)}"
+ end
+ end
+
+ def set_wrapped_signed_cookie
+ cookies.signed[:user_id] = JSONWrapper.new(45)
+ head :ok
+ end
+
def set_wrapped_encrypted_cookie
cookies.encrypted[:foo] = JSONWrapper.new('bar')
head :ok
@@ -207,68 +276,18 @@ class CookiesTest < ActionController::TestCase
tests TestController
+ SALT = 'b3c631c314c0bbca50c1b2843150fe33'
+
def setup
super
- @request.env["action_dispatch.key_generator"] = ActiveSupport::KeyGenerator.new("b3c631c314c0bbca50c1b2843150fe33", iterations: 2)
- @request.env["action_dispatch.signed_cookie_salt"] = "b3c631c314c0bbca50c1b2843150fe33"
- @request.env["action_dispatch.encrypted_cookie_salt"] = "b3c631c314c0bbca50c1b2843150fe33"
- @request.env["action_dispatch.encrypted_signed_cookie_salt"] = "b3c631c314c0bbca50c1b2843150fe33"
- @request.host = "www.nextangle.com"
- end
-
- def test_fetch
- x = Object.new
- assert_not request.cookie_jar.key?('zzzzzz')
- assert_equal x, request.cookie_jar.fetch('zzzzzz', x)
- assert_not request.cookie_jar.key?('zzzzzz')
- end
-
- def test_fetch_exists
- x = Object.new
- request.cookie_jar['foo'] = 'bar'
- assert_equal 'bar', request.cookie_jar.fetch('foo', x)
- end
-
- def test_fetch_block
- x = Object.new
- assert_not request.cookie_jar.key?('zzzzzz')
- assert_equal x, request.cookie_jar.fetch('zzzzzz') { x }
- end
-
- def test_key_is_to_s
- request.cookie_jar['foo'] = 'bar'
- assert_equal 'bar', request.cookie_jar.fetch(:foo)
- end
-
- def test_fetch_type_error
- assert_raises(KeyError) do
- request.cookie_jar.fetch(:omglolwut)
- end
- end
-
- def test_each
- request.cookie_jar['foo'] = :bar
- list = []
- request.cookie_jar.each do |k,v|
- list << [k, v]
- end
- assert_equal [['foo', :bar]], list
- end
+ @request.env["action_dispatch.key_generator"] = ActiveSupport::KeyGenerator.new(SALT, iterations: 2)
- def test_enumerable
- request.cookie_jar['foo'] = :bar
- actual = request.cookie_jar.map { |k,v| [k.to_s, v.to_s] }
- assert_equal [['foo', 'bar']], actual
- end
+ @request.env["action_dispatch.signed_cookie_salt"] =
+ @request.env["action_dispatch.encrypted_cookie_salt"] =
+ @request.env["action_dispatch.encrypted_signed_cookie_salt"] = SALT
- def test_key_methods
- assert !request.cookie_jar.key?(:foo)
- assert !request.cookie_jar.has_key?("foo")
-
- request.cookie_jar[:foo] = :bar
- assert request.cookie_jar.key?(:foo)
- assert request.cookie_jar.has_key?("foo")
+ @request.host = "www.nextangle.com"
end
def test_setting_cookie
@@ -1083,11 +1102,11 @@ class CookiesTest < ActionController::TestCase
assert_equal "david", cookies[:user_name]
get :noop
- assert_nil @response.headers["Set-Cookie"]
+ assert !@response.headers.include?("Set-Cookie")
assert_equal "david", cookies[:user_name]
get :noop
- assert_nil @response.headers["Set-Cookie"]
+ assert !@response.headers.include?("Set-Cookie")
assert_equal "david", cookies[:user_name]
end
diff --git a/actionpack/test/dispatch/response_test.rb b/actionpack/test/dispatch/response_test.rb
index 85cdcda655..82cc21b17a 100644
--- a/actionpack/test/dispatch/response_test.rb
+++ b/actionpack/test/dispatch/response_test.rb
@@ -196,7 +196,17 @@ class ResponseTest < ActiveSupport::TestCase
assert_equal('application/xml; charset=utf-16', resp.headers['Content-Type'])
end
- test "read content type without charset" do
+ test "read content type with default charset utf-8" do
+ original = ActionDispatch::Response.default_charset
+ begin
+ resp = ActionDispatch::Response.new(200, { "Content-Type" => "text/xml" })
+ assert_equal('utf-8', resp.charset)
+ ensure
+ ActionDispatch::Response.default_charset = original
+ end
+ end
+
+ test "read content type with charset utf-16" do
jruby_skip "https://github.com/jruby/jruby/issues/3138"
original = ActionDispatch::Response.default_charset
@@ -283,6 +293,65 @@ class ResponseTest < ActiveSupport::TestCase
end
end
+class ResponseHeadersTest < ActiveSupport::TestCase
+ def setup
+ @response = ActionDispatch::Response.create
+ @response.set_header 'Foo', '1'
+ end
+
+ test 'have_header?' do
+ assert @response.have_header? 'Foo'
+ assert_not @response.have_header? 'foo'
+ assert_not @response.have_header? nil
+ end
+
+ test 'get_header' do
+ assert_equal '1', @response.get_header('Foo')
+ assert_nil @response.get_header('foo')
+ assert_nil @response.get_header(nil)
+ end
+
+ test 'set_header' do
+ assert_equal '2', @response.set_header('Foo', '2')
+ assert @response.have_header?('Foo')
+ assert_equal '2', @response.get_header('Foo')
+
+ assert_nil @response.set_header('Foo', nil)
+ assert @response.have_header?('Foo')
+ assert_nil @response.get_header('Foo')
+ end
+
+ test 'delete_header' do
+ assert_nil @response.delete_header(nil)
+
+ assert_nil @response.delete_header('foo')
+ assert @response.have_header?('Foo')
+
+ assert_equal '1', @response.delete_header('Foo')
+ assert_not @response.have_header?('Foo')
+ end
+
+ test 'add_header' do
+ # Add a value to an existing header
+ assert_equal '1,2', @response.add_header('Foo', '2')
+ assert_equal '1,2', @response.get_header('Foo')
+
+ # Add nil to an existing header
+ assert_equal '1,2', @response.add_header('Foo', nil)
+ assert_equal '1,2', @response.get_header('Foo')
+
+ # Add nil to a nonexistent header
+ assert_nil @response.add_header('Bar', nil)
+ assert_not @response.have_header?('Bar')
+ assert_nil @response.get_header('Bar')
+
+ # Add a value to a nonexistent header
+ assert_equal '1', @response.add_header('Bar', '1')
+ assert @response.have_header?('Bar')
+ assert_equal '1', @response.get_header('Bar')
+ end
+end
+
class ResponseIntegrationTest < ActionDispatch::IntegrationTest
test "response cache control from railsish app" do
@app = lambda { |env|
diff --git a/actionpack/test/journey/router_test.rb b/actionpack/test/journey/router_test.rb
index 6a3af32946..15d51e5d6c 100644
--- a/actionpack/test/journey/router_test.rb
+++ b/actionpack/test/journey/router_test.rb
@@ -497,17 +497,6 @@ module ActionDispatch
private
- def add_routes router, paths, anchor = true
- paths.each do |path|
- if String === path
- path = Path::Pattern.from_string path
- else
- path
- end
- add_route @app, path, {}, [], {}, {}
- end
- end
-
def rails_env env, klass = ActionDispatch::Request
klass.new(rack_env(env))
end
diff --git a/actionview/lib/action_view/helpers/form_tag_helper.rb b/actionview/lib/action_view/helpers/form_tag_helper.rb
index af684e05c6..0e8127e29e 100644
--- a/actionview/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionview/lib/action_view/helpers/form_tag_helper.rb
@@ -450,9 +450,9 @@ module ActionView
disable_with_text ||= value.clone
tag_options.deep_merge!("data" => { "disable_with" => disable_with_text })
else
- tag_options.delete("data-disable-with")
tag_options["data"].delete(:disable_with) if tag_options["data"]
end
+ tag_options.delete("data-disable-with")
end
tag :input, tag_options
diff --git a/actionview/lib/action_view/layouts.rb b/actionview/lib/action_view/layouts.rb
index cabba967c9..a74a5e05f3 100644
--- a/actionview/lib/action_view/layouts.rb
+++ b/actionview/lib/action_view/layouts.rb
@@ -401,7 +401,7 @@ module ActionView
# ==== Parameters
# * <tt>formats</tt> - The formats accepted to this layout
# * <tt>require_layout</tt> - If set to true and layout is not found,
- # an ArgumentError exception is raised (defaults to false)
+ # an +ArgumentError+ exception is raised (defaults to false)
#
# ==== Returns
# * <tt>template</tt> - The template object for the default layout (or nil)
diff --git a/actionview/test/template/form_tag_helper_test.rb b/actionview/test/template/form_tag_helper_test.rb
index a9d9562580..de1eb89dc5 100644
--- a/actionview/test/template/form_tag_helper_test.rb
+++ b/actionview/test/template/form_tag_helper_test.rb
@@ -64,6 +64,18 @@ class FormTagHelperTest < ActionView::TestCase
assert_dom_equal expected, actual
end
+ def test_check_box_tag_disabled
+ actual = check_box_tag "admin","1", false, disabled: true
+ expected = %(<input id="admin" disabled="disabled" name="admin" type="checkbox" value="1" />)
+ assert_dom_equal expected, actual
+ end
+
+ def test_check_box_tag_default_checked
+ actual = check_box_tag "admin","1", true
+ expected = %(<input id="admin" checked="checked" name="admin" type="checkbox" value="1" />)
+ assert_dom_equal expected, actual
+ end
+
def test_check_box_tag_id_sanitized
label_elem = root_elem(check_box_tag("project[2][admin]"))
assert_match VALID_HTML_ID, label_elem['id']
@@ -351,12 +363,18 @@ class FormTagHelperTest < ActionView::TestCase
assert_dom_equal expected, actual
end
- def test_text_field_disabled
+ def test_text_field_tag_disabled
actual = text_field_tag "title", "Hello!", disabled: true
expected = %(<input id="title" name="title" disabled="disabled" type="text" value="Hello!" />)
assert_dom_equal expected, actual
end
+ def test_text_field_tag_with_placeholder_option
+ actual = text_field_tag "title", "Hello!", placeholder: 'Enter search term...'
+ expected = %(<input id="title" name="title" placeholder="Enter search term..." type="text" value="Hello!" />)
+ assert_dom_equal expected, actual
+ end
+
def test_text_field_tag_with_multiple_options
actual = text_field_tag "title", "Hello!", :size => 70, :maxlength => 80
expected = %(<input id="title" name="title" size="70" maxlength="80" type="text" value="Hello!" />)
@@ -450,21 +468,21 @@ class FormTagHelperTest < ActionView::TestCase
ActionView::Base.automatically_disable_submit_tag = true
end
- def test_data_disable_with_string
+ def test_submit_tag_having_data_disable_with_string
assert_dom_equal(
%(<input data-disable-with="Processing..." data-confirm="Are you sure?" name='commit' type="submit" value="Save" />),
submit_tag("Save", { "data-disable-with" => "Processing...", "data-confirm" => "Are you sure?" })
)
end
- def test_data_disable_with_boolean
+ def test_submit_tag_having_data_disable_with_boolean
assert_dom_equal(
%(<input data-confirm="Are you sure?" name='commit' type="submit" value="Save" />),
submit_tag("Save", { "data-disable-with" => false, "data-confirm" => "Are you sure?" })
)
end
- def test_data_hash_disable_with_boolean
+ def test_submit_tag_having_data_hash_disable_with_boolean
assert_dom_equal(
%(<input data-confirm="Are you sure?" name='commit' type="submit" value="Save" />),
submit_tag("Save", { :data => { :confirm => "Are you sure?", :disable_with => false } })
@@ -485,6 +503,14 @@ class FormTagHelperTest < ActionView::TestCase
)
end
+ def test_submit_tag_doesnt_have_data_disable_with_twice
+ assert_equal(
+ %(<input type="submit" name="commit" value="Save" data-confirm="Are you sure?" data-disable-with="Processing..." />),
+ submit_tag("Save", { "data-disable-with" => "Processing...", "data-confirm" => "Are you sure?" })
+ )
+ end
+
+
def test_button_tag
assert_dom_equal(
%(<button name="button" type="submit">Button</button>),
diff --git a/activemodel/CHANGELOG.md b/activemodel/CHANGELOG.md
index bf0120122d..a3368cd197 100644
--- a/activemodel/CHANGELOG.md
+++ b/activemodel/CHANGELOG.md
@@ -119,10 +119,10 @@
The preferred method to halt a callback chain from now on is to explicitly
`throw(:abort)`.
- In the past, returning `false` in an ActiveModel or ActiveModel::Validations
- `before_` callback had the side effect of halting the callback chain.
+ In the past, returning `false` in an Active Model `before_` callback had
+ the side effect of halting the callback chain.
This is not recommended anymore and, depending on the value of the
- `config.active_support.halt_callback_chains_on_return_false` option, will
+ `ActiveSupport.halt_callback_chains_on_return_false` option, will
either not work at all or display a deprecation warning.
diff --git a/activemodel/lib/active_model/forbidden_attributes_protection.rb b/activemodel/lib/active_model/forbidden_attributes_protection.rb
index b4fa378601..d2c6a89cc2 100644
--- a/activemodel/lib/active_model/forbidden_attributes_protection.rb
+++ b/activemodel/lib/active_model/forbidden_attributes_protection.rb
@@ -17,8 +17,9 @@ module ActiveModel
module ForbiddenAttributesProtection # :nodoc:
protected
def sanitize_for_mass_assignment(attributes)
- if attributes.respond_to?(:permitted?) && !attributes.permitted?
- raise ActiveModel::ForbiddenAttributesError
+ if attributes.respond_to?(:permitted?)
+ raise ActiveModel::ForbiddenAttributesError if !attributes.permitted?
+ attributes.to_h
else
attributes
end
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 6c0722e43c..6a40d32ef9 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,16 @@
+* Allow fixtures files to set the model class in the YAML file itself.
+
+ To load the fixtures file `accounts.yml` as the `User` model, use:
+
+ _fixture:
+ model_class: User
+ david:
+ name: David
+
+ Fixes #9516.
+
+ *Roque Pinel*
+
* Don't require a database connection to load a class which uses acceptance
validations.
@@ -1104,7 +1117,7 @@
In the past, returning `false` in an Active Record `before_` callback had the
side effect of halting the callback chain.
This is not recommended anymore and, depending on the value of the
- `config.active_support.halt_callback_chains_on_return_false` option, will
+ `ActiveSupport.halt_callback_chains_on_return_false` option, will
either not work at all or display a deprecation warning.
*claudiob*
diff --git a/activerecord/lib/active_record/attribute.rb b/activerecord/lib/active_record/attribute.rb
index 74b44810d4..3c4c8f10ec 100644
--- a/activerecord/lib/active_record/attribute.rb
+++ b/activerecord/lib/active_record/attribute.rb
@@ -5,8 +5,8 @@ module ActiveRecord
FromDatabase.new(name, value, type)
end
- def from_user(name, value, type)
- FromUser.new(name, value, type)
+ def from_user(name, value, type, original_attribute = nil)
+ FromUser.new(name, value, type, original_attribute)
end
def with_cast_value(name, value, type)
@@ -26,10 +26,11 @@ module ActiveRecord
# This method should not be called directly.
# Use #from_database or #from_user
- def initialize(name, value_before_type_cast, type)
+ def initialize(name, value_before_type_cast, type, original_attribute = nil)
@name = name
@value_before_type_cast = value_before_type_cast
@type = type
+ @original_attribute = original_attribute
end
def value
@@ -38,21 +39,33 @@ module ActiveRecord
@value
end
+ def original_value
+ if assigned?
+ original_attribute.original_value
+ else
+ type_cast(value_before_type_cast)
+ end
+ end
+
def value_for_database
type.serialize(value)
end
- def changed_from?(old_value)
- type.changed?(old_value, value, value_before_type_cast)
+ def changed?
+ changed_from_assignment? || changed_in_place?
+ end
+
+ def changed_in_place?
+ has_been_read? && type.changed_in_place?(original_value_for_database, value)
end
- def changed_in_place_from?(old_value)
- has_been_read? && type.changed_in_place?(old_value, value)
+ def forgetting_assignment
+ with_value_from_database(value_for_database)
end
def with_value_from_user(value)
type.assert_valid_value(value)
- self.class.from_user(name, value, type)
+ self.class.from_user(name, value, type, self)
end
def with_value_from_database(value)
@@ -64,7 +77,7 @@ module ActiveRecord
end
def with_type(type)
- self.class.new(name, value_before_type_cast, type)
+ self.class.new(name, value_before_type_cast, type, original_attribute)
end
def type_cast(*)
@@ -97,16 +110,39 @@ module ActiveRecord
protected
+ attr_reader :original_attribute
+ alias_method :assigned?, :original_attribute
+
def initialize_dup(other)
if defined?(@value) && @value.duplicable?
@value = @value.dup
end
end
+ def changed_from_assignment?
+ assigned? && type.changed?(original_value, value, value_before_type_cast)
+ end
+
+ def original_value_for_database
+ if assigned?
+ original_attribute.original_value_for_database
+ else
+ _original_value_for_database
+ end
+ end
+
+ def _original_value_for_database
+ value_for_database
+ end
+
class FromDatabase < Attribute # :nodoc:
def type_cast(value)
type.deserialize(value)
end
+
+ def _original_value_for_database
+ value_before_type_cast
+ end
end
class FromUser < Attribute # :nodoc:
diff --git a/activerecord/lib/active_record/attribute/user_provided_default.rb b/activerecord/lib/active_record/attribute/user_provided_default.rb
index 501590cf0e..fb8ad9163e 100644
--- a/activerecord/lib/active_record/attribute/user_provided_default.rb
+++ b/activerecord/lib/active_record/attribute/user_provided_default.rb
@@ -4,8 +4,7 @@ module ActiveRecord
class Attribute # :nodoc:
class UserProvidedDefault < FromUser
def initialize(name, value, type, database_default)
- super(name, value, type)
- @database_default = database_default
+ super(name, value, type, database_default)
end
def type_cast(value)
@@ -16,17 +15,9 @@ module ActiveRecord
end
end
- def changed_from?(old_value)
- super || changed_from?(database_default.value)
- end
-
def with_type(type)
- self.class.new(name, value_before_type_cast, type, database_default)
+ self.class.new(name, value_before_type_cast, type, original_attribute)
end
-
- protected
-
- attr_reader :database_default
end
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb
index 43c15841c2..e8a782ed13 100644
--- a/activerecord/lib/active_record/attribute_methods/dirty.rb
+++ b/activerecord/lib/active_record/attribute_methods/dirty.rb
@@ -35,23 +35,22 @@ module ActiveRecord
# <tt>reload</tt> the record and clears changed attributes.
def reload(*)
super.tap do
- clear_changes_information
+ @mutation_tracker = nil
+ @previous_mutation_tracker = nil
+ @changed_attributes = HashWithIndifferentAccess.new
end
end
- def init_internals
- super
- @mutation_tracker = AttributeMutationTracker.new(@attributes, @attributes.dup)
- end
-
def initialize_dup(other) # :nodoc:
super
- @mutation_tracker = AttributeMutationTracker.new(@attributes,
- self.class._default_attributes.deep_dup)
+ @attributes = self.class._default_attributes.map do |attr|
+ attr.with_value_from_user(@attributes.fetch_value(attr.name))
+ end
+ @mutation_tracker = nil
end
def changes_applied
- @previous_mutation_tracker = @mutation_tracker
+ @previous_mutation_tracker = mutation_tracker
@changed_attributes = HashWithIndifferentAccess.new
store_original_attributes
end
@@ -81,7 +80,7 @@ module ActiveRecord
if defined?(@cached_changed_attributes)
@cached_changed_attributes
else
- super.reverse_merge(@mutation_tracker.changed_values).freeze
+ super.reverse_merge(mutation_tracker.changed_values).freeze
end
end
@@ -96,17 +95,24 @@ module ActiveRecord
end
def attribute_changed_in_place?(attr_name)
- @mutation_tracker.changed_in_place?(attr_name)
+ mutation_tracker.changed_in_place?(attr_name)
end
private
+ def mutation_tracker
+ unless defined?(@mutation_tracker)
+ @mutation_tracker = nil
+ end
+ @mutation_tracker ||= AttributeMutationTracker.new(@attributes)
+ end
+
def changes_include?(attr_name)
- super || @mutation_tracker.changed?(attr_name)
+ super || mutation_tracker.changed?(attr_name)
end
def clear_attribute_change(attr_name)
- @mutation_tracker.forget_change(attr_name)
+ mutation_tracker.forget_change(attr_name)
end
def _update_record(*)
@@ -122,7 +128,8 @@ module ActiveRecord
end
def store_original_attributes
- @mutation_tracker = @mutation_tracker.now_tracking(@attributes)
+ @attributes = @attributes.map(&:forgetting_assignment)
+ @mutation_tracker = nil
end
def previous_mutation_tracker
diff --git a/activerecord/lib/active_record/attribute_mutation_tracker.rb b/activerecord/lib/active_record/attribute_mutation_tracker.rb
index 08eb8ba7ac..ba7348918b 100644
--- a/activerecord/lib/active_record/attribute_mutation_tracker.rb
+++ b/activerecord/lib/active_record/attribute_mutation_tracker.rb
@@ -1,14 +1,13 @@
module ActiveRecord
class AttributeMutationTracker # :nodoc:
- def initialize(attributes, original_attributes)
+ def initialize(attributes)
@attributes = attributes
- @original_attributes = original_attributes
end
def changed_values
attr_names.each_with_object({}.with_indifferent_access) do |attr_name, result|
if changed?(attr_name)
- result[attr_name] = original_attributes.fetch_value(attr_name)
+ result[attr_name] = attributes[attr_name].original_value
end
end
end
@@ -16,49 +15,34 @@ module ActiveRecord
def changes
attr_names.each_with_object({}.with_indifferent_access) do |attr_name, result|
if changed?(attr_name)
- result[attr_name] = [original_attributes.fetch_value(attr_name), attributes.fetch_value(attr_name)]
+ result[attr_name] = [attributes[attr_name].original_value, attributes.fetch_value(attr_name)]
end
end
end
def changed?(attr_name)
attr_name = attr_name.to_s
- modified?(attr_name) || changed_in_place?(attr_name)
+ attributes[attr_name].changed?
end
def changed_in_place?(attr_name)
- original_database_value = original_attributes[attr_name].value_before_type_cast
- attributes[attr_name].changed_in_place_from?(original_database_value)
+ attributes[attr_name].changed_in_place?
end
def forget_change(attr_name)
attr_name = attr_name.to_s
- original_attributes[attr_name] = attributes[attr_name].dup
- end
-
- def now_tracking(new_attributes)
- AttributeMutationTracker.new(new_attributes, clean_copy_of(new_attributes))
+ attributes[attr_name] = attributes[attr_name].forgetting_assignment
end
protected
- attr_reader :attributes, :original_attributes
+ attr_reader :attributes
private
def attr_names
attributes.keys
end
-
- def modified?(attr_name)
- attributes[attr_name].changed_from?(original_attributes.fetch_value(attr_name))
- end
-
- def clean_copy_of(attributes)
- attributes.map do |attr|
- attr.with_value_from_database(attr.value_for_database)
- end
- end
end
class NullMutationTracker # :nodoc:
diff --git a/activerecord/lib/active_record/connection_handling.rb b/activerecord/lib/active_record/connection_handling.rb
index d6b661ff76..2fc5e410f9 100644
--- a/activerecord/lib/active_record/connection_handling.rb
+++ b/activerecord/lib/active_record/connection_handling.rb
@@ -42,7 +42,7 @@ module ActiveRecord
#
# ActiveRecord::Base.establish_connection(:production)
#
- # The exceptions AdapterNotSpecified, AdapterNotFound and ArgumentError
+ # The exceptions +AdapterNotSpecified+, +AdapterNotFound+ and +ArgumentError+
# may be returned on an error.
def establish_connection(spec = nil)
spec ||= DEFAULT_ENV.call.to_sym
diff --git a/activerecord/lib/active_record/fixture_set/file.rb b/activerecord/lib/active_record/fixture_set/file.rb
index 8132310c91..f969556c50 100644
--- a/activerecord/lib/active_record/fixture_set/file.rb
+++ b/activerecord/lib/active_record/fixture_set/file.rb
@@ -17,24 +17,39 @@ module ActiveRecord
def initialize(file)
@file = file
- @rows = nil
end
def each(&block)
rows.each(&block)
end
+ def model_class
+ config_row['model_class']
+ end
private
def rows
- return @rows if @rows
+ @rows ||= raw_rows.reject { |fixture_name, _| fixture_name == '_fixture' }
+ end
+
+ def config_row
+ @config_row ||= begin
+ row = raw_rows.find { |fixture_name, _| fixture_name == '_fixture' }
+ if row
+ row.last
+ else
+ {'model_class': nil}
+ end
+ end
+ end
- begin
+ def raw_rows
+ @raw_rows ||= begin
data = YAML.load(render(IO.read(@file)))
+ data ? validate(data).to_a : []
rescue ArgumentError, Psych::SyntaxError => error
raise Fixture::FormatError, "a YAML error occurred parsing #{@file}. Please note that YAML must be consistently indented using spaces. Tabs are not allowed. Please have a look at http://www.yaml.org/faq.html\nThe exact error was:\n #{error.class}: #{error}", error.backtrace
end
- @rows = data ? validate(data).to_a : []
end
def render(content)
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index f1dc56df63..59df3c78f3 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -395,6 +395,20 @@ module ActiveRecord
# <<: *DEFAULTS
#
# Any fixture labeled "DEFAULTS" is safely ignored.
+ #
+ # == Configure the fixture model class
+ #
+ # It's possible to set the fixture's model class directly in the YAML file.
+ # This is helpful when fixtures are loaded outside tests and
+ # +set_fixture_class+ is not available (e.g.
+ # when running <tt>rake db:fixtures:load</tt>).
+ #
+ # _fixture:
+ # model_class: User
+ # david:
+ # name: David
+ #
+ # Any fixtures labeled "_fixture" are safely ignored.
class FixtureSet
#--
# An instance of FixtureSet is normally stored in a single YAML file and
@@ -578,21 +592,16 @@ module ActiveRecord
@name = name
@path = path
@config = config
- @model_class = nil
- if class_name.is_a?(Class) # TODO: Should be an AR::Base type class, or any?
- @model_class = class_name
- else
- @model_class = class_name.safe_constantize if class_name
- end
+ self.model_class = class_name
+
+ @fixtures = read_fixture_files(path)
@connection = connection
@table_name = ( model_class.respond_to?(:table_name) ?
model_class.table_name :
self.class.default_fixture_table_name(name, config) )
-
- @fixtures = read_fixture_files path, @model_class
end
def [](x)
@@ -761,13 +770,25 @@ module ActiveRecord
@column_names ||= @connection.columns(@table_name).collect(&:name)
end
- def read_fixture_files(path, model_class)
+ def model_class=(class_name)
+ if class_name.is_a?(Class) # TODO: Should be an AR::Base type class, or any?
+ @model_class = class_name
+ else
+ @model_class = class_name.safe_constantize if class_name
+ end
+ end
+
+ # Loads the fixtures from the YAML file at +path+.
+ # If the file sets the +model_class+ and current instance value is not set,
+ # it uses the file value.
+ def read_fixture_files(path)
yaml_files = Dir["#{path}/{**,*}/*.yml"].select { |f|
::File.file?(f)
} + [yaml_file_path(path)]
yaml_files.each_with_object({}) do |file, fixtures|
FixtureSet::File.open(file) do |fh|
+ self.model_class ||= fh.model_class if fh.model_class
fh.each do |fixture_name, row|
fixtures[fixture_name] = ActiveRecord::Fixture.new(row, model_class)
end
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index 7b53f6e5a0..3f02f73a5a 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -211,7 +211,7 @@ module ActiveRecord
def becomes(klass)
became = klass.new
became.instance_variable_set("@attributes", @attributes)
- became.instance_variable_set("@mutation_tracker", @mutation_tracker)
+ became.instance_variable_set("@mutation_tracker", @mutation_tracker) if defined?(@mutation_tracker)
became.instance_variable_set("@changed_attributes", attributes_changed_by_setter)
became.instance_variable_set("@new_record", new_record?)
became.instance_variable_set("@destroyed", destroyed?)
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index eb53a18f0f..ccb0ab18ae 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -558,11 +558,8 @@ module ActiveRecord
end
def where!(opts, *rest) # :nodoc:
- if Hash === opts
- opts = sanitize_forbidden_attributes(opts)
- references!(PredicateBuilder.references(opts))
- end
-
+ opts = sanitize_forbidden_attributes(opts)
+ references!(PredicateBuilder.references(opts)) if Hash === opts
self.where_clause += where_clause_factory.build(opts, rest)
self
end
@@ -619,6 +616,7 @@ module ActiveRecord
end
def having!(opts, *rest) # :nodoc:
+ opts = sanitize_forbidden_attributes(opts)
references!(PredicateBuilder.references(opts)) if Hash === opts
self.having_clause += having_clause_factory.build(opts, rest)
diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb
index b67201d94d..52d197718e 100644
--- a/activerecord/test/cases/attribute_methods_test.rb
+++ b/activerecord/test/cases/attribute_methods_test.rb
@@ -177,7 +177,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
if current_adapter?(:MysqlAdapter, :Mysql2Adapter)
def test_read_attributes_before_type_cast_on_boolean
- bool = Boolean.create({ "value" => false })
+ bool = Boolean.create!({ "value" => false })
if RUBY_PLATFORM =~ /java/
# JRuby will return the value before typecast as string
assert_equal "0", bool.reload.attributes_before_type_cast["value"]
@@ -542,9 +542,6 @@ class AttributeMethodsTest < ActiveRecord::TestCase
developer.save!
- assert_equal "50000", developer.salary_before_type_cast
- assert_equal 1337, developer.name_before_type_cast
-
assert_equal 50000, developer.salary
assert_equal "1337", developer.name
end
diff --git a/activerecord/test/cases/attribute_test.rb b/activerecord/test/cases/attribute_test.rb
index 0f216b7a13..a24a4fc6a4 100644
--- a/activerecord/test/cases/attribute_test.rb
+++ b/activerecord/test/cases/attribute_test.rb
@@ -182,12 +182,49 @@ module ActiveRecord
assert attribute.has_been_read?
end
+ test "an attribute is not changed if it hasn't been assigned or mutated" do
+ attribute = Attribute.from_database(:foo, 1, Type::Value.new)
+
+ refute attribute.changed?
+ end
+
+ test "an attribute is changed if it's been assigned a new value" do
+ attribute = Attribute.from_database(:foo, 1, Type::Value.new)
+ changed = attribute.with_value_from_user(2)
+
+ assert changed.changed?
+ end
+
+ test "an attribute is not changed if it's assigned the same value" do
+ attribute = Attribute.from_database(:foo, 1, Type::Value.new)
+ unchanged = attribute.with_value_from_user(1)
+
+ refute unchanged.changed?
+ end
+
test "an attribute can not be mutated if it has not been read,
and skips expensive calculations" do
type_which_raises_from_all_methods = Object.new
attribute = Attribute.from_database(:foo, "bar", type_which_raises_from_all_methods)
- assert_not attribute.changed_in_place_from?("bar")
+ assert_not attribute.changed_in_place?
+ end
+
+ test "an attribute is changed if it has been mutated" do
+ attribute = Attribute.from_database(:foo, "bar", Type::String.new)
+ attribute.value << "!"
+
+ assert attribute.changed_in_place?
+ assert attribute.changed?
+ end
+
+ test "an attribute can forget its changes" do
+ attribute = Attribute.from_database(:foo, "bar", Type::String.new)
+ changed = attribute.with_value_from_user("foo")
+ forgotten = changed.forgetting_assignment
+
+ assert changed.changed? # sanity check
+ refute forgotten.changed?
end
test "with_value_from_user validates the value" do
diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb
index aa10817527..d904b802fa 100644
--- a/activerecord/test/cases/calculations_test.rb
+++ b/activerecord/test/cases/calculations_test.rb
@@ -681,4 +681,36 @@ class CalculationsTest < ActiveRecord::TestCase
end
assert block_called
end
+
+ def test_having_with_strong_parameters
+ protected_params = Class.new do
+ attr_reader :permitted
+ alias :permitted? :permitted
+
+ def initialize(parameters)
+ @parameters = parameters
+ @permitted = false
+ end
+
+ def to_h
+ @parameters
+ end
+
+ def permit!
+ @permitted = true
+ self
+ end
+ end
+
+ params = protected_params.new(credit_limit: '50')
+
+ assert_raises(ActiveModel::ForbiddenAttributesError) do
+ Account.group(:id).having(params)
+ end
+
+ result = Account.group(:id).having(params.permit!)
+ assert_equal 50, result[0].credit_limit
+ assert_equal 50, result[1].credit_limit
+ assert_equal 50, result[2].credit_limit
+ end
end
diff --git a/activerecord/test/cases/fixture_set/file_test.rb b/activerecord/test/cases/fixture_set/file_test.rb
index 92efa8aca7..242e7a9bec 100644
--- a/activerecord/test/cases/fixture_set/file_test.rb
+++ b/activerecord/test/cases/fixture_set/file_test.rb
@@ -123,6 +123,18 @@ END
end
end
+ def test_removes_fixture_config_row
+ File.open(::File.join(FIXTURES_ROOT, 'other_posts.yml')) do |fh|
+ assert_equal(['second_welcome'], fh.each.map { |name, _| name })
+ end
+ end
+
+ def test_extracts_model_class_from_config_row
+ File.open(::File.join(FIXTURES_ROOT, 'other_posts.yml')) do |fh|
+ assert_equal 'Post', fh.model_class
+ end
+ end
+
private
def tmp_yaml(name, contents)
t = Tempfile.new name
diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb
index e3c4bd9f4a..a0eaa66e94 100644
--- a/activerecord/test/cases/fixtures_test.rb
+++ b/activerecord/test/cases/fixtures_test.rb
@@ -7,15 +7,16 @@ require 'models/binary'
require 'models/book'
require 'models/bulb'
require 'models/category'
+require 'models/comment'
require 'models/company'
require 'models/computer'
require 'models/course'
require 'models/developer'
+require 'models/doubloon'
require 'models/joke'
require 'models/matey'
require 'models/parrot'
require 'models/pirate'
-require 'models/doubloon'
require 'models/post'
require 'models/randomly_named_c1'
require 'models/reply'
@@ -516,6 +517,38 @@ class OverRideFixtureMethodTest < ActiveRecord::TestCase
end
end
+class FixtureWithSetModelClassTest < ActiveRecord::TestCase
+ fixtures :other_posts, :other_comments
+
+ # Set to false to blow away fixtures cache and ensure our fixtures are loaded
+ # and thus takes into account the +set_model_class+.
+ self.use_transactional_tests = false
+
+ def test_uses_fixture_class_defined_in_yaml
+ assert_kind_of Post, other_posts(:second_welcome)
+ end
+
+ def test_loads_the_associations_to_fixtures_with_set_model_class
+ post = other_posts(:second_welcome)
+ comment = other_comments(:second_greetings)
+ assert_equal [comment], post.comments
+ assert_equal post, comment.post
+ end
+end
+
+class SetFixtureClassPrevailsTest < ActiveRecord::TestCase
+ set_fixture_class bad_posts: Post
+ fixtures :bad_posts
+
+ # Set to false to blow away fixtures cache and ensure our fixtures are loaded
+ # and thus takes into account the +set_model_class+.
+ self.use_transactional_tests = false
+
+ def test_uses_set_fixture_class
+ assert_kind_of Post, bad_posts(:bad_welcome)
+ end
+end
+
class CheckSetTableNameFixturesTest < ActiveRecord::TestCase
set_fixture_class :funny_jokes => Joke
fixtures :funny_jokes
diff --git a/activerecord/test/cases/integration_test.rb b/activerecord/test/cases/integration_test.rb
index 80d17b8114..9169207b0a 100644
--- a/activerecord/test/cases/integration_test.rb
+++ b/activerecord/test/cases/integration_test.rb
@@ -96,7 +96,9 @@ class IntegrationTest < ActiveRecord::TestCase
owner.update_column :updated_at, Time.current
key = owner.cache_key
- assert pet.touch
+ travel(1.second) do
+ assert pet.touch
+ end
assert_not_equal key, owner.reload.cache_key
end
diff --git a/activerecord/test/cases/relation/where_test.rb b/activerecord/test/cases/relation/where_test.rb
index 6af31017d6..c3a1471205 100644
--- a/activerecord/test/cases/relation/where_test.rb
+++ b/activerecord/test/cases/relation/where_test.rb
@@ -276,5 +276,31 @@ module ActiveRecord
assert_equal essays(:david_modest_proposal), essay
end
+
+ def test_where_with_strong_parameters
+ protected_params = Class.new do
+ attr_reader :permitted
+ alias :permitted? :permitted
+
+ def initialize(parameters)
+ @parameters = parameters
+ @permitted = false
+ end
+
+ def to_h
+ @parameters
+ end
+
+ def permit!
+ @permitted = true
+ self
+ end
+ end
+
+ author = authors(:david)
+ params = protected_params.new(name: author.name)
+ assert_raises(ActiveModel::ForbiddenAttributesError) { Author.where(params) }
+ assert_equal author, Author.where(params.permit!).first
+ end
end
end
diff --git a/activerecord/test/fixtures/bad_posts.yml b/activerecord/test/fixtures/bad_posts.yml
new file mode 100644
index 0000000000..addee8e3bf
--- /dev/null
+++ b/activerecord/test/fixtures/bad_posts.yml
@@ -0,0 +1,9 @@
+# Please do not use this fixture without `set_fixture_class` as Post
+
+_fixture:
+ model_class: BadPostModel
+
+bad_welcome:
+ author_id: 1
+ title: Welcome to the another weblog
+ body: It's really nice today
diff --git a/activerecord/test/fixtures/other_comments.yml b/activerecord/test/fixtures/other_comments.yml
new file mode 100644
index 0000000000..55e8216ec7
--- /dev/null
+++ b/activerecord/test/fixtures/other_comments.yml
@@ -0,0 +1,6 @@
+_fixture:
+ model_class: Comment
+
+second_greetings:
+ post: second_welcome
+ body: Thank you for the second welcome
diff --git a/activerecord/test/fixtures/other_posts.yml b/activerecord/test/fixtures/other_posts.yml
new file mode 100644
index 0000000000..39ff763547
--- /dev/null
+++ b/activerecord/test/fixtures/other_posts.yml
@@ -0,0 +1,7 @@
+_fixture:
+ model_class: Post
+
+second_welcome:
+ author_id: 1
+ title: Welcome to the another weblog
+ body: It's really nice today
diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb
index 8c1f14bd36..0d90cbb110 100644
--- a/activerecord/test/models/author.rb
+++ b/activerecord/test/models/author.rb
@@ -144,9 +144,6 @@ class Author < ActiveRecord::Base
has_many :posts_with_signature, ->(record) { where("posts.title LIKE ?", "%by #{record.name.downcase}%") }, class_name: "Post"
- scope :relation_include_posts, -> { includes(:posts) }
- scope :relation_include_tags, -> { includes(:tags) }
-
attr_accessor :post_log
after_initialize :set_post_log
diff --git a/activerecord/test/models/parrot.rb b/activerecord/test/models/parrot.rb
index b26035d944..ddc9dcaf29 100644
--- a/activerecord/test/models/parrot.rb
+++ b/activerecord/test/models/parrot.rb
@@ -21,9 +21,3 @@ end
class DeadParrot < Parrot
belongs_to :killer, :class_name => 'Pirate', foreign_key: :killer_id
end
-
-class FunkyParrot < Parrot
- before_destroy do
- raise "before_destroy was called"
- end
-end
diff --git a/activerecord/test/models/person.rb b/activerecord/test/models/person.rb
index ad12f00d42..a4a9c6b0d4 100644
--- a/activerecord/test/models/person.rb
+++ b/activerecord/test/models/person.rb
@@ -37,7 +37,6 @@ class Person < ActiveRecord::Base
has_many :essays, primary_key: "first_name", foreign_key: "writer_id"
scope :males, -> { where(:gender => 'M') }
- scope :females, -> { where(:gender => 'F') }
end
class PersonWithDependentDestroyJobs < ActiveRecord::Base
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index a39344e464..19588d622c 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -291,18 +291,15 @@
In the past, callbacks could only be halted by explicitly providing a
terminator and by having a callback match the conditions of the terminator.
-* Add `Callbacks::CallbackChain.halt_and_display_warning_on_return_false`
+* Add `ActiveSupport.halt_callback_chains_on_return_false`
- Setting `Callbacks::CallbackChain.halt_and_display_warning_on_return_false`
+ Setting `ActiveSupport.halt_callback_chains_on_return_false`
to `true` will let an app support the deprecated way of halting Active Record,
- Active Model and Active Model validations callback chains by returning `false`.
+ and Active Model callback chains by returning `false`.
Setting the value to `false` will tell the app to ignore any `false` value
returned by those callbacks, and only halt the chain upon `throw(:abort)`.
- The value can also be set with the Rails configuration option
- `config.active_support.halt_callback_chains_on_return_false`.
-
When the configuration option is missing, its value is `true`, so older apps
ported to Rails 5.0 will not break (but display a deprecation warning).
For new Rails 5.0 apps, its value is set to `false` in an initializer, so
diff --git a/activesupport/activesupport.gemspec b/activesupport/activesupport.gemspec
index d236dbd200..93878518d7 100644
--- a/activesupport/activesupport.gemspec
+++ b/activesupport/activesupport.gemspec
@@ -24,6 +24,6 @@ Gem::Specification.new do |s|
s.add_dependency 'json', '~> 1.7', '>= 1.7.7'
s.add_dependency 'tzinfo', '~> 1.1'
s.add_dependency 'minitest', '~> 5.1'
- s.add_dependency 'concurrent-ruby', '~> 1.0.0.pre2', '< 2.0.0'
+ s.add_dependency 'concurrent-ruby', '~> 1.0.0.pre3', '< 2.0.0'
s.add_dependency 'method_source'
end
diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb
index 588d6c49f9..63277a65b4 100644
--- a/activesupport/lib/active_support.rb
+++ b/activesupport/lib/active_support.rb
@@ -75,11 +75,11 @@ module ActiveSupport
cattr_accessor :test_order # :nodoc:
def self.halt_callback_chains_on_return_false
- Callbacks::CallbackChain.halt_and_display_warning_on_return_false
+ Callbacks.halt_and_display_warning_on_return_false
end
def self.halt_callback_chains_on_return_false=(value)
- Callbacks::CallbackChain.halt_and_display_warning_on_return_false = value
+ Callbacks.halt_and_display_warning_on_return_false = value
end
end
diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb
index 3db9ea2ac0..252374e817 100644
--- a/activesupport/lib/active_support/callbacks.rb
+++ b/activesupport/lib/active_support/callbacks.rb
@@ -4,6 +4,7 @@ require 'active_support/core_ext/array/extract_options'
require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/kernel/reporting'
require 'active_support/core_ext/kernel/singleton_class'
+require 'active_support/core_ext/module/attribute_accessors'
require 'active_support/core_ext/string/filters'
require 'active_support/deprecation'
require 'thread'
@@ -66,6 +67,12 @@ module ActiveSupport
CALLBACK_FILTER_TYPES = [:before, :after, :around]
+ # If true, Active Record and Active Model callbacks returning +false+ will
+ # halt the entire callback chain and display a deprecation message.
+ # If false, callback chains will only be halted by calling +throw :abort+.
+ # Defaults to +true+.
+ mattr_accessor(:halt_and_display_warning_on_return_false) { true }
+
# Runs the callbacks for the given event.
#
# Calls the before and around callbacks in the order they were set, yields
@@ -450,12 +457,6 @@ module ActiveSupport
attr_reader :name, :config
- # If true, any callback returning +false+ will halt the entire callback
- # chain and display a deprecation message. If false, callback chains will
- # only be halted by calling +throw :abort+. Defaults to +true+.
- class_attribute :halt_and_display_warning_on_return_false
- self.halt_and_display_warning_on_return_false = true
-
def initialize(name, config)
@name = name
@config = {
@@ -760,7 +761,7 @@ module ActiveSupport
terminate = true
catch(:abort) do
result = result_lambda.call if result_lambda.is_a?(Proc)
- if CallbackChain.halt_and_display_warning_on_return_false && result == false
+ if Callbacks.halt_and_display_warning_on_return_false && result == false
display_deprecation_warning_for_false_terminator
else
terminate = false
diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb
index c60e833441..d589b67bf7 100644
--- a/activesupport/lib/active_support/core_ext/date/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/date/calculations.rb
@@ -26,7 +26,7 @@ class Date
Thread.current[:beginning_of_week] = find_beginning_of_week!(week_start)
end
- # Returns week start day symbol (e.g. :monday), or raises an ArgumentError for invalid day symbol.
+ # Returns week start day symbol (e.g. :monday), or raises an +ArgumentError+ for invalid day symbol.
def find_beginning_of_week!(week_start)
raise ArgumentError, "Invalid beginning of week: #{week_start}" unless ::Date::DAYS_INTO_WEEK.key?(week_start)
week_start
diff --git a/activesupport/lib/active_support/core_ext/hash/keys.rb b/activesupport/lib/active_support/core_ext/hash/keys.rb
index 05679f1dd0..07a282e8b6 100644
--- a/activesupport/lib/active_support/core_ext/hash/keys.rb
+++ b/activesupport/lib/active_support/core_ext/hash/keys.rb
@@ -64,7 +64,7 @@ class Hash
alias_method :to_options!, :symbolize_keys!
# Validates all keys in a hash match <tt>*valid_keys</tt>, raising
- # ArgumentError on a mismatch.
+ # +ArgumentError+ on a mismatch.
#
# Note that keys are treated differently than HashWithIndifferentAccess,
# meaning that string and symbol keys will not match.
diff --git a/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb b/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb
index a084177b9f..bf175a8a70 100644
--- a/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb
+++ b/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb
@@ -53,7 +53,7 @@ class Module
def mattr_reader(*syms)
options = syms.extract_options!
syms.each do |sym|
- raise NameError.new("invalid attribute name: #{sym}") unless sym =~ /^[_A-Za-z]\w*$/
+ raise NameError.new("invalid attribute name: #{sym}") unless sym =~ /\A[_A-Za-z]\w*\z/
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
@@#{sym} = nil unless defined? @@#{sym}
@@ -119,7 +119,7 @@ class Module
def mattr_writer(*syms)
options = syms.extract_options!
syms.each do |sym|
- raise NameError.new("invalid attribute name: #{sym}") unless sym =~ /^[_A-Za-z]\w*$/
+ raise NameError.new("invalid attribute name: #{sym}") unless sym =~ /\A[_A-Za-z]\w*\z/
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
@@#{sym} = nil unless defined? @@#{sym}
diff --git a/activesupport/lib/active_support/core_ext/time/zones.rb b/activesupport/lib/active_support/core_ext/time/zones.rb
index 133d3938eb..877dc84ec8 100644
--- a/activesupport/lib/active_support/core_ext/time/zones.rb
+++ b/activesupport/lib/active_support/core_ext/time/zones.rb
@@ -53,7 +53,7 @@ class Time
# Returns a TimeZone instance matching the time zone provided.
# Accepts the time zone in any format supported by <tt>Time.zone=</tt>.
- # Raises an ArgumentError for invalid time zones.
+ # Raises an +ArgumentError+ for invalid time zones.
#
# Time.find_zone! "America/New_York" # => #<ActiveSupport::TimeZone @name="America/New_York" ...>
# Time.find_zone! "EST" # => #<ActiveSupport::TimeZone @name="EST" ...>
diff --git a/activesupport/lib/active_support/testing/file_fixtures.rb b/activesupport/lib/active_support/testing/file_fixtures.rb
index 4c6a0801b8..affb84cda5 100644
--- a/activesupport/lib/active_support/testing/file_fixtures.rb
+++ b/activesupport/lib/active_support/testing/file_fixtures.rb
@@ -18,7 +18,7 @@ module ActiveSupport
# Returns a +Pathname+ to the fixture file named +fixture_name+.
#
- # Raises ArgumentError if +fixture_name+ can't be found.
+ # Raises +ArgumentError+ if +fixture_name+ can't be found.
def file_fixture(fixture_name)
path = Pathname.new(File.join(file_fixture_path, fixture_name))
diff --git a/activesupport/lib/active_support/testing/isolation.rb b/activesupport/lib/active_support/testing/isolation.rb
index 1de0a19998..edf8b30a0a 100644
--- a/activesupport/lib/active_support/testing/isolation.rb
+++ b/activesupport/lib/active_support/testing/isolation.rb
@@ -41,7 +41,23 @@ module ActiveSupport
pid = fork do
read.close
yield
- write.puts [Marshal.dump(self.dup)].pack("m")
+ begin
+ if error?
+ failures.map! { |e|
+ begin
+ Marshal.dump e
+ e
+ rescue TypeError
+ ex = Exception.new e.message
+ ex.set_backtrace e.backtrace
+ Minitest::UnexpectedError.new ex
+ end
+ }
+ end
+ result = Marshal.dump(self.dup)
+ end
+
+ write.puts [result].pack("m")
exit!
end
diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb
index a82a7dfea0..3592dcba39 100644
--- a/activesupport/lib/active_support/time_with_zone.rb
+++ b/activesupport/lib/active_support/time_with_zone.rb
@@ -41,6 +41,9 @@ module ActiveSupport
'Time'
end
+ PRECISIONS = Hash.new { |h, n| h[n] = "%FT%T.%#{n}N".freeze }
+ PRECISIONS[0] = '%FT%T'.freeze
+
include Comparable
attr_reader :time_zone
@@ -142,11 +145,7 @@ module ActiveSupport
#
# Time.zone.now.xmlschema # => "2014-12-04T11:02:37-05:00"
def xmlschema(fraction_digits = 0)
- fraction = if fraction_digits.to_i > 0
- (".%06i" % time.usec)[0, fraction_digits.to_i + 1]
- end
-
- "#{time.strftime("%Y-%m-%dT%H:%M:%S")}#{fraction}#{formatted_offset(true, 'Z')}"
+ "#{time.strftime(PRECISIONS[fraction_digits.to_i])}#{formatted_offset(true, 'Z'.freeze)}"
end
alias_method :iso8601, :xmlschema
diff --git a/activesupport/test/callbacks_test.rb b/activesupport/test/callbacks_test.rb
index 4f47e0519f..3b00ff87a0 100644
--- a/activesupport/test/callbacks_test.rb
+++ b/activesupport/test/callbacks_test.rb
@@ -776,7 +776,7 @@ module CallbacksTest
class CallbackFalseTerminatorWithConfigTrueTest < ActiveSupport::TestCase
def setup
- ActiveSupport::Callbacks::CallbackChain.halt_and_display_warning_on_return_false = true
+ ActiveSupport::Callbacks.halt_and_display_warning_on_return_false = true
end
def test_returning_false_does_not_halt_callback_if_config_variable_is_true
@@ -789,7 +789,7 @@ module CallbacksTest
class CallbackFalseTerminatorWithConfigFalseTest < ActiveSupport::TestCase
def setup
- ActiveSupport::Callbacks::CallbackChain.halt_and_display_warning_on_return_false = false
+ ActiveSupport::Callbacks.halt_and_display_warning_on_return_false = false
end
def test_returning_false_does_not_halt_callback_if_config_variable_is_false
diff --git a/activesupport/test/core_ext/module/attribute_accessor_test.rb b/activesupport/test/core_ext/module/attribute_accessor_test.rb
index 128c5e3d1a..0b0f3a2808 100644
--- a/activesupport/test/core_ext/module/attribute_accessor_test.rb
+++ b/activesupport/test/core_ext/module/attribute_accessor_test.rb
@@ -69,6 +69,20 @@ class ModuleAttributeAccessorTest < ActiveSupport::TestCase
end
end
assert_equal "invalid attribute name: 1nvalid", exception.message
+
+ exception = assert_raises NameError do
+ Class.new do
+ mattr_reader "valid_part\ninvalid_part"
+ end
+ end
+ assert_equal "invalid attribute name: valid_part\ninvalid_part", exception.message
+
+ exception = assert_raises NameError do
+ Class.new do
+ mattr_writer "valid_part\ninvalid_part"
+ end
+ end
+ assert_equal "invalid attribute name: valid_part\ninvalid_part", exception.message
end
def test_should_use_default_value_if_block_passed
diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb
index ccb7f02331..c40f0bacbf 100644
--- a/activesupport/test/core_ext/time_with_zone_test.rb
+++ b/activesupport/test/core_ext/time_with_zone_test.rb
@@ -110,14 +110,14 @@ class TimeWithZoneTest < ActiveSupport::TestCase
@twz += 0.1234560001 # advance the time by a fraction of a second
assert_equal "1999-12-31T19:00:00.123-05:00", @twz.xmlschema(3)
assert_equal "1999-12-31T19:00:00.123456-05:00", @twz.xmlschema(6)
- assert_equal "1999-12-31T19:00:00.123456-05:00", @twz.xmlschema(12)
+ assert_equal "1999-12-31T19:00:00.123456000100-05:00", @twz.xmlschema(12)
end
def test_xmlschema_with_fractional_seconds_lower_than_hundred_thousand
@twz += 0.001234 # advance the time by a fraction
assert_equal "1999-12-31T19:00:00.001-05:00", @twz.xmlschema(3)
assert_equal "1999-12-31T19:00:00.001234-05:00", @twz.xmlschema(6)
- assert_equal "1999-12-31T19:00:00.001234-05:00", @twz.xmlschema(12)
+ assert_equal "1999-12-31T19:00:00.001234000000-05:00", @twz.xmlschema(12)
end
def test_xmlschema_with_nil_fractional_seconds
diff --git a/guides/source/association_basics.md b/guides/source/association_basics.md
index 60790b33a4..999c533fb3 100644
--- a/guides/source/association_basics.md
+++ b/guides/source/association_basics.md
@@ -932,8 +932,11 @@ If you set the `:dependent` option to:
* `:destroy`, when the object is destroyed, `destroy` will be called on its
associated objects.
-* `:delete`, when the object is destroyed, all its associated objects will be
+* `:delete_all`, when the object is destroyed, all its associated objects will be
deleted directly from the database without calling their `destroy` method.
+* `:nullify`, causes the foreign key to be set to `NULL`. Callbacks are not executed.
+* `:restrict_with_exception`, causes an exception to be raised if there is an associated record
+* `:restrict_with_error`, causes an error to be added to the owner if there is an associated object
WARNING: You should not specify this option on a `belongs_to` association that is connected with a `has_many` association on the other class. Doing so can lead to orphaned records in your database.
diff --git a/guides/source/configuring.md b/guides/source/configuring.md
index f0d87e4dc8..afdb0ba7eb 100644
--- a/guides/source/configuring.md
+++ b/guides/source/configuring.md
@@ -535,7 +535,7 @@ There are a few configuration options available in Active Support:
* `config.active_support.time_precision` sets the precision of JSON encoded time values. Defaults to `3`.
-* `config.active_support.halt_callback_chains_on_return_false` specifies whether ActiveRecord, ActiveModel and ActiveModel::Validations callback chains can be halted by returning `false` in a 'before' callback. Defaults to `true`.
+* `ActiveSupport.halt_callback_chains_on_return_false` specifies whether Active Record and Active Model callback chains can be halted by returning `false` in a 'before' callback. Defaults to `true`.
* `ActiveSupport::Logger.silencer` is set to `false` to disable the ability to silence logging in a block. The default is `true`.
diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md
index 52464a1c51..490bda3571 100644
--- a/guides/source/upgrading_ruby_on_rails.md
+++ b/guides/source/upgrading_ruby_on_rails.md
@@ -55,23 +55,26 @@ Upgrading from Rails 4.2 to Rails 5.0
### Halting callback chains by returning `false`
-In Rails 4.2, when a 'before' callback returns `false` in ActiveRecord,
-ActiveModel and ActiveModel::Validations, then the entire callback chain
-is halted. In other words, successive 'before' callbacks are not executed,
-and neither is the action wrapped in callbacks.
+In Rails 4.2, when a 'before' callback returns `false` in Active Record
+and Active Model, then the entire callback chain is halted. In other words,
+successive 'before' callbacks are not executed, and neither is the action wrapped
+in callbacks.
-In Rails 5.0, returning `false` in a callback will not have this side effect
-of halting the callback chain. Instead, callback chains must be explicitly
-halted by calling `throw(:abort)`.
+In Rails 5.0, returning `false` in an Active Record or Active Model callback
+will not have this side effect of halting the callback chain. Instead, callback
+chains must be explicitly halted by calling `throw(:abort)`.
-When you upgrade from Rails 4.2 to Rails 5.0, returning `false` in a callback
-will still halt the callback chain, but you will receive a deprecation warning
-about this upcoming change.
+When you upgrade from Rails 4.2 to Rails 5.0, returning `false` in those kind of
+callbacks will still halt the callback chain, but you will receive a deprecation
+warning about this upcoming change.
When you are ready, you can opt into the new behavior and remove the deprecation
warning by adding the following configuration to your `config/application.rb`:
- config.active_support.halt_callback_chains_on_return_false = false
+ ActiveSupport.halt_callback_chains_on_return_false = false
+
+Note that this option will not affect Active Support callbacks since they never
+halted the chain when any value was returned.
See [#17227](https://github.com/rails/rails/pull/17227) for more details.
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index f007d11c72..3e45a09dec 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,3 +1,65 @@
+* Add fail fast to `bin/rails test`
+
+ Adding `--fail-fast` or `-f` when running tests will interrupt the run on
+ the first failure:
+
+ ```
+ # Running:
+
+ ................................................S......E
+
+ ArgumentError: Wups! Bet you didn't expect this!
+ test/models/bunny_test.rb:19:in `block in <class:BunnyTest>'
+
+ bin/rails test test/models/bunny_test.rb:18
+
+ ....................................F
+
+ This failed
+
+ bin/rails test test/models/bunny_test.rb:14
+
+ Interrupted. Exiting...
+
+
+ Finished in 0.051427s, 1808.3872 runs/s, 1769.4972 assertions/s.
+
+ ```
+
+ Note that any unexpected errors don't abort the run.
+
+ *Kasper Timm Hansen*
+
+* Add inline output to `bin/rails test`
+
+ Any failures or errors (and skips if running in verbose mode) are output
+ during a test run:
+
+ ```
+ # Running:
+
+ .....S..........................................F
+
+ This failed
+
+ bin/rails test test/models/bunny_test.rb:14
+
+ .................................E
+
+ ArgumentError: Wups! Bet you didn't expect this!
+ test/models/bunny_test.rb:19:in `block in <class:BunnyTest>'
+
+ bin/rails test test/models/bunny_test.rb:18
+
+ ....................
+
+ Finished in 0.069708s, 1477.6019 runs/s, 1448.9106 assertions/s.
+ ```
+
+ Output can be deferred to after a run with the `--defer-output` option.
+
+ *Kasper Timm Hansen*
+
* Fix displaying mailer previews on non local requests when config
`action_mailer.show_previews` is set
@@ -234,10 +296,11 @@
Newly generated Rails apps have a new initializer called
`callback_terminator.rb` which sets the value of the configuration option
- `config.active_support.halt_callback_chains_on_return_false` to `false`.
+ `ActiveSupport.halt_callback_chains_on_return_false` to `false`.
- As a result, new Rails apps do not halt callback chains when a callback
- returns `false`; only when they are explicitly halted with `throw(:abort)`.
+ As a result, new Rails apps do not halt Active Record and Active Model
+ callback chains when a callback returns `false`; only when they are
+ explicitly halted with `throw(:abort)`.
The terminator is *not* added when running `rake rails:update`, so returning
`false` will still work on old apps ported to Rails 5, displaying a
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index 8075068b3f..175965e565 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -156,15 +156,6 @@ module Rails
self
end
- # Implements call according to the Rack API. It simply
- # dispatches the request to the underlying middleware stack.
- def call(env)
- req = ActionDispatch::Request.new env
- env["ORIGINAL_FULLPATH"] = req.fullpath
- env["ORIGINAL_SCRIPT_NAME"] = req.script_name
- super(env)
- end
-
# Reload application routes regardless if they changed or not.
def reload_routes!
routes_reloader.reload!
@@ -514,5 +505,18 @@ module Rails
end
end
end
+
+ private
+
+ def build_request(env)
+ req = super
+ env["ORIGINAL_FULLPATH"] = req.fullpath
+ env["ORIGINAL_SCRIPT_NAME"] = req.script_name
+ req
+ end
+
+ def build_middleware
+ config.app_middleware + super
+ end
end
end
diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb
index 4fc7a1db62..75112f29b6 100644
--- a/railties/lib/rails/application/configuration.rb
+++ b/railties/lib/rails/application/configuration.rb
@@ -36,7 +36,6 @@ module Rails
@time_zone = "UTC"
@beginning_of_week = :monday
@log_level = nil
- @middleware = app_middleware
@generators = app_generators
@cache_store = [ :file_store, "#{root}/tmp/cache/" ]
@railties_order = [:all]
diff --git a/railties/lib/rails/configuration.rb b/railties/lib/rails/configuration.rb
index d99d27a756..30eafd59f2 100644
--- a/railties/lib/rails/configuration.rb
+++ b/railties/lib/rails/configuration.rb
@@ -33,9 +33,9 @@ module Rails
# config.middleware.delete ActionDispatch::Flash
#
class MiddlewareStackProxy
- def initialize
- @operations = []
- @delete_operations = []
+ def initialize(operations = [], delete_operations = [])
+ @operations = operations
+ @delete_operations = delete_operations
end
def insert_before(*args, &block)
@@ -71,6 +71,19 @@ module Rails
other
end
+
+ def +(other) # :nodoc:
+ MiddlewareStackProxy.new(@operations + other.operations, @delete_operations + other.delete_operations)
+ end
+
+ protected
+ def operations
+ @operations
+ end
+
+ def delete_operations
+ @delete_operations
+ end
end
class Generators #:nodoc:
diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb
index e90d41cbec..c4bb7d2109 100644
--- a/railties/lib/rails/engine.rb
+++ b/railties/lib/rails/engine.rb
@@ -2,6 +2,7 @@ require 'rails/railtie'
require 'rails/engine/railties'
require 'active_support/core_ext/module/delegation'
require 'pathname'
+require 'thread'
module Rails
# <tt>Rails::Engine</tt> allows you to wrap a specific Rails application or subset of
@@ -434,6 +435,7 @@ module Rails
@env_config = nil
@helpers = nil
@routes = nil
+ @app_build_lock = Mutex.new
super
end
@@ -504,10 +506,13 @@ module Rails
# Returns the underlying rack application for this engine.
def app
- @app ||= begin
- config.middleware = config.middleware.merge_into(default_middleware_stack)
- config.middleware.build(endpoint)
- end
+ @app || @app_build_lock.synchronize {
+ @app ||= begin
+ stack = default_middleware_stack
+ config.middleware = build_middleware.merge_into(stack)
+ config.middleware.build(endpoint)
+ end
+ }
end
# Returns the endpoint for this engine. If none is registered,
@@ -518,11 +523,8 @@ module Rails
# Define the Rack API for this engine.
def call(env)
- env.merge!(env_config)
- req = ActionDispatch::Request.new env
- req.routes = routes
- req.engine_script_name = req.script_name
- app.call(env)
+ req = build_request env
+ app.call req.env
end
# Defines additional Rack env configuration that is added on each call.
@@ -689,5 +691,19 @@ module Rails
def _all_load_paths #:nodoc:
@_all_load_paths ||= (config.paths.load_paths + _all_autoload_paths).uniq
end
+
+ private
+
+ def build_request(env)
+ env.merge!(env_config)
+ req = ActionDispatch::Request.new env
+ req.routes = routes
+ req.engine_script_name = req.script_name
+ req
+ end
+
+ def build_middleware
+ config.middleware
+ end
end
end
diff --git a/railties/lib/rails/engine/configuration.rb b/railties/lib/rails/engine/configuration.rb
index 62a4139d07..b4ddee3b1b 100644
--- a/railties/lib/rails/engine/configuration.rb
+++ b/railties/lib/rails/engine/configuration.rb
@@ -4,17 +4,14 @@ module Rails
class Engine
class Configuration < ::Rails::Railtie::Configuration
attr_reader :root
- attr_writer :middleware, :eager_load_paths, :autoload_once_paths, :autoload_paths
+ attr_accessor :middleware
+ attr_writer :eager_load_paths, :autoload_once_paths, :autoload_paths
def initialize(root=nil)
super()
@root = root
@generators = app_generators.dup
- end
-
- # Returns the middleware stack for the engine.
- def middleware
- @middleware ||= Rails::Configuration::MiddlewareStackProxy.new
+ @middleware = Rails::Configuration::MiddlewareStackProxy.new
end
# Holds generators configuration:
diff --git a/railties/lib/rails/generators/rails/app/templates/app/assets/config/manifest.js.tt b/railties/lib/rails/generators/rails/app/templates/app/assets/config/manifest.js.tt
new file mode 100644
index 0000000000..f80631bac6
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/app/assets/config/manifest.js.tt
@@ -0,0 +1,8 @@
+
+<% unless options.api? -%>
+//= link_tree ../images
+<% end -%>
+<% unless options.skip_javascript -%>
+//= link_directory ../javascripts .js
+<% end -%>
+//= link_directory ../stylesheets .css
diff --git a/railties/lib/rails/generators/rails/app/templates/app/assets/manifest.js.tt b/railties/lib/rails/generators/rails/app/templates/app/assets/manifest.js.tt
deleted file mode 100644
index 3325553f57..0000000000
--- a/railties/lib/rails/generators/rails/app/templates/app/assets/manifest.js.tt
+++ /dev/null
@@ -1,8 +0,0 @@
-
-<% unless options.api? -%>
-//= link_tree ./images
-<% end -%>
-<% unless options.skip_javascript -%>
-//= link ./javascripts/application.js
-<% end -%>
-//= link ./stylesheets/application.css
diff --git a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
index 8fea30189e..910c4e743e 100644
--- a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
+++ b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
@@ -106,7 +106,7 @@ task default: :test
def test_dummy_assets
template "rails/javascripts.js", "#{dummy_path}/app/assets/javascripts/application.js", force: true
template "rails/stylesheets.css", "#{dummy_path}/app/assets/stylesheets/application.css", force: true
- template "rails/dummy_manifest.js", "#{dummy_path}/app/assets/manifest.js", force: true
+ template "rails/dummy_manifest.js", "#{dummy_path}/app/assets/config/manifest.js", force: true
end
def test_dummy_clean
@@ -124,7 +124,7 @@ task default: :test
end
def assets_manifest
- template "rails/engine_manifest.js", "app/assets/#{underscored_name}_manifest.js"
+ template "rails/engine_manifest.js", "app/assets/config/#{underscored_name}_manifest.js"
end
def stylesheets
diff --git a/railties/lib/rails/generators/rails/plugin/templates/rails/dummy_manifest.js b/railties/lib/rails/generators/rails/plugin/templates/rails/dummy_manifest.js
index ace695a811..8d21b2b6fb 100644
--- a/railties/lib/rails/generators/rails/plugin/templates/rails/dummy_manifest.js
+++ b/railties/lib/rails/generators/rails/plugin/templates/rails/dummy_manifest.js
@@ -1,11 +1,11 @@
<% unless api? -%>
-//= link_tree ./images
+//= link_tree ../images
<% end -%>
<% unless options.skip_javascript -%>
-//= link ./javascripts/application.js
+//= link_directory ../javascripts .js
<% end -%>
-//= link ./stylesheets/application.css
+//= link_directory ../stylesheets .css
<% if mountable? && !api? -%>
//= link <%= underscored_name %>_manifest.js
<% end -%>
diff --git a/railties/lib/rails/generators/rails/plugin/templates/rails/engine_manifest.js b/railties/lib/rails/generators/rails/plugin/templates/rails/engine_manifest.js
index f8ef26982a..2f23844f5e 100644
--- a/railties/lib/rails/generators/rails/plugin/templates/rails/engine_manifest.js
+++ b/railties/lib/rails/generators/rails/plugin/templates/rails/engine_manifest.js
@@ -1,6 +1,6 @@
<% if mountable? -%>
-<% unless options.skip_javascript -%>
-//= link ./javascripts/<%= namespaced_name %>/application.js
+<% if !options.skip_javascript -%>
+//= link_directory ../javascripts/<%= namespaced_name %> .js
<% end -%>
-//= link ./stylesheets/<%= namespaced_name %>/application.css
+//= link_directory ../stylesheets/<%= namespaced_name %> .css
<% end -%>
diff --git a/railties/lib/rails/test_unit/minitest_plugin.rb b/railties/lib/rails/test_unit/minitest_plugin.rb
index 1923433c4a..3a0a58df88 100644
--- a/railties/lib/rails/test_unit/minitest_plugin.rb
+++ b/railties/lib/rails/test_unit/minitest_plugin.rb
@@ -14,6 +14,8 @@ module Minitest
opts.separator ""
opts.separator " bin/rails test test/controllers test/integration/login_test.rb"
opts.separator ""
+ opts.separator "By default test failures and errors are reported inline during a run."
+ opts.separator ""
opts.separator "Rails options:"
opts.on("-e", "--environment ENV",
@@ -26,6 +28,16 @@ module Minitest
options[:full_backtrace] = true
end
+ opts.on("-d", "--defer-output",
+ "Output test failures and errors after the test run") do
+ options[:output_inline] = false
+ end
+
+ opts.on("-f", "--fail-fast",
+ "Abort test run on first failure") do
+ options[:fail_fast] = true
+ end
+
options[:patterns] = opts.order!
end
diff --git a/railties/lib/rails/test_unit/reporter.rb b/railties/lib/rails/test_unit/reporter.rb
index 09b8675cf8..8f1116b6af 100644
--- a/railties/lib/rails/test_unit/reporter.rb
+++ b/railties/lib/rails/test_unit/reporter.rb
@@ -6,8 +6,25 @@ module Rails
class_attribute :executable
self.executable = "bin/rails test"
+ def record(result)
+ super
+
+ if output_inline? && result.failure && (!result.skipped? || options[:verbose])
+ io.puts
+ io.puts
+ io.puts result.failures.map(&:message)
+ io.puts
+ io.puts format_rerun_snippet(result)
+ io.puts
+ end
+
+ if fail_fast? && result.failure && !result.error? && !result.skipped?
+ raise Interrupt
+ end
+ end
+
def report
- return if filtered_results.empty?
+ return if output_inline? || filtered_results.empty?
io.puts
io.puts "Failed tests:"
io.puts
@@ -15,10 +32,7 @@ module Rails
end
def aggregated_results # :nodoc:
- filtered_results.map do |result|
- location, line = result.method(result.name).source_location
- "#{self.executable} #{relative_path_for(location)}:#{line}"
- end.join "\n"
+ filtered_results.map { |result| format_rerun_snippet(result) }.join "\n"
end
def filtered_results
@@ -32,5 +46,19 @@ module Rails
def relative_path_for(file)
file.sub(/^#{Rails.root}\/?/, '')
end
+
+ private
+ def output_inline?
+ options.fetch(:output_inline, true)
+ end
+
+ def fail_fast?
+ options[:fail_fast]
+ end
+
+ def format_rerun_snippet(result)
+ location, line = result.method(result.name).source_location
+ "#{self.executable} #{relative_path_for(location)}:#{line}"
+ end
end
end
diff --git a/railties/test/application/asset_debugging_test.rb b/railties/test/application/asset_debugging_test.rb
index fef65adda2..8b83784ed6 100644
--- a/railties/test/application/asset_debugging_test.rb
+++ b/railties/test/application/asset_debugging_test.rb
@@ -7,7 +7,10 @@ module ApplicationTests
include Rack::Test::Methods
def setup
- build_app(initializers: true)
+ # FIXME: shush Sass warning spam, not relevant to testing Railties
+ Kernel.silence_warnings do
+ build_app(initializers: true)
+ end
app_file "app/assets/javascripts/application.js", "//= require_tree ."
app_file "app/assets/javascripts/xmlhr.js", "function f1() { alert(); }"
@@ -33,12 +36,19 @@ module ApplicationTests
teardown_app
end
+ # FIXME: shush Sass warning spam, not relevant to testing Railties
+ def get(*)
+ Kernel.silence_warnings { super }
+ end
+
test "assets are concatenated when debug is off and compile is off either if debug_assets param is provided" do
# config.assets.debug and config.assets.compile are false for production environment
ENV["RAILS_ENV"] = "production"
output = Dir.chdir(app_path){ `bin/rake assets:precompile --trace 2>&1` }
assert $?.success?, output
- require "#{app_path}/config/environment"
+
+ # Load app env
+ app "production"
class ::PostsController < ActionController::Base ; end
@@ -51,8 +61,8 @@ module ApplicationTests
test "assets are served with sourcemaps when compile is true and debug_assets params is true" do
add_to_env_config "production", "config.assets.compile = true"
- ENV["RAILS_ENV"] = "production"
- require "#{app_path}/config/environment"
+ # Load app env
+ app "production"
class ::PostsController < ActionController::Base ; end
diff --git a/railties/test/application/assets_test.rb b/railties/test/application/assets_test.rb
index ddcd51f3fc..dca5cf2e5b 100644
--- a/railties/test/application/assets_test.rb
+++ b/railties/test/application/assets_test.rb
@@ -17,14 +17,23 @@ module ApplicationTests
end
def precompile!(env = nil)
- quietly do
- precompile_task = "bin/rake assets:precompile #{env} --trace 2>&1"
- output = Dir.chdir(app_path) { %x[ #{precompile_task} ] }
- assert $?.success?, output
- output
+ with_env env.to_h do
+ quietly do
+ precompile_task = "bin/rake assets:precompile --trace 2>&1"
+ output = Dir.chdir(app_path) { %x[ #{precompile_task} ] }
+ assert $?.success?, output
+ output
+ end
end
end
+ def with_env(env)
+ env.each { |k, v| ENV[k.to_s] = v }
+ yield
+ ensure
+ env.each_key { |k| ENV.delete k.to_s }
+ end
+
def clean_assets!
quietly do
assert Dir.chdir(app_path) { system('bin/rake assets:clobber') }
@@ -32,7 +41,8 @@ module ApplicationTests
end
def assert_file_exists(filename)
- assert Dir[filename].first, "missing #{filename}"
+ globbed = Dir[filename]
+ assert globbed.one?, "Found #{globbed.size} files matching #{filename}. All files in the directory: #{Dir.entries(File.dirname(filename)).inspect}"
end
def assert_no_file_exists(filename)
@@ -51,7 +61,10 @@ module ApplicationTests
add_to_env_config "development", "config.assets.digest = false"
- require "#{app_path}/config/environment"
+ # FIXME: shush Sass warning spam, not relevant to testing Railties
+ Kernel.silence_warnings do
+ require "#{app_path}/config/environment"
+ end
get "/assets/demo.js"
assert_equal 'a = "/assets/rails.png";', last_response.body.strip
@@ -62,8 +75,8 @@ module ApplicationTests
add_to_env_config "production", "config.assets.compile = true"
add_to_env_config "production", "config.assets.precompile = []"
- ENV["RAILS_ENV"] = "production"
- require "#{app_path}/config/environment"
+ # Load app env
+ app "production"
assert !defined?(Uglifier)
get "/assets/demo.js"
@@ -72,11 +85,10 @@ module ApplicationTests
end
test "precompile creates the file, gives it the original asset's content and run in production as default" do
- app_file "app/assets/manifest.js", "//= link_tree ./javascripts"
+ app_file "app/assets/config/manifest.js", "//= link_tree ../javascripts"
app_file "app/assets/javascripts/application.js", "alert();"
app_file "app/assets/javascripts/foo/application.js", "alert();"
- ENV["RAILS_ENV"] = nil
precompile!
files = Dir["#{app_path}/public/assets/application-*.js"]
@@ -88,7 +100,7 @@ module ApplicationTests
end
def test_precompile_does_not_hit_the_database
- app_file "app/assets/manifest.js", "//= link_tree ./javascripts"
+ app_file "app/assets/config/manifest.js", "//= link_tree ../javascripts"
app_file "app/assets/javascripts/application.js", "alert();"
app_file "app/assets/javascripts/foo/application.js", "alert();"
app_file "app/controllers/users_controller.rb", <<-eoruby
@@ -98,10 +110,9 @@ module ApplicationTests
class User < ActiveRecord::Base; raise 'should not be reached'; end
eoruby
- ENV['RAILS_ENV'] = 'production'
- ENV['DATABASE_URL'] = 'postgresql://baduser:badpass@127.0.0.1/dbname'
-
- precompile!
+ precompile! \
+ RAILS_ENV: 'production',
+ DATABASE_URL: 'postgresql://baduser:badpass@127.0.0.1/dbname'
files = Dir["#{app_path}/public/assets/application-*.js"]
files << Dir["#{app_path}/public/assets/foo/application-*.js"].first
@@ -109,9 +120,6 @@ module ApplicationTests
assert_not_nil file, "Expected application.js asset to be generated, but none found"
assert_equal "alert();".strip, File.read(file).strip
end
- ensure
- ENV.delete 'RAILS_ENV'
- ENV.delete 'DATABASE_URL'
end
test "precompile application.js and application.css and all other non JS/CSS files" do
@@ -171,10 +179,9 @@ module ApplicationTests
test 'precompile use assets defined in app env config' do
add_to_env_config 'production', 'config.assets.precompile = [ "something.js" ]'
-
app_file 'app/assets/javascripts/something.js.erb', 'alert();'
- precompile! 'RAILS_ENV=production'
+ precompile! RAILS_ENV: 'production'
assert_file_exists("#{app_path}/public/assets/something-*.js")
end
@@ -183,15 +190,17 @@ module ApplicationTests
add_to_config 'config.assets.precompile = [ "something_manifest.js" ]'
add_to_env_config 'production', 'config.assets.precompile += [ "another_manifest.js" ]'
- app_file 'app/assets/something_manifest.js', '//= link ./javascripts/something.js'
- app_file 'app/assets/another_manifest.js', '//= link ./javascripts/another.js'
+ app_file 'app/assets/config/something_manifest.js', '//= link something.js'
+ app_file 'app/assets/config/another_manifest.js', '//= link another.js'
app_file 'app/assets/javascripts/something.js.erb', 'alert();'
app_file 'app/assets/javascripts/another.js.erb', 'alert();'
- precompile! 'RAILS_ENV=production'
+ precompile! RAILS_ENV: 'production'
+ assert_file_exists("#{app_path}/public/assets/something_manifest-*.js")
assert_file_exists("#{app_path}/public/assets/something-*.js")
+ assert_file_exists("#{app_path}/public/assets/another_manifest-*.js")
assert_file_exists("#{app_path}/public/assets/another-*.js")
end
@@ -199,8 +208,8 @@ module ApplicationTests
add_to_config "config.action_controller.perform_caching = false"
add_to_env_config "production", "config.assets.compile = true"
- ENV["RAILS_ENV"] = "production"
- require "#{app_path}/config/environment"
+ # Load app env
+ app "production"
assert_equal Sprockets::CachedEnvironment, Rails.application.assets.class
end
@@ -211,8 +220,8 @@ module ApplicationTests
app_file "app/assets/javascripts/application.js", "alert();"
precompile!
- manifest = Dir["#{app_path}/public/assets/.sprockets-manifest-*.json"].first
+ manifest = Dir["#{app_path}/public/assets/.sprockets-manifest-*.json"].first
assets = ActiveSupport::JSON.decode(File.read(manifest))
assert_match(/application-([0-z]+)\.js/, assets["assets"]["application.js"])
assert_match(/application-([0-z]+)\.css/, assets["assets"]["application.css"])
@@ -233,14 +242,14 @@ module ApplicationTests
app_file "app/assets/javascripts/application.js", "alert();"
add_to_env_config "production", "config.serve_static_files = true"
- ENV["RAILS_ENV"] = "production"
- precompile!
+ precompile! RAILS_ENV: 'production'
manifest = Dir["#{app_path}/public/assets/.sprockets-manifest-*.json"].first
assets = ActiveSupport::JSON.decode(File.read(manifest))
asset_path = assets["assets"]["application.js"]
- require "#{app_path}/config/environment"
+ # Load app env
+ app "production"
# Checking if Uglifier is defined we can know if Sprockets was reached or not
assert !defined?(Uglifier)
@@ -249,12 +258,11 @@ module ApplicationTests
assert !defined?(Uglifier)
end
- test "precompile properly refers files referenced with asset_path and runs in the provided RAILS_ENV" do
+ test "precompile properly refers files referenced with asset_path" do
app_file "app/assets/images/rails.png", "notactuallyapng"
app_file "app/assets/stylesheets/application.css.erb", "p { background-image: url(<%= asset_path('rails.png') %>) }"
- add_to_env_config "test", "config.assets.digest = true"
- precompile!('RAILS_ENV=test')
+ precompile!
file = Dir["#{app_path}/public/assets/application-*.css"].first
assert_match(/\/assets\/rails-([0-z]+)\.png/, File.read(file))
@@ -265,8 +273,7 @@ module ApplicationTests
app_file "app/assets/stylesheets/application.css.erb", "p { background-image: url(<%= asset_path('rails.png') %>) }"
- ENV["RAILS_ENV"] = "production"
- precompile!
+ precompile! RAILS_ENV: 'production'
manifest = Dir["#{app_path}/public/assets/.sprockets-manifest-*.json"].first
assets = ActiveSupport::JSON.decode(File.read(manifest))
@@ -275,8 +282,8 @@ module ApplicationTests
app_file "app/assets/images/rails.png", "p { url: change }"
precompile!
- assets = ActiveSupport::JSON.decode(File.read(manifest))
+ assets = ActiveSupport::JSON.decode(File.read(manifest))
assert_not_equal asset_path, assets["assets"]["application.css"]
end
@@ -284,8 +291,7 @@ module ApplicationTests
app_file "app/assets/images/rails.png", "notactuallyapng"
app_file "app/assets/stylesheets/application.css.erb", "p { background-image: url(<%= asset_path('rails.png') %>) }"
- ENV["RAILS_ENV"] = "production"
- precompile!
+ precompile! RAILS_ENV: 'production'
file = Dir["#{app_path}/public/assets/application-*.css"].first
assert_match(/\/assets\/rails-([0-z]+)\.png/, File.read(file))
@@ -294,7 +300,7 @@ module ApplicationTests
test "precompile should handle utf8 filenames" do
filename = "レイルズ.png"
app_file "app/assets/images/#{filename}", "not an image really"
- app_file "app/assets/manifest.js", "//= link_tree ./images"
+ app_file "app/assets/config/manifest.js", "//= link_tree ../images"
add_to_config "config.assets.precompile = %w(manifest.js)"
precompile!
@@ -303,7 +309,8 @@ module ApplicationTests
assets = ActiveSupport::JSON.decode(File.read(manifest))
assert asset_path = assets["assets"].find { |(k, _)| k && k =~ /.png/ }[1]
- require "#{app_path}/config/environment"
+ # Load app env
+ app "development"
get "/assets/#{URI.parser.escape(asset_path)}"
assert_match "not an image really", last_response.body
@@ -326,8 +333,8 @@ module ApplicationTests
app_file "app/assets/javascripts/demo.js.erb", "<%= :alert %>();"
add_to_config "config.assets.compile = false"
- ENV["RAILS_ENV"] = "production"
- require "#{app_path}/config/environment"
+ # Load app env
+ app "production"
get "/assets/demo.js"
assert_equal 404, last_response.status
@@ -344,7 +351,8 @@ module ApplicationTests
add_to_env_config "development", "config.assets.digest = false"
- require "#{app_path}/config/environment"
+ # Load app env
+ app "development"
class ::OmgController < ActionController::Base
def index
@@ -370,7 +378,8 @@ module ApplicationTests
add_to_env_config "development", "config.assets.digest = false"
- require "#{app_path}/config/environment"
+ # Load app env
+ app "development"
get "/assets/demo.js"
assert_match "alert();", last_response.body
@@ -381,10 +390,10 @@ module ApplicationTests
app_with_assets_in_view
# config.assets.debug and config.assets.compile are false for production environment
- ENV["RAILS_ENV"] = "production"
- precompile!
+ precompile! RAILS_ENV: 'production'
- require "#{app_path}/config/environment"
+ # Load app env
+ app "production"
class ::PostsController < ActionController::Base ; end
@@ -400,6 +409,7 @@ module ApplicationTests
app_file "app/assets/javascripts/xmlhr.js.erb", "<%= Post.name %>"
precompile!
+
assert_equal "Post\n;\n", File.read(Dir["#{app_path}/public/assets/application-*.js"].first)
end
@@ -441,7 +451,10 @@ module ApplicationTests
app_with_assets_in_view
add_to_config "config.asset_host = 'example.com'"
add_to_env_config "development", "config.assets.digest = false"
- require "#{app_path}/config/environment"
+
+ # Load app env
+ app "development"
+
class ::PostsController < ActionController::Base; end
get '/posts', {}, {'HTTPS'=>'off'}
@@ -456,6 +469,7 @@ module ApplicationTests
add_to_config "config.assets.precompile = %w{rails.png image_loader.js}"
add_to_config "config.asset_host = 'example.com'"
add_to_env_config "development", "config.assets.digest = false"
+
precompile!
assert_match "src='//example.com/assets/rails.png'", File.read(Dir["#{app_path}/public/assets/image_loader-*.js"].first)
@@ -467,6 +481,7 @@ module ApplicationTests
app_file "app/assets/javascripts/app.js.erb", "var src='<%= image_path('rails.png') %>';"
add_to_config "config.assets.precompile = %w{rails.png app.js}"
add_to_env_config "development", "config.assets.digest = false"
+
precompile!
assert_match "src='/sub/uri/assets/rails.png'", File.read(Dir["#{app_path}/public/assets/app-*.js"].first)
diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb
index 464fc1688c..2f407cd851 100644
--- a/railties/test/application/configuration_test.rb
+++ b/railties/test/application/configuration_test.rb
@@ -34,8 +34,19 @@ module ApplicationTests
FileUtils.cp_r(app_path, new_app)
end
- def app
- @app ||= Rails.application
+ def app(env = 'development')
+ @app ||= begin
+ ENV['RAILS_ENV'] = env
+
+ # FIXME: shush Sass warning spam, not relevant to testing Railties
+ Kernel.silence_warnings do
+ require "#{app_path}/config/environment"
+ end
+
+ Rails.application
+ ensure
+ ENV.delete 'RAILS_ENV'
+ end
end
def setup
@@ -78,7 +89,9 @@ module ApplicationTests
require 'my_logger'
config.logger = MyLogger.new STDOUT
RUBY
- require "#{app_path}/config/environment"
+
+ app 'development'
+
assert_equal 'MyLogger', Rails.application.config.logger.class.name
end
@@ -97,7 +110,7 @@ module ApplicationTests
end
RUBY
- require "#{app_path}/config/environment"
+ app 'development'
ActiveRecord::Migrator.migrations_paths = ["#{app_path}/db/migrate"]
@@ -128,29 +141,29 @@ module ApplicationTests
test "Rails.application is nil until app is initialized" do
require 'rails'
assert_nil Rails.application
- require "#{app_path}/config/environment"
+ app 'development'
assert_equal AppTemplate::Application.instance, Rails.application
end
test "Rails.application responds to all instance methods" do
- require "#{app_path}/config/environment"
+ app 'development'
assert_respond_to Rails.application, :routes_reloader
assert_equal Rails.application.routes_reloader, AppTemplate::Application.routes_reloader
end
test "Rails::Application responds to paths" do
- require "#{app_path}/config/environment"
+ app 'development'
assert_respond_to AppTemplate::Application, :paths
assert_equal ["#{app_path}/app/views"], AppTemplate::Application.paths["app/views"].expanded
end
test "the application root is set correctly" do
- require "#{app_path}/config/environment"
+ app 'development'
assert_equal Pathname.new(app_path), Rails.application.root
end
test "the application root can be seen from the application singleton" do
- require "#{app_path}/config/environment"
+ app 'development'
assert_equal Pathname.new(app_path), AppTemplate::Application.root
end
@@ -162,7 +175,8 @@ module ApplicationTests
use_frameworks []
- require "#{app_path}/config/environment"
+ app 'development'
+
assert_equal Pathname.new(new_app), Rails.application.root
end
@@ -172,7 +186,7 @@ module ApplicationTests
use_frameworks []
Dir.chdir("#{app_path}") do
- require "#{app_path}/config/environment"
+ app 'development'
assert_equal Pathname.new("#{app_path}"), Rails.application.root
end
end
@@ -181,7 +195,9 @@ module ApplicationTests
add_to_config <<-RUBY
config.root = "#{app_path}"
RUBY
- require "#{app_path}/config/environment"
+
+ app 'development'
+
assert_instance_of Pathname, Rails.root
end
@@ -189,7 +205,9 @@ module ApplicationTests
add_to_config <<-RUBY
config.paths["public"] = "somewhere"
RUBY
- require "#{app_path}/config/environment"
+
+ app 'development'
+
assert_instance_of Pathname, Rails.public_path
end
@@ -199,12 +217,13 @@ module ApplicationTests
config.cache_classes = true
RUBY
- require "#{app_path}/config/application"
- assert Rails.application.initialize!
+ app 'development'
+
+ assert_equal :require, ActiveSupport::Dependencies.mechanism
end
test "application is always added to eager_load namespaces" do
- require "#{app_path}/config/application"
+ app 'development'
assert_includes Rails.application.config.eager_load_namespaces, AppTemplate::Application
end
@@ -218,7 +237,7 @@ module ApplicationTests
use_frameworks []
assert_nothing_raised do
- require "#{app_path}/config/application"
+ app 'development'
end
end
@@ -230,7 +249,7 @@ module ApplicationTests
RUBY
assert_nothing_raised do
- require "#{app_path}/config/application"
+ app 'development'
end
end
@@ -239,7 +258,7 @@ module ApplicationTests
Rails.application.config.filter_parameters += [ :password, :foo, 'bar' ]
RUBY
- require "#{app_path}/config/environment"
+ app 'development'
assert_equal [:password, :foo, 'bar'], Rails.application.env_config['action_dispatch.parameter_filter']
end
@@ -255,7 +274,7 @@ module ApplicationTests
assert !$prepared
- require "#{app_path}/config/environment"
+ app 'development'
get "/"
assert $prepared
@@ -267,7 +286,7 @@ module ApplicationTests
end
test "skipping config.encoding still results in 'utf-8' as the default" do
- require "#{app_path}/config/application"
+ app 'development'
assert_utf8
end
@@ -276,7 +295,7 @@ module ApplicationTests
config.encoding = "utf-8"
RUBY
- require "#{app_path}/config/application"
+ app 'development'
assert_utf8
end
@@ -285,7 +304,7 @@ module ApplicationTests
config.paths["public"] = "somewhere"
RUBY
- require "#{app_path}/config/application"
+ app 'development'
assert_equal Pathname.new(app_path).join("somewhere"), Rails.public_path
end
@@ -293,7 +312,7 @@ module ApplicationTests
restore_default_config
with_rails_env "production" do
- require "#{app_path}/config/environment"
+ app 'production'
assert_not app.config.serve_static_files
end
end
@@ -303,7 +322,7 @@ module ApplicationTests
with_rails_env "production" do
switch_env "RAILS_SERVE_STATIC_FILES", "1" do
- require "#{app_path}/config/environment"
+ app 'production'
assert app.config.serve_static_files
end
end
@@ -314,7 +333,7 @@ module ApplicationTests
with_rails_env "production" do
switch_env "RAILS_SERVE_STATIC_FILES", " " do
- require "#{app_path}/config/environment"
+ app 'production'
assert_not app.config.serve_static_files
end
end
@@ -363,8 +382,8 @@ module ApplicationTests
development:
secret_key_base:
YAML
- require "#{app_path}/config/environment"
+ app 'development'
assert_equal app.env_config['action_dispatch.key_generator'], Rails.application.key_generator
assert_equal app.env_config['action_dispatch.key_generator'].class, ActiveSupport::LegacyKeyGenerator
@@ -380,7 +399,8 @@ module ApplicationTests
development:
secret_key_base:
YAML
- require "#{app_path}/config/environment"
+
+ app 'development'
assert_deprecated(/You didn't set `secret_key_base`./) do
app.env_config
@@ -395,7 +415,8 @@ module ApplicationTests
development:
secret_token: 3b7cd727ee24e8444053437c36cc66c3
YAML
- require "#{app_path}/config/environment"
+
+ app 'development'
assert_equal '3b7cd727ee24e8444053437c36cc66c3', app.secrets.secret_token
end
@@ -426,7 +447,7 @@ module ApplicationTests
secret_key_base: 3b7cd727ee24e8444053437c36cc66c3
YAML
- require "#{app_path}/config/environment"
+ app 'development'
assert_equal '3b7cd727ee24e8444053437c36cc66c3', app.secrets.secret_key_base
end
@@ -436,7 +457,7 @@ module ApplicationTests
Rails.application.config.secret_key_base = "3b7cd727ee24e8444053437c36cc66c3"
RUBY
- require "#{app_path}/config/environment"
+ app 'development'
assert_equal '3b7cd727ee24e8444053437c36cc66c3', app.secrets.secret_key_base
end
@@ -449,7 +470,8 @@ module ApplicationTests
secret_key_base:
secret_token:
YAML
- require "#{app_path}/config/environment"
+
+ app 'development'
assert_equal 'b3c631c314c0bbca50c1b2843150fe33', app.secrets.secret_token
assert_equal 'b3c631c314c0bbca50c1b2843150fe33', app.config.secret_token
@@ -463,7 +485,8 @@ module ApplicationTests
aws_secret_access_key: myamazonsecretaccesskey
YAML
- require "#{app_path}/config/environment"
+ app 'development'
+
assert_equal 'myamazonaccesskeyid', app.secrets.aws_access_key_id
assert_equal 'myamazonsecretaccesskey', app.secrets.aws_secret_access_key
end
@@ -471,7 +494,8 @@ module ApplicationTests
test "blank config/secrets.yml does not crash the loading process" do
app_file 'config/secrets.yml', <<-YAML
YAML
- require "#{app_path}/config/environment"
+
+ app 'development'
assert_nil app.secrets.not_defined
end
@@ -484,7 +508,8 @@ module ApplicationTests
development:
secret_key_base:
YAML
- require "#{app_path}/config/environment"
+
+ app 'development'
assert_equal "iaminallyoursecretkeybase", app.secrets.secret_key_base
end
@@ -497,7 +522,8 @@ module ApplicationTests
development:
secret_key_base:
YAML
- require "#{app_path}/config/environment"
+
+ app 'development'
assert_equal 'b3c631c314c0bbca50c1b2843150fe33', app.config.secret_token
assert_equal nil, app.secrets.secret_key_base
@@ -512,7 +538,8 @@ module ApplicationTests
development:
secret_key_base:
YAML
- require "#{app_path}/config/environment"
+
+ app 'development'
assert_equal '', app.config.secret_token
assert_equal nil, app.secrets.secret_key_base
@@ -535,7 +562,6 @@ module ApplicationTests
end
test "default form builder specified as a string" do
-
app_file 'config/initializers/form_builder.rb', <<-RUBY
class CustomFormBuilder < ActionView::Helpers::FormBuilder
def text_field(attribute, *args)
@@ -567,7 +593,7 @@ module ApplicationTests
end
RUBY
- require "#{app_path}/config/environment"
+ app 'development'
get "/posts"
assert_match(/label/, last_response.body)
@@ -606,9 +632,9 @@ module ApplicationTests
end
RUBY
- require "#{app_path}/config/environment"
+ app 'development'
- params = {authenticity_token: token}
+ params = { authenticity_token: token }
get "/posts/1"
assert_match(/patch/, last_response.body)
@@ -659,9 +685,9 @@ module ApplicationTests
config.action_mailer.interceptors = MyMailInterceptor
RUBY
- require "#{app_path}/config/environment"
- require "mail"
+ app 'development'
+ require "mail"
_ = ActionMailer::Base
assert_equal [::MyMailInterceptor], ::Mail.send(:class_variable_get, "@@delivery_interceptors")
@@ -672,9 +698,9 @@ module ApplicationTests
config.action_mailer.interceptors = [MyMailInterceptor, "MyOtherMailInterceptor"]
RUBY
- require "#{app_path}/config/environment"
- require "mail"
+ app 'development'
+ require "mail"
_ = ActionMailer::Base
assert_equal [::MyMailInterceptor, ::MyOtherMailInterceptor], ::Mail.send(:class_variable_get, "@@delivery_interceptors")
@@ -685,9 +711,9 @@ module ApplicationTests
config.action_mailer.preview_interceptors = MyPreviewMailInterceptor
RUBY
- require "#{app_path}/config/environment"
- require "mail"
+ app 'development'
+ require "mail"
_ = ActionMailer::Base
assert_equal [ActionMailer::InlinePreviewInterceptor, ::MyPreviewMailInterceptor], ActionMailer::Base.preview_interceptors
@@ -698,9 +724,9 @@ module ApplicationTests
config.action_mailer.preview_interceptors = [MyPreviewMailInterceptor, "MyOtherPreviewMailInterceptor"]
RUBY
- require "#{app_path}/config/environment"
- require "mail"
+ app 'development'
+ require "mail"
_ = ActionMailer::Base
assert_equal [ActionMailer::InlinePreviewInterceptor, MyPreviewMailInterceptor, MyOtherPreviewMailInterceptor], ActionMailer::Base.preview_interceptors
@@ -711,9 +737,9 @@ module ApplicationTests
ActionMailer::Base.preview_interceptors.delete(ActionMailer::InlinePreviewInterceptor)
RUBY
- require "#{app_path}/config/environment"
- require "mail"
+ app 'development'
+ require "mail"
_ = ActionMailer::Base
assert_equal [], ActionMailer::Base.preview_interceptors
@@ -724,9 +750,9 @@ module ApplicationTests
config.action_mailer.observers = MyMailObserver
RUBY
- require "#{app_path}/config/environment"
- require "mail"
+ app 'development'
+ require "mail"
_ = ActionMailer::Base
assert_equal [::MyMailObserver], ::Mail.send(:class_variable_get, "@@delivery_notification_observers")
@@ -737,9 +763,9 @@ module ApplicationTests
config.action_mailer.observers = [MyMailObserver, "MyOtherMailObserver"]
RUBY
- require "#{app_path}/config/environment"
- require "mail"
+ app 'development'
+ require "mail"
_ = ActionMailer::Base
assert_equal [::MyMailObserver, ::MyOtherMailObserver], ::Mail.send(:class_variable_get, "@@delivery_notification_observers")
@@ -750,9 +776,9 @@ module ApplicationTests
config.action_mailer.deliver_later_queue_name = 'test_default'
RUBY
- require "#{app_path}/config/environment"
- require "mail"
+ app 'development'
+ require "mail"
_ = ActionMailer::Base
assert_equal 'test_default', ActionMailer::Base.send(:class_variable_get, "@@deliver_later_queue_name")
@@ -764,7 +790,7 @@ module ApplicationTests
config.time_zone = "Wellington"
RUBY
- require "#{app_path}/config/environment"
+ app 'development'
assert_equal "Wellington", Rails.application.config.time_zone
end
@@ -776,7 +802,7 @@ module ApplicationTests
RUBY
assert_raise(ArgumentError) do
- require "#{app_path}/config/environment"
+ app 'development'
end
end
@@ -786,7 +812,7 @@ module ApplicationTests
config.beginning_of_week = :wednesday
RUBY
- require "#{app_path}/config/environment"
+ app 'development'
assert_equal :wednesday, Rails.application.config.beginning_of_week
end
@@ -798,13 +824,14 @@ module ApplicationTests
RUBY
assert_raise(ArgumentError) do
- require "#{app_path}/config/environment"
+ app 'development'
end
end
test "config.action_view.cache_template_loading with cache_classes default" do
add_to_config "config.cache_classes = true"
- require "#{app_path}/config/environment"
+
+ app 'development'
require 'action_view/base'
assert_equal true, ActionView::Resolver.caching?
@@ -812,7 +839,8 @@ module ApplicationTests
test "config.action_view.cache_template_loading without cache_classes default" do
add_to_config "config.cache_classes = false"
- require "#{app_path}/config/environment"
+
+ app 'development'
require 'action_view/base'
assert_equal false, ActionView::Resolver.caching?
@@ -823,7 +851,8 @@ module ApplicationTests
config.cache_classes = true
config.action_view.cache_template_loading = false
RUBY
- require "#{app_path}/config/environment"
+
+ app 'development'
require 'action_view/base'
assert_equal false, ActionView::Resolver.caching?
@@ -834,7 +863,8 @@ module ApplicationTests
config.cache_classes = false
config.action_view.cache_template_loading = true
RUBY
- require "#{app_path}/config/environment"
+
+ app 'development'
require 'action_view/base'
assert_equal true, ActionView::Resolver.caching?
@@ -849,7 +879,7 @@ module ApplicationTests
require 'action_view/railtie'
require 'action_view/base'
- require "#{app_path}/config/environment"
+ app 'development'
assert_equal false, ActionView::Resolver.caching?
end
@@ -902,7 +932,7 @@ module ApplicationTests
end
RUBY
- require "#{app_path}/config/environment"
+ app 'development'
post "/posts.json", '{ "title": "foo", "name": "bar" }', "CONTENT_TYPE" => "application/json"
assert_equal '{"title"=>"foo"}', last_response.body
@@ -924,7 +954,7 @@ module ApplicationTests
config.action_controller.permit_all_parameters = true
RUBY
- require "#{app_path}/config/environment"
+ app 'development'
post "/posts", {post: {"title" =>"zomg"}}
assert_equal 'permitted', last_response.body
@@ -946,7 +976,7 @@ module ApplicationTests
config.action_controller.action_on_unpermitted_parameters = :raise
RUBY
- require "#{app_path}/config/environment"
+ app 'development'
assert_equal :raise, ActionController::Parameters.action_on_unpermitted_parameters
@@ -955,7 +985,7 @@ module ApplicationTests
end
test "config.action_controller.always_permitted_parameters are: controller, action by default" do
- require "#{app_path}/config/environment"
+ app 'development'
assert_equal %w(controller action), ActionController::Parameters.always_permitted_parameters
end
@@ -963,7 +993,9 @@ module ApplicationTests
add_to_config <<-RUBY
config.action_controller.always_permitted_parameters = %w( controller action format )
RUBY
- require "#{app_path}/config/environment"
+
+ app 'development'
+
assert_equal %w( controller action format ), ActionController::Parameters.always_permitted_parameters
end
@@ -984,7 +1016,7 @@ module ApplicationTests
config.action_controller.action_on_unpermitted_parameters = :raise
RUBY
- require "#{app_path}/config/environment"
+ app 'development'
assert_equal :raise, ActionController::Parameters.action_on_unpermitted_parameters
@@ -993,25 +1025,19 @@ module ApplicationTests
end
test "config.action_controller.action_on_unpermitted_parameters is :log by default on development" do
- ENV["RAILS_ENV"] = "development"
-
- require "#{app_path}/config/environment"
+ app 'development'
assert_equal :log, ActionController::Parameters.action_on_unpermitted_parameters
end
test "config.action_controller.action_on_unpermitted_parameters is :log by default on test" do
- ENV["RAILS_ENV"] = "test"
-
- require "#{app_path}/config/environment"
+ app 'test'
assert_equal :log, ActionController::Parameters.action_on_unpermitted_parameters
end
test "config.action_controller.action_on_unpermitted_parameters is false by default on production" do
- ENV["RAILS_ENV"] = "production"
-
- require "#{app_path}/config/environment"
+ app 'production'
assert_equal false, ActionController::Parameters.action_on_unpermitted_parameters
end
@@ -1089,17 +1115,14 @@ module ApplicationTests
test "config.active_record.dump_schema_after_migration is false on production" do
build_app
- ENV["RAILS_ENV"] = "production"
- require "#{app_path}/config/environment"
+ app 'production'
assert_not ActiveRecord::Base.dump_schema_after_migration
end
test "config.active_record.dump_schema_after_migration is true by default on development" do
- ENV["RAILS_ENV"] = "development"
-
- require "#{app_path}/config/environment"
+ app 'development'
assert ActiveRecord::Base.dump_schema_after_migration
end
@@ -1125,7 +1148,7 @@ module ApplicationTests
end
RUBY
- require "#{app_path}/config/environment"
+ app 'development'
assert_not Rails.configuration.ran_block
require 'rake'
@@ -1147,7 +1170,7 @@ module ApplicationTests
end
RUBY
- require "#{app_path}/config/environment"
+ app 'development'
assert_not Rails.configuration.ran_block
Rails.application.load_generators
@@ -1165,7 +1188,7 @@ module ApplicationTests
end
RUBY
- require "#{app_path}/config/environment"
+ app 'development'
assert_not Rails.configuration.ran_block
Rails.application.load_console
@@ -1183,7 +1206,7 @@ module ApplicationTests
end
RUBY
- require "#{app_path}/config/environment"
+ app 'development'
assert_not Rails.configuration.ran_block
Rails.application.load_runner
@@ -1199,14 +1222,14 @@ module ApplicationTests
end
RUBY
- require "#{app_path}/config/environment"
+ app 'development'
assert_kind_of Hash, Rails.application.config.database_configuration
end
test 'raises with proper error message if no database configuration found' do
FileUtils.rm("#{app_path}/config/database.yml")
- require "#{app_path}/config/environment"
+ app 'development'
err = assert_raises RuntimeError do
Rails.application.config.database_configuration
end
@@ -1214,25 +1237,23 @@ module ApplicationTests
end
test 'config.action_mailer.show_previews defaults to true in development' do
- Rails.env = "development"
- require "#{app_path}/config/environment"
+ app 'development'
assert Rails.application.config.action_mailer.show_previews
end
test 'config.action_mailer.show_previews defaults to false in production' do
- Rails.env = "production"
- require "#{app_path}/config/environment"
+ app 'production'
assert_equal false, Rails.application.config.action_mailer.show_previews
end
test 'config.action_mailer.show_previews can be set in the configuration file' do
- Rails.env = "production"
add_to_config <<-RUBY
config.action_mailer.show_previews = true
RUBY
- require "#{app_path}/config/environment"
+
+ app 'production'
assert_equal true, Rails.application.config.action_mailer.show_previews
end
@@ -1247,7 +1268,7 @@ module ApplicationTests
config.my_custom_config = config_for('custom')
RUBY
- require "#{app_path}/config/environment"
+ app 'development'
assert_equal 'custom key', Rails.application.config.my_custom_config['key']
end
@@ -1258,7 +1279,7 @@ module ApplicationTests
RUBY
exception = assert_raises(RuntimeError) do
- require "#{app_path}/config/environment"
+ app 'development'
end
assert_equal "Could not load configuration. No such file - #{app_path}/config/custom.yml", exception.message
@@ -1273,7 +1294,8 @@ module ApplicationTests
add_to_config <<-RUBY
config.my_custom_config = config_for('custom')
RUBY
- require "#{app_path}/config/environment"
+
+ app 'development'
assert_equal({}, Rails.application.config.my_custom_config)
end
@@ -1285,7 +1307,8 @@ module ApplicationTests
add_to_config <<-RUBY
config.my_custom_config = config_for('custom')
RUBY
- require "#{app_path}/config/environment"
+
+ app 'development'
assert_equal({}, Rails.application.config.my_custom_config)
end
@@ -1299,7 +1322,8 @@ module ApplicationTests
add_to_config <<-RUBY
config.my_custom_config = config_for('custom')
RUBY
- require "#{app_path}/config/environment"
+
+ app 'development'
assert_equal 'custom key', Rails.application.config.my_custom_config['key']
end
@@ -1315,7 +1339,7 @@ module ApplicationTests
RUBY
exception = assert_raises(RuntimeError) do
- require "#{app_path}/config/environment"
+ app 'development'
end
assert_match 'YAML syntax error occurred while parsing', exception.message
diff --git a/railties/test/application/test_runner_test.rb b/railties/test/application/test_runner_test.rb
index 2d47a31826..acfba21f1c 100644
--- a/railties/test/application/test_runner_test.rb
+++ b/railties/test/application/test_runner_test.rb
@@ -340,6 +340,36 @@ module ApplicationTests
assert_match '0 runs, 0 assertions', run_test_command('')
end
+ def test_output_inline_by_default
+ app_file 'test/models/post_test.rb', <<-RUBY
+ require 'test_helper'
+
+ class PostTest < ActiveSupport::TestCase
+ def test_post
+ assert false, 'wups!'
+ end
+ end
+ RUBY
+
+ output = run_test_command('test/models/post_test.rb')
+ assert_match %r{Running:\n\nF\n\nwups!\n\nbin/rails test test/models/post_test.rb:4}, output
+ end
+
+ def test_fail_fast
+ app_file 'test/models/post_test.rb', <<-RUBY
+ require 'test_helper'
+
+ class PostTest < ActiveSupport::TestCase
+ def test_post
+ assert false, 'wups!'
+ end
+ end
+ RUBY
+
+ assert_match(/Interrupt/,
+ capture(:stderr) { run_test_command('test/models/post_test.rb --fail-fast') })
+ end
+
def test_raise_error_when_specified_file_does_not_exist
error = capture(:stderr) { run_test_command('test/not_exists.rb') }
assert_match(%r{cannot load such file.+test/not_exists\.rb}, error)
diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb
index 41d36680e0..df3c2ca66d 100644
--- a/railties/test/isolation/abstract_unit.rb
+++ b/railties/test/isolation/abstract_unit.rb
@@ -51,7 +51,12 @@ module TestHelpers
old_env = ENV["RAILS_ENV"]
@app ||= begin
ENV["RAILS_ENV"] = env
- require "#{app_path}/config/environment"
+
+ # FIXME: shush Sass warning spam, not relevant to testing Railties
+ Kernel.silence_warnings do
+ require "#{app_path}/config/environment"
+ end
+
Rails.application
end
ensure
@@ -161,18 +166,18 @@ module TestHelpers
require "action_view/railtie"
require 'action_dispatch/middleware/flash'
- app = Class.new(Rails::Application)
- app.config.eager_load = false
- app.secrets.secret_key_base = "3b7cd727ee24e8444053437c36cc66c4"
- app.config.session_store :cookie_store, key: "_myapp_session"
- app.config.active_support.deprecation = :log
- app.config.active_support.test_order = :random
- app.config.log_level = :info
+ @app = Class.new(Rails::Application)
+ @app.config.eager_load = false
+ @app.secrets.secret_key_base = "3b7cd727ee24e8444053437c36cc66c4"
+ @app.config.session_store :cookie_store, key: "_myapp_session"
+ @app.config.active_support.deprecation = :log
+ @app.config.active_support.test_order = :random
+ @app.config.log_level = :info
- yield app if block_given?
- app.initialize!
+ yield @app if block_given?
+ @app.initialize!
- app.routes.draw do
+ @app.routes.draw do
get "/" => "omg#index"
end
@@ -297,7 +302,10 @@ module TestHelpers
end
def boot_rails
- require File.expand_path('../../../../load_paths', __FILE__)
+ # FIXME: shush Sass warning spam, not relevant to testing Railties
+ Kernel.silence_warnings do
+ require File.expand_path('../../../../load_paths', __FILE__)
+ end
end
end
end
diff --git a/railties/test/test_unit/reporter_test.rb b/railties/test/test_unit/reporter_test.rb
index 3066ba82d6..59fdf4bc36 100644
--- a/railties/test/test_unit/reporter_test.rb
+++ b/railties/test/test_unit/reporter_test.rb
@@ -57,6 +57,59 @@ class TestUnitReporterTest < ActiveSupport::TestCase
end
end
+ test "outputs failures inline" do
+ @reporter.record(failed_test)
+ @reporter.report
+
+ assert_match %r{\A\n\nboo\n\nbin/rails test .*test/test_unit/reporter_test.rb:6\n\n\z}, @output.string
+ end
+
+ test "outputs errors inline" do
+ @reporter.record(errored_test)
+ @reporter.report
+
+ assert_match %r{\A\n\nArgumentError: wups\n No backtrace\n\nbin/rails test .*test/test_unit/reporter_test.rb:6\n\n\z}, @output.string
+ end
+
+ test "outputs skipped tests inline if verbose" do
+ verbose = Rails::TestUnitReporter.new @output, verbose: true
+ verbose.record(skipped_test)
+ verbose.report
+
+ assert_match %r{\A\n\nskipchurches, misstemples\n\nbin/rails test .*test/test_unit/reporter_test.rb:6\n\n\z}, @output.string
+ end
+
+ test "does not output rerun snippets after run" do
+ @reporter.record(failed_test)
+ @reporter.report
+
+ assert_no_match 'Failed tests:', @output.string
+ end
+
+ test "fail fast interrupts run on failure" do
+ fail_fast = Rails::TestUnitReporter.new @output, fail_fast: true
+ interrupt_raised = false
+
+ # Minitest passes through Interrupt, catch it manually.
+ begin
+ fail_fast.record(failed_test)
+ rescue Interrupt
+ interrupt_raised = true
+ ensure
+ assert interrupt_raised, 'Expected Interrupt to be raised.'
+ end
+ end
+
+ test "fail fast does not interrupt run errors or skips" do
+ fail_fast = Rails::TestUnitReporter.new @output, fail_fast: true
+
+ fail_fast.record(errored_test)
+ assert_no_match 'Failed tests:', @output.string
+
+ fail_fast.record(skipped_test)
+ assert_no_match 'Failed tests:', @output.string
+ end
+
private
def assert_rerun_snippet_count(snippet_count)
assert_equal snippet_count, @output.string.scan(%r{^bin/rails test }).size
@@ -72,6 +125,12 @@ class TestUnitReporterTest < ActiveSupport::TestCase
ft
end
+ def errored_test
+ et = ExampleTest.new(:woot)
+ et.failures << Minitest::UnexpectedError.new(ArgumentError.new("wups"))
+ et
+ end
+
def passing_test
ExampleTest.new(:woot)
end
@@ -79,7 +138,7 @@ class TestUnitReporterTest < ActiveSupport::TestCase
def skipped_test
st = ExampleTest.new(:woot)
st.failures << begin
- raise Minitest::Skip
+ raise Minitest::Skip, "skipchurches, misstemples"
rescue Minitest::Assertion => e
e
end