Skip to content

Commit

Permalink
Another experimental implementation: try to use env['action_dispatch.…
Browse files Browse the repository at this point in the history
…routes'] to determine whether we are in call inside Engine or application. Calls without env present should always generate the prefix
  • Loading branch information
drogus committed Jul 6, 2010
1 parent bbf78b2 commit 1f2a634
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 7 deletions.
27 changes: 27 additions & 0 deletions actionpack/lib/action_dispatch/routing/mapper.rb
Expand Up @@ -231,14 +231,41 @@ def mount(app, options = nil)

raise "A rack application must be specified" unless path

name = (options[:as] ||= get_named_route_name(app))

match(path, options.merge(:to => app, :anchor => false))

define_generate_prefix(app, name)
self
end

def default_url_options=(options)
@set.default_url_options = options
end
alias_method :default_url_options, :default_url_options=

private
def get_named_route_name(app)
return unless app.respond_to?(:routes)
class_name = app.class.is_a?(Class) ? app.name : app.class.name
ActiveSupport::Inflector.underscore(class_name).gsub("/", "_")
end

def define_generate_prefix(app, name)
return unless app.respond_to?(:routes)

_route = @set.named_routes.routes[name.to_sym]
_router = @set
app.routes.class_eval do
define_method :_generate_prefix do |options|
keys = _route.segment_keys + ActionDispatch::Routing::RouteSet::RESERVED_OPTIONS
prefix_options = options.reject { |k, v| !(keys).include?(k) }
# we must actually delete prefix segment keys to avoid passing them to next url_for
_route.segment_keys.each { |k| options.delete(k) }
_router.url_helpers.send("#{name}_path", prefix_options)
end
end
end
end

module HttpHelpers
Expand Down
15 changes: 10 additions & 5 deletions actionpack/lib/action_dispatch/routing/route_set.rb
Expand Up @@ -198,6 +198,7 @@ def define_url_helper(route, name, kind, options)
options[:_positional_keys] = route.segment_keys
end

options[:routes] = _original_routes
url_for(options)
end
end
Expand Down Expand Up @@ -310,10 +311,9 @@ def add_route(app, conditions = {}, requirements = {}, defaults = {}, name = nil
end

class Generator #:nodoc:
attr_reader :options, :recall, :set, :script_name, :named_route
attr_reader :options, :recall, :set, :named_route

def initialize(options, recall, set, extras = false)
@script_name = options.delete(:script_name)
@named_route = options.delete(:use_route)
@options = options.dup
@recall = recall.dup
Expand Down Expand Up @@ -409,7 +409,7 @@ def generate
return [path, params.keys] if @extras

path << "?#{params.to_query}" if params.any?
"#{script_name}#{path}"
path
rescue Rack::Mount::RoutingError
raise error
end
Expand Down Expand Up @@ -471,7 +471,6 @@ def url_for(options)
rewritten_url = ""

path_segments = options.delete(:_path_segments)

unless options[:only_path]
rewritten_url << (options[:protocol] || "http")
rewritten_url << "://" unless rewritten_url.match("://")
Expand All @@ -483,9 +482,15 @@ def url_for(options)
rewritten_url << ":#{options.delete(:port)}" if options.key?(:port)
end

path = [options.delete(:script_name)]
if !options.delete(:skip_prefix)
path << _generate_prefix(options)
end

path_options = options.except(*RESERVED_OPTIONS)
path_options = yield(path_options) if block_given?
path = generate(path_options, path_segments || {})
path << generate(path_options, path_segments || {})
path = path.compact.join ''

# ROUTES TODO: This can be called directly, so script_name should probably be set in the routes
rewritten_url << (options[:trailing_slash] ? path.sub(/\?|\z/) { "/" + $& } : path)
Expand Down
8 changes: 7 additions & 1 deletion actionpack/lib/action_dispatch/routing/url_for.rb
Expand Up @@ -128,7 +128,13 @@ def url_for(options = nil)
when String
options
when nil, Hash
_routes.url_for(url_options.merge((options || {}).symbolize_keys))
_original_routes = options ? options.delete(:routes) : nil

if respond_to?(:env) && env && _original_routes.equal?(env["action_dispatch.routes"])
options[:skip_prefix] = true
end

(_original_routes || _routes).url_for(url_options.merge((options || {}).symbolize_keys))
else
polymorphic_url(options)
end
Expand Down
2 changes: 1 addition & 1 deletion actionpack/test/controller/routing_test.rb
Expand Up @@ -251,7 +251,7 @@ def test_optimised_named_route_with_host
map.pages 'pages', :controller => 'content', :action => 'show_page', :host => 'foo.com'
end
x = setup_for_named_route
x.expects(:url_for).with(:host => 'foo.com', :only_path => false, :controller => 'content', :action => 'show_page', :use_route => :pages).once
x.expects(:url_for).with(:host => 'foo.com', :only_path => false, :controller => 'content', :action => 'show_page', :use_route => :pages, :router => rs).once
x.send(:pages_url)
end

Expand Down
89 changes: 89 additions & 0 deletions actionpack/test/dispatch/prefix_generation_test.rb
@@ -0,0 +1,89 @@
require 'abstract_unit'

module TestGenerationPrefix
class WithMountedEngine < ActionDispatch::IntegrationTest
class BlogEngine
def self.routes
@routes ||= begin
routes = ActionDispatch::Routing::RouteSet.new
routes.draw do
match "/posts/:id", :to => "inside_engine_generating#index", :as => :post
end

routes
end
end

def self.call(env)
env['action_dispatch.routes'] = routes
routes.call(env)
end
end

class RailsApplication
def self.routes
@routes ||= begin
routes = ActionDispatch::Routing::RouteSet.new
routes.draw do
scope "/:omg", :omg => "awesome" do
mount BlogEngine => "/blog"
end
match "/generate", :to => "outside_engine_generating#index"
end

routes
end
end

def self.call(env)
env['action_dispatch.routes'] = routes
routes.call(env)
end
end

class ::InsideEngineGeneratingController < ActionController::Base
include BlogEngine.routes.url_helpers
def index
render :text => post_path(:id => params[:id])
end
end

class ::OutsideEngineGeneratingController < ActionController::Base
include RailsApplication.routes.url_helpers
include BlogEngine.routes.url_helpers
def index
render :text => post_path(:id => 1)
end
end

RailsApplication.routes # force draw
include BlogEngine.routes.url_helpers

test "generating URL with prefix" do
assert_equal "/awesome/blog/posts/1", post_path(:id => 1)
end

test "use SCRIPT_NAME inside the engine" do
env = Rack::MockRequest.env_for("/posts/1")
env["SCRIPT_NAME"] = "/pure-awesomness/blog"
response = ActionDispatch::Response.new(*BlogEngine.call(env))
assert_equal "/pure-awesomness/blog/posts/1", response.body
end

test "prepend prefix outside the engine" do
env = Rack::MockRequest.env_for("/generate")
env["SCRIPT_NAME"] = "/something" # it could be set by passenger
response = ActionDispatch::Response.new(*RailsApplication.call(env))
assert_equal "/something/awesome/blog/posts/1", response.body
end

test "generating urls with options for both prefix and named_route" do
assert_equal "/pure-awesomness/blog/posts/3", post_path(:id => 3, :omg => "pure-awesomness")
end

test "generating urls with url_for should prepend the prefix" do
path = BlogEngine.routes.url_for(:omg => 'omg', :controller => "inside_engine_generating", :action => "index", :id => 1, :only_path => true)
assert_equal "/omg/blog/posts/1", path
end
end
end

0 comments on commit 1f2a634

Please sign in to comment.