From f38e2e03351da463f84f6850fa10718ece98ff26 Mon Sep 17 00:00:00 2001 From: Carlhuda Date: Mon, 8 Mar 2010 12:24:49 -0800 Subject: [PATCH] Add support for mount RackApp, :at => "/sprockets" with a shorthand of mount Sprockets => "/sprockets". This is different from the match syntax in that it cannot be used for controller/action and it does not assume an anchor at the end of the match. For instance, in the above example, if the client asked for "/sprockets/foo.js", the Sprockets app would have a SCRIPT_NAME of "/sprockets" and PATH_INFO of "/foo.js". --- .../lib/action_dispatch/routing/mapper.rb | 27 ++++++++++---- .../lib/action_dispatch/routing/route.rb | 4 +-- .../lib/action_dispatch/routing/route_set.rb | 4 +-- actionpack/test/dispatch/mount_test.rb | 36 +++++++++++++++++++ 4 files changed, 61 insertions(+), 10 deletions(-) create mode 100644 actionpack/test/dispatch/mount_test.rb diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 7a33259054759..e6c563d7b7d99 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -1,5 +1,3 @@ -require "active_support/core_ext/hash/except" - module ActionDispatch module Routing class Mapper @@ -38,7 +36,7 @@ def initialize(set, scope, args) end def to_route - [ app, conditions, requirements, defaults, @options[:as] ] + [ app, conditions, requirements, defaults, @options[:as], @options[:anchor] ] end private @@ -66,7 +64,7 @@ def using_to_shorthand?(args, options) # match "account/overview" def using_match_shorthand?(args, options) - args.present? && options.except(:via).empty? && !args.first.include?(':') + args.present? && options.except(:via, :anchor).empty? && !args.first.include?(':') end def normalize_path(path) @@ -87,7 +85,7 @@ def conditions end def requirements - @requirements ||= (@options[:constraints] || {}).tap do |requirements| + @requirements ||= returning(@options[:constraints] || {}) do |requirements| requirements.reverse_merge!(@scope[:constraints]) if @scope[:constraints] @options.each { |k, v| requirements[k] = v if v.is_a?(Regexp) } end @@ -176,7 +174,8 @@ def root(options = {}) end def match(*args) - @set.add_route(*Mapping.new(@set, @scope, args).to_route) + mapping = Mapping.new(@set, @scope, args).to_route + @set.add_route(*mapping) self end end @@ -295,6 +294,7 @@ def match(*args) options = args.extract_options! options = (@scope[:options] || {}).merge(options) + options[:anchor] = true unless options.key?(:anchor) if @scope[:name_prefix] && !options[:as].blank? options[:as] = "#{@scope[:name_prefix]}_#{options[:as]}" @@ -538,6 +538,21 @@ def nested end end + def mount(app, options = nil) + if options + path = options.delete(:at) + else + options = app + app, path = options.find { |k, v| k.respond_to?(:call) } + options.delete(app) if app + end + + raise "A rack application must be specified" unless path + + match(path, options.merge(:to => app, :anchor => false)) + self + end + def match(*args) options = args.extract_options! diff --git a/actionpack/lib/action_dispatch/routing/route.rb b/actionpack/lib/action_dispatch/routing/route.rb index e6e44d3546976..6f37eadd6b9f1 100644 --- a/actionpack/lib/action_dispatch/routing/route.rb +++ b/actionpack/lib/action_dispatch/routing/route.rb @@ -4,7 +4,7 @@ class Route #:nodoc: attr_reader :app, :conditions, :defaults, :name attr_reader :path, :requirements - def initialize(app, conditions = {}, requirements = {}, defaults = {}, name = nil) + def initialize(app, conditions, requirements, defaults, name, anchor) @app = app @defaults = defaults @name = name @@ -17,7 +17,7 @@ def initialize(app, conditions = {}, requirements = {}, defaults = {}, name = ni if path = conditions[:path_info] @path = path - conditions[:path_info] = ::Rack::Mount::Strexp.compile(path, requirements, SEPARATORS) + conditions[:path_info] = ::Rack::Mount::Strexp.compile(path, requirements, SEPARATORS, anchor) end @conditions = conditions.inject({}) { |h, (k, v)| diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 5c246d8781a17..550e54e955d01 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -293,8 +293,8 @@ def empty? routes.empty? end - def add_route(app, conditions = {}, requirements = {}, defaults = {}, name = nil) - route = Route.new(app, conditions, requirements, defaults, name) + def add_route(app, conditions = {}, requirements = {}, defaults = {}, name = nil, anchor = true) + route = Route.new(app, conditions, requirements, defaults, name, anchor) @set.add_route(*route) named_routes[name] = route if name routes << route diff --git a/actionpack/test/dispatch/mount_test.rb b/actionpack/test/dispatch/mount_test.rb new file mode 100644 index 0000000000000..f89e5fda07b9b --- /dev/null +++ b/actionpack/test/dispatch/mount_test.rb @@ -0,0 +1,36 @@ +require 'abstract_unit' + +class TestRoutingMount < ActionDispatch::IntegrationTest + SprocketsApp = lambda { |env| + [200, {"Content-Type" => "text/html"}, ["#{env["SCRIPT_NAME"]} -- #{env["PATH_INFO"]}"]] + } + + Router = ActionDispatch::Routing::RouteSet.new + Router.draw do + mount SprocketsApp, :at => "/sprockets" + mount SprocketsApp => "/shorthand" + + scope "/its_a" do + mount SprocketsApp, :at => "/sprocket" + end + end + + def app + Router + end + + def test_mounting_sets_script_name + get "/sprockets/omg" + assert_equal "/sprockets -- /omg", response.body + end + + def test_mounting_works_with_scope + get "/its_a/sprocket/omg" + assert_equal "/its_a/sprocket -- /omg", response.body + end + + def test_mounting_with_shorthand + get "/shorthand/omg" + assert_equal "/shorthand -- /omg", response.body + end +end \ No newline at end of file