diff --git a/actionpack/lib/action_dispatch/middleware/stack.rb b/actionpack/lib/action_dispatch/middleware/stack.rb index ca66ff2c73..201eccebed 100644 --- a/actionpack/lib/action_dispatch/middleware/stack.rb +++ b/actionpack/lib/action_dispatch/middleware/stack.rb @@ -122,6 +122,24 @@ module ActionDispatch middlewares.delete_if { |m| m.klass == target } end + def move(target, source) + source_index = assert_index(source, :before) + source_middleware = middlewares.delete_at(source_index) + + target_index = assert_index(target, :before) + middlewares.insert(target_index, source_middleware) + end + + alias_method :move_before, :move + + def move_after(target, source) + source_index = assert_index(source, :after) + source_middleware = middlewares.delete_at(source_index) + + target_index = assert_index(target, :after) + middlewares.insert(target_index + 1, source_middleware) + end + def use(klass, *args, &block) middlewares.push(build_middleware(klass, args, block)) end diff --git a/actionpack/test/dispatch/middleware_stack_test.rb b/actionpack/test/dispatch/middleware_stack_test.rb index c534e60c74..a01eab82a7 100644 --- a/actionpack/test/dispatch/middleware_stack_test.rb +++ b/actionpack/test/dispatch/middleware_stack_test.rb @@ -93,6 +93,60 @@ class MiddlewareStackTest < ActiveSupport::TestCase assert_equal FooMiddleware, @stack[0].klass end + test "move moves middleware at the integer index" do + @stack.move(0, BarMiddleware) + assert_equal BarMiddleware, @stack[0].klass + assert_equal FooMiddleware, @stack[1].klass + end + + test "move requires the moved middleware to be in the stack" do + assert_raises RuntimeError do + @stack.move(0, BazMiddleware) + end + end + + test "move preserves the arguments of the moved middleware" do + @stack.use BazMiddleware, true, foo: "bar" + @stack.move_before(FooMiddleware, BazMiddleware) + + assert_equal [true, foo: "bar"], @stack.first.args + end + + test "move_before moves middleware before another middleware class" do + @stack.move_before(FooMiddleware, BarMiddleware) + assert_equal BarMiddleware, @stack[0].klass + assert_equal FooMiddleware, @stack[1].klass + end + + test "move_after requires the moved middleware to be in the stack" do + assert_raises RuntimeError do + @stack.move_after(BarMiddleware, BazMiddleware) + end + end + + test "move_after moves middleware after the integer index" do + @stack.insert_after(BarMiddleware, BazMiddleware) + @stack.move_after(0, BazMiddleware) + assert_equal FooMiddleware, @stack[0].klass + assert_equal BazMiddleware, @stack[1].klass + assert_equal BarMiddleware, @stack[2].klass + end + + test "move_after moves middleware after another middleware class" do + @stack.insert_after(BarMiddleware, BazMiddleware) + @stack.move_after(BarMiddleware, FooMiddleware) + assert_equal BarMiddleware, @stack[0].klass + assert_equal FooMiddleware, @stack[1].klass + assert_equal BazMiddleware, @stack[2].klass + end + + test "move_afters preserves the arguments of the moved middleware" do + @stack.use BazMiddleware, true, foo: "bar" + @stack.move_after(FooMiddleware, BazMiddleware) + + assert_equal [true, foo: "bar"], @stack[1].args + end + test "unshift adds a new middleware at the beginning of the stack" do @stack.unshift MiddlewareStackTest::BazMiddleware assert_equal BazMiddleware, @stack.first.klass diff --git a/guides/source/configuring.md b/guides/source/configuring.md index 07b0579b02..c262087370 100644 --- a/guides/source/configuring.md +++ b/guides/source/configuring.md @@ -317,6 +317,19 @@ Middlewares can also be completely swapped out and replaced with others: config.middleware.swap ActionController::Failsafe, Lifo::Failsafe ``` +Middlewares can be moved from one place to another: + +```ruby +config.middleware.move_before ActionDispatch::Flash, Magical::Unicorns +``` + +This will move the `Magical::Unicorns` middleware before +`ActionDispatch::Flash`. You can also move it after: + +```ruby +config.middleware.move_after ActionDispatch::Flash, Magical::Unicorns +``` + They can also be removed from the stack completely: ```ruby diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md index e4850ad915..862045e812 100644 --- a/railties/CHANGELOG.md +++ b/railties/CHANGELOG.md @@ -1,3 +1,21 @@ +* Introduce middleware move operations + + With this change, you no longer need to delete and reinsert a middleware to + move it from one place to another in the stack: + + ```ruby + config.middleware.move_before ActionDispatch::Flash, Magical::Unicorns + ``` + + This will move the `Magical::Unicorns` middleware before + `ActionDispatch::Flash`. You can also move it after with: + + ```ruby + config.middleware.move_after ActionDispatch::Flash, Magical::Unicorns + ``` + + *Genadi Samokovarov* + * Generators that inherit from NamedBase respect `--force` option *Josh Brody* diff --git a/railties/lib/rails/configuration.rb b/railties/lib/rails/configuration.rb index 7fcd8097b5..b8c0637078 100644 --- a/railties/lib/rails/configuration.rb +++ b/railties/lib/rails/configuration.rb @@ -30,6 +30,15 @@ module Rails # # config.middleware.swap ActionDispatch::Flash, Magical::Unicorns # + # Middlewares can be moved from one place to another: + # + # config.middleware.move_before ActionDispatch::Flash, Magical::Unicorns + # + # This will move the Magical::Unicorns middleware before the + # ActionDispatch::Flash. You can also move it after: + # + # config.middleware.move_after ActionDispatch::Flash, Magical::Unicorns + # # And finally they can also be removed from the stack completely: # # config.middleware.delete ActionDispatch::Flash @@ -66,6 +75,16 @@ module Rails @delete_operations << -> middleware { middleware.send(__method__, *args, &block) } end + def move_before(*args, &block) + @delete_operations << -> middleware { middleware.send(__method__, *args, &block) } + end + + alias :move :move_before + + def move_after(*args, &block) + @delete_operations << -> middleware { middleware.send(__method__, *args, &block) } + end + def unshift(*args, &block) @operations << -> middleware { middleware.send(__method__, *args, &block) } end diff --git a/railties/test/configuration/middleware_stack_proxy_test.rb b/railties/test/configuration/middleware_stack_proxy_test.rb index b67142f4c2..a40ec8f8e5 100644 --- a/railties/test/configuration/middleware_stack_proxy_test.rb +++ b/railties/test/configuration/middleware_stack_proxy_test.rb @@ -18,6 +18,11 @@ module Rails assert_playback :insert_before, :foo end + def test_playback_insert + @stack.insert :foo + assert_playback :insert_before, :foo + end + def test_playback_insert_after @stack.insert_after :foo assert_playback :insert_after, :foo @@ -38,6 +43,21 @@ module Rails assert_playback :delete, :foo end + def test_playback_move_before + @stack.move_before :foo + assert_playback :move_before, :foo + end + + def test_playback_move + @stack.move :foo + assert_playback :move_before, :foo + end + + def test_playback_move_after + @stack.move_after :foo + assert_playback :move_after, :foo + end + def test_order @stack.swap :foo @stack.delete :foo