From 6c95e0f879aafa5921cd7898d5951b9a926d3c9a Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Sun, 18 Jul 2010 19:49:51 +0200 Subject: Add mounted_helpers to routes 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) --- actionpack/lib/abstract_controller/rendering.rb | 1 + actionpack/lib/action_controller/railtie.rb | 3 +- .../lib/action_controller/railties/url_helpers.rb | 26 +++++++ actionpack/lib/action_dispatch/routing/mapper.rb | 5 +- .../lib/action_dispatch/routing/route_set.rb | 59 +++++++++++++++ actionpack/lib/action_dispatch/routing/url_for.rb | 28 ++------ actionpack/test/dispatch/prefix_generation_test.rb | 84 ++++++++++++++++++---- actionpack/test/dispatch/url_for_test.rb | 52 -------------- railties/test/railties/mounted_engine_test.rb | 32 ++++++--- 9 files changed, 192 insertions(+), 98 deletions(-) create mode 100644 actionpack/lib/action_controller/railties/url_helpers.rb delete mode 100644 actionpack/test/dispatch/url_for_test.rb diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb index b81d5954eb..5d9b35d297 100644 --- a/actionpack/lib/abstract_controller/rendering.rb +++ b/actionpack/lib/abstract_controller/rendering.rb @@ -52,6 +52,7 @@ module AbstractController if controller.respond_to?(:_routes) include controller._routes.url_helpers + include controller._routes.mounted_helpers end # TODO: Fix RJS to not require this diff --git a/actionpack/lib/action_controller/railtie.rb b/actionpack/lib/action_controller/railtie.rb index cd2dfafbe6..7496dd57b2 100644 --- a/actionpack/lib/action_controller/railtie.rb +++ b/actionpack/lib/action_controller/railtie.rb @@ -51,6 +51,7 @@ module ActionController 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 @@ -63,4 +64,4 @@ module ActionController ActionController::Routing::Routes = proxy end end -end \ No newline at end of file +end diff --git a/actionpack/lib/action_controller/railties/url_helpers.rb b/actionpack/lib/action_controller/railties/url_helpers.rb new file mode 100644 index 0000000000..3e6f211cda --- /dev/null +++ b/actionpack/lib/action_controller/railties/url_helpers.rb @@ -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 diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 88152ac290..ef1bee106a 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -285,13 +285,14 @@ module ActionDispatch 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 diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index b3945a4963..0f8bb5c504 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -261,6 +261,65 @@ module ActionDispatch 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 diff --git a/actionpack/lib/action_dispatch/routing/url_for.rb b/actionpack/lib/action_dispatch/routing/url_for.rb index 30b456f3df..19db730b6a 100644 --- a/actionpack/lib/action_dispatch/routing/url_for.rb +++ b/actionpack/lib/action_dispatch/routing/url_for.rb @@ -123,28 +123,14 @@ module ActionDispatch # 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 diff --git a/actionpack/test/dispatch/prefix_generation_test.rb b/actionpack/test/dispatch/prefix_generation_test.rb index 2eb592c8d0..7fe11447b8 100644 --- a/actionpack/test/dispatch/prefix_generation_test.rb +++ b/actionpack/test/dispatch/prefix_generation_test.rb @@ -13,6 +13,7 @@ module TestGenerationPrefix 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 @@ -31,9 +32,11 @@ module TestGenerationPrefix 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 @@ -47,8 +50,27 @@ module TestGenerationPrefix 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 @@ -59,17 +81,30 @@ module TestGenerationPrefix 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 @@ -83,9 +118,6 @@ module TestGenerationPrefix include RailsApplication.routes.url_helpers end - # force draw - RailsApplication.routes - def app RailsApplication end @@ -124,7 +156,12 @@ module TestGenerationPrefix 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" @@ -143,6 +180,16 @@ module TestGenerationPrefix 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) @@ -167,19 +214,28 @@ module TestGenerationPrefix 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 diff --git a/actionpack/test/dispatch/url_for_test.rb b/actionpack/test/dispatch/url_for_test.rb deleted file mode 100644 index 3dc96d27d7..0000000000 --- a/actionpack/test/dispatch/url_for_test.rb +++ /dev/null @@ -1,52 +0,0 @@ -require 'abstract_unit' - -module UrlForGeneration - class UrlForTest < ActionDispatch::IntegrationTest - - Routes = ActionDispatch::Routing::RouteSet.new - Routes.draw { match "/foo", :to => "my_route_generating#index", :as => :foo } - - class BlogEngine - def self.routes - @routes ||= begin - routes = ActionDispatch::Routing::RouteSet.new - routes.draw do - resources :posts - end - routes - end - end - end - - 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 - - include Routes.url_helpers - - test "url_for with named url helpers" do - assert_equal "/posts", url_for(BlogEngine, :posts_path) - end - - test "url_for with polymorphic routes" do - assert_equal "http://www.example.com/posts/1", url_for(BlogEngine, Post.new) - end - - test "url_for with named url helper with arguments" do - assert_equal "/posts/1", url_for(BlogEngine, :post_path, 1) - assert_equal "/posts/1", url_for(BlogEngine, :post_path, :id => 1) - assert_equal "/posts/1.json", url_for(BlogEngine, :post_path, :id => 1, :format => :json) - end - end -end diff --git a/railties/test/railties/mounted_engine_test.rb b/railties/test/railties/mounted_engine_test.rb index 87bcf9b1f3..21c8658436 100644 --- a/railties/test/railties/mounted_engine_test.rb +++ b/railties/test/railties/mounted_engine_test.rb @@ -16,9 +16,10 @@ module ApplicationTests app_file 'config/routes.rb', <<-RUBY AppTemplate::Application.routes.draw do |map| match "/engine_route" => "application_generating#engine_route" + match "/engine_route_in_view" => "application_generating#engine_route_in_view" match "/url_for_engine_route" => "application_generating#url_for_engine_route" scope "/:user", :user => "anonymous" do - mount Blog::Engine => "/blog" + mount Blog::Engine => "/blog", :as => "blog_engine" end root :to => 'main#index' end @@ -39,6 +40,7 @@ module ApplicationTests Blog::Engine.routes.draw do resources :posts do get :generate_application_route + get :application_route_in_view end end RUBY @@ -46,27 +48,34 @@ module ApplicationTests @plugin.write "app/controllers/posts_controller.rb", <<-RUBY class PostsController < ActionController::Base def index - render :text => url_for(Blog::Engine, :post_path, 1) + render :text => blog_engine.post_path(1) end def generate_application_route - path = url_for(Rails.application, - :controller => "main", - :action => "index", - :only_path => true) + path = app.url_for(:controller => "main", + :action => "index", + :only_path => true) render :text => path end + + def application_route_in_view + render :inline => "<%= app.root_path %>" + end end RUBY app_file "app/controllers/application_generating_controller.rb", <<-RUBY class ApplicationGeneratingController < ActionController::Base def engine_route - render :text => url_for(Blog::Engine, :posts_path) + render :text => blog_engine.posts_path + end + + def engine_route_in_view + render :inline => "<%= blog_engine.posts_path %>" end def url_for_engine_route - render :text => url_for(Blog::Engine, :controller => "posts", :action => "index", :user => "john", :only_path => true) + render :text => blog_engine.url_for(:controller => "posts", :action => "index", :user => "john", :only_path => true) end end RUBY @@ -103,6 +112,10 @@ module ApplicationTests # test generating engine's route from application get "/engine_route" assert_equal "/anonymous/blog/posts", last_response.body + + get "/engine_route_in_view" + assert_equal "/anonymous/blog/posts", last_response.body + get "/url_for_engine_route" assert_equal "/john/blog/posts", last_response.body @@ -120,6 +133,9 @@ module ApplicationTests get "/someone/blog/generate_application_route" assert_equal "/", last_response.body + get "/somone/blog/application_route_in_view" + 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' -- cgit v1.2.3