1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Fix handling SCRIPT_NAME from within mounted engine's

When you mount your application at a path, for example /myapp, server
should set SCRIPT_NAME to /myapp. With such information, rails
application knows that it's mounted at /myapp path and it should generate
routes relative to that path.

Before this patch, rails handled SCRIPT_NAME correctly only for regular
apps, but it failed to do it for mounted engines. The solution was to
hardcode default_url_options[:script_name], which is not the best answer
- it will work only when application is mounted at a fixed path.

This patch fixes the situation by respecting original value of
SCRIPT_NAME when generating application's routes from engine and the
other way round - when you generate engine's routes from application.

This is done by using one of 2 pieces of information in env - current
SCRIPT_NAME or SCRIPT_NAME for a corresponding router. This is because
we have 2 cases to handle:

- generating engine's route from application: in this situation
  SCRIPT_NAME is basically SCRIPT_NAME set by the server and it
  indicates the place where application is mounted, so we can just pass
  it as :original_script_name in url_options. :original_script_name is
  used because if we use :script_name, router will ignore generating
  prefix for engine

- generating application's route from engine: in this situation we
  already lost information about the SCRIPT_NAME that server used. For
  example if application is mounted at /myapp and engine is mounted at
  /blog, at this point SCRIPT_NAME is equal /myapp/blog. Because of that
  we need to keep reference to /myapp SCRIPT_NAME by binding it to the
  current router. Later on we can extract it and use when generating url

Please note that starting from now you *should not* use
default_url_options[:script_name] explicitly if your server already
passes correct SCRIPT_NAME to rack env.

(closes #6933)
This commit is contained in:
Piotr Sarnacki 2012-08-10 23:27:51 +02:00
parent f2557112a5
commit 5b3bb61f3f
7 changed files with 78 additions and 41 deletions

View file

@ -30,9 +30,15 @@ module ActionController
:_recall => request.symbolized_path_parameters
).freeze
if _routes.equal?(env["action_dispatch.routes"])
if (same_origin = _routes.equal?(env["action_dispatch.routes"])) ||
(script_name = env["ROUTES_#{_routes.object_id}_SCRIPT_NAME"]) ||
(original_script_name = env['SCRIPT_NAME'])
@_url_options.dup.tap do |options|
options[:script_name] = request.script_name.dup
if original_script_name
options[:original_script_name] = original_script_name
else
options[:script_name] = same_origin ? request.script_name.dup : script_name
end
options.freeze
end
else

View file

@ -163,9 +163,9 @@ module ActionDispatch
private
def define_named_route_methods(name, route)
define_url_helper route, :"#{name}_path",
define_url_helper route, :"#{name}_path",
route.defaults.merge(:use_route => name, :only_path => true)
define_url_helper route, :"#{name}_url",
define_url_helper route, :"#{name}_url",
route.defaults.merge(:use_route => name, :only_path => false)
end
@ -465,7 +465,7 @@ module ActionDispatch
def use_recall_for(key)
if @recall[key] && (!@options.key?(key) || @options[key] == @recall[key])
if !named_route_exists? || segment_keys.include?(key)
@options[key] = @recall.delete(key)
@options[key] = @recall.delete(key)
end
end
end
@ -574,7 +574,8 @@ module ActionDispatch
end
RESERVED_OPTIONS = [:host, :protocol, :port, :subdomain, :domain, :tld_length,
:trailing_slash, :anchor, :params, :only_path, :script_name]
:trailing_slash, :anchor, :params, :only_path, :script_name,
:original_script_name]
def mounted?
false
@ -594,7 +595,13 @@ module ActionDispatch
user, password = extract_authentication(options)
recall = options.delete(:_recall)
script_name = options.delete(:script_name).presence || _generate_prefix(options)
original_script_name = options.delete(:original_script_name).presence
script_name = options.delete(:script_name).presence || _generate_prefix(options)
if script_name && original_script_name
script_name = original_script_name + script_name
end
path_options = options.except(*RESERVED_OPTIONS)
path_options = yield(path_options) if block_given?

View file

@ -166,18 +166,6 @@ module TestGenerationPrefix
assert_equal "/generate", last_response.body
end
test "[ENGINE] generating application's url includes default_url_options[:script_name]" do
RailsApplication.routes.default_url_options = {:script_name => "/something"}
get "/pure-awesomeness/blog/url_to_application"
assert_equal "/something/generate", last_response.body
end
test "[ENGINE] generating application's url should give higher priority to default_url_options[:script_name]" do
RailsApplication.routes.default_url_options = {:script_name => "/something"}
get "/pure-awesomeness/blog/url_to_application", {}, 'SCRIPT_NAME' => '/foo'
assert_equal "/something/generate", last_response.body
end
test "[ENGINE] generating engine's url with polymorphic path" do
get "/pure-awesomeness/blog/polymorphic_path_for_engine"
assert_equal "/pure-awesomeness/blog/posts/1", last_response.body
@ -200,12 +188,6 @@ module TestGenerationPrefix
assert_equal "/something/awesome/blog/posts/1", last_response.body
end
test "[APP] generating engine's route should give higher priority to default_url_options[:script_name]" do
RailsApplication.routes.default_url_options = {:script_name => "/something"}
get "/generate", {}, 'SCRIPT_NAME' => "/foo"
assert_equal "/something/awesome/blog/posts/1", last_response.body
end
test "[APP] generating engine's url with polymorphic path" do
get "/polymorphic_path_for_engine"
assert_equal "/awesome/blog/posts/1", last_response.body

