aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb19
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb4
-rw-r--r--actionpack/lib/action_dispatch/routing/routes_proxy.rb7
-rw-r--r--railties/CHANGELOG.md6
-rw-r--r--railties/test/railties/engine_test.rb86
5 files changed, 112 insertions, 10 deletions
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index d1c5b5a7ff..a2ba4e26ea 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -652,18 +652,25 @@ module ActionDispatch
def define_generate_prefix(app, name)
_route = @set.named_routes.get name
_routes = @set
- app.routes.define_mounted_helper(name)
+
+ script_namer = ->(options) do
+ prefix_options = options.slice(*_route.segment_keys)
+ prefix_options[:relative_url_root] = "".freeze
+ # We must actually delete prefix segment keys to avoid passing them to next url_for.
+ _route.segment_keys.each { |k| options.delete(k) }
+ _routes.url_helpers.send("#{name}_path", prefix_options)
+ end
+
+ app.routes.define_mounted_helper(name, script_namer)
+
app.routes.extend Module.new {
def optimize_routes_generation?; false; end
+
define_method :find_script_name do |options|
if options.key? :script_name
super(options)
else
- prefix_options = options.slice(*_route.segment_keys)
- prefix_options[:relative_url_root] = "".freeze
- # We must actually delete prefix segment keys to avoid passing them to next url_for.
- _route.segment_keys.each { |k| options.delete(k) }
- _routes.url_helpers.send("#{name}_path", prefix_options)
+ script_namer.call(options)
end
end
}
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index ebe809f64e..a146d1fb1a 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -449,7 +449,7 @@ module ActionDispatch
MountedHelpers
end
- def define_mounted_helper(name)
+ def define_mounted_helper(name, script_namer = nil)
return if MountedHelpers.method_defined?(name)
routes = self
@@ -457,7 +457,7 @@ module ActionDispatch
MountedHelpers.class_eval do
define_method "_#{name}" do
- RoutesProxy.new(routes, _routes_context, helpers)
+ RoutesProxy.new(routes, _routes_context, helpers, script_namer)
end
end
diff --git a/actionpack/lib/action_dispatch/routing/routes_proxy.rb b/actionpack/lib/action_dispatch/routing/routes_proxy.rb
index c1423f770f..7a6c2e95d8 100644
--- a/actionpack/lib/action_dispatch/routing/routes_proxy.rb
+++ b/actionpack/lib/action_dispatch/routing/routes_proxy.rb
@@ -8,9 +8,10 @@ module ActionDispatch
attr_accessor :scope, :routes
alias :_routes :routes
- def initialize(routes, scope, helpers)
+ def initialize(routes, scope, helpers, script_namer = nil)
@routes, @scope = routes, scope
@helpers = helpers
+ @script_namer = script_namer
end
def url_options
@@ -29,7 +30,9 @@ module ActionDispatch
self.class.class_eval <<-RUBY, __FILE__, __LINE__ + 1
def #{method}(*args)
options = args.extract_options!
- args << url_options.merge((options || {}).symbolize_keys)
+ options = url_options.merge((options || {}).symbolize_keys)
+ options.reverse_merge!(script_name: @script_namer.call(options)) if @script_namer
+ args << options
@helpers.#{method}(*args)
end
RUBY
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index f8137d95a8..d1ccf5d97d 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,3 +1,9 @@
+* Allow mounting the same engine several times in different locations.
+
+ Fixes #20204.
+
+ *David Rodríguez*
+
* Clear screenshot files in `tmp:clear` task.
*Yuji Yaginuma*
diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb
index e382a7a873..0379394f31 100644
--- a/railties/test/railties/engine_test.rb
+++ b/railties/test/railties/engine_test.rb
@@ -1341,6 +1341,92 @@ YAML
assert_equal "/foo/bukkits/bukkit", last_response.body
end
+ test "isolated engine can be mounted under multiple static locations" do
+ app_file "app/controllers/foos_controller.rb", <<-RUBY
+ class FoosController < ApplicationController
+ def through_fruits
+ render plain: fruit_bukkits.posts_path
+ end
+
+ def through_vegetables
+ render plain: vegetable_bukkits.posts_path
+ end
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ scope "/fruits" do
+ mount Bukkits::Engine => "/bukkits", as: :fruit_bukkits
+ end
+
+ scope "/vegetables" do
+ mount Bukkits::Engine => "/bukkits", as: :vegetable_bukkits
+ end
+
+ get "/through_fruits" => "foos#through_fruits"
+ get "/through_vegetables" => "foos#through_vegetables"
+ end
+ RUBY
+
+ @plugin.write "config/routes.rb", <<-RUBY
+ Bukkits::Engine.routes.draw do
+ resources :posts, only: :index
+ end
+ RUBY
+
+ boot_rails
+
+ get("/through_fruits")
+ assert_equal "/fruits/bukkits/posts", last_response.body
+
+ get("/through_vegetables")
+ assert_equal "/vegetables/bukkits/posts", last_response.body
+ end
+
+ test "isolated engine can be mounted under multiple dynamic locations" do
+ app_file "app/controllers/foos_controller.rb", <<-RUBY
+ class FoosController < ApplicationController
+ def through_fruits
+ render plain: fruit_bukkits.posts_path(fruit_id: 1)
+ end
+
+ def through_vegetables
+ render plain: vegetable_bukkits.posts_path(vegetable_id: 1)
+ end
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ resources :fruits do
+ mount Bukkits::Engine => "/bukkits"
+ end
+
+ resources :vegetables do
+ mount Bukkits::Engine => "/bukkits"
+ end
+
+ get "/through_fruits" => "foos#through_fruits"
+ get "/through_vegetables" => "foos#through_vegetables"
+ end
+ RUBY
+
+ @plugin.write "config/routes.rb", <<-RUBY
+ Bukkits::Engine.routes.draw do
+ resources :posts, only: :index
+ end
+ RUBY
+
+ boot_rails
+
+ get("/through_fruits")
+ assert_equal "/fruits/1/bukkits/posts", last_response.body
+
+ get("/through_vegetables")
+ assert_equal "/vegetables/1/bukkits/posts", last_response.body
+ end
+
private
def app
Rails.application