diff options
46 files changed, 382 insertions, 233 deletions
diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb index d21a778d8d..e5f3cb8e8d 100644 --- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb +++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb @@ -136,7 +136,7 @@ module ActionController #:nodoc: # This is the method that defines the application behavior when a request is found to be unverified. def handle_unverified_request request = @controller.request - request.session = NullSessionHash.new(request.env) + request.session = NullSessionHash.new(request) request.env['action_dispatch.request.flash_hash'] = nil request.env['rack.session.options'] = { skip: true } request.cookie_jar = NullCookieJar.build(request, {}) @@ -145,8 +145,8 @@ module ActionController #:nodoc: protected class NullSessionHash < Rack::Session::Abstract::SessionHash #:nodoc: - def initialize(env) - super(nil, env) + def initialize(req) + super(nil, req) @data = {} @loaded = true end diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb index e012fa617e..d14483dc72 100644 --- a/actionpack/lib/action_controller/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -36,11 +36,15 @@ module ActionController end def query_string=(string) - @env[Rack::QUERY_STRING] = string + set_header Rack::QUERY_STRING, string end def request_parameters=(params) - @env["action_dispatch.request.request_parameters"] = params + set_header "action_dispatch.request.request_parameters", params + end + + def content_type=(type) + set_header 'CONTENT_TYPE', type end def assign_parameters(routes, controller_path, action, parameters, generated_path, query_string_keys) @@ -67,10 +71,12 @@ module ActionController end else if ENCODER.should_multipart?(non_path_parameters) - @env['CONTENT_TYPE'] = ENCODER.content_type + self.content_type = ENCODER.content_type data = ENCODER.build_multipart non_path_parameters else - @env['CONTENT_TYPE'] ||= 'application/x-www-form-urlencoded' + get_header('CONTENT_TYPE') do |k| + set_header k, 'application/x-www-form-urlencoded' + end # FIXME: setting `request_parametes` is normally handled by the # params parser middleware, and we should remove this roundtripping @@ -92,8 +98,8 @@ module ActionController end end - @env['CONTENT_LENGTH'] = data.length.to_s - @env['rack.input'] = StringIO.new(data) + set_header 'CONTENT_LENGTH', data.length.to_s + set_header 'rack.input', StringIO.new(data) end @env["PATH_INFO"] ||= generated_path @@ -450,7 +456,7 @@ module ActionController end if body.present? - @request.env['RAW_POST_DATA'] = body + @request.set_header 'RAW_POST_DATA', body end if http_method.present? @@ -472,15 +478,15 @@ module ActionController end self.cookies.update @request.cookies - @request.env['HTTP_COOKIE'] = cookies.to_header - @request.env['action_dispatch.cookies'] = nil + @request.set_header 'HTTP_COOKIE', cookies.to_header + @request.delete_header 'action_dispatch.cookies' @request = TestRequest.new scrub_env!(@request.env), @request.session @response = build_response @response_klass @response.request = @request @controller.recycle! - @request.env['REQUEST_METHOD'] = http_method + @request.set_header 'REQUEST_METHOD', http_method parameters = parameters.symbolize_keys @@ -494,24 +500,28 @@ module ActionController @request.flash.update(flash || {}) if xhr - @request.env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest' - @request.env['HTTP_ACCEPT'] ||= [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ') + @request.set_header 'HTTP_X_REQUESTED_WITH', 'XMLHttpRequest' + @request.get_header('HTTP_ACCEPT') do |k| + @request.set_header k, [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ') + end end @controller.request = @request @controller.response = @response - @request.env["SCRIPT_NAME"] ||= @controller.config.relative_url_root + @request.get_header("SCRIPT_NAME") do |k| + @request.set_header k, @controller.config.relative_url_root + end @controller.recycle! @controller.process(action) - @request.env.delete 'HTTP_COOKIE' + @request.delete_header 'HTTP_COOKIE' - if cookies = @request.env['action_dispatch.cookies'] + if @request.have_cookie_jar? unless @response.committed? - cookies.write(@response) - self.cookies.update(cookies.instance_variable_get(:@cookies)) + @request.cookie_jar.write(@response) + self.cookies.update(@request.cookie_jar.instance_variable_get(:@cookies)) end end @response.prepare! @@ -523,8 +533,8 @@ module ActionController end if xhr - @request.env.delete 'HTTP_X_REQUESTED_WITH' - @request.env.delete 'HTTP_ACCEPT' + @request.delete_header 'HTTP_X_REQUESTED_WITH' + @request.delete_header 'HTTP_ACCEPT' end @request.query_string = '' diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index cf4f654ed6..e4c5b0bd29 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -8,48 +8,50 @@ require 'active_support/json' module ActionDispatch class Request < Rack::Request def cookie_jar - env['action_dispatch.cookies'.freeze] ||= Cookies::CookieJar.build(self, cookies) + get_header('action_dispatch.cookies'.freeze) do |k| + self.cookie_jar = Cookies::CookieJar.build(self, cookies) + end end # :stopdoc: def have_cookie_jar? - env.key? 'action_dispatch.cookies'.freeze + has_header? 'action_dispatch.cookies'.freeze end def cookie_jar=(jar) - env['action_dispatch.cookies'.freeze] = jar + set_header 'action_dispatch.cookies'.freeze, jar end def key_generator - env[Cookies::GENERATOR_KEY] + get_header Cookies::GENERATOR_KEY end def signed_cookie_salt - env[Cookies::SIGNED_COOKIE_SALT] + get_header Cookies::SIGNED_COOKIE_SALT end def encrypted_cookie_salt - env[Cookies::ENCRYPTED_COOKIE_SALT] + get_header Cookies::ENCRYPTED_COOKIE_SALT end def encrypted_signed_cookie_salt - env[Cookies::ENCRYPTED_SIGNED_COOKIE_SALT] + get_header Cookies::ENCRYPTED_SIGNED_COOKIE_SALT end def secret_token - env[Cookies::SECRET_TOKEN] + get_header Cookies::SECRET_TOKEN end def secret_key_base - env[Cookies::SECRET_KEY_BASE] + get_header Cookies::SECRET_KEY_BASE end def cookies_serializer - env[Cookies::COOKIES_SERIALIZER] + get_header Cookies::COOKIES_SERIALIZER end def cookies_digest - env[Cookies::COOKIES_DIGEST] + get_header Cookies::COOKIES_DIGEST end # :startdoc: end diff --git a/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb index 226a688fe2..66bb74b9c5 100644 --- a/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb +++ b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb @@ -55,18 +55,17 @@ module ActionDispatch response rescue Exception => exception raise exception unless request.show_exceptions? - render_exception(env, exception) + render_exception(request, exception) end private - def render_exception(env, exception) - backtrace_cleaner = env['action_dispatch.backtrace_cleaner'] + def render_exception(request, exception) + backtrace_cleaner = request.get_header('action_dispatch.backtrace_cleaner') wrapper = ExceptionWrapper.new(backtrace_cleaner, exception) - log_error(env, wrapper) + log_error(request, wrapper) - if env['action_dispatch.show_detailed_exceptions'] - request = Request.new(env) + if request.get_header('action_dispatch.show_detailed_exceptions') traces = wrapper.traces trace_to_show = 'Application Trace' @@ -108,8 +107,8 @@ module ActionDispatch [status, {'Content-Type' => "#{format}; charset=#{Response.default_charset}", 'Content-Length' => body.bytesize.to_s}, [body]] end - def log_error(env, wrapper) - logger = logger(env) + def log_error(request, wrapper) + logger = logger(request) return unless logger exception = wrapper.exception @@ -125,8 +124,8 @@ module ActionDispatch end end - def logger(env) - env['action_dispatch.logger'] || stderr_logger + def logger(request) + request.logger || stderr_logger end def stderr_logger diff --git a/actionpack/lib/action_dispatch/middleware/flash.rb b/actionpack/lib/action_dispatch/middleware/flash.rb index 379e7ffdc5..6041f84834 100644 --- a/actionpack/lib/action_dispatch/middleware/flash.rb +++ b/actionpack/lib/action_dispatch/middleware/flash.rb @@ -6,15 +6,17 @@ module ActionDispatch # read a notice you put there or <tt>flash["notice"] = "hello"</tt> # to put a new one. def flash - @env[Flash::KEY] ||= Flash::FlashHash.from_session_value(session["flash"]) + flash = flash_hash + return flash if flash + self.flash = Flash::FlashHash.from_session_value(session["flash"]) end def flash=(flash) - @env[Flash::KEY] = flash + set_header Flash::KEY, flash end def flash_hash # :nodoc: - @env[Flash::KEY] + get_header Flash::KEY end end diff --git a/actionpack/lib/action_dispatch/middleware/public_exceptions.rb b/actionpack/lib/action_dispatch/middleware/public_exceptions.rb index 7cde76b30e..0f27984550 100644 --- a/actionpack/lib/action_dispatch/middleware/public_exceptions.rb +++ b/actionpack/lib/action_dispatch/middleware/public_exceptions.rb @@ -17,8 +17,8 @@ module ActionDispatch end def call(env) - status = env["PATH_INFO"][1..-1].to_i request = ActionDispatch::Request.new(env) + status = request.path_info[1..-1].to_i content_type = request.formats.first body = { :status => status, :error => Rack::Utils::HTTP_STATUS_CODES.fetch(status, Rack::Utils::HTTP_STATUS_CODES[500]) } diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb index 12d8dab8eb..64695f9738 100644 --- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb +++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb @@ -31,7 +31,7 @@ module ActionDispatch @app.call(env) rescue Exception => exception if request.show_exceptions? - render_exception(env, exception) + render_exception(request, exception) else raise exception end @@ -39,14 +39,14 @@ module ActionDispatch private - def render_exception(env, exception) - backtrace_cleaner = env['action_dispatch.backtrace_cleaner'] + def render_exception(request, exception) + backtrace_cleaner = request.get_header 'action_dispatch.backtrace_cleaner' wrapper = ExceptionWrapper.new(backtrace_cleaner, exception) status = wrapper.status_code - env["action_dispatch.exception"] = wrapper.exception - env["action_dispatch.original_path"] = env["PATH_INFO"] - env["PATH_INFO"] = "/#{status}" - response = @exceptions_app.call(env) + request.set_header "action_dispatch.exception", wrapper.exception + request.set_header "action_dispatch.original_path", request.path_info + request.path_info = "/#{status}" + response = @exceptions_app.call(request.env) response[1]['X-Cascade'] == 'pass' ? pass_response(status) : response rescue Exception => failsafe_error $stderr.puts "Error during failsafe response: #{failsafe_error}\n #{failsafe_error.backtrace * "\n "}" diff --git a/actionpack/test/abstract/translation_test.rb b/actionpack/test/abstract/translation_test.rb index 8289252dfc..1435928578 100644 --- a/actionpack/test/abstract/translation_test.rb +++ b/actionpack/test/abstract/translation_test.rb @@ -24,7 +24,6 @@ module AbstractController }, }, }) - @controller.stubs(action_name: :index) end def test_action_controller_base_responds_to_translate @@ -44,25 +43,34 @@ module AbstractController end def test_lazy_lookup - assert_equal 'bar', @controller.t('.foo') + @controller.stub :action_name, :index do + assert_equal 'bar', @controller.t('.foo') + end end def test_lazy_lookup_with_symbol - assert_equal 'bar', @controller.t(:'.foo') + @controller.stub :action_name, :index do + assert_equal 'bar', @controller.t(:'.foo') + end end def test_lazy_lookup_fallback - assert_equal 'no_action_tr', @controller.t(:'.no_action') + @controller.stub :action_name, :index do + assert_equal 'no_action_tr', @controller.t(:'.no_action') + end end def test_default_translation - assert_equal 'bar', @controller.t('one.two') + @controller.stub :action_name, :index do + assert_equal 'bar', @controller.t('one.two') + end end def test_localize time, expected = Time.gm(2000), 'Sat, 01 Jan 2000 00:00:00 +0000' - I18n.stubs(:localize).with(time).returns(expected) - assert_equal expected, @controller.l(time) + I18n.stub :localize, expected do + assert_equal expected, @controller.l(time) + end end end end diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index fa0b6087ba..7ea4249e7e 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -397,6 +397,7 @@ def jruby_skip(message = '') end require 'mocha/setup' # FIXME: stop using mocha +require 'minitest/mock' class ForkingExecutor class Server diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index 9acdc29aeb..4a6086cf78 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -1,6 +1,5 @@ require 'abstract_unit' require 'controller/fake_models' -require 'pathname' class TestControllerWithExtraEtags < ActionController::Base etag { nil } @@ -295,9 +294,10 @@ class ExpiresInRenderTest < ActionController::TestCase def test_date_header_when_expires_in time = Time.mktime(2011,10,30) - Time.stubs(:now).returns(time) - get :conditional_hello_with_expires_in - assert_equal Time.now.httpdate, @response.headers["Date"] + Time.stub :now, time do + get :conditional_hello_with_expires_in + assert_equal Time.now.httpdate, @response.headers["Date"] + end end end diff --git a/actionpack/test/controller/request_forgery_protection_test.rb b/actionpack/test/controller/request_forgery_protection_test.rb index 868520a219..f3b0ecac10 100644 --- a/actionpack/test/controller/request_forgery_protection_test.rb +++ b/actionpack/test/controller/request_forgery_protection_test.rb @@ -1,5 +1,4 @@ require 'abstract_unit' -require 'digest/sha1' require "active_support/log_subscriber/test_helper" # common controller actions @@ -133,7 +132,6 @@ end module RequestForgeryProtectionTests def setup @token = "cf50faa3fe97702ca1ae" - @controller.stubs(:form_authenticity_token).returns(@token) @controller.stubs(:valid_authenticity_token?).with{ |_, t| t == @token }.returns(true) @controller.stubs(:valid_authenticity_token?).with{ |_, t| t != @token }.returns(false) @old_request_forgery_protection_token = ActionController::Base.request_forgery_protection_token @@ -145,17 +143,21 @@ module RequestForgeryProtectionTests end def test_should_render_form_with_token_tag - assert_not_blocked do - get :index + @controller.stub :form_authenticity_token, @token do + assert_not_blocked do + get :index + end + assert_select 'form>input[name=?][value=?]', 'custom_authenticity_token', @token end - assert_select 'form>input[name=?][value=?]', 'custom_authenticity_token', @token end def test_should_render_button_to_with_token_tag - assert_not_blocked do - get :show_button + @controller.stub :form_authenticity_token, @token do + assert_not_blocked do + get :show_button + end + assert_select 'form>input[name=?][value=?]', 'custom_authenticity_token', @token end - assert_select 'form>input[name=?][value=?]', 'custom_authenticity_token', @token end def test_should_render_form_without_token_tag_if_remote @@ -199,17 +201,21 @@ module RequestForgeryProtectionTests end def test_should_render_form_with_token_tag_if_remote_and_authenticity_token_requested - assert_not_blocked do - get :form_for_remote_with_token + @controller.stub :form_authenticity_token, @token do + assert_not_blocked do + get :form_for_remote_with_token + end + assert_select 'form>input[name=?][value=?]', 'custom_authenticity_token', @token end - assert_select 'form>input[name=?][value=?]', 'custom_authenticity_token', @token end def test_should_render_form_with_token_tag_with_authenticity_token_requested - assert_not_blocked do - get :form_for_with_token + @controller.stub :form_authenticity_token, @token do + assert_not_blocked do + get :form_for_with_token + end + assert_select 'form>input[name=?][value=?]', 'custom_authenticity_token', @token end - assert_select 'form>input[name=?][value=?]', 'custom_authenticity_token', @token end def test_should_allow_get @@ -402,11 +408,12 @@ class RequestForgeryProtectionControllerUsingResetSessionTest < ActionController end test 'should emit a csrf-param meta tag and a csrf-token meta tag' do - @controller.stubs(:form_authenticity_token).returns(@token + '<=?') - get :meta - assert_select 'meta[name=?][content=?]', 'csrf-param', 'custom_authenticity_token' - assert_select 'meta[name=?]', 'csrf-token' - assert_match(/cf50faa3fe97702ca1ae<=\?/, @response.body) + @controller.stub :form_authenticity_token, @token + '<=?' do + get :meta + assert_select 'meta[name=?][content=?]', 'csrf-param', 'custom_authenticity_token' + assert_select 'meta[name=?]', 'csrf-token' + assert_match(/cf50faa3fe97702ca1ae<=\?/, @response.body) + end end end @@ -485,30 +492,36 @@ class FreeCookieControllerTest < ActionController::TestCase def setup @controller = FreeCookieController.new @token = "cf50faa3fe97702ca1ae" - - SecureRandom.stubs(:base64).returns(@token) super end def test_should_not_render_form_with_token_tag - get :index - assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token, false + SecureRandom.stub :base64, @token do + get :index + assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token, false + end end def test_should_not_render_button_to_with_token_tag - get :show_button - assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token, false + SecureRandom.stub :base64, @token do + get :show_button + assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token, false + end end def test_should_allow_all_methods_without_token - [:post, :patch, :put, :delete].each do |method| - assert_nothing_raised { send(method, :index)} + SecureRandom.stub :base64, @token do + [:post, :patch, :put, :delete].each do |method| + assert_nothing_raised { send(method, :index)} + end end end test 'should not emit a csrf-token meta tag' do - get :meta - assert @response.body.blank? + SecureRandom.stub :base64, @token do + get :meta + assert @response.body.blank? + end end end @@ -529,11 +542,11 @@ class CustomAuthenticityParamControllerTest < ActionController::TestCase def test_should_not_warn_if_form_authenticity_param_matches_form_authenticity_token ActionController::Base.logger = @logger - @controller.stubs(:valid_authenticity_token?).returns(:true) - begin - post :index, params: { custom_token_name: 'foobar' } - assert_equal 0, @logger.logged(:warn).size + @controller.stub :valid_authenticity_token?, :true do + post :index, params: { custom_token_name: 'foobar' } + assert_equal 0, @logger.logged(:warn).size + end ensure ActionController::Base.logger = @old_logger end diff --git a/actionpack/test/controller/test_case_test.rb b/actionpack/test/controller/test_case_test.rb index 1c5de983d8..1f073b4124 100644 --- a/actionpack/test/controller/test_case_test.rb +++ b/actionpack/test/controller/test_case_test.rb @@ -4,6 +4,8 @@ require 'active_support/json/decoding' require 'rails/engine' class TestCaseTest < ActionController::TestCase + def self.fixture_path; end; + class TestController < ActionController::Base def no_op render plain: 'dummy' @@ -849,10 +851,10 @@ XML end def test_fixture_path_is_accessed_from_self_instead_of_active_support_test_case - TestCaseTest.stubs(:fixture_path).returns(FILES_DIR) - - uploaded_file = fixture_file_upload('/mona_lisa.jpg', 'image/png') - assert_equal File.open("#{FILES_DIR}/mona_lisa.jpg", READ_PLAIN).read, uploaded_file.read + TestCaseTest.stub :fixture_path, FILES_DIR do + uploaded_file = fixture_file_upload('/mona_lisa.jpg', 'image/png') + assert_equal File.open("#{FILES_DIR}/mona_lisa.jpg", READ_PLAIN).read, uploaded_file.read + end end def test_test_uploaded_file_with_binary @@ -893,13 +895,13 @@ XML end def test_fixture_file_upload_relative_to_fixture_path - TestCaseTest.stubs(:fixture_path).returns(FILES_DIR) - uploaded_file = fixture_file_upload("mona_lisa.jpg", "image/jpg") - assert_equal File.open("#{FILES_DIR}/mona_lisa.jpg", READ_PLAIN).read, uploaded_file.read + TestCaseTest.stub :fixture_path, FILES_DIR do + uploaded_file = fixture_file_upload("mona_lisa.jpg", "image/jpg") + assert_equal File.open("#{FILES_DIR}/mona_lisa.jpg", READ_PLAIN).read, uploaded_file.read + end end def test_fixture_file_upload_ignores_nil_fixture_path - TestCaseTest.stubs(:fixture_path).returns(nil) uploaded_file = fixture_file_upload("#{FILES_DIR}/mona_lisa.jpg", "image/jpg") assert_equal File.open("#{FILES_DIR}/mona_lisa.jpg", READ_PLAIN).read, uploaded_file.read end diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb index aca28ae8d1..3454e60697 100644 --- a/actionpack/test/dispatch/cookies_test.rb +++ b/actionpack/test/dispatch/cookies_test.rb @@ -321,10 +321,12 @@ class CookiesTest < ActionController::TestCase end def test_setting_cookie_with_secure_when_always_write_cookie_is_true - ActionDispatch::Cookies::CookieJar.any_instance.stubs(:always_write_cookie).returns(true) + old_cookie, @request.cookie_jar.always_write_cookie = @request.cookie_jar.always_write_cookie, true get :authenticate_with_secure assert_cookie_header "user_name=david; path=/; secure" assert_equal({"user_name" => "david"}, @response.cookies) + ensure + @request.cookie_jar.always_write_cookie = old_cookie end def test_not_setting_cookie_with_secure diff --git a/actionpack/test/dispatch/exception_wrapper_test.rb b/actionpack/test/dispatch/exception_wrapper_test.rb index 3dac582407..f37cce4d45 100644 --- a/actionpack/test/dispatch/exception_wrapper_test.rb +++ b/actionpack/test/dispatch/exception_wrapper_test.rb @@ -17,8 +17,6 @@ module ActionDispatch end setup do - Rails.stubs(:root).returns(Pathname.new('.')) - @cleaner = ActiveSupport::BacktraceCleaner.new @cleaner.add_silencer { |line| line !~ /^lib/ } end diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 332a550de0..8972f3e74d 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -1,4 +1,3 @@ -# encoding: UTF-8 require 'erb' require 'abstract_unit' require 'controller/fake_controllers' @@ -4190,11 +4189,11 @@ class TestNamedRouteUrlHelpers < ActionDispatch::IntegrationTest include Routes.url_helpers test "url helpers do not ignore nil parameters when using non-optimized routes" do - Routes.stubs(:optimize_routes_generation?).returns(false) - - get "/categories/1" - assert_response :success - assert_raises(ActionController::UrlGenerationError) { product_path(nil) } + Routes.stub :optimize_routes_generation?, false do + get "/categories/1" + assert_response :success + assert_raises(ActionController::UrlGenerationError) { product_path(nil) } + end end end diff --git a/actionpack/test/dispatch/session/cookie_store_test.rb b/actionpack/test/dispatch/session/cookie_store_test.rb index e432c65c62..f07e215e3a 100644 --- a/actionpack/test/dispatch/session/cookie_store_test.rb +++ b/actionpack/test/dispatch/session/cookie_store_test.rb @@ -274,28 +274,32 @@ class CookieStoreTest < ActionDispatch::IntegrationTest with_test_route_set(:expire_after => 5.hours) do # First request accesses the session time = Time.local(2008, 4, 24) - Time.stubs(:now).returns(time) - expected_expiry = (time + 5.hours).gmtime.strftime("%a, %d %b %Y %H:%M:%S -0000") + cookie_body = nil - cookies[SessionKey] = SignedBar + Time.stub :now, time do + expected_expiry = (time + 5.hours).gmtime.strftime("%a, %d %b %Y %H:%M:%S -0000") - get '/set_session_value' - assert_response :success + cookies[SessionKey] = SignedBar - cookie_body = response.body - assert_equal "_myapp_session=#{cookie_body}; path=/; expires=#{expected_expiry}; HttpOnly", - headers['Set-Cookie'] + get '/set_session_value' + assert_response :success + + cookie_body = response.body + assert_equal "_myapp_session=#{cookie_body}; path=/; expires=#{expected_expiry}; HttpOnly", + headers['Set-Cookie'] + end # Second request does not access the session time = Time.local(2008, 4, 25) - Time.stubs(:now).returns(time) - expected_expiry = (time + 5.hours).gmtime.strftime("%a, %d %b %Y %H:%M:%S -0000") + Time.stub :now, time do + expected_expiry = (time + 5.hours).gmtime.strftime("%a, %d %b %Y %H:%M:%S -0000") - get '/no_session_access' - assert_response :success + get '/no_session_access' + assert_response :success - assert_equal "_myapp_session=#{cookie_body}; path=/; expires=#{expected_expiry}; HttpOnly", - headers['Set-Cookie'] + assert_equal "_myapp_session=#{cookie_body}; path=/; expires=#{expected_expiry}; HttpOnly", + headers['Set-Cookie'] + end end end diff --git a/actionpack/test/dispatch/test_request_test.rb b/actionpack/test/dispatch/test_request_test.rb index ede1cec4e6..51c469a61a 100644 --- a/actionpack/test/dispatch/test_request_test.rb +++ b/actionpack/test/dispatch/test_request_test.rb @@ -53,10 +53,8 @@ class TestRequestTest < ActiveSupport::TestCase assert_cookies({"user_name" => "david"}, req.cookie_jar) end - test "does not complain when Rails.application is nil" do - Rails.stubs(:application).returns(nil) + test "does not complain when there is no application config" do req = ActionDispatch::TestRequest.create({}) - assert_equal false, req.env.empty? end diff --git a/actionview/test/abstract_unit.rb b/actionview/test/abstract_unit.rb index 35f520ed1c..12870acaa6 100644 --- a/actionview/test/abstract_unit.rb +++ b/actionview/test/abstract_unit.rb @@ -16,6 +16,7 @@ silence_warnings do end require 'active_support/testing/autorun' +require 'active_support/testing/method_call_assertions' require 'action_controller' require 'action_view' require 'action_view/testing/resolvers' @@ -281,3 +282,6 @@ def jruby_skip(message = '') end require 'mocha/setup' # FIXME: stop using mocha +class ActiveSupport::TestCase + include ActiveSupport::Testing::MethodCallAssertions +end diff --git a/actionview/test/template/asset_tag_helper_test.rb b/actionview/test/template/asset_tag_helper_test.rb index 01fc66bed6..496b33b35e 100644 --- a/actionview/test/template/asset_tag_helper_test.rb +++ b/actionview/test/template/asset_tag_helper_test.rb @@ -588,11 +588,13 @@ class AssetTagHelperTest < ActionView::TestCase end end - @controller.request.stubs(:ssl?).returns(false) - assert_equal "http://assets15.example.com/images/xml.png", image_path("xml.png") + @controller.request.stub(:ssl?, false) do + assert_equal "http://assets15.example.com/images/xml.png", image_path("xml.png") + end - @controller.request.stubs(:ssl?).returns(true) - assert_equal "http://localhost/images/xml.png", image_path("xml.png") + @controller.request.stub(:ssl?, true) do + assert_equal "http://localhost/images/xml.png", image_path("xml.png") + end end end diff --git a/actionview/test/template/form_helper_test.rb b/actionview/test/template/form_helper_test.rb index 37f2d7cad6..aef137935a 100644 --- a/actionview/test/template/form_helper_test.rb +++ b/actionview/test/template/form_helper_test.rb @@ -2003,21 +2003,22 @@ class FormHelperTest < ActionView::TestCase def test_form_for_with_remote_without_html @post.persisted = false - @post.stubs(:to_key).returns(nil) - form_for(@post, remote: true) do |f| - concat f.text_field(:title) - concat f.text_area(:body) - concat f.check_box(:secret) - end + @post.stub(:to_key, nil) do + form_for(@post, remote: true) do |f| + concat f.text_field(:title) + concat f.text_area(:body) + concat f.check_box(:secret) + end - expected = whole_form("/posts", "new_post", "new_post", remote: true) do - "<input name='post[title]' type='text' id='post_title' value='Hello World' />" + - "<textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea>" + - "<input name='post[secret]' type='hidden' value='0' />" + - "<input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />" - end + expected = whole_form("/posts", "new_post", "new_post", remote: true) do + "<input name='post[title]' type='text' id='post_title' value='Hello World' />" + + "<textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea>" + + "<input name='post[secret]' type='hidden' value='0' />" + + "<input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />" + end - assert_dom_equal expected, output_buffer + assert_dom_equal expected, output_buffer + end end def test_form_for_without_object @@ -2220,16 +2221,17 @@ class FormHelperTest < ActionView::TestCase def test_submit_with_object_as_new_record_and_locale_strings with_locale :submit do @post.persisted = false - @post.stubs(:to_key).returns(nil) - form_for(@post) do |f| - concat f.submit - end + @post.stub(:to_key, nil) do + form_for(@post) do |f| + concat f.submit + end - expected = whole_form('/posts', 'new_post', 'new_post') do - "<input name='commit' data-disable-with='Create Post' type='submit' value='Create Post' />" - end + expected = whole_form('/posts', 'new_post', 'new_post') do + "<input name='commit' data-disable-with='Create Post' type='submit' value='Create Post' />" + end - assert_dom_equal expected, output_buffer + assert_dom_equal expected, output_buffer + end end end @@ -2807,11 +2809,12 @@ class FormHelperTest < ActionView::TestCase def test_nested_fields_label_translation_with_more_than_10_records @post.comments = Array.new(11) { |id| Comment.new(id + 1) } - I18n.expects(:t).with('post.comments.body', default: [:"comment.body", ''], scope: "helpers.label").times(11).returns "Write body here" - - form_for(@post) do |f| - f.fields_for(:comments) do |cf| - concat cf.label(:body) + params = 11.times.map { ['post.comments.body', default: [:"comment.body", ''], scope: "helpers.label"] } + assert_called_with(I18n, :t, params, returns: "Write body here") do + form_for(@post) do |f| + f.fields_for(:comments) do |cf| + concat cf.label(:body) + end end end end diff --git a/actionview/test/template/form_options_helper_i18n_test.rb b/actionview/test/template/form_options_helper_i18n_test.rb index 4972ea6511..26ede09a5f 100644 --- a/actionview/test/template/form_options_helper_i18n_test.rb +++ b/actionview/test/template/form_options_helper_i18n_test.rb @@ -14,8 +14,9 @@ class FormOptionsHelperI18nTests < ActionView::TestCase end def test_select_with_prompt_true_translates_prompt_message - I18n.expects(:translate).with('helpers.select.prompt', { :default => 'Please select' }) - select('post', 'category', [], :prompt => true) + assert_called_with(I18n, :translate, ['helpers.select.prompt', { :default => 'Please select' }]) do + select('post', 'category', [], :prompt => true) + end end def test_select_with_translated_prompt diff --git a/actionview/test/template/lookup_context_test.rb b/actionview/test/template/lookup_context_test.rb index 4f7823045e..14dafd7d63 100644 --- a/actionview/test/template/lookup_context_test.rb +++ b/actionview/test/template/lookup_context_test.rb @@ -108,10 +108,11 @@ class LookupContextTest < ActiveSupport::TestCase end test "found templates respects given formats if one cannot be found from template or handler" do - ActionView::Template::Handlers::Builder.expects(:default_format).returns(nil) - @lookup_context.formats = [:text] - template = @lookup_context.find("hello", %w(test)) - assert_equal [:text], template.formats + assert_called(ActionView::Template::Handlers::Builder, :default_format, returns: nil) do + @lookup_context.formats = [:text] + template = @lookup_context.find("hello", %w(test)) + assert_equal [:text], template.formats + end end test "adds fallbacks to view paths when required" do @@ -210,45 +211,50 @@ end class LookupContextWithFalseCaching < ActiveSupport::TestCase def setup @resolver = ActionView::FixtureResolver.new("test/_foo.erb" => ["Foo", Time.utc(2000)]) - ActionView::Resolver.stubs(:caching?).returns(false) @lookup_context = ActionView::LookupContext.new(@resolver, {}) end test "templates are always found in the resolver but timestamp is checked before being compiled" do - template = @lookup_context.find("foo", %w(test), true) - assert_equal "Foo", template.source - - # Now we are going to change the template, but it won't change the returned template - # since the timestamp is the same. - @resolver.hash["test/_foo.erb"][0] = "Bar" - template = @lookup_context.find("foo", %w(test), true) - assert_equal "Foo", template.source - - # Now update the timestamp. - @resolver.hash["test/_foo.erb"][1] = Time.now.utc - template = @lookup_context.find("foo", %w(test), true) - assert_equal "Bar", template.source + ActionView::Resolver.stub(:caching?, false) do + template = @lookup_context.find("foo", %w(test), true) + assert_equal "Foo", template.source + + # Now we are going to change the template, but it won't change the returned template + # since the timestamp is the same. + @resolver.hash["test/_foo.erb"][0] = "Bar" + template = @lookup_context.find("foo", %w(test), true) + assert_equal "Foo", template.source + + # Now update the timestamp. + @resolver.hash["test/_foo.erb"][1] = Time.now.utc + template = @lookup_context.find("foo", %w(test), true) + assert_equal "Bar", template.source + end end test "if no template was found in the second lookup, with no cache, raise error" do - template = @lookup_context.find("foo", %w(test), true) - assert_equal "Foo", template.source + ActionView::Resolver.stub(:caching?, false) do + template = @lookup_context.find("foo", %w(test), true) + assert_equal "Foo", template.source - @resolver.hash.clear - assert_raise ActionView::MissingTemplate do - @lookup_context.find("foo", %w(test), true) + @resolver.hash.clear + assert_raise ActionView::MissingTemplate do + @lookup_context.find("foo", %w(test), true) + end end end test "if no template was cached in the first lookup, retrieval should work in the second call" do - @resolver.hash.clear - assert_raise ActionView::MissingTemplate do - @lookup_context.find("foo", %w(test), true) - end + ActionView::Resolver.stub(:caching?, false) do + @resolver.hash.clear + assert_raise ActionView::MissingTemplate do + @lookup_context.find("foo", %w(test), true) + end - @resolver.hash["test/_foo.erb"] = ["Foo", Time.utc(2000)] - template = @lookup_context.find("foo", %w(test), true) - assert_equal "Foo", template.source + @resolver.hash["test/_foo.erb"] = ["Foo", Time.utc(2000)] + template = @lookup_context.find("foo", %w(test), true) + assert_equal "Foo", template.source + end end end diff --git a/actionview/test/template/template_test.rb b/actionview/test/template/template_test.rb index d3b51cd629..921011b073 100644 --- a/actionview/test/template/template_test.rb +++ b/actionview/test/template/template_test.rb @@ -118,15 +118,17 @@ class TestERBTemplate < ActiveSupport::TestCase def test_refresh_with_templates @template = new_template("Hello", :virtual_path => "test/foo/bar") @template.locals = [:key] - @context.lookup_context.expects(:find_template).with("bar", %w(test/foo), false, [:key]).returns("template") - assert_equal "template", @template.refresh(@context) + assert_called_with(@context.lookup_context, :find_template,["bar", %w(test/foo), false, [:key]], returns: "template") do + assert_equal "template", @template.refresh(@context) + end end def test_refresh_with_partials @template = new_template("Hello", :virtual_path => "test/_foo") @template.locals = [:key] - @context.lookup_context.expects(:find_template).with("foo", %w(test), true, [:key]).returns("partial") - assert_equal "partial", @template.refresh(@context) + assert_called_with(@context.lookup_context, :find_template,[ "foo", %w(test), true, [:key]], returns: "partial") do + assert_equal "partial", @template.refresh(@context) + end end def test_refresh_raises_an_error_without_virtual_path diff --git a/actionview/test/template/test_case_test.rb b/actionview/test/template/test_case_test.rb index 80fbaa8392..b057d43ee0 100644 --- a/actionview/test/template/test_case_test.rb +++ b/actionview/test/template/test_case_test.rb @@ -20,6 +20,7 @@ module ActionView class TestCase helper ASharedTestHelper + DeveloperStruct = Struct.new(:name) module SharedTests def self.included(test_case) @@ -50,7 +51,7 @@ module ActionView end test "works without testing a helper module" do - assert_equal 'Eloy', render('developers/developer', :developer => stub(:name => 'Eloy')) + assert_equal 'Eloy', render('developers/developer', :developer => DeveloperStruct.new('Eloy')) end test "can render a layout with block" do @@ -69,13 +70,15 @@ module ActionView end test "delegates notice to request.flash[:notice]" do - view.request.flash.expects(:[]).with(:notice) - view.notice + assert_called_with(view.request.flash, :[], [:notice]) do + view.notice + end end test "delegates alert to request.flash[:alert]" do - view.request.flash.expects(:[]).with(:alert) - view.alert + assert_called_with(view.request.flash, :[], [:alert]) do + view.alert + end end test "uses controller lookup context" do @@ -119,7 +122,7 @@ module ActionView test "helper class that is being tested is always included in view instance" do @controller.controller_path = 'test' - @customers = [stub(:name => 'Eloy'), stub(:name => 'Manfred')] + @customers = [DeveloperStruct.new('Eloy'), DeveloperStruct.new('Manfred')] assert_match(/Hello: EloyHello: Manfred/, render(:partial => 'test/from_helper')) end end @@ -255,15 +258,15 @@ module ActionView end test "is able to render partials with local variables" do - assert_equal 'Eloy', render('developers/developer', :developer => stub(:name => 'Eloy')) + assert_equal 'Eloy', render('developers/developer', :developer => DeveloperStruct.new('Eloy')) assert_equal 'Eloy', render(:partial => 'developers/developer', - :locals => { :developer => stub(:name => 'Eloy') }) + :locals => { :developer => DeveloperStruct.new('Eloy') }) end test "is able to render partials from templates and also use instance variables" do @controller.controller_path = "test" - @customers = [stub(:name => 'Eloy'), stub(:name => 'Manfred')] + @customers = [DeveloperStruct.new('Eloy'), DeveloperStruct.new('Manfred')] assert_match(/Hello: EloyHello: Manfred/, render(:file => 'test/list')) end @@ -272,7 +275,7 @@ module ActionView view - @customers = [stub(:name => 'Eloy'), stub(:name => 'Manfred')] + @customers = [DeveloperStruct.new('Eloy'), DeveloperStruct.new('Manfred')] assert_match(/Hello: EloyHello: Manfred/, render(:file => 'test/list')) end diff --git a/actionview/test/template/translation_helper_test.rb b/actionview/test/template/translation_helper_test.rb index 5dc281adb2..749d0dd7fd 100644 --- a/actionview/test/template/translation_helper_test.rb +++ b/actionview/test/template/translation_helper_test.rb @@ -42,14 +42,16 @@ class TranslationHelperTest < ActiveSupport::TestCase end def test_delegates_setting_to_i18n - I18n.expects(:translate).with(:foo, :locale => 'en', :raise => true).returns("") - translate :foo, :locale => 'en' + assert_called_with(I18n, :translate, [:foo, :locale => 'en', :raise => true], returns: "") do + translate :foo, :locale => 'en' + end end def test_delegates_localize_to_i18n @time = Time.utc(2008, 7, 8, 12, 18, 38) - I18n.expects(:localize).with(@time) - localize @time + assert_called_with(I18n, :localize, [@time]) do + localize @time + end end def test_returns_missing_translation_message_wrapped_into_span @@ -125,8 +127,9 @@ class TranslationHelperTest < ActiveSupport::TestCase end def test_translate_escapes_interpolations_in_translations_with_a_html_suffix + word_struct = Struct.new(:to_s) assert_equal '<a>Hello <World></a>', translate(:'translations.interpolated_html', :word => '<World>') - assert_equal '<a>Hello <World></a>', translate(:'translations.interpolated_html', :word => stub(:to_s => "<World>")) + assert_equal '<a>Hello <World></a>', translate(:'translations.interpolated_html', :word => word_struct.new("<World>")) end def test_translate_with_html_count diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 0841b2f679..d880470d97 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,9 @@ +* Only try to nullify has_one target association if the record is persisted. + + Fixes #21223. + + *Agis Anastasopoulos* + * Uniqueness validator raises descriptive error when running on a persisted record without primary key. diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index b5a8c81fe4..29c711082a 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -227,6 +227,31 @@ module ActiveRecord @association.last(*args) end + # Gives a record (or N records if a parameter is supplied) from the collection + # using the same rules as <tt>ActiveRecord::Base.take</tt>. + # + # class Person < ActiveRecord::Base + # has_many :pets + # end + # + # person.pets + # # => [ + # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>, + # # #<Pet id: 2, name: "Spook", person_id: 1>, + # # #<Pet id: 3, name: "Choo-Choo", person_id: 1> + # # ] + # + # person.pets.take # => #<Pet id: 1, name: "Fancy-Fancy", person_id: 1> + # + # person.pets.take(2) + # # => [ + # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>, + # # #<Pet id: 2, name: "Spook", person_id: 1> + # # ] + # + # another_person_without.pets # => [] + # another_person_without.pets.take # => nil + # another_person_without.pets.take(2) # => [] def take(n = nil) @association.take(n) end diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb index 5a92bc5e8a..1829453d73 100644 --- a/activerecord/lib/active_record/associations/has_one_association.rb +++ b/activerecord/lib/active_record/associations/has_one_association.rb @@ -65,7 +65,7 @@ module ActiveRecord when :destroy target.destroy when :nullify - target.update_columns(reflection.foreign_key => nil) + target.update_columns(reflection.foreign_key => nil) if target.persisted? end end end diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb index 5a8afaf4d2..d46e7ad235 100644 --- a/activerecord/test/cases/associations/has_one_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_associations_test.rb @@ -107,6 +107,14 @@ class HasOneAssociationsTest < ActiveRecord::TestCase assert_nil Account.find(old_account_id).firm_id end + def test_nullification_on_destroyed_association + developer = Developer.create!(name: "Someone") + ship = Ship.create!(name: "Planet Caravan", developer: developer) + ship.destroy + assert !ship.persisted? + assert !developer.persisted? + end + def test_natural_assignment_to_nil_after_destroy firm = companies(:rails_core) old_account_id = firm.account.id diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index 782a7906cc..128a242495 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -132,13 +132,9 @@ class MigrationTest < ActiveRecord::TestCase Person.connection.drop_table :testings2, if_exists: true end - def connection - ActiveRecord::Base.connection - end - def test_migration_instance_has_connection migration = Class.new(ActiveRecord::Migration).new - assert_equal connection, migration.connection + assert_equal ActiveRecord::Base.connection, migration.connection end def test_method_missing_delegates_to_connection diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb index 42e7507631..cdf63957f4 100644 --- a/activerecord/test/cases/persistence_test.rb +++ b/activerecord/test/cases/persistence_test.rb @@ -17,6 +17,7 @@ require 'models/minivan' require 'models/owner' require 'models/person' require 'models/pet' +require 'models/ship' require 'models/toy' require 'rexml/document' diff --git a/activerecord/test/cases/readonly_test.rb b/activerecord/test/cases/readonly_test.rb index 1c919f0b57..5f6eb41240 100644 --- a/activerecord/test/cases/readonly_test.rb +++ b/activerecord/test/cases/readonly_test.rb @@ -7,6 +7,7 @@ require 'models/computer' require 'models/project' require 'models/reader' require 'models/person' +require 'models/ship' class ReadOnlyTest < ActiveRecord::TestCase fixtures :authors, :posts, :comments, :developers, :projects, :developers_projects, :people, :readers diff --git a/activerecord/test/models/developer.rb b/activerecord/test/models/developer.rb index d2a5a7fc49..8ac7a9df6a 100644 --- a/activerecord/test/models/developer.rb +++ b/activerecord/test/models/developer.rb @@ -50,6 +50,7 @@ class Developer < ActiveRecord::Base has_many :firms, :through => :contracts, :source => :firm has_many :comments, ->(developer) { where(body: "I'm #{developer.name}") } has_many :ratings, through: :comments + has_one :ship, dependent: :nullify scope :jamises, -> { where(:name => 'Jamis') } diff --git a/activerecord/test/models/ship.rb b/activerecord/test/models/ship.rb index 95172e4d3e..e333b964ab 100644 --- a/activerecord/test/models/ship.rb +++ b/activerecord/test/models/ship.rb @@ -3,6 +3,7 @@ class Ship < ActiveRecord::Base belongs_to :pirate belongs_to :update_only_pirate, :class_name => 'Pirate' + belongs_to :developer, dependent: :destroy has_many :parts, :class_name => 'ShipPart' has_many :treasures diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index 6f34115534..994ece9244 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -673,6 +673,7 @@ ActiveRecord::Schema.define do create_table :ships, force: true do |t| t.string :name t.integer :pirate_id + t.belongs_to :developer t.integer :update_only_pirate_id # Conventionally named column for counter_cache t.integer :treasures_count, default: 0 diff --git a/activesupport/lib/active_support/log_subscriber/test_helper.rb b/activesupport/lib/active_support/log_subscriber/test_helper.rb index 70ac4a4d5c..cbc20c103d 100644 --- a/activesupport/lib/active_support/log_subscriber/test_helper.rb +++ b/activesupport/lib/active_support/log_subscriber/test_helper.rb @@ -11,6 +11,7 @@ module ActiveSupport # include ActiveSupport::LogSubscriber::TestHelper # # def setup + # super # ActiveRecord::LogSubscriber.attach_to(:active_record) # end # diff --git a/guides/source/action_controller_overview.md b/guides/source/action_controller_overview.md index 09fbdc0d32..d2173c39f6 100644 --- a/guides/source/action_controller_overview.md +++ b/guides/source/action_controller_overview.md @@ -1174,7 +1174,7 @@ end WARNING: You shouldn't do `rescue_from Exception` or `rescue_from StandardError` unless you have a particular reason as it will cause serious side-effects (e.g. you won't be able to see exception details and tracebacks during development). -NOTE: Certain exceptions are only rescuable from the `ApplicationController` class, as they are raised before the controller gets initialized and the action gets executed. See Pratik Naik's [article](http://m.onkey.org/2008/7/20/rescue-from-dispatching) on the subject for more information. +NOTE: Certain exceptions are only rescuable from the `ApplicationController` class, as they are raised before the controller gets initialized and the action gets executed. Force HTTPS protocol -------------------- diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md index 4b4d70d3ce..c0a4a0ba39 100644 --- a/guides/source/active_record_querying.md +++ b/guides/source/active_record_querying.md @@ -341,8 +341,6 @@ User.find_each(begin_at: 2000, batch_size: 5000) do |user| end ``` -Another example would be if you wanted multiple workers handling the same processing queue. You could have each worker handle 10000 records by setting the appropriate `:begin_at` option on each worker. - **`:end_at`** Similar to the `:begin_at` option, `:end_at` allows you to configure the last ID of the sequence whenever the highest ID is not the one you need. @@ -356,6 +354,10 @@ User.find_each(begin_at: 2000, end_at: 10000, batch_size: 5000) do |user| end ``` +Another example would be if you wanted multiple workers handling the same +processing queue. You could have each worker handle 10000 records by setting the +appropriate `:begin_at` and `:end_at` options on each worker. + #### `find_in_batches` The `find_in_batches` method is similar to `find_each`, since both retrieve batches of records. The difference is that `find_in_batches` yields _batches_ to the block as an array of models, instead of individually. The following example will yield to the supplied block an array of up to 1000 invoices at a time, with the final block containing any remaining invoices: diff --git a/guides/source/active_support_instrumentation.md b/guides/source/active_support_instrumentation.md index cd44c685ba..e5a560edd0 100644 --- a/guides/source/active_support_instrumentation.md +++ b/guides/source/active_support_instrumentation.md @@ -395,6 +395,38 @@ INFO. Cache stores may add their own keys } ``` +Active Job +-------- + +### enqueue_at.active_job + +| Key | Value | +| ------------ | -------------------------------------- | +| `:adapter` | QueueAdapter object processing the job | +| `:job` | Job object | + +### enqueue.active_job + +| Key | Value | +| ------------ | -------------------------------------- | +| `:adapter` | QueueAdapter object processing the job | +| `:job` | Job object | + +### perform_start.active_job + +| Key | Value | +| ------------ | -------------------------------------- | +| `:adapter` | QueueAdapter object processing the job | +| `:job` | Job object | + +### perform.active_job + +| Key | Value | +| ------------ | -------------------------------------- | +| `:adapter` | QueueAdapter object processing the job | +| `:job` | Job object | + + Railties -------- diff --git a/guides/source/api_app.md b/guides/source/api_app.md index 29ca872254..1652d91086 100644 --- a/guides/source/api_app.md +++ b/guides/source/api_app.md @@ -367,7 +367,7 @@ controller modules by default: methods returning `ActionDispatch::Request` and `ActionDispatch::Response` objects. - `ActionController::DataStreaming`: Support for `send_file` and `send_data`. -- `AbstractController::Callbacks`: Support for `before_filter` and friends. +- `AbstractController::Callbacks`: Support for `before_action` and friends. - `ActionController::Instrumentation`: Support for the instrumentation hooks defined by Action Controller (see [the instrumentation guide](active_support_instrumentation.html#action-controller)). diff --git a/guides/source/api_documentation_guidelines.md b/guides/source/api_documentation_guidelines.md index 8e9833c381..a4feff798d 100644 --- a/guides/source/api_documentation_guidelines.md +++ b/guides/source/api_documentation_guidelines.md @@ -87,7 +87,7 @@ Please use American English (*color*, *center*, *modularize*, etc). See [a list Comma ------- -Please use Oxford comma (*red, white, and blue* style). See [the detail of Oxford comma](http://en.wikipedia.org/wiki/Serial_comma). +Please use the Oxford comma (*red, white, and blue* style). See [the detail of Oxford comma](http://en.wikipedia.org/wiki/Serial_comma). Example Code ------------ diff --git a/guides/source/debugging_rails_applications.md b/guides/source/debugging_rails_applications.md index 40d5bd2012..fe8757fbd5 100644 --- a/guides/source/debugging_rails_applications.md +++ b/guides/source/debugging_rails_applications.md @@ -351,6 +351,7 @@ by asking the debugger for help. Type: `help` help -- prints this help. help <cmd> -- prints help on command <cmd>. help <cmd> <subcmd> -- prints help on <cmd>'s subcommand <subcmd>. +``` To see the previous ten lines you should type `list-` (or `l-`). @@ -458,12 +459,12 @@ The debugger can list, stop, resume and switch between running threads by using the `thread` command (or the abbreviated `th`). This command has a handful of options: -* `thread` shows the current thread. -* `thread list` is used to list all threads and their statuses. The plus + +* `thread`: shows the current thread. +* `thread list`: is used to list all threads and their statuses. The plus + character and the number indicates the current thread of execution. -* `thread stop _n_` stop thread _n_. -* `thread resume _n_` resumes thread _n_. -* `thread switch _n_` switches the current thread context to _n_. +* `thread stop _n_`: stop thread _n_. +* `thread resume _n_`: resumes thread _n_. +* `thread switch _n_`: switches the current thread context to _n_. This command is very helpful when you are debugging concurrent threads and need to verify that there are no race conditions in your code. @@ -743,12 +744,12 @@ To list all active catchpoints use `catch`. There are two ways to resume execution of an application that is stopped in the debugger: -* `continue` [line-specification] \(or `c`): resume program execution, at the +* `continue [line-specification]` \(or `c`): resume program execution, at the address where your script last stopped; any breakpoints set at that address are bypassed. The optional argument line-specification allows you to specify a line number to set a one-time breakpoint which is deleted when that breakpoint is reached. -* `finish` [frame-number] \(or `fin`): execute until the selected stack frame +* `finish [frame-number]` \(or `fin`): execute until the selected stack frame returns. If no frame number is given, the application will run until the currently selected frame returns. The currently selected frame starts out the most-recent frame or 0 if no frame positioning (e.g up, down or frame) has been diff --git a/guides/source/security.md b/guides/source/security.md index 850d111bd7..93c270064a 100644 --- a/guides/source/security.md +++ b/guides/source/security.md @@ -760,7 +760,7 @@ s = sanitize(user_input, tags: tags, attributes: %w(href title)) This allows only the given tags and does a good job, even against all kinds of tricks and malformed tags. -As a second step, _it is good practice to escape all output of the application_, especially when re-displaying user input, which hasn't been input-filtered (as in the search form example earlier on). _Use `escapeHTML()` (or its alias `h()`) method_ to replace the HTML input characters &, ", <, > by their uninterpreted representations in HTML (`&`, `"`, `<`;, and `>`). However, it can easily happen that the programmer forgets to use it, so _it is recommended to use the SafeErb gem. SafeErb reminds you to escape strings from external sources. +As a second step, _it is good practice to escape all output of the application_, especially when re-displaying user input, which hasn't been input-filtered (as in the search form example earlier on). _Use `escapeHTML()` (or its alias `h()`) method_ to replace the HTML input characters &, ", <, and > by their uninterpreted representations in HTML (`&`, `"`, `<`, and `>`). However, it can easily happen that the programmer forgets to use it, so _it is recommended to use the SafeErb gem. SafeErb reminds you to escape strings from external sources. ##### Obfuscation and Encoding Injection diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md index 21adac272d..faf6846e4b 100644 --- a/railties/CHANGELOG.md +++ b/railties/CHANGELOG.md @@ -1,3 +1,7 @@ +* `rails server` will now honour the `PORT` environment variable + + *David Cornu* + * Plugins generated using `rails plugin new` are now generated with the version number set to 0.1.0. diff --git a/railties/lib/rails/commands/server.rb b/railties/lib/rails/commands/server.rb index c8fb58ab05..d3ea441f8e 100644 --- a/railties/lib/rails/commands/server.rb +++ b/railties/lib/rails/commands/server.rb @@ -86,7 +86,7 @@ module Rails def default_options super.merge({ - Port: 3000, + Port: ENV.fetch('PORT', 3000).to_i, DoNotReverseLookup: true, environment: (ENV['RAILS_ENV'] || ENV['RACK_ENV'] || "development").dup, daemonize: false, diff --git a/railties/test/commands/server_test.rb b/railties/test/commands/server_test.rb index 7a063aeddf..3be4a74f74 100644 --- a/railties/test/commands/server_test.rb +++ b/railties/test/commands/server_test.rb @@ -44,6 +44,13 @@ class Rails::ServerTest < ActiveSupport::TestCase end end + def test_environment_with_port + switch_env "PORT", "1234" do + server = Rails::Server.new + assert_equal 1234, server.options[:Port] + end + end + def test_caching_without_option args = [] options = Rails::Server::Options.new.parse!(args) |