Skip to content

Commit

Permalink
Add mounted_helpers to routes
Browse files Browse the repository at this point in the history
mounted_helpers are a bit similar to url_helpers. They're automatically
included in controllers for Rails.application and each of mounted
Engines. Mounted helper allows to call url_for and named helpers for
given application.

Given Blog::Engine mounted as blog_engine, there are 2 helpers defined:
app and blog_engine. You can call routes for app and engine using those
helpers:

app.root_url
app.url_for(:controller => "foo")
blog_engine.posts_path
blog_engine.url_for(@post)
  • Loading branch information
drogus committed Sep 3, 2010
1 parent e9791be commit 6c95e0f
Show file tree
Hide file tree
Showing 9 changed files with 192 additions and 98 deletions.
1 change: 1 addition & 0 deletions actionpack/lib/abstract_controller/rendering.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ def view_context_class

if controller.respond_to?(:_routes)
include controller._routes.url_helpers
include controller._routes.mounted_helpers
end

# TODO: Fix RJS to not require this
Expand Down
3 changes: 2 additions & 1 deletion actionpack/lib/action_controller/railtie.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ class Railtie < Rails::Railtie

ActiveSupport.on_load(:action_controller) do
include app.routes.url_helpers
include app.routes.mounted_helpers(:app)
options.each { |k,v| send("#{k}=", v) }
end
end
Expand All @@ -63,4 +64,4 @@ class Railtie < Rails::Railtie
ActionController::Routing::Routes = proxy
end
end
end
end
26 changes: 26 additions & 0 deletions actionpack/lib/action_controller/railties/url_helpers.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
module ActionController
module Railties

module UrlHelpers
def self.with(routes)
Module.new do
define_method(:inherited) do |klass|
super(klass)
klass.send(:include, routes.url_helpers)
end
end
end
end

module MountedHelpers
def self.with(routes, name = nil)
Module.new do
define_method(:inherited) do |klass|
super(klass)
klass.send(:include, routes.mounted_helpers(name))
end
end
end
end
end
end
5 changes: 3 additions & 2 deletions actionpack/lib/action_dispatch/routing/mapper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -285,13 +285,14 @@ def define_generate_prefix(app, name)
return unless app.respond_to?(:routes)