View file

@ -1,5 +1,10 @@
## Rails 4.0.0 (unreleased) ##
* Correctly handle SCRIPT_NAME when generating routes to engine in application
that's mounted at a sub-uri. With this behavior, you *should not* use
default_url_options[:script_name] to set proper application's mount point by
yourself. *Piotr Sarnacki*
* The migration generator will now produce AddXXXToYYY/RemoveXXXFromYYY migrations with references statements, for instance
rails g migration AddReferencesToProducts user:references supplier:references{polymorphic}

View file

@ -494,7 +494,11 @@ module Rails
# Define the Rack API for this engine.
def call(env)
app.call(env.merge!(env_config))
env.merge!(env_config)
if env['SCRIPT_NAME']
env.merge! "ROUTES_#{routes.object_id}_SCRIPT_NAME" => env['SCRIPT_NAME'].dup
end
app.call(env)
end
# Defines additional Rack env configuration that is added on each call.

View file

@ -1193,6 +1193,54 @@ YAML
last_response.body.split("\n").map(&:strip)
end
test "paths are properly generated when application is mounted at sub-path" do
@plugin.write "lib/bukkits.rb", <<-RUBY
module Bukkits
class Engine < ::Rails::Engine
isolate_namespace Bukkits
end
end
RUBY
app_file "app/controllers/bar_controller.rb", <<-RUBY
class BarController < ApplicationController
def index
render :text => bukkits.bukkit_path
end
end
RUBY
app_file "config/routes.rb", <<-RUBY
AppTemplate::Application.routes.draw do
get '/bar' => 'bar#index', :as => 'bar'
mount Bukkits::Engine => "/bukkits", :as => "bukkits"
end
RUBY
@plugin.write "config/routes.rb", <<-RUBY
Bukkits::Engine.routes.draw do
get '/bukkit' => 'bukkit#index'
end
RUBY
@plugin.write "app/controllers/bukkits/bukkit_controller.rb", <<-RUBY
class Bukkits::BukkitController < ActionController::Base
def index
render :text => main_app.bar_path
end
end
RUBY
boot_rails
get("/bukkits/bukkit", {}, {'SCRIPT_NAME' => '/foo'})
assert_equal '/foo/bar', last_response.body
get("/bar", {}, {'SCRIPT_NAME' => '/foo'})
assert_equal '/foo/bukkits/bukkit', last_response.body
end
private
def app
Rails.application

View file

@ -163,24 +163,14 @@ module ApplicationTests
end
end
def reset_script_name!
Rails.application.routes.default_url_options = {}
end
def script_name(script_name)
Rails.application.routes.default_url_options = {:script_name => script_name}
end
test "routes generation in engine and application" do
# test generating engine's route from engine
get "/john/blog/posts"
assert_equal "/john/blog/posts/1", last_response.body
# test generating engine's route from engine with default_url_options
script_name "/foo"
get "/john/blog/posts", {}, 'SCRIPT_NAME' => "/foo"
assert_equal "/foo/john/blog/posts/1", last_response.body
reset_script_name!
# test generating engine's route from application
get "/engine_route"
@ -193,14 +183,11 @@ module ApplicationTests
assert_equal "/john/blog/posts", last_response.body
# test generating engine's route from application with default_url_options
script_name "/foo"
get "/engine_route", {}, 'SCRIPT_NAME' => "/foo"
assert_equal "/foo/anonymous/blog/posts", last_response.body
script_name "/foo"
get "/url_for_engine_route", {}, 'SCRIPT_NAME' => "/foo"
assert_equal "/foo/john/blog/posts", last_response.body
reset_script_name!
# test generating application's route from engine
get "/someone/blog/generate_application_route"
@ -210,10 +197,8 @@ module ApplicationTests
assert_equal "/", last_response.body
# test generating application's route from engine with default_url_options
script_name "/foo"
get "/someone/blog/generate_application_route", {}, 'SCRIPT_NAME' => '/foo'
assert_equal "/foo/", last_response.body
reset_script_name!
# test polymorphic routes
get "/polymorphic_route"