diff options
63 files changed, 641 insertions, 379 deletions
diff --git a/Gemfile.lock b/Gemfile.lock index 7cd4832b3a..4d9f39c3e7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -118,7 +118,7 @@ PATH activesupport (= 5.0.0.alpha) arel (= 7.0.0.alpha) activesupport (5.0.0.alpha) - concurrent-ruby (~> 0.9.0) + concurrent-ruby (~> 0.9.1) i18n (~> 0.7) json (~> 1.7, >= 1.7.7) method_source diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb index a4e4992cfe..2801db0154 100644 --- a/actionpack/lib/action_controller/caching.rb +++ b/actionpack/lib/action_controller/caching.rb @@ -1,6 +1,5 @@ require 'fileutils' require 'uri' -require 'set' module ActionController # \Caching is a cheap way of speeding up slow applications by keeping the result of diff --git a/actionpack/lib/action_controller/metal/etag_with_template_digest.rb b/actionpack/lib/action_controller/metal/etag_with_template_digest.rb index f9303efe6c..669cf55bca 100644 --- a/actionpack/lib/action_controller/metal/etag_with_template_digest.rb +++ b/actionpack/lib/action_controller/metal/etag_with_template_digest.rb @@ -25,7 +25,7 @@ module ActionController class_attribute :etag_with_template_digest self.etag_with_template_digest = true - ActiveSupport.on_load :action_view, yield: true do |action_view_base| + ActiveSupport.on_load :action_view, yield: true do etag do |options| determine_template_etag(options) if etag_with_template_digest end diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb index d14483dc72..103986e44a 100644 --- a/actionpack/lib/action_controller/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -39,10 +39,6 @@ module ActionController set_header Rack::QUERY_STRING, string end - def request_parameters=(params) - set_header "action_dispatch.request.request_parameters", params - end - def content_type=(type) set_header 'CONTENT_TYPE', type end @@ -102,7 +98,9 @@ module ActionController set_header 'rack.input', StringIO.new(data) end - @env["PATH_INFO"] ||= generated_path + get_header("PATH_INFO") do |k| + set_header k, generated_path + end path_parameters[:controller] = controller_path path_parameters[:action] = action diff --git a/actionpack/lib/action_dispatch/http/cache.rb b/actionpack/lib/action_dispatch/http/cache.rb index cc1cb3f0f0..be87343b20 100644 --- a/actionpack/lib/action_dispatch/http/cache.rb +++ b/actionpack/lib/action_dispatch/http/cache.rb @@ -8,13 +8,13 @@ module ActionDispatch HTTP_IF_NONE_MATCH = 'HTTP_IF_NONE_MATCH'.freeze def if_modified_since - if since = env[HTTP_IF_MODIFIED_SINCE] + if since = get_header(HTTP_IF_MODIFIED_SINCE) Time.rfc2822(since) rescue nil end end def if_none_match - env[HTTP_IF_NONE_MATCH] + get_header HTTP_IF_NONE_MATCH end def if_none_match_etags diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb index 4748a54550..a3c6f015d9 100644 --- a/actionpack/lib/action_dispatch/http/request.rb +++ b/actionpack/lib/action_dispatch/http/request.rb @@ -35,7 +35,8 @@ module ActionDispatch HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM HTTP_NEGOTIATE HTTP_PRAGMA HTTP_CLIENT_IP HTTP_X_FORWARDED_FOR HTTP_VERSION - HTTP_X_REQUEST_ID + HTTP_X_REQUEST_ID HTTP_X_FORWARDED_HOST + SERVER_ADDR ].freeze ENV_METHODS.each do |env| @@ -323,7 +324,9 @@ module ActionDispatch # Override Rack's GET method to support indifferent access def GET - @env["action_dispatch.request.query_parameters"] ||= normalize_encode_params(super || {}) + get_header("action_dispatch.request.query_parameters") do |k| + set_header k, normalize_encode_params(super || {}) + end rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e raise ActionController::BadRequest.new(:query, e) end @@ -331,7 +334,9 @@ module ActionDispatch # Override Rack's POST method to support indifferent access def POST - @env["action_dispatch.request.request_parameters"] ||= normalize_encode_params(super || {}) + get_header("action_dispatch.request.request_parameters") do + self.request_parameters = normalize_encode_params(super || {}) + end rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e raise ActionController::BadRequest.new(:request, e) end @@ -352,6 +357,7 @@ module ActionDispatch end def request_parameters=(params) + raise if params.nil? set_header("action_dispatch.request.request_parameters".freeze, params) end diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb index 6fcf49030b..e413954066 100644 --- a/actionpack/lib/action_dispatch/http/url.rb +++ b/actionpack/lib/action_dispatch/http/url.rb @@ -229,10 +229,10 @@ module ActionDispatch # req = Request.new 'HTTP_HOST' => 'example.com:8080' # req.raw_host_with_port # => "example.com:8080" def raw_host_with_port - if forwarded = env["HTTP_X_FORWARDED_HOST"].presence + if forwarded = x_forwarded_host.presence forwarded.split(/,\s?/).last else - env['HTTP_HOST'] || "#{env['SERVER_NAME'] || env['SERVER_ADDR']}:#{env['SERVER_PORT']}" + get_header('HTTP_HOST') || "#{server_name || server_addr}:#{get_header('SERVER_PORT')}" end end @@ -348,7 +348,7 @@ module ActionDispatch end def server_port - @env['SERVER_PORT'].to_i + get_header('SERVER_PORT').to_i end # Returns the \domain part of a \host, such as "rubyonrails.org" in "www.rubyonrails.org". You can specify diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index e4c5b0bd29..6d0387cf74 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -1,6 +1,4 @@ require 'active_support/core_ext/hash/keys' -require 'active_support/core_ext/module/attribute_accessors' -require 'active_support/core_ext/object/blank' require 'active_support/key_generator' require 'active_support/message_verifier' require 'active_support/json' @@ -8,7 +6,7 @@ require 'active_support/json' module ActionDispatch class Request < Rack::Request def cookie_jar - get_header('action_dispatch.cookies'.freeze) do |k| + get_header('action_dispatch.cookies'.freeze) do self.cookie_jar = Cookies::CookieJar.build(self, cookies) end end diff --git a/actionpack/lib/action_dispatch/middleware/params_parser.rb b/actionpack/lib/action_dispatch/middleware/params_parser.rb index 402ad778fa..9cde9c9b98 100644 --- a/actionpack/lib/action_dispatch/middleware/params_parser.rb +++ b/actionpack/lib/action_dispatch/middleware/params_parser.rb @@ -37,7 +37,9 @@ module ActionDispatch def call(env) request = Request.new(env) - request.request_parameters = parse_formatted_parameters(request, @parsers) + parse_formatted_parameters(request, @parsers) do |params| + request.request_parameters = params + end @app.call(env) end @@ -48,7 +50,7 @@ module ActionDispatch strategy = parsers.fetch(request.content_mime_type) { return nil } - strategy.call(request.raw_post) + yield strategy.call(request.raw_post) rescue => e # JSON or Ruby code block errors logger(request).debug "Error occurred while parsing request parameters.\nContents:\n\n#{request.raw_post}" diff --git a/actionpack/lib/action_dispatch/middleware/stack.rb b/actionpack/lib/action_dispatch/middleware/stack.rb index 0430ce3b9a..90e2ae6802 100644 --- a/actionpack/lib/action_dispatch/middleware/stack.rb +++ b/actionpack/lib/action_dispatch/middleware/stack.rb @@ -87,7 +87,7 @@ module ActionDispatch middlewares.freeze.reverse.inject(app) { |a, e| e.build(a) } end - protected + private def assert_index(index, where) index = get_class index @@ -96,8 +96,6 @@ module ActionDispatch i end - private - def get_class(klass) if klass.is_a?(String) || klass.is_a?(Symbol) classcache = ActiveSupport::Dependencies::Reference diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 51bafb539f..c3a9422ceb 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -368,7 +368,7 @@ module ActionDispatch end def dispatcher(raise_on_name_error) - @set.dispatcher raise_on_name_error + Routing::RouteSet::Dispatcher.new raise_on_name_error end end diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index af6f0de556..4e29476117 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -28,7 +28,7 @@ module ActionDispatch def serve(req) params = req.path_parameters - controller = controller_reference(req) do + controller = req.controller_class do return [404, {'X-Cascade' => 'pass'}, []] end dispatch(controller, params[:action], req) @@ -40,11 +40,6 @@ module ActionDispatch end end - protected - def controller_reference(req, &block) - req.controller_class(&block) - end - private def dispatch(controller, action, req) @@ -171,9 +166,9 @@ module ActionDispatch private def optimized_helper(args) - params = parameterize_args(args) { |k| + params = parameterize_args(args) do raise_generation_error(args) - } + end @route.format params end @@ -285,7 +280,7 @@ module ActionDispatch attr_accessor :formatter, :set, :named_routes, :default_scope, :router attr_accessor :disable_clear_and_finalize, :resources_path_names - attr_accessor :default_url_options, :dispatcher_class + attr_accessor :default_url_options attr_reader :env_key alias :routes :set @@ -328,7 +323,6 @@ module ActionDispatch @set = Journey::Routes.new @router = Journey::Router.new @set @formatter = Journey::Formatter.new self - @dispatcher_class = Routing::RouteSet::Dispatcher end def relative_url_root @@ -343,6 +337,11 @@ module ActionDispatch ActionDispatch::Request end + def make_request(env) + request_class.new env + end + private :make_request + def draw(&block) clear! unless @disable_clear_and_finalize eval_block(block) @@ -386,10 +385,6 @@ module ActionDispatch @prepend.each { |blk| eval_block(blk) } end - def dispatcher(raise_on_name_error) - dispatcher_class.new(raise_on_name_error) - end - module MountedHelpers extend ActiveSupport::Concern include UrlFor @@ -700,7 +695,7 @@ module ActionDispatch end def call(env) - req = request_class.new(env) + req = make_request(env) req.path_info = Journey::Router::Utils.normalize_path(req.path_info) @router.serve(req) end @@ -716,7 +711,7 @@ module ActionDispatch raise ActionController::RoutingError, e.message end - req = request_class.new(env) + req = make_request(env) @router.recognize(req) do |route, params| params.merge!(extras) params.each do |key, value| diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index 7ea4249e7e..1a61139dfe 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -19,7 +19,6 @@ begin rescue LoadError puts "'drb/unix' is not available" end -require 'tempfile' PROCESS_COUNT = (ENV['N'] || 4).to_i @@ -42,6 +41,8 @@ module Rails def env @_env ||= ActiveSupport::StringInquirer.new(ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "test") end + + def root; end; end end @@ -117,23 +118,38 @@ class ActionDispatch::IntegrationTest < ActiveSupport::TestCase get ':controller(/:action)' end - # Stub Rails dispatcher so it does not get controller references and - # simply return the controller#action as Rack::Body. - class StubDispatcher < ::ActionDispatch::Routing::RouteSet::Dispatcher - protected - def controller_reference(controller_param) - controller_param.params[:controller] + class DeadEndRoutes < ActionDispatch::Routing::RouteSet + # Stub Rails dispatcher so it does not get controller references and + # simply return the controller#action as Rack::Body. + class NullController + def initialize(controller_name) + @controller = controller_name + @action = nil + end + + def action(action_name) + @action = action_name + self + end + + def call(env) + [200, {'Content-Type' => 'text/html'}, ["#{@controller}##{@action}"]] + end + end + + class NullControllerRequest < DelegateClass(ActionDispatch::Request) + def controller_class + NullController.new params[:controller] + end end - def dispatch(controller, action, env) - [200, {'Content-Type' => 'text/html'}, ["#{controller}##{action}"]] + def make_request env + NullControllerRequest.new super end end - def self.stub_controllers(config = nil) - route_set = ActionDispatch::Routing::RouteSet.new(*[config].compact) - route_set.dispatcher_class = StubDispatcher - yield route_set + def self.stub_controllers(config = ActionDispatch::Routing::RouteSet::DEFAULT_CONFIG) + yield DeadEndRoutes.new(config) end def with_routing(&block) @@ -323,39 +339,37 @@ module RoutingTestHelpers end class TestSet < ActionDispatch::Routing::RouteSet - attr_reader :strict - - def initialize(block, strict = false) - @block = block - @strict = strict - super() - end - - class Dispatcher < ActionDispatch::Routing::RouteSet::Dispatcher - def initialize(defaults, set, block) - super(defaults) + class Request < DelegateClass(ActionDispatch::Request) + def initialize(target, helpers, block, strict) + super(target) + @helpers = helpers @block = block - @set = set - end - - def controller(params, default_controller=true) - super(params, @set.strict) + @strict = strict end - def controller_reference(controller_param) + def controller_class + helpers = @helpers block = @block - set = @set - super if @set.strict - Class.new(ActionController::Base) { - include set.url_helpers + Class.new(@strict ? super : ActionController::Base) { + include helpers define_method(:process) { |name| block.call(self) } def to_a; [200, {}, []]; end } end end - def dispatcher defaults - TestSet::Dispatcher.new defaults, self, @block + attr_reader :strict + + def initialize(block, strict = false) + @block = block + @strict = strict + super() + end + + private + + def make_request(env) + Request.new super, url_helpers, @block, strict end end end @@ -369,13 +383,11 @@ class ThreadsController < ResourcesController; end class MessagesController < ResourcesController; end class CommentsController < ResourcesController; end class ReviewsController < ResourcesController; end -class LogosController < ResourcesController; end class AccountsController < ResourcesController; end class AdminController < ResourcesController; end class ProductsController < ResourcesController; end class ImagesController < ResourcesController; end -class PreferencesController < ResourcesController; end module Backoffice class ProductsController < ResourcesController; end @@ -397,7 +409,7 @@ def jruby_skip(message = '') end require 'mocha/setup' # FIXME: stop using mocha -require 'minitest/mock' +require 'active_support/testing/method_call_assertions' class ForkingExecutor class Server @@ -469,3 +481,7 @@ if RUBY_ENGINE == "ruby" && PROCESS_COUNT > 0 # Use N processes (N defaults to 4) Minitest.parallel_executor = ForkingExecutor.new(PROCESS_COUNT) end + +class ActiveSupport::TestCase + include ActiveSupport::Testing::MethodCallAssertions +end diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb index dc4c32b07e..de7e800ac1 100644 --- a/actionpack/test/controller/integration_test.rb +++ b/actionpack/test/controller/integration_test.rb @@ -26,289 +26,335 @@ class SessionTest < ActiveSupport::TestCase end def test_follow_redirect_raises_when_no_redirect - @session.stubs(:redirect?).returns(false) - assert_raise(RuntimeError) { @session.follow_redirect! } + @session.stub :redirect?, false do + assert_raise(RuntimeError) { @session.follow_redirect! } + end end def test_request_via_redirect_uses_given_method path = "/somepath"; args = {:id => '1'}; headers = {"X-Test-Header" => "testvalue"} - @session.expects(:process).with(:put, path, params: args, headers: headers) - @session.stubs(:redirect?).returns(false) - @session.request_via_redirect(:put, path, params: args, headers: headers) + assert_called_with @session, :process, [:put, path, params: args, headers: headers] do + @session.stub :redirect?, false do + @session.request_via_redirect(:put, path, params: args, headers: headers) + end + end end def test_deprecated_request_via_redirect_uses_given_method path = "/somepath"; args = { id: '1' }; headers = { "X-Test-Header" => "testvalue" } - @session.expects(:process).with(:put, path, params: args, headers: headers) - @session.stubs(:redirect?).returns(false) - assert_deprecated { @session.request_via_redirect(:put, path, args, headers) } + assert_called_with @session, :process, [:put, path, params: args, headers: headers] do + @session.stub :redirect?, false do + assert_deprecated { @session.request_via_redirect(:put, path, args, headers) } + end + end end def test_request_via_redirect_follows_redirects path = "/somepath"; args = {:id => '1'}; headers = {"X-Test-Header" => "testvalue"} - @session.stubs(:redirect?).returns(true, true, false) - @session.expects(:follow_redirect!).times(2) - @session.request_via_redirect(:get, path, params: args, headers: headers) + value_series = [true, true, false] + assert_called @session, :follow_redirect!, times: 2 do + @session.stub :redirect?, ->{ value_series.shift } do + @session.request_via_redirect(:get, path, params: args, headers: headers) + end + end end def test_request_via_redirect_returns_status path = "/somepath"; args = {:id => '1'}; headers = {"X-Test-Header" => "testvalue"} - @session.stubs(:redirect?).returns(false) - @session.stubs(:status).returns(200) - assert_equal 200, @session.request_via_redirect(:get, path, params: args, headers: headers) + @session.stub :redirect?, false do + @session.stub :status, 200 do + assert_equal 200, @session.request_via_redirect(:get, path, params: args, headers: headers) + end + end end def test_deprecated_get_via_redirect path = "/somepath"; args = { id: '1' }; headers = { "X-Test-Header" => "testvalue" } - @session.expects(:request_via_redirect).with(:get, path, args, headers) - assert_deprecated do - @session.get_via_redirect(path, args, headers) + assert_called_with @session, :request_via_redirect, [:get, path, args, headers] do + assert_deprecated do + @session.get_via_redirect(path, args, headers) + end end end def test_deprecated_post_via_redirect path = "/somepath"; args = { id: '1' }; headers = { "X-Test-Header" => "testvalue" } - @session.expects(:request_via_redirect).with(:post, path, args, headers) - assert_deprecated do - @session.post_via_redirect(path, args, headers) + assert_called_with @session, :request_via_redirect, [:post, path, args, headers] do + assert_deprecated do + @session.post_via_redirect(path, args, headers) + end end end def test_deprecated_patch_via_redirect path = "/somepath"; args = { id: '1' }; headers = { "X-Test-Header" => "testvalue" } - @session.expects(:request_via_redirect).with(:patch, path, args, headers) - assert_deprecated do - @session.patch_via_redirect(path, args, headers) + assert_called_with @session, :request_via_redirect, [:patch, path, args, headers] do + assert_deprecated do + @session.patch_via_redirect(path, args, headers) + end end end def test_deprecated_put_via_redirect path = "/somepath"; args = { id: '1' }; headers = { "X-Test-Header" => "testvalue" } - @session.expects(:request_via_redirect).with(:put, path, args, headers) - assert_deprecated do - @session.put_via_redirect(path, args, headers) + assert_called_with @session, :request_via_redirect, [:put, path, args, headers] do + assert_deprecated do + @session.put_via_redirect(path, args, headers) + end end end def test_deprecated_delete_via_redirect path = "/somepath"; args = { id: '1' }; headers = { "X-Test-Header" => "testvalue" } - @session.expects(:request_via_redirect).with(:delete, path, args, headers) - assert_deprecated do - @session.delete_via_redirect(path, args, headers) + assert_called_with @session, :request_via_redirect, [:delete, path, args, headers] do + assert_deprecated do + @session.delete_via_redirect(path, args, headers) + end end end def test_get path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:get, path, params: params, headers: headers) - @session.get(path, params: params, headers: headers) + + assert_called_with @session, :process, [:get, path, params: params, headers: headers] do + @session.get(path, params: params, headers: headers) + end end def test_get_with_env_and_headers path = "/index"; params = "blah"; headers = { location: 'blah' }; env = { 'HTTP_X_REQUESTED_WITH' => 'XMLHttpRequest' } - @session.expects(:process).with(:get, path, params: params, headers: headers, env: env) - @session.get(path, params: params, headers: headers, env: env) + assert_called_with @session, :process, [:get, path, params: params, headers: headers, env: env] do + @session.get(path, params: params, headers: headers, env: env) + end end def test_deprecated_get path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:get, path, params: params, headers: headers) - assert_deprecated { - @session.get(path, params, headers) - } + + assert_called_with @session, :process, [:get, path, params: params, headers: headers] do + assert_deprecated { + @session.get(path, params, headers) + } + end end def test_post path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:post, path, params: params, headers: headers) - assert_deprecated { - @session.post(path, params, headers) - } + assert_called_with @session, :process, [:post, path, params: params, headers: headers] do + assert_deprecated { + @session.post(path, params, headers) + } + end end def test_deprecated_post path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:post, path, params: params, headers: headers) - @session.post(path, params: params, headers: headers) + assert_called_with @session, :process, [:post, path, params: params, headers: headers] do + @session.post(path, params: params, headers: headers) + end end def test_patch path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:patch, path, params: params, headers: headers) - @session.patch(path, params: params, headers: headers) + assert_called_with @session, :process, [:patch, path, params: params, headers: headers] do + @session.patch(path, params: params, headers: headers) + end end def test_deprecated_patch path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:patch, path, params: params, headers: headers) - assert_deprecated { - @session.patch(path, params, headers) - } + assert_called_with @session, :process, [:patch, path, params: params, headers: headers] do + assert_deprecated { + @session.patch(path, params, headers) + } + end end def test_put path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:put, path, params: params, headers: headers) - @session.put(path, params: params, headers: headers) + assert_called_with @session, :process, [:put, path, params: params, headers: headers] do + @session.put(path, params: params, headers: headers) + end end def test_deprecated_put path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:put, path, params: params, headers: headers) - assert_deprecated { - @session.put(path, params, headers) - } + assert_called_with @session, :process, [:put, path, params: params, headers: headers] do + assert_deprecated { + @session.put(path, params, headers) + } + end end def test_delete path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:delete, path, params: params, headers: headers) - assert_deprecated { - @session.delete(path,params,headers) - } + assert_called_with @session, :process, [:delete, path, params: params, headers: headers] do + assert_deprecated { + @session.delete(path,params,headers) + } + end end def test_deprecated_delete path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:delete, path, params: params, headers: headers) - @session.delete(path, params: params, headers: headers) + assert_called_with @session, :process, [:delete, path, params: params, headers: headers] do + @session.delete(path, params: params, headers: headers) + end end def test_head path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:head, path, params: params, headers: headers) - @session.head(path, params: params, headers: headers) + assert_called_with @session, :process, [:head, path, params: params, headers: headers] do + @session.head(path, params: params, headers: headers) + end end def deprecated_test_head path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:head, path, params: params, headers: headers) - assert_deprecated { - @session.head(path, params, headers) - } + assert_called_with @session, :process, [:head, path, params: params, headers: headers] do + assert_deprecated { + @session.head(path, params, headers) + } + end end def test_xml_http_request_get path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:get, path, params: params, headers: headers, xhr: true) - @session.get(path, params: params, headers: headers, xhr: true) + assert_called_with @session, :process, [:get, path, params: params, headers: headers, xhr: true] do + @session.get(path, params: params, headers: headers, xhr: true) + end end def test_deprecated_xml_http_request_get path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:get, path, params: params, headers: headers, xhr: true) - @session.get(path, params: params, headers: headers, xhr: true) + assert_called_with @session, :process, [:get, path, params: params, headers: headers, xhr: true] do + @session.get(path, params: params, headers: headers, xhr: true) + end end def test_deprecated_args_xml_http_request_get path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:get, path, params: params, headers: headers, xhr: true) - assert_deprecated(/xml_http_request/) { - @session.xml_http_request(:get, path, params, headers) - } + assert_called_with @session, :process, [:get, path, params: params, headers: headers, xhr: true] do + assert_deprecated(/xml_http_request/) { + @session.xml_http_request(:get, path, params, headers) + } + end end def test_xml_http_request_post path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:post, path, params: params, headers: headers, xhr: true) - @session.post(path, params: params, headers: headers, xhr: true) + assert_called_with @session, :process, [:post, path, params: params, headers: headers, xhr: true] do + @session.post(path, params: params, headers: headers, xhr: true) + end end def test_deprecated_xml_http_request_post path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:post, path, params: params, headers: headers, xhr: true) - @session.post(path, params: params, headers: headers, xhr: true) + assert_called_with @session, :process, [:post, path, params: params, headers: headers, xhr: true] do + @session.post(path, params: params, headers: headers, xhr: true) + end end def test_deprecated_args_xml_http_request_post path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:post, path, params: params, headers: headers, xhr: true) - assert_deprecated(/xml_http_request/) { @session.xml_http_request(:post,path,params,headers) } + assert_called_with @session, :process, [:post, path, params: params, headers: headers, xhr: true] do + assert_deprecated(/xml_http_request/) { @session.xml_http_request(:post,path,params,headers) } + end end def test_xml_http_request_patch path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:patch, path, params: params, headers: headers, xhr: true) - @session.patch(path, params: params, headers: headers, xhr: true) + assert_called_with @session, :process, [:patch, path, params: params, headers: headers, xhr: true] do + @session.patch(path, params: params, headers: headers, xhr: true) + end end def test_deprecated_xml_http_request_patch path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:patch, path, params: params, headers: headers, xhr: true) - @session.patch(path, params: params, headers: headers, xhr: true) + assert_called_with @session, :process, [:patch, path, params: params, headers: headers, xhr: true] do + @session.patch(path, params: params, headers: headers, xhr: true) + end end def test_deprecated_args_xml_http_request_patch path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:patch, path, params: params, headers: headers, xhr: true) - assert_deprecated(/xml_http_request/) { @session.xml_http_request(:patch,path,params,headers) } + assert_called_with @session, :process, [:patch, path, params: params, headers: headers, xhr: true] do + assert_deprecated(/xml_http_request/) { @session.xml_http_request(:patch,path,params,headers) } + end end def test_xml_http_request_put path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:put, path, params: params, headers: headers, xhr: true) - @session.put(path, params: params, headers: headers, xhr: true) + assert_called_with @session, :process, [:put, path, params: params, headers: headers, xhr: true] do + @session.put(path, params: params, headers: headers, xhr: true) + end end def test_deprecated_xml_http_request_put path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:put, path, params: params, headers: headers, xhr: true) - @session.put(path, params: params, headers: headers, xhr: true) + assert_called_with @session, :process, [:put, path, params: params, headers: headers, xhr: true] do + @session.put(path, params: params, headers: headers, xhr: true) + end end def test_deprecated_args_xml_http_request_put path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:put, path, params: params, headers: headers, xhr: true) - assert_deprecated(/xml_http_request/) { @session.xml_http_request(:put, path, params, headers) } + assert_called_with @session, :process, [:put, path, params: params, headers: headers, xhr: true] do + assert_deprecated(/xml_http_request/) { @session.xml_http_request(:put, path, params, headers) } + end end def test_xml_http_request_delete path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:delete, path, params: params, headers: headers, xhr: true) - @session.delete(path, params: params, headers: headers, xhr: true) + assert_called_with @session, :process, [:delete, path, params: params, headers: headers, xhr: true] do + @session.delete(path, params: params, headers: headers, xhr: true) + end end def test_deprecated_xml_http_request_delete path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:delete, path, params: params, headers: headers, xhr: true) - assert_deprecated { @session.xml_http_request(:delete, path, params: params, headers: headers) } + assert_called_with @session, :process, [:delete, path, params: params, headers: headers, xhr: true] do + assert_deprecated { @session.xml_http_request(:delete, path, params: params, headers: headers) } + end end def test_deprecated_args_xml_http_request_delete path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:delete, path, params: params, headers: headers, xhr: true) - assert_deprecated(/xml_http_request/) { @session.xml_http_request(:delete, path, params, headers) } + assert_called_with @session, :process, [:delete, path, params: params, headers: headers, xhr: true] do + assert_deprecated(/xml_http_request/) { @session.xml_http_request(:delete, path, params, headers) } + end end def test_xml_http_request_head path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:head, path, params: params, headers: headers, xhr: true) - @session.head(path, params: params, headers: headers, xhr: true) + assert_called_with @session, :process, [:head, path, params: params, headers: headers, xhr: true] do + @session.head(path, params: params, headers: headers, xhr: true) + end end def test_deprecated_xml_http_request_head path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:head, path, params: params, headers: headers, xhr: true) - assert_deprecated(/xml_http_request/) { @session.xml_http_request(:head, path, params: params, headers: headers) } + assert_called_with @session, :process, [:head, path, params: params, headers: headers, xhr: true] do + assert_deprecated(/xml_http_request/) { @session.xml_http_request(:head, path, params: params, headers: headers) } + end end def test_deprecated_args_xml_http_request_head path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:head, path, params: params, headers: headers, xhr: true) - assert_deprecated { @session.xml_http_request(:head, path, params, headers) } + assert_called_with @session, :process, [:head, path, params: params, headers: headers, xhr: true] do + assert_deprecated { @session.xml_http_request(:head, path, params, headers) } + end end end class IntegrationTestTest < ActiveSupport::TestCase def setup @test = ::ActionDispatch::IntegrationTest.new(:app) - @test.class.stubs(:fixture_table_names).returns([]) - @session = @test.open_session end def test_opens_new_session @@ -340,14 +386,8 @@ end # Tests that integration tests don't call Controller test methods for processing. # Integration tests have their own setup and teardown. class IntegrationTestUsesCorrectClass < ActionDispatch::IntegrationTest - def self.fixture_table_names - [] - end - def test_integration_methods_called reset! - @integration_session.stubs(:generic_url_rewriter) - @integration_session.stubs(:process) %w( get post head patch put delete ).each do |verb| assert_nothing_raised("'#{verb}' should use integration test methods") { __send__(verb, '/') } diff --git a/actionpack/test/controller/live_stream_test.rb b/actionpack/test/controller/live_stream_test.rb index 6ba361f2f7..e9c19b7acf 100644 --- a/actionpack/test/controller/live_stream_test.rb +++ b/actionpack/test/controller/live_stream_test.rb @@ -315,7 +315,7 @@ module ActionController t = Thread.new(@response) { |resp| resp.await_commit _, _, body = resp.to_a - body.each do |part| + body.each do @controller.latch.wait body.close break @@ -339,7 +339,7 @@ module ActionController t = Thread.new(@response) { |resp| resp.await_commit _, _, body = resp.to_a - body.each do |part| + body.each do body.close break end diff --git a/actionpack/test/controller/request_forgery_protection_test.rb b/actionpack/test/controller/request_forgery_protection_test.rb index f3b0ecac10..90fd8669c2 100644 --- a/actionpack/test/controller/request_forgery_protection_test.rb +++ b/actionpack/test/controller/request_forgery_protection_test.rb @@ -131,9 +131,7 @@ end # common test methods module RequestForgeryProtectionTests def setup - @token = "cf50faa3fe97702ca1ae" - @controller.stubs(:valid_authenticity_token?).with{ |_, t| t == @token }.returns(true) - @controller.stubs(:valid_authenticity_token?).with{ |_, t| t != @token }.returns(false) + @token = Base64.strict_encode64('railstestrailstestrailstestrails') @old_request_forgery_protection_token = ActionController::Base.request_forgery_protection_token ActionController::Base.request_forgery_protection_token = :custom_authenticity_token end @@ -255,37 +253,53 @@ module RequestForgeryProtectionTests end def test_should_allow_post_with_token - assert_not_blocked { post :index, params: { custom_authenticity_token: @token } } + session[:_csrf_token] = @token + @controller.stub :form_authenticity_token, @token do + assert_not_blocked { post :index, params: { custom_authenticity_token: @token } } + end end def test_should_allow_patch_with_token - assert_not_blocked { patch :index, params: { custom_authenticity_token: @token } } + session[:_csrf_token] = @token + @controller.stub :form_authenticity_token, @token do + assert_not_blocked { patch :index, params: { custom_authenticity_token: @token } } + end end def test_should_allow_put_with_token - assert_not_blocked { put :index, params: { custom_authenticity_token: @token } } + session[:_csrf_token] = @token + @controller.stub :form_authenticity_token, @token do + assert_not_blocked { put :index, params: { custom_authenticity_token: @token } } + end end def test_should_allow_delete_with_token - assert_not_blocked { delete :index, params: { custom_authenticity_token: @token } } + session[:_csrf_token] = @token + @controller.stub :form_authenticity_token, @token do + assert_not_blocked { delete :index, params: { custom_authenticity_token: @token } } + end end def test_should_allow_post_with_token_in_header + session[:_csrf_token] = @token @request.env['HTTP_X_CSRF_TOKEN'] = @token assert_not_blocked { post :index } end def test_should_allow_delete_with_token_in_header + session[:_csrf_token] = @token @request.env['HTTP_X_CSRF_TOKEN'] = @token assert_not_blocked { delete :index } end def test_should_allow_patch_with_token_in_header + session[:_csrf_token] = @token @request.env['HTTP_X_CSRF_TOKEN'] = @token assert_not_blocked { patch :index } end def test_should_allow_put_with_token_in_header + session[:_csrf_token] = @token @request.env['HTTP_X_CSRF_TOKEN'] = @token assert_not_blocked { put :index } end @@ -339,6 +353,7 @@ module RequestForgeryProtectionTests # Allow non-GET requests since GET is all a remote <script> tag can muster. def test_should_allow_non_get_js_without_xhr_header + session[:_csrf_token] = @token assert_cross_origin_not_blocked { post :same_origin_js, params: { custom_authenticity_token: @token } } assert_cross_origin_not_blocked { post :same_origin_js, params: { format: 'js', custom_authenticity_token: @token } } assert_cross_origin_not_blocked do @@ -412,7 +427,8 @@ class RequestForgeryProtectionControllerUsingResetSessionTest < ActionController get :meta assert_select 'meta[name=?][content=?]', 'csrf-param', 'custom_authenticity_token' assert_select 'meta[name=?]', 'csrf-token' - assert_match(/cf50faa3fe97702ca1ae<=\?/, @response.body) + regexp = "#{@token}<=\?" + assert_match(/#{regexp}/, @response.body) end end end diff --git a/actionpack/test/controller/resources_test.rb b/actionpack/test/controller/resources_test.rb index dd7c128566..4490abf7b2 100644 --- a/actionpack/test/controller/resources_test.rb +++ b/actionpack/test/controller/resources_test.rb @@ -149,7 +149,7 @@ class ResourcesTest < ActionController::TestCase end end - assert_restful_named_routes_for :messages do |options| + assert_restful_named_routes_for :messages do actions.each_key do |action| assert_named_route "/messages/#{action}", "#{action}_messages_path", :action => action end @@ -179,7 +179,7 @@ class ResourcesTest < ActionController::TestCase end end - assert_restful_named_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do |options| + assert_restful_named_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do actions.each_key do |action| assert_named_route "/threads/1/messages/#{action}", "#{action}_thread_messages_path", :action => action end @@ -206,7 +206,7 @@ class ResourcesTest < ActionController::TestCase end end - assert_restful_named_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do |options| + assert_restful_named_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do actions.each_key do |action| assert_named_route "/threads/1/messages/#{action}", "#{action}_thread_messages_path", :action => action end @@ -236,7 +236,7 @@ class ResourcesTest < ActionController::TestCase end end - assert_restful_named_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do |options| + assert_restful_named_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do actions.each_key do |action| assert_named_route "/threads/1/messages/#{action}.xml", "#{action}_thread_messages_path", :action => action, :format => 'xml' end @@ -253,7 +253,7 @@ class ResourcesTest < ActionController::TestCase assert_recognizes(options.merge(mark_options), :path => mark_path, :method => method) end - assert_restful_named_routes_for :messages do |options| + assert_restful_named_routes_for :messages do assert_named_route mark_path, :mark_message_path, mark_options end end @@ -278,7 +278,7 @@ class ResourcesTest < ActionController::TestCase assert_recognizes(options.merge(mark_options), :path => mark_path, :method => method) end - assert_restful_named_routes_for :messages, :path_names => {:new => 'nuevo'} do |options| + assert_restful_named_routes_for :messages, :path_names => {:new => 'nuevo'} do assert_named_route mark_path, :mark_message_path, mark_options end end @@ -304,7 +304,7 @@ class ResourcesTest < ActionController::TestCase assert_recognizes(options.merge(action_options), :path => action_path, :method => method) end - assert_restful_named_routes_for :messages do |options| + assert_restful_named_routes_for :messages do assert_named_route action_path, "#{action}_message_path".to_sym, action_options end end @@ -351,7 +351,7 @@ class ResourcesTest < ActionController::TestCase assert_recognizes(options.merge(preview_options), :path => preview_path, :method => :post) end - assert_restful_named_routes_for :messages do |options| + assert_restful_named_routes_for :messages do assert_named_route preview_path, :preview_new_message_path, preview_options end end @@ -373,7 +373,7 @@ class ResourcesTest < ActionController::TestCase assert_recognizes(options.merge(preview_options), :path => preview_path, :method => :post) end - assert_restful_named_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do |options| + assert_restful_named_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do assert_named_route preview_path, :preview_new_thread_message_path, preview_options end end @@ -395,7 +395,7 @@ class ResourcesTest < ActionController::TestCase assert_recognizes(options.merge(preview_options), :path => preview_path, :method => :post) end - assert_restful_named_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do |options| + assert_restful_named_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do assert_named_route preview_path, :preview_new_thread_message_path, preview_options end end @@ -519,9 +519,9 @@ class ResourcesTest < ActionController::TestCase end def test_should_create_multiple_singleton_resource_routes - with_singleton_resources :account, :logo do + with_singleton_resources :account, :product do assert_singleton_restful_for :account - assert_singleton_restful_for :logo + assert_singleton_restful_for :product end end @@ -553,7 +553,7 @@ class ResourcesTest < ActionController::TestCase assert_recognizes(options.merge(reset_options), :path => reset_path, :method => method) end - assert_singleton_named_routes_for :account do |options| + assert_singleton_named_routes_for :account do assert_named_route reset_path, :reset_account_path, reset_options end end @@ -577,7 +577,7 @@ class ResourcesTest < ActionController::TestCase assert_recognizes(options.merge(action_options), :path => action_path, :method => method) end - assert_singleton_named_routes_for :account do |options| + assert_singleton_named_routes_for :account do assert_named_route action_path, "#{action}_account_path".to_sym, action_options end end @@ -1070,8 +1070,8 @@ class ResourcesTest < ActionController::TestCase end def test_singleton_resource_name_is_not_singularized - with_singleton_resources(:preferences) do - assert_singleton_restful_for :preferences + with_singleton_resources(:products) do + assert_singleton_restful_for :products end end diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index feb3e7eab7..4a2b02a003 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -336,6 +336,16 @@ class LegacyRouteSetTests < ActiveSupport::TestCase assert_equal({controller: 'content', action: 'translate', url: 'example'}, rs.recognize_path('/example')) end + def test_route_with_regexp_for_action + rs.draw { get '/:controller/:action', action: /auth[-|_].+/ } + + assert_equal({ action: 'auth_google', controller: 'content' }, rs.recognize_path('/content/auth_google')) + assert_equal({ action: 'auth-facebook', controller: 'content' }, rs.recognize_path('/content/auth-facebook')) + + assert_equal '/content/auth_google', url_for(rs, { controller: "content", action: "auth_google" }) + assert_equal '/content/auth-facebook', url_for(rs, { controller: "content", action: "auth-facebook" }) + end + def test_route_with_regexp_for_controller rs.draw do get ':controller/:admintoken(/:action(/:id))', :controller => /admin\/.+/ @@ -1742,40 +1752,10 @@ class RouteSetTest < ActiveSupport::TestCase include ActionDispatch::RoutingVerbs - class TestSet < ActionDispatch::Routing::RouteSet - def initialize(block) - @block = block - super() - end - - class Dispatcher < ActionDispatch::Routing::RouteSet::Dispatcher - def initialize(defaults, set, block) - super(defaults) - @block = block - @set = set - end - - def controller_reference(controller_param) - block = @block - set = @set - Class.new(ActionController::Base) { - include set.url_helpers - define_method(:process) { |name| block.call(self) } - def to_a; [200, {}, []]; end - } - end - end - - def dispatcher defaults - TestSet::Dispatcher.new defaults, self, @block - end - end - alias :routes :set def test_generate_with_optional_params_recalls_last_request - controller = nil - @set = TestSet.new ->(c) { controller = c } + @set = make_set false set.draw do get "blog/", :controller => "blog", :action => "index" diff --git a/actionpack/test/controller/test_case_test.rb b/actionpack/test/controller/test_case_test.rb index 1f073b4124..b3c3979c84 100644 --- a/actionpack/test/controller/test_case_test.rb +++ b/actionpack/test/controller/test_case_test.rb @@ -160,7 +160,7 @@ XML def setup super @controller = TestController.new - @request.env['PATH_INFO'] = nil + @request.delete_header 'PATH_INFO' @routes = ActionDispatch::Routing::RouteSet.new.tap do |r| r.draw do get ':controller(/:action(/:id))' diff --git a/actionpack/test/dispatch/callbacks_test.rb b/actionpack/test/dispatch/callbacks_test.rb index f767b07e75..5ba76d9ab9 100644 --- a/actionpack/test/dispatch/callbacks_test.rb +++ b/actionpack/test/dispatch/callbacks_test.rb @@ -28,7 +28,7 @@ class DispatcherTest < ActiveSupport::TestCase assert_equal 4, Foo.a assert_equal 4, Foo.b - dispatch do |env| + dispatch do raise "error" end rescue nil assert_equal 6, Foo.a diff --git a/actionpack/test/dispatch/debug_exceptions_test.rb b/actionpack/test/dispatch/debug_exceptions_test.rb index a867aee7ec..f9f379780c 100644 --- a/actionpack/test/dispatch/debug_exceptions_test.rb +++ b/actionpack/test/dispatch/debug_exceptions_test.rb @@ -71,14 +71,6 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest end end - def setup - app = ActiveSupport::OrderedOptions.new - app.config = ActiveSupport::OrderedOptions.new - app.config.assets = ActiveSupport::OrderedOptions.new - app.config.assets.prefix = '/sprockets' - Rails.stubs(:application).returns(app) - end - RoutesApp = Struct.new(:routes).new(SharedTestRoutes) ProductionApp = ActionDispatch::DebugExceptions.new(Boomer.new(false), RoutesApp) DevelopmentApp = ActionDispatch::DebugExceptions.new(Boomer.new(true), RoutesApp) @@ -338,36 +330,37 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest test 'debug exceptions app shows user code that caused the error in source view' do @app = DevelopmentApp - Rails.stubs(:root).returns(Pathname.new('.')) - cleaner = ActiveSupport::BacktraceCleaner.new.tap do |bc| - bc.add_silencer { |line| line =~ /method_that_raises/ } - bc.add_silencer { |line| line !~ %r{test/dispatch/debug_exceptions_test.rb} } - end + Rails.stub :root, Pathname.new('.') do + cleaner = ActiveSupport::BacktraceCleaner.new.tap do |bc| + bc.add_silencer { |line| line =~ /method_that_raises/ } + bc.add_silencer { |line| line !~ %r{test/dispatch/debug_exceptions_test.rb} } + end - get '/framework_raises', headers: { 'action_dispatch.backtrace_cleaner' => cleaner } + get '/framework_raises', headers: { 'action_dispatch.backtrace_cleaner' => cleaner } - # Assert correct error - assert_response 500 - assert_select 'h2', /error in framework/ + # Assert correct error + assert_response 500 + assert_select 'h2', /error in framework/ - # assert source view line is the call to method_that_raises - assert_select 'div.source:not(.hidden)' do - assert_select 'pre .line.active', /method_that_raises/ - end + # assert source view line is the call to method_that_raises + assert_select 'div.source:not(.hidden)' do + assert_select 'pre .line.active', /method_that_raises/ + end - # assert first source view (hidden) that throws the error - assert_select 'div.source:first' do - assert_select 'pre .line.active', /raise StandardError\.new/ - end + # assert first source view (hidden) that throws the error + assert_select 'div.source:first' do + assert_select 'pre .line.active', /raise StandardError\.new/ + end - # assert application trace refers to line that calls method_that_raises is first - assert_select '#Application-Trace' do - assert_select 'pre code a:first', %r{test/dispatch/debug_exceptions_test\.rb:\d+:in `call} - end + # assert application trace refers to line that calls method_that_raises is first + assert_select '#Application-Trace' do + assert_select 'pre code a:first', %r{test/dispatch/debug_exceptions_test\.rb:\d+:in `call} + end - # assert framework trace that that threw the error is first - assert_select '#Framework-Trace' do - assert_select 'pre code a:first', /method_that_raises/ + # assert framework trace that that threw the error is first + assert_select '#Framework-Trace' do + assert_select 'pre code a:first', /method_that_raises/ + end end end end diff --git a/actionpack/test/dispatch/live_response_test.rb b/actionpack/test/dispatch/live_response_test.rb index 5cfa5f7b3b..1c128365c7 100644 --- a/actionpack/test/dispatch/live_response_test.rb +++ b/actionpack/test/dispatch/live_response_test.rb @@ -65,7 +65,7 @@ module ActionController latch = Concurrent::CountDownLatch.new t = Thread.new { - @response.stream.each do |chunk| + @response.stream.each do latch.count_down end } diff --git a/actionpack/test/dispatch/routing/inspector_test.rb b/actionpack/test/dispatch/routing/inspector_test.rb index 4047214843..24bd4b04ec 100644 --- a/actionpack/test/dispatch/routing/inspector_test.rb +++ b/actionpack/test/dispatch/routing/inspector_test.rb @@ -12,12 +12,6 @@ module ActionDispatch class RoutesInspectorTest < ActiveSupport::TestCase def setup @set = ActionDispatch::Routing::RouteSet.new - app = ActiveSupport::OrderedOptions.new - app.config = ActiveSupport::OrderedOptions.new - app.config.assets = ActiveSupport::OrderedOptions.new - app.config.assets.prefix = '/sprockets' - Rails.stubs(:application).returns(app) - Rails.stubs(:env).returns("development") end def draw(options = {}, &block) @@ -316,9 +310,6 @@ module ActionDispatch def test_inspect_routes_shows_resources_route_when_assets_disabled @set = ActionDispatch::Routing::RouteSet.new - app = ActiveSupport::OrderedOptions.new - - Rails.stubs(:application).returns(app) output = draw do get '/cart', to: 'cart#show' diff --git a/actionview/lib/action_view/layouts.rb b/actionview/lib/action_view/layouts.rb index 9d636c8c9e..cabba967c9 100644 --- a/actionview/lib/action_view/layouts.rb +++ b/actionview/lib/action_view/layouts.rb @@ -277,7 +277,7 @@ module ActionView remove_possible_method(:_layout) prefixes = _implied_layout_name =~ /\blayouts/ ? [] : ["layouts"] - default_behavior = "lookup_context.find_all('#{_implied_layout_name}', #{prefixes.inspect}).first || super" + default_behavior = "lookup_context.find_all('#{_implied_layout_name}', #{prefixes.inspect}, false, [], { formats: formats }).first || super" name_clause = if name default_behavior else @@ -316,7 +316,7 @@ module ActionView end self.class_eval <<-RUBY, __FILE__, __LINE__ + 1 - def _layout + def _layout(formats) if _conditional_layout? #{layout_definition} else @@ -372,7 +372,7 @@ module ActionView end # This will be overwritten by _write_layout_method - def _layout; end + def _layout(*); end # Determine the layout for a given name, taking into account the name type. # @@ -382,8 +382,8 @@ module ActionView case name when String then _normalize_layout(name) when Proc then name - when true then Proc.new { _default_layout(true) } - when :default then Proc.new { _default_layout(false) } + when true then Proc.new { |formats| _default_layout(formats, true) } + when :default then Proc.new { |formats| _default_layout(formats, false) } when false, nil then nil else raise ArgumentError, @@ -399,14 +399,15 @@ module ActionView # Optionally raises an exception if the layout could not be found. # # ==== 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) # # ==== Returns # * <tt>template</tt> - The template object for the default layout (or nil) - def _default_layout(require_layout = false) + def _default_layout(formats, require_layout = false) begin - value = _layout if action_has_layout? + value = _layout(formats) if action_has_layout? rescue NameError => e raise e, "Could not render layout: #{e.message}" end diff --git a/actionview/lib/action_view/lookup_context.rb b/actionview/lib/action_view/lookup_context.rb index f4a10aa393..fba9a44cb1 100644 --- a/actionview/lib/action_view/lookup_context.rb +++ b/actionview/lib/action_view/lookup_context.rb @@ -229,21 +229,5 @@ module ActionView super(default_locale) end - - # Uses the first format in the formats array for layout lookup. - def with_layout_format - if formats.size == 1 - yield - else - old_formats = formats - _set_detail(:formats, formats[0,1]) - - begin - yield - ensure - _set_detail(:formats, old_formats) - end - end - end end end diff --git a/actionview/lib/action_view/renderer/streaming_template_renderer.rb b/actionview/lib/action_view/renderer/streaming_template_renderer.rb index 3ab2cd36fc..f38e2764d0 100644 --- a/actionview/lib/action_view/renderer/streaming_template_renderer.rb +++ b/actionview/lib/action_view/renderer/streaming_template_renderer.rb @@ -47,7 +47,7 @@ module ActionView return [super] unless layout_name && template.supports_streaming? locals ||= {} - layout = layout_name && find_layout(layout_name, locals.keys) + layout = layout_name && find_layout(layout_name, locals.keys, [formats.first]) Body.new do |buffer| delayed_render(buffer, template, layout, @view, locals) diff --git a/actionview/lib/action_view/renderer/template_renderer.rb b/actionview/lib/action_view/renderer/template_renderer.rb index dbb4855e39..75217e1630 100644 --- a/actionview/lib/action_view/renderer/template_renderer.rb +++ b/actionview/lib/action_view/renderer/template_renderer.rb @@ -57,7 +57,7 @@ module ActionView end def render_with_layout(path, locals) #:nodoc: - layout = path && find_layout(path, locals.keys) + layout = path && find_layout(path, locals.keys, [formats.first]) content = yield(layout) if layout @@ -72,27 +72,28 @@ module ActionView # This is the method which actually finds the layout using details in the lookup # context object. If no layout is found, it checks if at least a layout with # the given name exists across all details before raising the error. - def find_layout(layout, keys) - with_layout_format { resolve_layout(layout, keys) } + def find_layout(layout, keys, formats) + resolve_layout(layout, keys, formats) end - def resolve_layout(layout, keys) + def resolve_layout(layout, keys, formats) + details = @details.dup + details[:formats] = formats + case layout when String begin if layout =~ /^\// - with_fallbacks { find_template(layout, nil, false, keys, @details) } + with_fallbacks { find_template(layout, nil, false, keys, details) } else - find_template(layout, nil, false, keys, @details) + find_template(layout, nil, false, keys, details) end rescue ActionView::MissingTemplate all_details = @details.merge(:formats => @lookup_context.default_formats) raise unless template_exists?(layout, nil, false, keys, all_details) end when Proc - resolve_layout(layout.call, keys) - when FalseClass - nil + resolve_layout(layout.call(formats), keys, formats) else layout end diff --git a/activejob/CHANGELOG.md b/activejob/CHANGELOG.md index 637096e935..965b556fab 100644 --- a/activejob/CHANGELOG.md +++ b/activejob/CHANGELOG.md @@ -1,3 +1,8 @@ +* Implement a simple `AsyncJob` processor and associated `AsyncAdapter` that + queue jobs to a `concurrent-ruby` thread pool. + + *Jerry D'Antonio* + * Implement `provider_job_id` for `queue_classic` adapter. This requires the latest, currently unreleased, version of queue_classic. diff --git a/activejob/Rakefile b/activejob/Rakefile index 8c86df3c91..d9648a7f16 100644 --- a/activejob/Rakefile +++ b/activejob/Rakefile @@ -1,6 +1,6 @@ require 'rake/testtask' -ACTIVEJOB_ADAPTERS = %w(inline delayed_job qu que queue_classic resque sidekiq sneakers sucker_punch backburner test) +ACTIVEJOB_ADAPTERS = %w(async inline delayed_job qu que queue_classic resque sidekiq sneakers sucker_punch backburner test) ACTIVEJOB_ADAPTERS -= %w(queue_classic) if defined?(JRUBY_VERSION) task default: :test diff --git a/activejob/lib/active_job.rb b/activejob/lib/active_job.rb index 3d4f63b261..eb8091a805 100644 --- a/activejob/lib/active_job.rb +++ b/activejob/lib/active_job.rb @@ -32,6 +32,7 @@ module ActiveJob autoload :Base autoload :QueueAdapters autoload :ConfiguredJob + autoload :AsyncJob autoload :TestCase autoload :TestHelper end diff --git a/activejob/lib/active_job/async_job.rb b/activejob/lib/active_job/async_job.rb new file mode 100644 index 0000000000..7fcffc4c24 --- /dev/null +++ b/activejob/lib/active_job/async_job.rb @@ -0,0 +1,75 @@ +require 'concurrent' +require 'thread_safe' + +module ActiveJob + # == Active Job Async Job + # + # When enqueueing jobs with Async Job each job will be executed asynchronously + # on a +concurrent-ruby+ thread pool. All job data is retained in memory. + # Because job data is not saved to a persistent datastore there is no + # additional infrastructure needed and jobs process quickly. The lack of + # persistence, however, means that all unprocessed jobs will be lost on + # application restart. Therefore in-memory queue adapters are unsuitable for + # most production environments but are excellent for development and testing. + # + # Read more about Concurrent Ruby {here}[https://github.com/ruby-concurrency/concurrent-ruby]. + # + # To use Async Job set the queue_adapter config to +:async+. + # + # Rails.application.config.active_job.queue_adapter = :async + # + # Async Job supports job queues specified with +queue_as+. Queues are created + # automatically as needed and each has its own thread pool. + class AsyncJob + + DEFAULT_EXECUTOR_OPTIONS = { + min_threads: [2, Concurrent.processor_count].max, + max_threads: Concurrent.processor_count * 10, + auto_terminate: true, + idletime: 60, # 1 minute + max_queue: 0, # unlimited + fallback_policy: :caller_runs # shouldn't matter -- 0 max queue + }.freeze + + QUEUES = ThreadSafe::Cache.new do |hash, queue_name| #:nodoc: + hash.compute_if_absent(queue_name) { ActiveJob::AsyncJob.create_thread_pool } + end + + class << self + # Forces jobs to process immediately when testing the Active Job gem. + # This should only be called from within unit tests. + def perform_immediately! #:nodoc: + @perform_immediately = true + end + + # Allows jobs to run asynchronously when testing the Active Job gem. + # This should only be called from within unit tests. + def perform_asynchronously! #:nodoc: + @perform_immediately = false + end + + def create_thread_pool #:nodoc: + if @perform_immediately + Concurrent::ImmediateExecutor.new + else + Concurrent::ThreadPoolExecutor.new(DEFAULT_EXECUTOR_OPTIONS) + end + end + + def enqueue(job_data, queue: 'default') #:nodoc: + QUEUES[queue].post(job_data) { |job| ActiveJob::Base.execute(job) } + end + + def enqueue_at(job_data, timestamp, queue: 'default') #:nodoc: + delay = timestamp - Time.current.to_f + if delay > 0 + Concurrent::ScheduledTask.execute(delay, args: [job_data], executor: QUEUES[queue]) do |job| + ActiveJob::Base.execute(job) + end + else + enqueue(job_data, queue: queue) + end + end + end + end +end diff --git a/activejob/lib/active_job/queue_adapters.rb b/activejob/lib/active_job/queue_adapters.rb index e8ceabaeba..aeb1fe1e73 100644 --- a/activejob/lib/active_job/queue_adapters.rb +++ b/activejob/lib/active_job/queue_adapters.rb @@ -12,6 +12,8 @@ module ActiveJob # * {Sidekiq}[http://sidekiq.org] # * {Sneakers}[https://github.com/jondot/sneakers] # * {Sucker Punch}[https://github.com/brandonhilkert/sucker_punch] + # * {Active Job Async Job}[http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters/AsyncAdapter.html] + # * {Active Job Inline}[http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters/InlineAdapter.html] # # === Backends Features # @@ -26,6 +28,7 @@ module ActiveJob # | Sidekiq | Yes | Yes | Yes | Queue | No | Job | # | Sneakers | Yes | Yes | No | Queue | Queue | No | # | Sucker Punch | Yes | Yes | No | No | No | No | + # | Active Job Async | Yes | Yes | Yes | No | No | No | # | Active Job Inline | No | Yes | N/A | N/A | N/A | N/A | # # ==== Async @@ -96,9 +99,15 @@ module ActiveJob # # N/A: The adapter does not run in a separate process, and therefore doesn't # support retries. + # + # === Async and Inline Queue Adapters + # + # Active Job has two built-in queue adapters intended for development and + # testing: +:async+ and +:inline+. module QueueAdapters extend ActiveSupport::Autoload + autoload :AsyncAdapter autoload :InlineAdapter autoload :BackburnerAdapter autoload :DelayedJobAdapter diff --git a/activejob/lib/active_job/queue_adapters/async_adapter.rb b/activejob/lib/active_job/queue_adapters/async_adapter.rb new file mode 100644 index 0000000000..3fc27f56e7 --- /dev/null +++ b/activejob/lib/active_job/queue_adapters/async_adapter.rb @@ -0,0 +1,23 @@ +require 'active_job/async_job' + +module ActiveJob + module QueueAdapters + # == Active Job Async adapter + # + # When enqueueing jobs with the Async adapter the job will be executed + # asynchronously using {AsyncJob}[http://api.rubyonrails.org/classes/ActiveJob/AsyncJob.html]. + # + # To use +AsyncJob+ set the queue_adapter config to +:async+. + # + # Rails.application.config.active_job.queue_adapter = :async + class AsyncAdapter + def enqueue(job) #:nodoc: + ActiveJob::AsyncJob.enqueue(job.serialize, queue: job.queue_name) + end + + def enqueue_at(job, timestamp) #:nodoc: + ActiveJob::AsyncJob.enqueue_at(job.serialize, timestamp, queue: job.queue_name) + end + end + end +end diff --git a/activejob/test/adapters/async.rb b/activejob/test/adapters/async.rb new file mode 100644 index 0000000000..df58027599 --- /dev/null +++ b/activejob/test/adapters/async.rb @@ -0,0 +1,5 @@ +require 'concurrent' +require 'active_job/async_job' + +ActiveJob::Base.queue_adapter = :async +ActiveJob::AsyncJob.perform_immediately! diff --git a/activejob/test/cases/async_job_test.rb b/activejob/test/cases/async_job_test.rb new file mode 100644 index 0000000000..2642cfc608 --- /dev/null +++ b/activejob/test/cases/async_job_test.rb @@ -0,0 +1,42 @@ +require 'helper' +require 'jobs/hello_job' +require 'jobs/queue_as_job' + +class AsyncJobTest < ActiveSupport::TestCase + def using_async_adapter? + ActiveJob::Base.queue_adapter.is_a? ActiveJob::QueueAdapters::AsyncAdapter + end + + setup do + ActiveJob::AsyncJob.perform_asynchronously! + end + + teardown do + ActiveJob::AsyncJob::QUEUES.clear + ActiveJob::AsyncJob.perform_immediately! + end + + test "#create_thread_pool returns a thread_pool" do + thread_pool = ActiveJob::AsyncJob.create_thread_pool + assert thread_pool.is_a? Concurrent::ExecutorService + assert_not thread_pool.is_a? Concurrent::ImmediateExecutor + end + + test "#create_thread_pool returns an ImmediateExecutor after #perform_immediately! is called" do + ActiveJob::AsyncJob.perform_immediately! + thread_pool = ActiveJob::AsyncJob.create_thread_pool + assert thread_pool.is_a? Concurrent::ImmediateExecutor + end + + test "enqueuing without specifying a queue uses the default queue" do + skip unless using_async_adapter? + HelloJob.perform_later + assert ActiveJob::AsyncJob::QUEUES.key? 'default' + end + + test "enqueuing to a queue that does not exist creates the queue" do + skip unless using_async_adapter? + QueueAsJob.perform_later + assert ActiveJob::AsyncJob::QUEUES.key? QueueAsJob::MY_QUEUE.to_s + end +end diff --git a/activejob/test/integration/queuing_test.rb b/activejob/test/integration/queuing_test.rb index ca8047ef0b..125ba54302 100644 --- a/activejob/test/integration/queuing_test.rb +++ b/activejob/test/integration/queuing_test.rb @@ -11,7 +11,7 @@ class QueuingTest < ActiveSupport::TestCase end test 'should not run jobs queued on a non-listening queue' do - skip if adapter_is?(:inline, :sucker_punch, :que) + skip if adapter_is?(:inline, :async, :sucker_punch, :que) old_queue = TestJob.queue_name begin diff --git a/activejob/test/jobs/queue_as_job.rb b/activejob/test/jobs/queue_as_job.rb new file mode 100644 index 0000000000..897aef52e5 --- /dev/null +++ b/activejob/test/jobs/queue_as_job.rb @@ -0,0 +1,10 @@ +require_relative '../support/job_buffer' + +class QueueAsJob < ActiveJob::Base + MY_QUEUE = :low_priority + queue_as MY_QUEUE + + def perform(greeter = "David") + JobBuffer.add("#{greeter} says hello") + end +end diff --git a/activejob/test/support/integration/adapters/async.rb b/activejob/test/support/integration/adapters/async.rb new file mode 100644 index 0000000000..42beb12b1f --- /dev/null +++ b/activejob/test/support/integration/adapters/async.rb @@ -0,0 +1,9 @@ +module AsyncJobsManager + def setup + ActiveJob::Base.queue_adapter = :async + end + + def clear_jobs + ActiveJob::AsyncJob::QUEUES.clear + end +end diff --git a/activemodel/lib/active_model/serialization.rb b/activemodel/lib/active_model/serialization.rb index 58d68d9620..44bc9efa47 100644 --- a/activemodel/lib/active_model/serialization.rb +++ b/activemodel/lib/active_model/serialization.rb @@ -31,12 +31,10 @@ module ActiveModel # of the attributes hash's keys. In order to override this behavior, take a look # at the private method +read_attribute_for_serialization+. # - # Most of the time though, either the JSON or XML serializations are needed. - # Both of these modules automatically include the - # <tt>ActiveModel::Serialization</tt> module, so there is no need to - # explicitly include it. + # JSON Serialization module is automatically include the <tt>ActiveModel::Serialization</tt> + # module, so there is no need to explicitly include it. # - # A minimal implementation including XML and JSON would be: + # A minimal implementation including JSON would be: # # class Person # include ActiveModel::Serializers::JSON diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index a830b0e0e4..19ef37e228 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -619,10 +619,10 @@ module ActiveRecord # @tag = @post.tags.build name: "ruby" # @tag.save # - # The last line ought to save the through record (a <tt>Taggable</tt>). This will only work if the + # The last line ought to save the through record (a <tt>Tagging</tt>). This will only work if the # <tt>:inverse_of</tt> is set: # - # class Taggable < ActiveRecord::Base + # class Tagging < ActiveRecord::Base # belongs_to :post # belongs_to :tag, inverse_of: :taggings # end @@ -643,7 +643,7 @@ module ActiveRecord # You can turn off the automatic detection of inverse associations by setting # the <tt>:inverse_of</tt> option to <tt>false</tt> like so: # - # class Taggable < ActiveRecord::Base + # class Tagging < ActiveRecord::Base # belongs_to :tag, inverse_of: false # end # diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 55a7e053bc..4b66d8cd36 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1,5 +1,4 @@ require 'yaml' -require 'set' require 'active_support/benchmarkable' require 'active_support/dependencies' require 'active_support/descendants_tracker' diff --git a/activerecord/lib/active_record/enum.rb b/activerecord/lib/active_record/enum.rb index 91a13cb0cd..4902fcb1a2 100644 --- a/activerecord/lib/active_record/enum.rb +++ b/activerecord/lib/active_record/enum.rb @@ -18,10 +18,9 @@ module ActiveRecord # conversation.archived? # => true # conversation.status # => "archived" # - # # conversation.update! status: 1 + # # conversation.status = 1 # conversation.status = "archived" # - # # conversation.update! status: nil # conversation.status = nil # conversation.status.nil? # => true # conversation.status # => nil diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 04c6124eef..923dc1799a 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -459,7 +459,7 @@ module ActiveRecord # Or equivalently, if +TenderloveMigration+ is defined as in the # documentation for Migration: # - # require_relative '2012121212_tenderlove_migration' + # require_relative '20121212123456_tenderlove_migration' # # class FixupTLMigration < ActiveRecord::Migration # def change diff --git a/activerecord/lib/active_record/migration/command_recorder.rb b/activerecord/lib/active_record/migration/command_recorder.rb index dcc2362397..b52e89d792 100644 --- a/activerecord/lib/active_record/migration/command_recorder.rb +++ b/activerecord/lib/active_record/migration/command_recorder.rb @@ -14,6 +14,13 @@ module ActiveRecord # * rename_index # * rename_table class CommandRecorder + ReversibleAndIrreversibleMethods = [:create_table, :create_join_table, :rename_table, :add_column, :remove_column, + :rename_index, :rename_column, :add_index, :remove_index, :add_timestamps, :remove_timestamps, + :change_column_default, :add_reference, :remove_reference, :transaction, + :drop_join_table, :drop_table, :execute_block, :enable_extension, + :change_column, :execute, :remove_columns, :change_column_null, + :add_foreign_key, :remove_foreign_key + ] include JoinTable attr_accessor :commands, :delegate, :reverting @@ -70,14 +77,7 @@ module ActiveRecord super || delegate.respond_to?(*args) end - [:create_table, :create_join_table, :rename_table, :add_column, :remove_column, - :rename_index, :rename_column, :add_index, :remove_index, :add_timestamps, :remove_timestamps, - :add_reference, :remove_reference, :transaction, - :drop_join_table, :drop_table, :execute_block, :enable_extension, - :change_column, :execute, :remove_columns, :change_column_null, - :add_foreign_key, :remove_foreign_key - # irreversible methods need to be here too - ].each do |method| + ReversibleAndIrreversibleMethods.each do |method| class_eval <<-EOV, __FILE__, __LINE__ + 1 def #{method}(*args, &block) # def create_table(*args, &block) record(:"#{method}", args, &block) # record(:create_table, args, &block) diff --git a/activerecord/test/cases/invertible_migration_test.rb b/activerecord/test/cases/invertible_migration_test.rb index 99230aa3d5..3bc4fa379f 100644 --- a/activerecord/test/cases/invertible_migration_test.rb +++ b/activerecord/test/cases/invertible_migration_test.rb @@ -1,5 +1,8 @@ require "cases/helper" +class Horse < ActiveRecord::Base +end + module ActiveRecord class InvertibleMigrationTest < ActiveRecord::TestCase class SilentMigration < ActiveRecord::Migration @@ -76,6 +79,20 @@ module ActiveRecord end end + class ChangeColumnDefault1 < SilentMigration + def change + create_table("horses") do |t| + t.column :name, :string, default: "Sekitoba" + end + end + end + + class ChangeColumnDefault2 < SilentMigration + def change + change_column_default :horses, :name, from: "Sekitoba", to: "Diomed" + end + end + class LegacyMigration < ActiveRecord::Migration def self.up create_table("horses") do |t| @@ -223,6 +240,22 @@ module ActiveRecord assert !revert.connection.table_exists?("horses") end + def test_migrate_revert_change_column_default + index_definition = ["horses", [:name, :color]] + migration1 = ChangeColumnDefault1.new + migration1.migrate(:up) + assert_equal "Sekitoba", Horse.new.name + + migration2 = ChangeColumnDefault2.new + migration2.migrate(:up) + Horse.reset_column_information + assert_equal "Diomed", Horse.new.name + + migration2.migrate(:down) + Horse.reset_column_information + assert_equal "Sekitoba", Horse.new.name + end + def test_revert_order block = Proc.new{|t| t.string :name } recorder = ActiveRecord::Migration::CommandRecorder.new(ActiveRecord::Base.connection) diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index 11e42f28dc..128a242495 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -115,7 +115,7 @@ class MigrationTest < ActiveRecord::TestCase end def test_migration_version - ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/version_check", 20131219224947) + assert_nothing_raised { ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/version_check", 20131219224947) } end def test_create_table_with_force_true_does_not_drop_nonexisting_table 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/activesupport/activesupport.gemspec b/activesupport/activesupport.gemspec index 6d2ae9be76..67b6663915 100644 --- a/activesupport/activesupport.gemspec +++ b/activesupport/activesupport.gemspec @@ -25,6 +25,6 @@ Gem::Specification.new do |s| s.add_dependency 'tzinfo', '~> 1.1' s.add_dependency 'minitest', '~> 5.1' s.add_dependency 'thread_safe','~> 0.3', '>= 0.3.4' - s.add_dependency 'concurrent-ruby', '~> 0.9.0' + s.add_dependency 'concurrent-ruby', '~> 0.9.1' s.add_dependency 'method_source' end diff --git a/activesupport/lib/active_support/core_ext/kernel/reporting.rb b/activesupport/lib/active_support/core_ext/kernel/reporting.rb index 9189e6d977..8afc258df8 100644 --- a/activesupport/lib/active_support/core_ext/kernel/reporting.rb +++ b/activesupport/lib/active_support/core_ext/kernel/reporting.rb @@ -1,5 +1,3 @@ -require 'tempfile' - module Kernel # Sets $VERBOSE to nil for the duration of the block and back to its original # value afterwards. diff --git a/activesupport/lib/active_support/core_ext/string/strip.rb b/activesupport/lib/active_support/core_ext/string/strip.rb index 086c610976..9fdd9d8d2e 100644 --- a/activesupport/lib/active_support/core_ext/string/strip.rb +++ b/activesupport/lib/active_support/core_ext/string/strip.rb @@ -17,8 +17,8 @@ class String # # the user would see the usage message aligned against the left margin. # - # Technically, it looks for the least indented line in the whole string, and removes - # that amount of leading whitespace. + # Technically, it looks for the least indented non-empty line + # in the whole string, and removes that amount of leading whitespace. def strip_heredoc indent = scan(/^[ \t]*(?=\S)/).min.try(:size) || 0 gsub(/^[ \t]{#{indent}}/, '') diff --git a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb index 9b264cbb79..6572ff78a6 100644 --- a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb +++ b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb @@ -20,7 +20,7 @@ module ActiveSupport private def method_missing(called, *args, &block) - warn caller, called, args + warn caller_locations, called, args target.__send__(called, *args, &block) end end diff --git a/activesupport/lib/active_support/deprecation/reporting.rb b/activesupport/lib/active_support/deprecation/reporting.rb index a7d265d732..bbe25c9260 100644 --- a/activesupport/lib/active_support/deprecation/reporting.rb +++ b/activesupport/lib/active_support/deprecation/reporting.rb @@ -14,7 +14,7 @@ module ActiveSupport def warn(message = nil, callstack = nil) return if silenced - callstack ||= caller(2) + callstack ||= caller_locations(2) deprecation_message(callstack, message).tap do |m| behavior.each { |b| b.call(m, callstack) } end @@ -37,7 +37,7 @@ module ActiveSupport end def deprecation_warning(deprecated_method_name, message = nil, caller_backtrace = nil) - caller_backtrace ||= caller(2) + caller_backtrace ||= caller_locations(2) deprecated_method_warning(deprecated_method_name, message).tap do |msg| warn(msg, caller_backtrace) end @@ -79,6 +79,17 @@ module ActiveSupport end def extract_callstack(callstack) + return _extract_callstack(callstack) if callstack.first.is_a? String + + rails_gem_root = File.expand_path("../../../../..", __FILE__) + "/" + offending_line = callstack.find { |frame| + !frame.absolute_path.start_with?(rails_gem_root) + } || callstack.first + [offending_line.path, offending_line.lineno, offending_line.label] + end + + def _extract_callstack(callstack) + warn "Please pass `caller_locations` to the deprecation API" if $VERBOSE rails_gem_root = File.expand_path("../../../../..", __FILE__) + "/" offending_line = callstack.find { |line| !line.start_with?(rails_gem_root) } || callstack.first if offending_line diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb index f8f1b9ac2c..5dd9c423e3 100644 --- a/activesupport/lib/active_support/time_with_zone.rb +++ b/activesupport/lib/active_support/time_with_zone.rb @@ -246,6 +246,7 @@ module ActiveSupport utc.future? end + # Returns +true+ if +other+ is equal to current object. def eql?(other) other.eql?(utc) end @@ -329,6 +330,11 @@ module ActiveSupport EOV end + # Returns Array of parts of Time in sequence of + # [seconds, minutes, hours, day, month, year, weekday, yearday, dst?, zone]. + # + # now = Time.zone.now # => Tue, 18 Aug 2015 02:29:27 UTC +00:00 + # now.to_a # => [27, 29, 2, 18, 8, 2015, 2, 230, false, "UTC"] def to_a [time.sec, time.min, time.hour, time.day, time.mon, time.year, time.wday, time.yday, dst?, zone] end @@ -358,11 +364,14 @@ module ActiveSupport utc.to_r end - # Return an instance of Time in the system timezone. + # Returns an instance of Time in the system timezone. def to_time utc.to_time end + # Returns an instance of DateTime in the UTC timezone. + # + # Time.zone.now.to_datetime # => Tue, 18 Aug 2015 02:32:20 +0000 def to_datetime utc.to_datetime.new_offset(Rational(utc_offset, 86_400)) end diff --git a/guides/source/active_record_migrations.md b/guides/source/active_record_migrations.md index 4e5902fb3d..67cd86c19b 100644 --- a/guides/source/active_record_migrations.md +++ b/guides/source/active_record_migrations.md @@ -652,7 +652,7 @@ can't be done. You can use Active Record's ability to rollback migrations using the `revert` method: ```ruby -require_relative '2012121212_example_migration' +require_relative '20121212123456_example_migration' class FixupExampleMigration < ActiveRecord::Migration def change diff --git a/guides/source/debugging_rails_applications.md b/guides/source/debugging_rails_applications.md index fe8757fbd5..a05abb61d6 100644 --- a/guides/source/debugging_rails_applications.md +++ b/guides/source/debugging_rails_applications.md @@ -623,7 +623,8 @@ Processing by ArticlesController#index as HTML If we use `next`, we won't go deep inside method calls. Instead, `byebug` will go to the next line within the same context. In this case, it is the last line of the current method, so `byebug` will return to the next line of the caller -+method. +method. + ``` (byebug) next diff --git a/guides/source/testing.md b/guides/source/testing.md index 3a691220cf..1cc49a36d2 100644 --- a/guides/source/testing.md +++ b/guides/source/testing.md @@ -50,7 +50,7 @@ By default, every Rails application has three environments: development, test, a Each environment's configuration can be modified similarly. In this case, we can modify our test environment by changing the options found in `config/environments/test.rb`. -NOTE: Your test are run under RAILS_ENV=test. +NOTE: Your tests are run under `RAILS_ENV=test`. ### Rails meets Minitest @@ -336,7 +336,8 @@ All the basic assertions such as `assert_equal` defined in `Minitest::Assertions Each of these classes include `Minitest::Assertions`, allowing us to use all of the basic assertions in our tests. -NOTE: For more information on `Minitest`, refer to [Minitest](http://docs.seattlerb.org/minitest) +NOTE: For more information on `Minitest`, refer to [its +documentation](http://docs.seattlerb.org/minitest). ### The Rails Test Runner @@ -355,7 +356,8 @@ Finished tests in 0.009262s, 107.9680 tests/s, 107.9680 assertions/s. This will run all test methods from the test case. -You can also run a particular test method from the test case by providing the `-n` or `--name` flag and the `test method name`. +You can also run a particular test method from the test case by providing the +`-n` or `--name` flag and the test's method name. ```bash $ bin/rails test test/models/article_test.rb -n test_the_truth @@ -469,7 +471,8 @@ user_<%= n %>: #### Fixtures in Action -Rails by default automatically loads all fixtures from the `test/fixtures` directory for your models and controllers test. Loading involves three steps: +Rails automatically loads all fixtures from the `test/fixtures` directory by +default. Loading involves three steps: 1. Remove any existing data from the table corresponding to the fixture 2. Load the fixture data into the table @@ -505,7 +508,8 @@ Model Testing Model tests are used to test the various models of your application. -For creating Rails model tests, we use the 'test/model' directory for your application. Rails provides a generator to create an model test skeleton for you. +Rails model tests are stored under the `test/models` directory. Rails provides +a generator to create a model test skeleton for you. ```bash $ bin/rails generate test_unit:model article title:string body:text @@ -563,7 +567,8 @@ We'll start by generating our integration test skeleton: $ bin/rails generate integration_test blog_flow ``` -It should have created a test file placeholder for us, with the output of the previous command you should see: +It should have created a test file placeholder for us. With the output of the +previous command you should see: ```bash invoke test_unit 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/application/initializers/frameworks_test.rb b/railties/test/application/initializers/frameworks_test.rb index 7bba910b9e..6e3707cc27 100644 --- a/railties/test/application/initializers/frameworks_test.rb +++ b/railties/test/application/initializers/frameworks_test.rb @@ -1,5 +1,4 @@ require "isolation/abstract_unit" -require 'set' module ApplicationTests class FrameworksTest < ActiveSupport::TestCase 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) diff --git a/railties/test/path_generation_test.rb b/railties/test/path_generation_test.rb index 27e64b97b7..a16adc72a6 100644 --- a/railties/test/path_generation_test.rb +++ b/railties/test/path_generation_test.rb @@ -11,26 +11,26 @@ class PathGenerationTest < ActiveSupport::TestCase super() end - class Dispatcher < ActionDispatch::Routing::RouteSet::Dispatcher - def initialize(defaults, set, block) - super(defaults) + class Request < DelegateClass(ActionDispatch::Request) + def initialize(target, url_helpers, block) + super(target) + @url_helpers = url_helpers @block = block - @set = set end - def controller_reference(controller_param) + def controller_class + url_helpers = @url_helpers block = @block - set = @set Class.new(ActionController::Base) { - include set.url_helpers + include url_helpers define_method(:process) { |name| block.call(self) } def to_a; [200, {}, []]; end } end end - def dispatcher defaults - TestSet::Dispatcher.new defaults, self, @block + def make_request(env) + Request.new super, self.url_helpers, @block end end diff --git a/tasks/release.rb b/tasks/release.rb index d8c1390eef..2c7e927679 100644 --- a/tasks/release.rb +++ b/tasks/release.rb @@ -98,7 +98,7 @@ namespace :all do task :push => FRAMEWORKS.map { |f| "#{f}:push" } + ['rails:push'] task :ensure_clean_state do - unless `git status -s | grep -v RAILS_VERSION`.strip.empty? + unless `git status -s | grep -v 'RAILS_VERSION\\|CHANGELOG'`.strip.empty? abort "[ABORTING] `git status` reports a dirty tree. Make sure all changes are committed" end |