_route = @set.named_routes.routes[name.to_sym]
_router = @set
_routes = @set
app.routes.define_mounted_helper(name)
app.routes.class_eval do
define_method :_generate_prefix do |options|
prefix_options = options.slice(*_route.segment_keys)
# 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)
_routes.url_helpers.send("#{name}_path", prefix_options)
end
end
end
Expand Down
59 changes: 59 additions & 0 deletions actionpack/lib/action_dispatch/routing/route_set.rb
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,65 @@ def install_helpers(destinations = [ActionController::Base, ActionView::Base], r
named_routes.install(destinations, regenerate_code)
end

class RoutesProxy
include ActionDispatch::Routing::UrlFor

%w(url_options polymorphic_url polymorphic_path).each do |method|
self.class_eval <<-RUBY, __FILE__, __LINE__ +1
def #{method}(*args)
scope.send(:_with_routes, routes) do
scope.#{method}(*args)
end
end
RUBY
end

attr_accessor :scope, :routes
alias :_routes :routes

def initialize(routes, scope)
@routes, @scope = routes, scope
end

def method_missing(method, *args)
if routes.url_helpers.respond_to?(method)
self.class.class_eval <<-RUBY, __FILE__, __LINE__ + 1
def #{method}(*args)
options = args.extract_options!
args << url_options.merge((options || {}).symbolize_keys)
routes.url_helpers.#{method}(*args)
end
RUBY
send(method, *args)
else
super
end
end
end

module MountedHelpers
end

def mounted_helpers(name = nil)
define_mounted_helper(name) if name
MountedHelpers
end

def define_mounted_helper(name, helpers = nil)
routes = self
MountedHelpers.class_eval do
define_method "_#{name}" do
RoutesProxy.new(routes, self)
end
end

MountedHelpers.class_eval <<-RUBY
def #{name}
@#{name} ||= _#{name}
end
RUBY
end

def url_helpers
@url_helpers ||= begin
routes = self
Expand Down
28 changes: 7 additions & 21 deletions actionpack/lib/action_dispatch/routing/url_for.rb
Original file line number Diff line number Diff line change
Expand Up @@ -123,28 +123,14 @@ def url_options
# url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :anchor => 'ok', :only_path => true # => '/tasks/testing#ok'
# url_for :controller => 'tasks', :action => 'testing', :trailing_slash=>true # => 'http://somehost.org/tasks/testing/'
# url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :number => '33' # => 'http://somehost.org/tasks/testing?number=33'
def url_for(options = nil, *args)
if options.respond_to?(:routes)
_with_routes(options.routes) do
if args.first.is_a? Symbol
named_route = args.shift
url_for _routes.url_helpers.send("hash_for_#{named_route}", *args)
else
url_for(*args)
end
end
def url_for(options = nil)
case options
when String
options
when nil, Hash
_routes.url_for((options || {}).reverse_merge!(url_options).symbolize_keys)
else
case options
when String
options
when nil, Hash
routes = (options ? options.delete(:routes) : nil) || _routes
_with_routes(routes) do
routes.url_for((options || {}).reverse_merge!(url_options).symbolize_keys)
end
else
polymorphic_url(options)
end
polymorphic_url(options)
end
end

Expand Down
84 changes: 70 additions & 14 deletions actionpack/test/dispatch/prefix_generation_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ def self.routes
match "/posts/:id", :to => "inside_engine_generating#show", :as => :post
match "/posts", :to => "inside_engine_generating#index", :as => :posts
match "/url_to_application", :to => "inside_engine_generating#url_to_application"
match "/polymorphic_path_for_engine", :to => "inside_engine_generating#polymorphic_path_for_engine"
end

routes
Expand All @@ -31,9 +32,11 @@ def self.routes
routes = ActionDispatch::Routing::RouteSet.new
routes.draw do
scope "/:omg", :omg => "awesome" do
mount BlogEngine => "/blog"
mount BlogEngine => "/blog", :as => "blog_engine"
end
match "/generate", :to => "outside_engine_generating#index"
match "/polymorphic_path_for_engine", :to => "outside_engine_generating#polymorphic_path_for_engine"
match "/polymorphic_with_url_for", :to => "outside_engine_generating#polymorphic_with_url_for"
root :to => "outside_engine_generating#index"
end

Expand All @@ -47,8 +50,27 @@ def self.call(env)
end
end

# force draw
RailsApplication.routes

class Post
extend ActiveModel::Naming

def to_param
"1"
end

def self.model_name
klass = "Post"
def klass.name; self end

ActiveModel::Name.new(klass)
end
end

class ::InsideEngineGeneratingController < ActionController::Base
include BlogEngine.routes.url_helpers
include RailsApplication.routes.mounted_helpers(:app)

def index
render :text => posts_path
Expand All @@ -59,17 +81,30 @@ def show
end

def url_to_application
path = url_for( RailsApplication,
:controller => "outside_engine_generating",
:action => "index",
:only_path => true)
path = app.url_for( :controller => "outside_engine_generating",
:action => "index",
:only_path => true)
render :text => path
end

def polymorphic_path_for_engine
render :text => polymorphic_path(Post.new)
end
end

class ::OutsideEngineGeneratingController < ActionController::Base
include BlogEngine.routes.mounted_helpers

def index
render :text => url_for(BlogEngine, :post_path, :id => 1)
render :text => blog_engine.post_path(:id => 1)
end

def polymorphic_path_for_engine
render :text => blog_engine.polymorphic_path(Post.new)
end

def polymorphic_with_url_for
render :text => blog_engine.url_for(Post.new)
end
end

Expand All @@ -83,9 +118,6 @@ class AppObject
include RailsApplication.routes.url_helpers
end

# force draw
RailsApplication.routes

def app
RailsApplication
end
Expand Down Expand Up @@ -124,7 +156,12 @@ def setup
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
end

# Inside Application
test "[APP] generating engine's route includes prefix" do
get "/generate"
Expand All @@ -143,6 +180,16 @@ def setup
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
end

test "[APP] generating engine's url with url_for(@post)" do
get "/polymorphic_with_url_for"
assert_equal "http://example.org/awesome/blog/posts/1", last_response.body
end

# Inside any Object
test "[OBJECT] generating engine's route includes prefix" do
assert_equal "/awesome/blog/posts/1", engine_object.post_path(:id => 1)
Expand All @@ -167,19 +214,28 @@ def setup
end

test "[OBJECT] generating engine's route with url_for" do
path = engine_object.url_for(BlogEngine,
:controller => "inside_engine_generating",
path = engine_object.url_for(:controller => "inside_engine_generating",
:action => "show",
:only_path => true,
:omg => "omg",
:id => 1)
assert_equal "/omg/blog/posts/1", path
end

path = engine_object.url_for(BlogEngine, :posts_path)
test "[OBJECT] generating engine's route with named helpers" do
path = engine_object.posts_path
assert_equal "/awesome/blog/posts", path

path = engine_object.url_for(BlogEngine, :posts_url, :host => "example.com")
path = engine_object.posts_url(:host => "example.com")
assert_equal "http://example.com/awesome/blog/posts", path
end

test "[OBJECT] generating engine's route with polymorphic_url" do
path = engine_object.polymorphic_path(Post.new)
assert_equal "/awesome/blog/posts/1", path

path = engine_object.polymorphic_url(Post.new, :host => "www.example.com")
assert_equal "http://www.example.com/awesome/blog/posts/1", path
end
end
end
52 changes: 0 additions & 52 deletions actionpack/test/dispatch/url_for_test.rb

This file was deleted.

Loading

0 comments on commit 6c95e0f

Please sign in to comment.