aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack
diff options
context:
space:
mode:
authorDavid Heinemeier Hansson <david@loudthinking.com>2005-02-15 01:45:35 +0000
committerDavid Heinemeier Hansson <david@loudthinking.com>2005-02-15 01:45:35 +0000
commitb1999be5a7efd67e2602c37ed898aa8433661863 (patch)
tree03bc833276075d802d0ce0ad261baed3d7232533 /actionpack
parent88a3343ed57c01ca358da8473d15fc4d2b4a5bff (diff)
downloadrails-b1999be5a7efd67e2602c37ed898aa8433661863.tar.gz
rails-b1999be5a7efd67e2602c37ed898aa8433661863.tar.bz2
rails-b1999be5a7efd67e2602c37ed898aa8433661863.zip
A hopefully more successful attempt at the Routing branch merge
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@617 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
Diffstat (limited to 'actionpack')
-rw-r--r--actionpack/lib/action_controller/assertions/action_pack_assertions.rb32
-rwxr-xr-xactionpack/lib/action_controller/base.rb32
-rw-r--r--actionpack/lib/action_controller/cgi_process.rb10
-rw-r--r--actionpack/lib/action_controller/helpers.rb35
-rwxr-xr-xactionpack/lib/action_controller/request.rb16
-rw-r--r--actionpack/lib/action_controller/rescue.rb18
-rw-r--r--actionpack/lib/action_controller/routing.rb260
-rw-r--r--actionpack/lib/action_controller/scaffolding.rb2
-rw-r--r--actionpack/lib/action_controller/templates/rescues/routing_error.rhtml8
-rw-r--r--actionpack/lib/action_controller/test_process.rb22
-rw-r--r--actionpack/lib/action_controller/url_rewriter.rb96
-rw-r--r--actionpack/lib/action_view/partials.rb2
-rw-r--r--actionpack/test/controller/cookie_test.rb9
-rw-r--r--actionpack/test/controller/helper_test.rb37
-rw-r--r--actionpack/test/controller/render_test.rb136
-rw-r--r--actionpack/test/controller/request_test.rb22
-rw-r--r--actionpack/test/controller/routing_tests.rb409
-rw-r--r--actionpack/test/controller/url_obsolete.rb487
-rw-r--r--actionpack/test/fixtures/fun/games/hello_world.rhtml1
-rw-r--r--actionpack/test/fixtures/helpers/fun/games_helper.rb3
20 files changed, 1441 insertions, 196 deletions
diff --git a/actionpack/lib/action_controller/assertions/action_pack_assertions.rb b/actionpack/lib/action_controller/assertions/action_pack_assertions.rb
index c26941cd6b..a8819550e6 100644
--- a/actionpack/lib/action_controller/assertions/action_pack_assertions.rb
+++ b/actionpack/lib/action_controller/assertions/action_pack_assertions.rb
@@ -141,7 +141,7 @@ module Test #:nodoc:
end
end
end
-
+
# ensure our redirection url is an exact match
def assert_redirect_url(url=nil, message=nil)
assert_redirect(message)
@@ -158,6 +158,36 @@ module Test #:nodoc:
assert_block(msg) { response.redirect_url_match?(pattern) }
end
+ # -- routing assertions --------------------------------------------------
+
+ # Asserts that the routing of the given path is handled correctly and that the parsed options match.
+ # Also verifies that the provided options can be used to generate the provided path.
+ def assert_routing(path, options, defaults={}, extras={}, message=nil)
+ defaults[:controller] ||= options[:controller] # Assume given controller,
+ request = ActionController::TestRequest.new({}, {}, nil)
+ request.path_parameters = defaults.clone
+
+ ActionController::Routing::Routes.reload if ActionController::Routing::Routes.empty? # Load routes.rb if it hasn't been loaded.
+
+ generated_path, found_extras = ActionController::Routing::Routes.generate(options, request)
+ generated_path = generated_path.join('/')
+ msg = build_message(message, "found extras <?>, not <?>", found_extras, extras)
+ assert_block(msg) { found_extras == extras }
+
+ msg = build_message(message, "The generated path <?> did not match <?>", generated_path, path)
+ assert_block(msg) { path == generated_path }
+
+ request = ActionController::TestRequest.new({}, {}, nil)
+ request.path = path
+ ActionController::Routing::Routes.recognize!(request)
+
+ expected_options = options.clone
+ extras.each {|k,v| expected_options.delete k}
+
+ msg = build_message(message, "The recognized options <?> did not match <?>", request.path_parameters, expected_options)
+ assert_block(msg) { request.path_parameters == expected_options }
+ end
+
# -- template assertions ------------------------------------------------
# ensure that a template object with the given name exists
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index cfcd46d985..5d7ccf72f1 100755
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -1,5 +1,6 @@
require 'action_controller/request'
require 'action_controller/response'
+require 'action_controller/routing'
require 'action_controller/url_rewriter'
require 'action_controller/support/class_attribute_accessors'
require 'action_controller/support/class_inheritable_attributes'
@@ -13,6 +14,15 @@ module ActionController #:nodoc:
end
class MissingTemplate < ActionControllerError #:nodoc:
end
+ class RoutingError < ActionControllerError
+ attr_reader :failures
+ def initialize(message, failures=[])
+ super(message)
+ @failures = failures
+ end
+ end
+ class UnknownController < ActionControllerError #:nodoc:
+ end
class UnknownAction < ActionControllerError #:nodoc:
end
class MissingFile < ActionControllerError #:nodoc:
@@ -205,6 +215,12 @@ module ActionController #:nodoc:
# should instead be implemented in the controller to determine when debugging screens should be shown.
@@consider_all_requests_local = true
cattr_accessor :consider_all_requests_local
+
+ # Enable or disable the collection of failure information for RoutingErrors.
+ # This information can be extremely useful when tweaking custom routes, but is
+ # pointless once routes have been tested and verified.
+ @@debug_routes = true
+ cattr_accessor :debug_routes
# Template root determines the base from which template references will be made. So a call to render("test/template")
# will be converted to "#{template_root}/test/template.rhtml".
@@ -261,6 +277,14 @@ module ActionController #:nodoc:
def controller_name
Inflector.underscore(controller_class_name.sub(/Controller/, ""))
end
+
+ # Convert the class name from something like "OneModule::TwoModule::NeatController" to "one_module/two_module/neat".
+ def controller_path
+ components = self.name.to_s.split('::').collect { |name| name.underscore }
+ components[-1] = $1 if /^(.*)_controller$/ =~ components[-1]
+ components.shift if components.first == 'controllers' # Transitional conditional to accomodate root Controllers module
+ components.join('/')
+ end
end
public
@@ -337,10 +361,6 @@ module ActionController #:nodoc:
end
end
- def module_name
- @params["module"]
- end
-
# Converts the class name from something like "OneModule::TwoModule::NeatController" to "NeatController".
def controller_class_name
self.class.controller_class_name
@@ -594,7 +614,7 @@ module ActionController #:nodoc:
end
def initialize_current_url
- @url = UrlRewriter.new(@request, controller_name, action_name)
+ @url = UrlRewriter.new(@request, @params.clone())
end
def log_processing
@@ -691,7 +711,7 @@ module ActionController #:nodoc:
end
def default_template_name(default_action_name = action_name)
- module_name ? "#{module_name}/#{controller_name}/#{default_action_name}" : "#{controller_name}/#{default_action_name}"
+ "#{self.class.controller_path}/#{default_action_name}"
end
end
end
diff --git a/actionpack/lib/action_controller/cgi_process.rb b/actionpack/lib/action_controller/cgi_process.rb
index 92b19fbf18..c301b322e5 100644
--- a/actionpack/lib/action_controller/cgi_process.rb
+++ b/actionpack/lib/action_controller/cgi_process.rb
@@ -46,8 +46,16 @@ module ActionController #:nodoc:
super()
end
+ def query_string
+ return @cgi.query_string unless @cgi.query_string.nil? || @cgi.query_string.empty?
+ parts = env['REQUEST_URI'].split('?')
+ parts.shift
+ return parts.join('?')
+ end
+
def query_parameters
- @cgi.query_string ? CGIMethods.parse_query_parameters(@cgi.query_string) : {}
+ qs = self.query_string
+ qs.empty? ? {} : CGIMethods.parse_query_parameters(query_string)
end
def request_parameters
diff --git a/actionpack/lib/action_controller/helpers.rb b/actionpack/lib/action_controller/helpers.rb
index 1201d31946..a97fd93410 100644
--- a/actionpack/lib/action_controller/helpers.rb
+++ b/actionpack/lib/action_controller/helpers.rb
@@ -48,25 +48,22 @@ module ActionController #:nodoc:
def helper(*args, &block)
args.flatten.each do |arg|
case arg
- when Module
- add_template_helper(arg)
- when String, Symbol
- file_name = Inflector.underscore(arg.to_s.downcase) + '_helper'
- class_name = Inflector.camelize(file_name)
- begin
- require_dependency(file_name)
- rescue LoadError => load_error
- requiree = / -- (.*?)(\.rb)?$/.match(load_error).to_a[1]
- if requiree == file_name
- raise LoadError, "Missing helper file helpers/#{file_name}.rb"
- else
- raise LoadError, "Can't load file: #{requiree}"
+ when Module
+ add_template_helper(arg)
+ when String, Symbol
+ file_name = arg.to_s.underscore + '_helper'
+ class_name = file_name.camelize
+
+ begin
+ require_dependency(file_name)
+ rescue LoadError => load_error
+ requiree = / -- (.*?)(\.rb)?$/.match(load_error).to_a[1]
+ raise LoadError, requiree == file_name ? "Missing helper file helpers/#{file_name}.rb" : "Can't load file: #{requiree}"
end
- end
- raise ArgumentError, "Missing #{class_name} module in helpers/#{file_name}.rb" unless Object.const_defined?(class_name)
- add_template_helper(Object.const_get(class_name))
- else
- raise ArgumentError, 'helper expects String, Symbol, or Module argument'
+
+ add_template_helper(class_name.constantize)
+ else
+ raise ArgumentError, 'helper expects String, Symbol, or Module argument'
end
end
@@ -95,7 +92,7 @@ module ActionController #:nodoc:
def inherited(child)
inherited_without_helper(child)
begin
- child.helper(child.controller_name)
+ child.helper(child.controller_path)
rescue ArgumentError, LoadError
# No default helper available for this controller
end
diff --git a/actionpack/lib/action_controller/request.rb b/actionpack/lib/action_controller/request.rb
index 3e2344c3cb..2e7957caf9 100755
--- a/actionpack/lib/action_controller/request.rb
+++ b/actionpack/lib/action_controller/request.rb
@@ -3,7 +3,8 @@ module ActionController
class AbstractRequest
# Returns both GET and POST parameters in a single hash.
def parameters
- @parameters ||= request_parameters.update(query_parameters)
+ # puts "#{request_parameters.inspect} | #{query_parameters.inspect} | #{path_parameters.inspect}"
+ @parameters ||= request_parameters.merge(query_parameters).merge(path_parameters).with_indifferent_access
end
def method
@@ -73,7 +74,7 @@ module ActionController
end
def request_uri
- env['REQUEST_URI']
+ (%r{^\w+\://[^/]+(/.*|$)$} =~ env['REQUEST_URI']) ? $1 : env['REQUEST_URI'] # Remove domain, which webrick puts into the request_uri.
end
def protocol
@@ -85,7 +86,7 @@ module ActionController
end
def path
- request_uri ? request_uri.split('?').first : ''
+ path = request_uri ? request_uri.split('?').first : ''
end
def port
@@ -100,7 +101,16 @@ module ActionController
def host_with_port
env['HTTP_HOST'] || host + port_string
end
+
+ def path_parameters=(parameters)
+ @path_parameters = parameters
+ @parameters = nil
+ end
+ def path_parameters
+ @path_parameters ||= {}
+ end
+
#--
# Must be implemented in the concrete request
#++
diff --git a/actionpack/lib/action_controller/rescue.rb b/actionpack/lib/action_controller/rescue.rb
index eb7f614de0..907cebc250 100644
--- a/actionpack/lib/action_controller/rescue.rb
+++ b/actionpack/lib/action_controller/rescue.rb
@@ -48,7 +48,11 @@ module ActionController #:nodoc:
# Overwrite to implement public exception handling (for requests answering false to <tt>local_request?</tt>).
def rescue_action_in_public(exception) #:doc:
- render_text "<html><body><h1>Application error (Rails)</h1></body></html>"
+ case exception
+ when RoutingError, UnknownAction then
+ render_text(IO.read(File.join(RAILS_ROOT, 'public', '404.html')), "404 Not Found")
+ else render_text "<html><body><h1>Application error (Rails)</h1></body></html>"
+ end
end
# Overwrite to expand the meaning of a local request in order to show local rescues on other occurences than
@@ -66,7 +70,7 @@ module ActionController #:nodoc:
@contents = @template.render_file(template_path_for_local_rescue(exception), false)
@headers["Content-Type"] = "text/html"
- render_file(rescues_path("layout"), "500 Internal Error")
+ render_file(rescues_path("layout"), response_code_for_rescue(exception))
end
private
@@ -110,13 +114,21 @@ module ActionController #:nodoc:
rescues_path(
case exception
when MissingTemplate then "missing_template"
+ when RoutingError then "routing_error"
when UnknownAction then "unknown_action"
when ActionView::TemplateError then "template_error"
- else "diagnostics"
+ else raise ;"diagnostics"
end
)
end
+ def response_code_for_rescue(exception)
+ case exception
+ when UnknownAction, RoutingError then "404 Page Not Found"
+ else "500 Internal Error"
+ end
+ end
+
def clean_backtrace(exception)
exception.backtrace.collect { |line| Object.const_defined?(:RAILS_ROOT) ? line.gsub(RAILS_ROOT, "") : line }
end
diff --git a/actionpack/lib/action_controller/routing.rb b/actionpack/lib/action_controller/routing.rb
new file mode 100644
index 0000000000..39ce487e62
--- /dev/null
+++ b/actionpack/lib/action_controller/routing.rb
@@ -0,0 +1,260 @@
+module ActionController
+ module Routing
+ ROUTE_FILE = defined?(RAILS_ROOT) ? File.expand_path(File.join(RAILS_ROOT, 'config', 'routes')) : nil
+
+ class Route
+ attr_reader :defaults # The defaults hash
+
+ def initialize(path, hash={})
+ raise ArgumentError, "Second argument must be a hash!" unless hash.kind_of?(Hash)
+ @defaults = {}
+ @requirements = {}
+ self.items = path
+ hash.each do |k, v|
+ raise TypeError, "Hash may only contain symbols!" unless k.kind_of? Symbol
+ (@items.include?(k) ? @defaults : @requirements)[k] = v
+ end
+
+ # Add in defaults for :action and :id.
+ [[:action, 'index'], [:id, nil]].each do |name, default|
+ @defaults[name] = default if @items.include?(name) && ! (@requirements.key?(name) || @defaults.key?(name))
+ end
+ end
+
+ # Generate a URL given the provided options.
+ # All values in options should be symbols.
+ # Returns the path and the unused names in a 2 element array.
+ # If generation fails, [nil, nil] is returned
+ # Generation can fail because of a missing value, or because an equality check fails.
+ #
+ # Generate urls will be as short as possible. If the last component of a url is equal to the default value,
+ # then that component is removed. This is applied as many times as possible. So, your index controller's
+ # index action will generate []
+ def generate(options, defaults={})
+ non_matching = @requirements.inject([]) {|a, (k, v)| ((options[k] || defaults[k]) == v) ? a : a << k}
+ return nil, "Options mismatch requirements: #{non_matching.join ', '}" unless non_matching.empty?
+
+ used_names = @requirements.inject({}) {|hash, (k, v)| hash[k] = true; hash}
+ components = @items.collect do |item|
+ if item.kind_of? Symbol
+ used_names[item] = true
+ value = options[item] || defaults[item] || @defaults[item]
+ return nil, "#{item.inspect} was not given and has no default." if value.nil? && ! (@defaults.key?(item) && @defaults[item].nil?) # Don't leave if nil value.
+ defaults = {} unless defaults == {} || value == defaults[item] # Stop using defaults if this component isn't the same as the default.
+ value
+ else item
+ end
+ end
+
+ @items.reverse_each do |item| # Remove default components from the end of the generated url.
+ break unless item.kind_of?(Symbol) && @defaults[item] == components.last
+ components.pop
+ end
+
+ # If we have any nil components then we can't proceed.
+ # This might need to be changed. In some cases we may be able to return all componets after nil as extras.
+ missing = []; components.each_with_index {|c, i| missing << @items[i] if c.nil?}
+ return nil, "No values provided for component#{'s' if missing.length > 1} #{missing.join ', '} but values are required due to use of later components" unless missing.empty? # how wide is your screen?
+
+ unused = (options.keys - used_names.keys).inject({}) do |unused, key|
+ unused[key] = options[key] if options[key] != @defaults[key]
+ unused
+ end
+
+ components.collect! {|c| c.to_s}
+ components.unshift(components.shift + '/') if components.length == 1 && @items.first == :controller # Add '/' to controllers
+
+ return components, unused
+ end
+
+ # Recognize the provided path, returning a hash of recognized values, or [nil, reason] if the path isn't recognized.
+ # The path should be a list of component strings.
+ # Options is a hash of the ?k=v pairs
+ def recognize(components, options={})
+ options = options.clone
+ components = components.clone
+ controller_class = nil
+
+ @items.each do |item|
+ if item == :controller # Special case for controller
+ if components.empty? && @defaults[:controller]
+ controller_class, leftover = eat_path_to_controller(@defaults[:controller].split('/'))
+ raise RoutingError, "Default controller does not exist: #{@defaults[:controller]}" if controller_class.nil? || leftover.empty? == false
+ else
+ controller_class, remaining_components = eat_path_to_controller(components)
+ return nil, "No controller found at subpath #{components.join('/')}" if controller_class.nil?
+ components = remaining_components
+ end
+ options[:controller] = controller_class.controller_path
+ elsif item.kind_of? Symbol
+ value = components.shift || @defaults[item]
+ return nil, "No value or default for parameter #{item.inspect}" if value.nil? && ! (@defaults.key?(item) && @defaults[item].nil?)
+ options[item] = value
+ else
+ return nil, "No value available for component #{item.inspect}" if components.empty?
+ component = components.shift
+ return nil, "Value for component #{item.inspect} doesn't match #{component}" if component != item
+ end
+ end
+
+ if controller_class.nil? && @requirements[:controller] # Load a default controller
+ controller_class, extras = eat_path_to_controller(@requirements[:controller].split('/'))
+ raise RoutingError, "Illegal controller path for route default: #{@requirements[:controller]}" unless controller_class && extras.empty?
+ options[:controller] = controller_class.controller_path
+ end
+ options = @requirements.merge(options)
+
+ return nil, "Route recognition didn't find a controller class!" unless controller_class
+ return nil, "Unused components were left: #{components.join '/'}" unless components.empty?
+ options.delete_if {|k, v| v.nil?} # Remove nil values.
+ return controller_class, options
+ end
+
+ def inspect
+ when_str = @requirements.empty? ? "" : " when #{@requirements.inspect}"
+ default_str = @defaults.empty? ? "" : " || #{@defaults.inspect}"
+ "<#{self.class.to_s} #{@items.collect{|c| c.kind_of?(String) ? c : c.inspect}.join('/').inspect}#{default_str}#{when_str}>"
+ end
+
+ protected
+ # Find the controller given a list of path components.
+ # Return the controller class and the unused path components.
+ def eat_path_to_controller(path)
+ path.inject([Controllers, 1]) do |(mod, length), name|
+ name = name.camelize
+ controller_name = name + "Controller"
+ return mod.const_get(controller_name), path[length..-1] if mod.const_available? controller_name
+ return nil, nil unless mod.const_available? name
+ [mod.const_get(name), length + 1]
+ end
+ return nil, nil # Path ended, but no controller found.
+ end
+
+ def items=(path)
+ items = path.split('/').collect {|c| (/^:(\w+)$/ =~ c) ? $1.intern : c} if path.kind_of?(String) # split and convert ':xyz' to symbols
+ items.shift if items.first == ""
+ items.pop if items.last == ""
+ @items = items
+
+ # Verify uniqueness of each component.
+ @items.inject({}) do |seen, item|
+ if item.kind_of? Symbol
+ raise ArgumentError, "Illegal route path -- duplicate item #{item}\n #{path.inspect}" if seen.key? item
+ seen[item] = true
+ end
+ seen
+ end
+ end
+ end
+
+ class RouteSet
+ def initialize
+ @routes = []
+ end
+
+ def add_route(route)
+ raise TypeError, "#{route.inspect} is not a Route instance!" unless route.kind_of?(Route)
+ @routes << route
+ end
+ def empty?
+ @routes.empty?
+ end
+ def each
+ @routes.each {|route| yield route}
+ end
+
+ # Generate a path for the provided options
+ # Returns the path as an array of components and a hash of unused names
+ # Raises RoutingError if not route can handle the provided components.
+ #
+ # Note that we don't return the first generated path. We do this so that when a route
+ # generates a path from a subset of the available options we can keep looking for a
+ # route which can generate a path that uses more options.
+ # Note that we *do* return immediately if
+ def generate(options, request)
+ raise RoutingError, "There are no routes defined!" if @routes.empty?
+ options = options.symbolize_keys
+ defaults = request.path_parameters.symbolize_keys
+ expand_controller_path!(options, defaults)
+
+ failures = []
+ selected = nil
+ self.each do |route|
+ path, unused = route.generate(options, defaults)
+ if path.nil?
+ failures << [route, unused] if ActionController::Base.debug_routes
+ else
+ return path, unused if unused.empty? # Found a perfect route -- we're finished.
+ if selected.nil? || unused.length < selected.last.length
+ failures << [selected.first, "A better url than #{selected[1]} was found."] if selected
+ selected = [route, path, unused]
+ end
+ end
+ end
+
+ return selected[1..-1] unless selected.nil?
+ raise RoutingError.new("Generation failure: No route for url_options #{options.inspect}, defaults: #{defaults.inspect}", failures)
+ end
+
+ # Recognize the provided path.
+ # Raise RoutingError if the path can't be recognized.
+ def recognize!(request)
+ path = ((%r{^/?(.*)/?$} =~ request.path) ? $1 : request.path).split('/')
+ raise RoutingError, "There are no routes defined!" if @routes.empty?
+
+ failures = []
+ self.each do |route|
+ controller, options = route.recognize(path)
+ if controller.nil?
+ failures << [route, options] if ActionController::Base.debug_routes
+ else
+ options.each {|k, v| request.path_parameters[k] = CGI.unescape(v)}
+ return controller
+ end
+ end
+
+ raise RoutingError.new("No route for path: #{path.join('/').inspect}", failures)
+ end
+
+ def expand_controller_path!(options, defaults)
+ if options[:controller]
+ if /^\// =~ options[:controller]
+ options[:controller] = options[:controller][1..-1]
+ defaults.clear # Sending to absolute controller implies fresh defaults
+ else
+ relative_to = defaults[:controller] ? defaults[:controller].split('/')[0..-2].join('/') : ''
+ options[:controller] = relative_to.empty? ? options[:controller] : "#{relative_to}/#{options[:controller]}"
+ end
+ else
+ options[:controller] = defaults[:controller]
+ end
+ end
+
+ def route(*args)
+ add_route(Route.new(*args))
+ end
+ alias :connect :route
+
+ def reload
+ begin require_dependency(ROUTE_FILE)
+ rescue LoadError, ScriptError => e
+ raise RoutingError, "Cannot load config/routes.rb:\n #{e.message}"
+ ensure # Ensure that there is at least one route:
+ connect(':controller/:action/:id', :action => 'index', :id => nil) if @routes.empty?
+ end
+ end
+
+ def draw
+ @routes.clear
+ yield self
+ end
+ end
+
+ def self.draw(*args, &block)
+ Routes.draw(*args) {|*args| block.call(*args)}
+ end
+
+ Routes = RouteSet.new
+ #Routes.reload # Do this here, so that server will die on load if SyntaxError or whatnot.
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_controller/scaffolding.rb b/actionpack/lib/action_controller/scaffolding.rb
index 9c1311efa3..140df73972 100644
--- a/actionpack/lib/action_controller/scaffolding.rb
+++ b/actionpack/lib/action_controller/scaffolding.rb
@@ -149,7 +149,7 @@ module ActionController
private
def render#{suffix}_scaffold(action = caller_method_name(caller))
- if template_exists?("\#{controller_name}/\#{action}")
+ if template_exists?("\#{self.class.controller_path}/\#{action}")
render_action(action)
else
@scaffold_class = #{class_name}
diff --git a/actionpack/lib/action_controller/templates/rescues/routing_error.rhtml b/actionpack/lib/action_controller/templates/rescues/routing_error.rhtml
new file mode 100644
index 0000000000..82c01e10c9
--- /dev/null
+++ b/actionpack/lib/action_controller/templates/rescues/routing_error.rhtml
@@ -0,0 +1,8 @@
+<h1>Routing Error</h1>
+<p><%=h @exception.message %></p>
+<% unless @exception.failures.empty? %><p>
+ <h2>Failure reasons:</h2>
+ <% @exception.failures.each do |route, reason| %>
+ <%=h route.inspect.gsub('\\', '') %> failed because <%=h reason.downcase %><br />
+ <% end %>
+</p><% end %>
diff --git a/actionpack/lib/action_controller/test_process.rb b/actionpack/lib/action_controller/test_process.rb
index d4dfe7933d..3223da198c 100644
--- a/actionpack/lib/action_controller/test_process.rb
+++ b/actionpack/lib/action_controller/test_process.rb
@@ -31,8 +31,8 @@ module ActionController #:nodoc:
class TestRequest < AbstractRequest #:nodoc:
attr_accessor :cookies
- attr_accessor :query_parameters, :request_parameters, :session, :env
- attr_accessor :host, :path, :request_uri, :remote_addr
+ attr_accessor :query_parameters, :request_parameters, :path, :session, :env
+ attr_accessor :host, :remote_addr
def initialize(query_parameters = nil, request_parameters = nil, session = nil)
@query_parameters = query_parameters || {}
@@ -58,11 +58,28 @@ module ActionController #:nodoc:
@parameters = nil
end
+ # Used to check AbstractRequest's request_uri functionality.
+ # Disables the use of @path and @request_uri so superclass can handle those.
+ def set_REQUEST_URI(value)
+ @env["REQUEST_URI"] = value
+ @request_uri = nil
+ @path = nil
+ end
+
def request_uri=(uri)
@request_uri = uri
@path = uri.split("?").first
end
+ def request_uri
+ @request_uri || super()
+ end
+
+ def path
+ @path || super()
+ end
+
+
private
def initialize_containers
@env, @cookies = {}, {}
@@ -237,6 +254,7 @@ module Test
def process(action, parameters = nil, session = nil)
@request.env['REQUEST_METHOD'] ||= "GET"
@request.action = action.to_s
+ @request.path_parameters = { :controller => @controller.class.controller_path }
@request.parameters.update(parameters) unless parameters.nil?
@request.session = ActionController::TestSession.new(session) unless session.nil?
@controller.process(@request, @response)
diff --git a/actionpack/lib/action_controller/url_rewriter.rb b/actionpack/lib/action_controller/url_rewriter.rb
index 3451e6acc9..a1b1a2a57b 100644
--- a/actionpack/lib/action_controller/url_rewriter.rb
+++ b/actionpack/lib/action_controller/url_rewriter.rb
@@ -1,41 +1,29 @@
module ActionController
# Rewrites URLs for Base.redirect_to and Base.url_for in the controller.
+
class UrlRewriter #:nodoc:
- VALID_OPTIONS = [:action, :action_prefix, :action_suffix, :application_prefix, :module, :controller, :controller_prefix, :anchor, :params, :path_params, :id, :only_path, :overwrite_params, :host, :protocol ]
-
- def initialize(request, controller, action)
- @request, @controller, @action = request, controller, action
+ RESERVED_OPTIONS = [:anchor, :params, :path_params, :only_path, :host, :protocol]
+ def initialize(request, parameters)
+ @request, @parameters = request, parameters
@rewritten_path = @request.path ? @request.path.dup : ""
end
def rewrite(options = {})
- validate_options(VALID_OPTIONS, options.keys)
-
- rewrite_url(
- rewrite_path(@rewritten_path, resolve_aliases(options)),
- options
- )
- end
-
- def to_s
- to_str
+ rewrite_url(rewrite_path(options), options)
end
def to_str
- "#{@request.protocol}, #{@request.host_with_port}, #{@request.path}, #{@controller}, #{@action}, #{@request.parameters.inspect}"
+ "#{@request.protocol}, #{@request.host_with_port}, #{@request.path}, #{@parameters[:controller]}, #{@parameters[:action]}, #{@request.parameters.inspect}"
end
+ alias_method :to_s, :to_str
+
private
def validate_options(valid_option_keys, supplied_option_keys)
unknown_option_keys = supplied_option_keys - valid_option_keys
raise(ActionController::ActionControllerError, "Unknown options: #{unknown_option_keys}") unless unknown_option_keys.empty?
end
-
- def resolve_aliases(options)
- options[:controller_prefix] = options[:module] unless options[:module].nil?
- options
- end
-
+
def rewrite_url(path, options)
rewritten_url = ""
rewritten_url << (options[:protocol] || @request.protocol) unless options[:only_path]
@@ -45,15 +33,18 @@ module ActionController
rewritten_url << path
rewritten_url << build_query_string(new_parameters(options)) if options[:params] || options[:overwrite_params]
rewritten_url << "##{options[:anchor]}" if options[:anchor]
+
return rewritten_url
end
- def rewrite_path(path, options)
- include_id_in_path_params(options)
-
- path = rewrite_action(path, options) if options[:action] || options[:action_prefix]
- path = rewrite_path_params(path, options) if options[:path_params]
- path = rewrite_controller(path, options) if options[:controller] || options[:controller_prefix]
+ def rewrite_path(options)
+ options = options.symbolize_keys
+ RESERVED_OPTIONS.each {|k| options.delete k}
+
+ path, extras = Routing::Routes.generate(options, @request)
+ path = "/#{path.join('/')}"
+ path += build_query_string(extras)
+
return path
end
@@ -76,49 +67,6 @@ module ActionController
end
end
- def rewrite_action(path, options)
- # This regex assumes that "index" actions won't be included in the URL
- all, controller_prefix, action_prefix, action_suffix =
- /^\/(.*)#{@controller}\/(.*)#{@action == "index" ? "" : @action}(.*)/.match(path).to_a
-
- if @action == "index"
- if action_prefix == "index"
- # we broke the parsing assumption that this would be excluded, so
- # don't tell action_name about our little boo-boo
- path = path.sub(action_prefix, action_name(options, nil))
- elsif action_prefix && !action_prefix.empty?
- path = path.sub(%r(/#{action_prefix}/?), "/" + action_name(options, action_prefix))
- else
- path = path.sub(%r(#{@controller}/?$), @controller + "/" + action_name(options)) # " ruby-mode
- end
- else
- path = path.sub(
- @controller + "/" + (action_prefix || "") + @action + (action_suffix || ""),
- @controller + "/" + action_name(options, action_prefix)
- )
- end
-
- if options[:controller_prefix] && !options[:controller]
- ensure_slash_suffix(options, :controller_prefix)
- if controller_prefix
- path = path.sub(controller_prefix, options[:controller_prefix])
- else
- path = options[:controller_prefix] + path
- end
- end
-
- return path
- end
-
- def rewrite_controller(path, options)
- all, controller_prefix = /^\/(.*?)#{@controller}/.match(path).to_a
- path = "/"
- path << controller_name(options, controller_prefix)
- path << action_name(options) if options[:action]
- path << path_params_in_list(options) if options[:path_params]
- return path
- end
-
def action_name(options, action_prefix = nil, action_suffix = nil)
ensure_slash_suffix(options, :action_prefix)
ensure_slash_prefix(options, :action_suffix)
@@ -174,16 +122,16 @@ module ActionController
def build_query_string(hash)
elements = []
query_string = ""
-
+
hash.each do |key, value|
+ key = key.to_s
key = CGI.escape key
key += '[]' if value.class == Array
value = [ value ] unless value.class == Array
value.each { |val| elements << "#{key}=#{CGI.escape(val.to_s)}" }
- end
-
- unless elements.empty? then query_string << ("?" + elements.join("&")) end
+ end
+ query_string << ("?" + elements.join("&")) unless elements.empty?
return query_string
end
end
diff --git a/actionpack/lib/action_view/partials.rb b/actionpack/lib/action_view/partials.rb
index f771f0a826..bbb38778b7 100644
--- a/actionpack/lib/action_view/partials.rb
+++ b/actionpack/lib/action_view/partials.rb
@@ -60,7 +60,7 @@ module ActionView
if partial_path.include?('/')
return File.dirname(partial_path), File.basename(partial_path)
else
- return controller.send(:controller_name), partial_path
+ return controller.class.controller_path, partial_path
end
end
diff --git a/actionpack/test/controller/cookie_test.rb b/actionpack/test/controller/cookie_test.rb
index 3f9dafacd6..2b5b1cae9f 100644
--- a/actionpack/test/controller/cookie_test.rb
+++ b/actionpack/test/controller/cookie_test.rb
@@ -28,10 +28,6 @@ class CookieTest < Test::Unit::TestCase
render_text "hello world"
end
- def access_frozen_cookies
- @cookies["wont"] = "work"
- end
-
def rescue_action(e) raise end
end
@@ -67,11 +63,6 @@ class CookieTest < Test::Unit::TestCase
assert_equal 2, process_request.headers["cookie"].size
end
- def test_setting_cookie_on_frozen_instance_variable
- @request.action = "access_frozen_cookies"
- assert_raises(TypeError) { process_request }
- end
-
private
def process_request
TestController.process(@request, @response)
diff --git a/actionpack/test/controller/helper_test.rb b/actionpack/test/controller/helper_test.rb
index b824e40125..0a9840f705 100644
--- a/actionpack/test/controller/helper_test.rb
+++ b/actionpack/test/controller/helper_test.rb
@@ -1,20 +1,30 @@
require File.dirname(__FILE__) + '/../abstract_unit'
+$:.unshift(File.dirname(__FILE__) + '/../fixtures/helpers')
-class HelperTest < Test::Unit::TestCase
- HELPER_PATHS = %w(/../fixtures/helpers)
+class TestController < ActionController::Base
+ attr_accessor :delegate_attr
+ def delegate_method() end
+ def rescue_action(e) raise end
+end
+
+module Fun
+ class GamesController < ActionController::Base
+ def render_hello_world
+ render_template "hello: <%= stratego %>"
+ end
- class TestController < ActionController::Base
- attr_accessor :delegate_attr
- def delegate_method() end
def rescue_action(e) raise end
end
+end
- module LocalAbcHelper
- def a() end
- def b() end
- def c() end
- end
+module LocalAbcHelper
+ def a() end
+ def b() end
+ def c() end
+end
+class HelperTest < Test::Unit::TestCase
+ HELPER_PATHS = %w(/../fixtures/helpers)
def setup
# Increment symbol counter.
@@ -102,6 +112,13 @@ class HelperTest < Test::Unit::TestCase
assert template_methods.include?('delegate_attr=')
end
+ def test_helper_for_nested_controller
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ @request.action = "render_hello_world"
+
+ assert_equal "hello: Iz guuut!", Fun::GamesController.process(@request, @response).body
+ end
private
def helper_methods; TestHelper.instance_methods end
diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb
index f983960e2e..afffd15793 100644
--- a/actionpack/test/controller/render_test.rb
+++ b/actionpack/test/controller/render_test.rb
@@ -2,86 +2,91 @@ require File.dirname(__FILE__) + '/../abstract_unit'
Customer = Struct.new("Customer", :name)
-class RenderTest < Test::Unit::TestCase
- class TestController < ActionController::Base
- layout :determine_layout
-
+module Fun
+ class GamesController < ActionController::Base
def hello_world
end
+ end
+end
- def render_hello_world
- render "test/hello_world"
- end
-
- def render_hello_world_from_variable
- @person = "david"
- render_text "hello #{@person}"
- end
- def render_action_hello_world
- render_action "hello_world"
- end
-
- def render_text_hello_world
- render_text "hello world"
- end
+class TestController < ActionController::Base
+ layout :determine_layout
- def render_custom_code
- render_text "hello world", "404 Moved"
- end
-
- def render_xml_hello
- @name = "David"
- render "test/hello"
- end
+ def hello_world
+ end
- def greeting
- # let's just rely on the template
- end
+ def render_hello_world
+ render "test/hello_world"
+ end
- def layout_test
- render_action "hello_world"
- end
-
- def builder_layout_test
- render_action "hello"
- end
+ def render_hello_world_from_variable
+ @person = "david"
+ render_text "hello #{@person}"
+ end
- def partials_list
- @customers = [ Customer.new("david"), Customer.new("mary") ]
- render_action "list"
- end
+ def render_action_hello_world
+ render_action "hello_world"
+ end
+
+ def render_text_hello_world
+ render_text "hello world"
+ end
- def modgreet
- end
+ def render_custom_code
+ render_text "hello world", "404 Moved"
+ end
+
+ def render_xml_hello
+ @name = "David"
+ render "test/hello"
+ end
- def rescue_action(e) raise end
-
- private
- def determine_layout
- case action_name
- when "layout_test": "layouts/standard"
- when "builder_layout_test": "layouts/builder"
- end
- end
+ def greeting
+ # let's just rely on the template
end
- TestController.template_root = File.dirname(__FILE__) + "/../fixtures/"
+ def layout_test
+ render_action "hello_world"
+ end
- class TestLayoutController < ActionController::Base
- layout "layouts/standard"
-
- def hello_world
- end
+ def builder_layout_test
+ render_action "hello"
+ end
+
+ def partials_list
+ @customers = [ Customer.new("david"), Customer.new("mary") ]
+ render_action "list"
+ end
+
+ def rescue_action(e) raise end
- def hello_world_outside_layout
+ private
+ def determine_layout
+ case action_name
+ when "layout_test": "layouts/standard"
+ when "builder_layout_test": "layouts/builder"
+ end
end
+end
- def rescue_action(e)
- raise unless ActionController::MissingTemplate === e
- end
+TestController.template_root = File.dirname(__FILE__) + "/../fixtures/"
+
+class TestLayoutController < ActionController::Base
+ layout "layouts/standard"
+
+ def hello_world
end
+
+ def hello_world_outside_layout
+ end
+
+ def rescue_action(e)
+ raise unless ActionController::MissingTemplate === e
+ end
+end
+class RenderTest < Test::Unit::TestCase
def setup
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
@@ -170,10 +175,9 @@ class RenderTest < Test::Unit::TestCase
assert_equal "Hello: davidHello: mary", process_request.body
end
- def test_module_rendering
- @request.action = "modgreet"
- @request.parameters["module"] = "scope"
- assert_equal "<p>Beautiful modules!</p>", process_request.body
+ def test_nested_rendering
+ @request.action = "hello_world"
+ assert_equal "Living in a nested world", Fun::GamesController.process(@request, @response).body
end
private
diff --git a/actionpack/test/controller/request_test.rb b/actionpack/test/controller/request_test.rb
index ebab660eab..c31cdd460b 100644
--- a/actionpack/test/controller/request_test.rb
+++ b/actionpack/test/controller/request_test.rb
@@ -31,6 +31,28 @@ class RequestTest < Test::Unit::TestCase
@request.port = 8080
assert_equal ":8080", @request.port_string
end
+
+ def test_request_uri
+ @request.set_REQUEST_URI "http://www.rubyonrails.org/path/of/some/uri?mapped=1"
+ assert_equal "/path/of/some/uri?mapped=1", @request.request_uri
+ assert_equal "/path/of/some/uri", @request.path
+
+ @request.set_REQUEST_URI "http://www.rubyonrails.org/path/of/some/uri"
+ assert_equal "/path/of/some/uri", @request.request_uri
+ assert_equal "/path/of/some/uri", @request.path
+
+ @request.set_REQUEST_URI "/path/of/some/uri"
+ assert_equal "/path/of/some/uri", @request.request_uri
+ assert_equal "/path/of/some/uri", @request.path
+
+ @request.set_REQUEST_URI "/"
+ assert_equal "/", @request.request_uri
+ assert_equal "/", @request.path
+
+ @request.set_REQUEST_URI "/?m=b"
+ assert_equal "/?m=b", @request.request_uri
+ assert_equal "/", @request.path
+ end
def test_host_with_port
@request.env['HTTP_HOST'] = "rubyonrails.org:8080"
diff --git a/actionpack/test/controller/routing_tests.rb b/actionpack/test/controller/routing_tests.rb
new file mode 100644
index 0000000000..cdc9c4e53c
--- /dev/null
+++ b/actionpack/test/controller/routing_tests.rb
@@ -0,0 +1,409 @@
+# Code Generated by ZenTest v. 2.3.0
+# Couldn't find class for name Routing
+# classname: asrt / meth = ratio%
+# ActionController::Routing::RouteSet: 0 / 16 = 0.00%
+# ActionController::Routing::RailsRoute: 0 / 4 = 0.00%
+# ActionController::Routing::Route: 0 / 8 = 0.00%
+
+RAILS_ROOT = ""
+require File.dirname(__FILE__) + '/../abstract_unit'
+require 'test/unit'
+require 'cgi'
+
+class FakeController
+ attr_reader :controller_path
+ attr_reader :name
+ def initialize(name, controller_path)
+ @name = name
+ @controller_path = controller_path
+ end
+ def kind_of?(x)
+ x === Class || x == FakeController
+ end
+end
+
+module Controllers
+ module Admin
+ UserController = FakeController.new 'Admin::UserController', 'admin/user'
+ AccessController = FakeController.new 'Admin::AccessController', 'admin/access'
+ end
+ module Editing
+ PageController = FakeController.new 'Editing::PageController', 'editing/page'
+ ImageController = FakeController.new 'Editing::ImageController', 'editing/image'
+ end
+ module User
+ NewsController = FakeController.new 'User::NewsController', 'user/news'
+ PaymentController = FakeController.new 'User::PaymentController', 'user/payment'
+ end
+ ContentController = FakeController.new 'ContentController', 'content'
+ ResourceController = FakeController.new 'ResourceController', 'resource'
+end
+
+# Extend the modules with the required methods...
+[Controllers, Controllers::Admin, Controllers::Editing, Controllers::User].each do |mod|
+ mod.instance_eval('alias :const_available? :const_defined?')
+ mod.constants.each {|k| Object.const_set(k, mod.const_get(k))} # export the modules & controller classes.
+end
+
+
+class RouteTests < Test::Unit::TestCase
+ def route(*args)
+ return @route if @route && (args.empty? || @args == args)
+ @args = args
+ @route = ActionController::Routing::Route.new(*args)
+ return @route
+ end
+
+ def setup
+ self.route '/:controller/:action/:id'
+ @defaults = {:controller => 'content', :action => 'show', :id => '314'}
+ end
+
+ # Don't put a leading / on the url.
+ # Make sure the controller is one from the above fake Controllers module.
+ def verify_recognize(url, expected_options, reason='')
+ url = url.split('/') if url.kind_of? String
+ reason = ": #{reason}" unless reason.empty?
+ controller_class, options = @route.recognize(url)
+ assert_not_equal nil, controller_class, "#{@route.inspect} didn't recognize #{url}#{reason}\n #{options}"
+ assert_equal expected_options, options, "#{@route.inspect} produced wrong options for #{url}#{reason}"
+ end
+
+ # The expected url should not have a leading /
+ # You can use @defaults if you want a set of plausible defaults
+ def verify_generate(expected_url, expected_extras, options, defaults, reason='')
+ reason = "#{reason}: " unless reason.empty?
+ components, extras = @route.generate(options, defaults)
+ assert_not_equal nil, components, "#{reason}#{@route.inspect} didn't generate for \n options = #{options.inspect}\n defaults = #{defaults.inspect}\n #{extras}"
+ assert_equal expected_extras, extras, "#{reason} #{@route.inspect}.generate: incorrect extra's"
+ assert_equal expected_url, components.join('/'), "#{reason} #{@route.inspect}.generate: incorrect url"
+ end
+
+ def test_recognize_default_unnested_with_action_and_id
+ verify_recognize('content/action/id', {:controller => 'content', :action => 'action', :id => 'id'})
+ verify_recognize('content/show/10', {:controller => 'content', :action => 'show', :id => '10'})
+ end
+ def test_generate_default_unnested_with_action_and_id_no_extras
+ verify_generate('content/action/id', {}, {:controller => 'content', :action => 'action', :id => 'id'}, @defaults)
+ verify_generate('content/show/10', {}, {:controller => 'content', :action => 'show', :id => '10'}, @defaults)
+ end
+ def test_generate_default_unnested_with_action_and_id
+ verify_generate('content/action/id', {:a => 'a'}, {:controller => 'content', :action => 'action', :id => 'id', :a => 'a'}, @defaults)
+ verify_generate('content/show/10', {:a => 'a'}, {:controller => 'content', :action => 'show', :id => '10', :a => 'a'}, @defaults)
+ end
+
+ # Note that we can't put tests here for proper relative controller handline
+ # because that is handled by RouteSet.
+ def test_recognize_default_nested_with_action_and_id
+ verify_recognize('admin/user/action/id', {:controller => 'admin/user', :action => 'action', :id => 'id'})
+ verify_recognize('admin/user/show/10', {:controller => 'admin/user', :action => 'show', :id => '10'})
+ end
+ def test_generate_default_nested_with_action_and_id_no_extras
+ verify_generate('admin/user/action/id', {}, {:controller => 'admin/user', :action => 'action', :id => 'id'}, @defaults)
+ verify_generate('admin/user/show/10', {}, {:controller => 'admin/user', :action => 'show', :id => '10'}, @defaults)
+ end
+ def test_generate_default_nested_with_action_and_id_relative_to_root
+ verify_generate('admin/user/action/id', {:a => 'a'}, {:controller => 'admin/user', :action => 'action', :id => 'id', :a => 'a'}, @defaults)
+ verify_generate('admin/user/show/10', {:a => 'a'}, {:controller => 'admin/user', :action => 'show', :id => '10', :a => 'a'}, @defaults)
+ end
+
+ def test_recognize_default_nested_with_action
+ verify_recognize('admin/user/action', {:controller => 'admin/user', :action => 'action'})
+ verify_recognize('admin/user/show', {:controller => 'admin/user', :action => 'show'})
+ end
+ def test_generate_default_nested_with_action_no_extras
+ verify_generate('admin/user/action', {}, {:controller => 'admin/user', :action => 'action'}, @defaults)
+ verify_generate('admin/user/show', {}, {:controller => 'admin/user', :action => 'show'}, @defaults)
+ end
+ def test_generate_default_nested_with_action
+ verify_generate('admin/user/action', {:a => 'a'}, {:controller => 'admin/user', :action => 'action', :a => 'a'}, @defaults)
+ verify_generate('admin/user/show', {:a => 'a'}, {:controller => 'admin/user', :action => 'show', :a => 'a'}, @defaults)
+ end
+
+ def test_recognize_default_nested_with_id_and_index
+ verify_recognize('admin/user/index/hello', {:controller => 'admin/user', :id => 'hello', :action => 'index'})
+ verify_recognize('admin/user/index/10', {:controller => 'admin/user', :id => "10", :action => 'index'})
+ end
+ def test_generate_default_nested_with_id_no_extras
+ verify_generate('admin/user/index/hello', {}, {:controller => 'admin/user', :id => 'hello'}, @defaults)
+ verify_generate('admin/user/index/10', {}, {:controller => 'admin/user', :id => 10}, @defaults)
+ end
+ def test_generate_default_nested_with_id
+ verify_generate('admin/user/index/hello', {:a => 'a'}, {:controller => 'admin/user', :id => 'hello', :a => 'a'}, @defaults)
+ verify_generate('admin/user/index/10', {:a => 'a'}, {:controller => 'admin/user', :id => 10, :a => 'a'}, @defaults)
+ end
+
+ def test_recognize_default_nested
+ verify_recognize('admin/user', {:controller => 'admin/user', :action => 'index'})
+ verify_recognize('admin/user', {:controller => 'admin/user', :action => 'index'})
+ end
+ def test_generate_default_nested_no_extras
+ verify_generate('admin/user/', {}, {:controller => 'admin/user'}, @defaults)
+ verify_generate('admin/user/', {}, {:controller => 'admin/user'}, @defaults)
+ end
+ def test_generate_default_nested
+ verify_generate('admin/user/', {:a => 'a'}, {:controller => 'admin/user', :a => 'a'}, @defaults)
+ verify_generate('admin/user/', {:a => 'a'}, {:controller => 'admin/user', :a => 'a'}, @defaults)
+ end
+
+ # Test generate with a default controller set.
+ def test_generate_default_controller
+ route '/:controller/:action/:id', :action => 'index', :id => nil, :controller => 'content'
+ @defaults[:controller] = 'resource'
+
+ verify_generate('', {}, {:controller => 'content'}, @defaults)
+ verify_generate('', {}, {:controller => 'content', :action => 'index'}, @defaults)
+ verify_generate('content/not-index', {}, {:controller => 'content', :action => 'not-index'}, @defaults)
+ verify_generate('content/index/10', {}, {:controller => 'content', :id => 10}, @defaults)
+ verify_generate('content/index/hi', {}, {:controller => 'content', :action => 'index', :id => 'hi'}, @defaults)
+ verify_generate('', {:a => 'a'}, {:controller => 'content', :a => 'a'}, @defaults)
+ verify_generate('', {:a => 'a'}, {:controller => 'content', :a => 'a'}, @defaults)
+
+ # Call some other generator tests
+ test_generate_default_unnested_with_action_and_id
+ test_generate_default_nested_with_action_and_id_no_extras
+ test_generate_default_nested_with_id
+ test_generate_default_nested_with_id_no_extras
+ end
+
+ # Test generate with a default controller set.
+ def test_generate_default_controller
+ route '/:controller/:action/:id', :action => 'index', :id => nil, :controller => 'content'
+ @defaults[:controller] = 'resource'
+ verify_recognize('', {:controller => 'content', :action => 'index'})
+ verify_recognize('content', {:controller => 'content', :action => 'index'})
+ verify_recognize('content/index', {:controller => 'content', :action => 'index'})
+ verify_recognize('content/index/10', {:controller => 'content', :action => 'index', :id => '10'})
+ end
+ # Make sure generation & recognition don't happen in some cases:
+ def test_no_generate_on_no_options
+ assert_equal nil, @route.generate({}, {})[0]
+ end
+ def test_requirements
+ route 'some_static/route', :controller => 'content'
+ assert_equal nil, @route.generate({}, {})[0]
+ assert_equal nil, @route.generate({:controller => "dog"}, {})[0]
+ assert_equal nil, @route.recognize([])[0]
+ assert_equal nil, @route.recognize(%w{some_static route with more than expected})[0]
+ end
+
+ def test_basecamp
+ route 'clients/', :controller => 'content'
+ verify_generate('clients', {}, {:controller => 'content'}, {}) # Would like to have clients/
+ verify_generate('clients', {}, {:controller => 'content'}, @defaults)
+ end
+
+ def test_basecamp2
+ route 'clients/:client_name/:project_name/', :controller => 'content', :action => 'start_page_redirect'
+ verify_recognize('clients/projects/2', {:controller => 'content', :client_name => 'projects', :project_name => '2', :action => 'start_page_redirect'})
+ end
+
+ def test_xal_style_dates
+ route 'articles/:category/:year/:month/:day', :controller => 'content', :action => 'list_articles', :category => 'all', :year => nil, :month => nil, :day =>nil
+ verify_recognize('articles', {:controller => 'content', :action => 'list_articles', :category => 'all'})
+ verify_recognize('articles/porn', {:controller => 'content', :action => 'list_articles', :category => 'porn'})
+ verify_recognize('articles/news/2005/08', {:controller => 'content', :action => 'list_articles', :category => 'news', :year => '2005', :month => '08'})
+ verify_recognize('articles/news/2005/08/04', {:controller => 'content', :action => 'list_articles', :category => 'news', :year => '2005', :month => '08', :day => '04'})
+ assert_equal nil, @route.recognize(%w{articles too many components are here})[0]
+ assert_equal nil, @route.recognize('')[0]
+
+ verify_generate('articles', {}, {:controller => 'content', :action => 'list_articles'}, @defaults)
+ verify_generate('articles', {}, {:controller => 'content', :action => 'list_articles', :category => 'all'}, @defaults)
+ verify_generate('articles/news', {}, {:controller => 'content', :action => 'list_articles', :category => 'news'}, @defaults)
+ verify_generate('articles/news/2005', {}, {:controller => 'content', :action => 'list_articles', :category => 'news', :year => '2005'}, @defaults)
+ verify_generate('articles/news/2005/05', {}, {:controller => 'content', :action => 'list_articles', :category => 'news', :year => '2005', :month => '05'}, @defaults)
+ verify_generate('articles/news/2005/05/16', {}, {:controller => 'content', :action => 'list_articles', :category => 'news', :year => '2005', :month => '05', :day => '16'}, @defaults)
+
+ assert_equal nil, @route.generate({:controller => 'content', :action => 'list_articles', :day => '2'}, @defaults)[0]
+ # The above case should fail because a nil value cannot be present in a path.
+ # In other words, since :day is given, :month and :year must be given too.
+ end
+
+
+ def test_no_controller
+ route 'some/:special/:route', :controller => 'a/missing/controller', :action => 'anything'
+ assert_raises(ActionController::RoutingError, "Should raise due to nonexistant controller") {@route.recognize(%w{some matching path})}
+ end
+ def test_bad_controller_path
+ assert_equal nil, @route.recognize(%w{no such controller fake_action id})[0]
+ end
+ def test_too_short_path
+ assert_equal nil, @route.recognize([])[0]
+ route 'some/static/route', :controller => 'content', :action => 'show'
+ assert_equal nil, route.recognize([])[0]
+ end
+ def test_too_long_path
+ assert_equal nil, @route.recognize(%w{content action id some extra components})[0]
+ end
+ def test_incorrect_static_component
+ route 'some/static/route', :controller => 'content', :action => 'show'
+ assert_equal nil, route.recognize(%w{an non_matching path})[0]
+ end
+ def test_no_controller_defined
+ route 'some/:path/:without/a/controller'
+ assert_equal nil, route.recognize(%w{some matching path a controller})[0]
+ end
+
+ def test_mismatching_requirements
+ route 'some/path', :controller => 'content', :action => 'fish'
+ assert_equal nil, route.generate({:controller => 'admin/user', :action => 'list'})[0]
+ assert_equal nil, route.generate({:controller => 'content', :action => 'list'})[0]
+ assert_equal nil, route.generate({:controller => 'admin/user', :action => 'fish'})[0]
+ end
+
+ def test_missing_value_for_generate
+ assert_equal nil, route.generate({})[0] # :controller is missing
+ end
+ def test_nils_inside_generated_path
+ route 'show/:year/:month/:day', :month => nil, :day => nil, :controller => 'content', :action => 'by_date'
+ assert_equal nil, route.generate({:year => 2005, :day => 10})[0]
+ end
+
+ def test_expand_controller_path_non_nested_no_leftover
+ controller, leftovers = @route.send :eat_path_to_controller, %w{content}
+ assert_equal Controllers::ContentController, controller
+ assert_equal [], leftovers
+ end
+ def test_expand_controller_path_non_nested_with_leftover
+ controller, leftovers = @route.send :eat_path_to_controller, %w{content action id}
+ assert_equal Controllers::ContentController, controller
+ assert_equal %w{action id}, leftovers
+ end
+ def test_expand_controller_path_nested_no_leftover
+ controller, leftovers = @route.send :eat_path_to_controller, %w{admin user}
+ assert_equal Controllers::Admin::UserController, controller
+ assert_equal [], leftovers
+ end
+ def test_expand_controller_path_nested_no_leftover
+ controller, leftovers = @route.send :eat_path_to_controller, %w{admin user action id}
+ assert_equal Controllers::Admin::UserController, controller
+ assert_equal %w{action id}, leftovers
+ end
+end
+
+class RouteSetTests < Test::Unit::TestCase
+ def setup
+ @set = ActionController::Routing::RouteSet.new
+ @rails_route = ActionController::Routing::Route.new '/:controller/:action/:id', :action => 'index', :id => nil
+ @request = ActionController::TestRequest.new({}, {}, nil)
+ end
+ def test_emptyness
+ assert_equal true, @set.empty?, "New RouteSets should respond to empty? with true."
+ @set.each { flunk "New RouteSets should be empty." }
+ end
+ def test_add_illegal_route
+ assert_raises(TypeError) {@set.add_route "I'm not actually a route."}
+ end
+ def test_add_normal_route
+ @set.add_route @rails_route
+ seen = false
+ @set.each do |route|
+ assert_equal @rails_route, route
+ flunk("Each should have yielded only a single route!") if seen
+ seen = true
+ end
+ end
+
+ def test_expand_controller_path_non_relative
+ defaults = {:controller => 'admin/user', :action => 'list'}
+ options = {:controller => '/content'}
+ @set.expand_controller_path!(options, defaults)
+ assert_equal({:controller => 'content'}, options)
+ end
+ def test_expand_controller_path_relative_to_nested
+ defaults = {:controller => 'admin/user', :action => 'list'}
+ options = {:controller => 'access'}
+ @set.expand_controller_path!(options, defaults)
+ assert_equal({:controller => 'admin/access'}, options)
+ end
+ def test_expand_controller_path_relative_to_root
+ defaults = {:controller => 'content', :action => 'list'}
+ options = {:controller => 'resource'}
+ @set.expand_controller_path!(options, defaults)
+ assert_equal({:controller => 'resource'}, options)
+ end
+ def test_expand_controller_path_into_module
+ defaults = {:controller => 'content', :action => 'list'}
+ options = {:controller => 'admin/user'}
+ @set.expand_controller_path!(options, defaults)
+ assert_equal({:controller => 'admin/user'}, options)
+ end
+ def test_expand_controller_path_switch_module_with_absolute
+ defaults = {:controller => 'user/news', :action => 'list'}
+ options = {:controller => '/admin/user'}
+ @set.expand_controller_path!(options, defaults)
+ assert_equal({:controller => 'admin/user'}, options)
+ end
+ def test_expand_controller_no_default
+ options = {:controller => 'content'}
+ @set.expand_controller_path!(options, {})
+ assert_equal({:controller => 'content'}, options)
+ end
+
+ # Don't put a leading / on the url.
+ # Make sure the controller is one from the above fake Controllers module.
+ def verify_recognize(expected_controller, expected_path_parameters=nil, path=nil)
+ @set.add_route(@rails_route) if @set.empty?
+ @request.path = path if path
+ controller = @set.recognize!(@request)
+ assert_equal expected_controller, controller
+ assert_equal expected_path_parameters, @request.path_parameters if expected_path_parameters
+ end
+
+ # The expected url should not have a leading /
+ # You can use @defaults if you want a set of plausible defaults
+ def verify_generate(expected_url, options, expected_extras={})
+ @set.add_route(@rails_route) if @set.empty?
+ components, extras = @set.generate(options, @request)
+ assert_equal expected_extras, extras, "#incorrect extra's"
+ assert_equal expected_url, components.join('/'), "incorrect url"
+ end
+ def typical_request
+ @request.path_parameters = {:controller => 'content', :action => 'show', :id => '10'}
+ end
+ def typical_nested_request
+ @request.path_parameters = {:controller => 'admin/user', :action => 'grant', :id => '02seckar'}
+ end
+
+ def test_generate_typical_controller_action_path
+ typical_request
+ verify_generate('content/list', {:controller => 'content', :action => 'list'})
+ end
+ def test_generate_typical_controller_index_path_explicit_index
+ typical_request
+ verify_generate('content/', {:controller => 'content', :action => 'index'})
+ end
+ def test_generate_typical_controller_index_path_explicit_index
+ typical_request
+ verify_generate('content/', {:controller => 'content', :action => 'index'})
+ end
+ def test_generate_typical_controller_index_path_implicit_index
+ typical_request
+ @request.path_parameters[:controller] = 'resource'
+ verify_generate('content/', {:controller => 'content'})
+ end
+
+ def test_generate_no_perfect_route
+ typical_request
+ verify_generate('admin/user/show/43seckar', {:controller => 'admin/user', :action => 'show', :id => '43seckar', :likes_fishing => 'fuzzy(0.3)'}, {:likes_fishing => 'fuzzy(0.3)'})
+ end
+
+ def test_generate_no_match
+ @set.add_route(@rails_route)
+ @request.path_parameters = {}
+ assert_raises(ActionController::RoutingError) {@set.generate({}, @request)}
+ end
+
+
+ def test_encoded_strings
+ verify_recognize(Controllers::Admin::UserController, {:controller => 'admin/user', :action => 'info', :id => "Nicholas Seckar"}, path='/admin/user/info/Nicholas%20Seckar')
+ end
+end
+
+#require '../assertions/action_pack_assertions.rb'
+class AssertionRoutingTests < Test::Unit::TestCase
+ def test_assert_routing
+ ActionController::Routing::Routes.reload rescue nil
+ assert_routing('content/', {:controller => 'content', :action => 'index'})
+ end
+end
diff --git a/actionpack/test/controller/url_obsolete.rb b/actionpack/test/controller/url_obsolete.rb
new file mode 100644
index 0000000000..4b6544dbf7
--- /dev/null
+++ b/actionpack/test/controller/url_obsolete.rb
@@ -0,0 +1,487 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+require 'action_controller/url_rewriter'
+
+MockRequest = Struct.new("MockRequest", :protocol, :host, :port, :path, :parameters, :path_parameters)
+class MockRequest
+ def host_with_port
+ if (protocol == "http://" && port == 80) || (protocol == "https://" && port == 443)
+ host
+ else
+ host + ":#{port}"
+ end
+ end
+end
+
+class UrlMockFactory
+ def self.create(path, parameters)
+ ActionController::UrlRewriter.new(
+ MockRequest.new("http://", "example.com", 80, path, parameters),
+ parameters
+ )
+ end
+end
+
+# old-style support for .new
+module ActionController
+ class UrlRewriter
+ def self.old_new(request, controller, action)
+ request.parameters[:controller] = controller
+ request.parameters[:action] = action
+ return new(request, request.parameters)
+ end
+ end
+end
+class UrlTest < Test::Unit::TestCase
+ def setup
+ @library_url = ActionController::UrlRewriter.old_new(MockRequest.new(
+ "http://",
+ "www.singlefile.com",
+ 80,
+ "/library/books/ISBN/0743536703/show",
+ { "type" => "ISBN", "code" => "0743536703" }
+ ), "books", "show")
+
+ @library_url_using_module = ActionController::UrlRewriter.old_new(MockRequest.new(
+ "http://",
+ "www.singlefile.com",
+ 80,
+ "/library/books/ISBN/0743536703/show",
+ { "type" => "ISBN", "code" => "0743536703", "module" => "library" }
+ ), "books", "show")
+
+ @library_url_on_index = ActionController::UrlRewriter.old_new(MockRequest.new(
+ "http://",
+ "www.singlefile.com",
+ 80,
+ "/library/books/ISBN/0743536703/",
+ { "type" => "ISBN", "code" => "0743536703" }
+ ), "books", "index")
+
+ @clean_urls = [
+ ActionController::UrlRewriter.old_new(MockRequest.new(
+ "http://", "www.singlefile.com", 80, "/identity/", {}
+ ), "identity", "index"),
+ ActionController::UrlRewriter.old_new(MockRequest.new(
+ "http://", "www.singlefile.com", 80, "/identity", {}
+ ), "identity", "index")
+ ]
+
+ @clean_url_with_id = ActionController::UrlRewriter.old_new(MockRequest.new(
+ "http://", "www.singlefile.com", 80, "/identity/show/5", { "id" => "5" }
+ ), "identity", "show")
+
+ @clean_url_with_same_action_and_controller_name = ActionController::UrlRewriter.old_new(MockRequest.new(
+ "http://", "www.singlefile.com", 80, "/login/login", { }
+ ), "login", "login")
+
+ @clean_url_with_same_action_and_controller_and_module_name = ActionController::UrlRewriter.old_new(MockRequest.new(
+ "http://", "www.singlefile.com", 80, "/login/login/login", { "module" => "login" }
+ ), "login", "login")
+
+ @clean_url_with_id_as_char = ActionController::UrlRewriter.old_new(MockRequest.new(
+ "http://", "www.singlefile.com", 80, "/teachers/show/t", { "id" => "t" }
+ ), "teachers", "show")
+ end
+
+ def test_clean_action
+ assert_equal "http://www.singlefile.com/library/books/ISBN/0743536703/edit", @library_url.rewrite(:action => "edit")
+ end
+
+ def test_clean_action_to_another_host
+ assert_equal(
+ "http://www.booksphere.com/library/books/ISBN/0743536703/edit",
+ @library_url.rewrite(:action => "edit", :host => "www.booksphere.com")
+ )
+ end
+
+ def test_clean_action_to_another_host_and_protocol
+ assert_equal(
+ "https://www.booksphere.com/library/books/ISBN/0743536703/edit",
+ @library_url.rewrite(:action => "edit", :host => "www.booksphere.com", :protocol => "https://")
+ )
+ end
+
+ def test_clean_action_with_only_path
+ assert_equal "/library/books/ISBN/0743536703/edit", @library_url.rewrite(:action => "edit", :only_path => true)
+ end
+
+ def test_action_from_index
+ assert_equal "http://www.singlefile.com/library/books/ISBN/0743536703/edit", @library_url_on_index.rewrite(:action => "edit")
+ end
+
+ def test_action_from_index_on_clean
+ @clean_urls.each do |url|
+ assert_equal "http://www.singlefile.com/identity/edit", url.rewrite(:action => "edit")
+ end
+ end
+
+ def test_action_without_prefix
+ assert_equal "http://www.singlefile.com/library/books/", @library_url.rewrite(:action => "index", :action_prefix => "")
+ end
+
+ def test_action_with_prefix
+ assert_equal(
+ "http://www.singlefile.com/library/books/XTC/123/show",
+ @library_url.rewrite(:action => "show", :action_prefix => "XTC/123")
+ )
+ end
+
+ def test_action_prefix_alone
+ assert_equal(
+ "http://www.singlefile.com/library/books/XTC/123/",
+ @library_url.rewrite(:action_prefix => "XTC/123")
+ )
+ end
+
+ def test_action_with_suffix
+ assert_equal(
+ "http://www.singlefile.com/library/books/show/XTC/123",
+ @library_url.rewrite(:action => "show", :action_prefix => "", :action_suffix => "XTC/123")
+ )
+ end
+
+ def test_clean_controller
+ assert_equal "http://www.singlefile.com/library/settings/", @library_url.rewrite(:controller => "settings")
+ end
+
+ def test_clean_controller_prefix
+ assert_equal "http://www.singlefile.com/shop/", @library_url.rewrite(:controller_prefix => "shop")
+ end
+
+ def test_clean_controller_with_module
+ assert_equal "http://www.singlefile.com/shop/purchases/", @library_url.rewrite(:module => "shop", :controller => "purchases")
+ end
+
+ def test_getting_out_of_a_module
+ assert_equal "http://www.singlefile.com/purchases/", @library_url_using_module.rewrite(:module => false, :controller => "purchases")
+ end
+
+ def test_controller_and_action
+ assert_equal "http://www.singlefile.com/library/settings/show", @library_url.rewrite(:controller => "settings", :action => "show")
+ end
+
+ def test_controller_and_action_and_anchor
+ assert_equal(
+ "http://www.singlefile.com/library/settings/show#5",
+ @library_url.rewrite(:controller => "settings", :action => "show", :anchor => "5")
+ )
+ end
+
+ def test_controller_and_action_and_empty_overwrite_params_and_anchor
+ assert_equal(
+ "http://www.singlefile.com/library/settings/show?code=0743536703&type=ISBN#5",
+ @library_url.rewrite(:controller => "settings", :action => "show", :overwrite_params => {}, :anchor => "5")
+ )
+ end
+
+ def test_controller_and_action_and_overwrite_params_and_anchor
+ assert_equal(
+ "http://www.singlefile.com/library/settings/show?code=0000001&type=ISBN#5",
+ @library_url.rewrite(:controller => "settings", :action => "show", :overwrite_params => {"code"=>"0000001"}, :anchor => "5")
+ )
+ end
+
+ def test_controller_and_action_and_overwrite_params_with_nil_value_and_anchor
+ assert_equal(
+ "http://www.singlefile.com/library/settings/show?type=ISBN#5",
+ @library_url.rewrite(:controller => "settings", :action => "show", :overwrite_params => {"code" => nil}, :anchor => "5")
+ )
+ end
+
+ def test_controller_and_action_params_and_overwrite_params_and_anchor
+ assert_equal(
+ "http://www.singlefile.com/library/settings/show?code=0000001&version=5.0#5",
+ @library_url.rewrite(:controller => "settings", :action => "show", :params=>{"version" => "5.0"}, :overwrite_params => {"code"=>"0000001"}, :anchor => "5")
+ )
+ end
+
+ def test_controller_and_action_and_params_anchor
+ assert_equal(
+ "http://www.singlefile.com/library/settings/show?update=1#5",
+ @library_url.rewrite(:controller => "settings", :action => "show", :params => { "update" => "1"}, :anchor => "5")
+ )
+ end
+
+ def test_controller_and_index_action
+ assert_equal "http://www.singlefile.com/library/settings/", @library_url.rewrite(:controller => "settings", :action => "index")
+ end
+
+ def test_same_controller_and_action_names
+ assert_equal "http://www.singlefile.com/login/logout", @clean_url_with_same_action_and_controller_name.rewrite(:action => "logout")
+ end
+
+ def xtest_same_module_and_controller_and_action_names
+ assert_equal "http://www.singlefile.com/login/login/logout", @clean_url_with_same_action_and_controller_and_module_name.rewrite(:action => "logout")
+ end
+
+ def test_controller_and_action_with_same_name_as_controller
+ @clean_urls.each do |url|
+ assert_equal "http://www.singlefile.com/anything/identity", url.rewrite(:controller => "anything", :action => "identity")
+ end
+ end
+
+ def test_controller_and_index_action_without_controller_prefix
+ assert_equal(
+ "http://www.singlefile.com/settings/",
+ @library_url.rewrite(:controller => "settings", :action => "index", :controller_prefix => "")
+ )
+ end
+
+ def test_controller_and_index_action_with_controller_prefix
+ assert_equal(
+ "http://www.singlefile.com/fantastic/settings/show",
+ @library_url.rewrite(:controller => "settings", :action => "show", :controller_prefix => "fantastic")
+ )
+ end
+
+ def test_path_parameters
+ assert_equal "http://www.singlefile.com/library/books/EXBC/0743536703/show", @library_url.rewrite(:path_params => {"type" => "EXBC"})
+ end
+
+ def test_parameters
+ assert_equal(
+ "http://www.singlefile.com/library/books/ISBN/0743536703/show?delete=1&name=David",
+ @library_url.rewrite(:params => {"delete" => "1", "name" => "David"})
+ )
+ end
+
+ def test_parameters_with_id
+ @clean_urls.each do |url|
+ assert_equal(
+ "http://www.singlefile.com/identity/show?name=David&id=5",
+ url.rewrite(
+ :action => "show",
+ :params => { "id" => "5", "name" => "David" }
+ )
+ )
+ end
+ end
+
+ def test_parameters_with_array
+ @clean_urls.each do |url|
+ assert_equal(
+ "http://www.singlefile.com/identity/show?id[]=3&id[]=5&id[]=10",
+ url.rewrite(
+ :action => "show",
+ :params => { 'id' => [ 3, 5, 10 ] } )
+ )
+ end
+ end
+
+ def test_action_with_id
+ assert_equal(
+ "http://www.singlefile.com/identity/show/7",
+ @clean_url_with_id.rewrite(
+ :action => "show",
+ :id => 7
+ )
+ )
+ @clean_urls.each do |url|
+ assert_equal(
+ "http://www.singlefile.com/identity/index/7",
+ url.rewrite(:id => 7)
+ )
+ end
+ end
+
+ def test_parameters_with_id_and_away
+ assert_equal(
+ "http://www.singlefile.com/identity/show/25?name=David",
+ @clean_url_with_id.rewrite(
+ :path_params => { "id" => "25" },
+ :params => { "name" => "David" }
+ )
+ )
+ end
+
+ def test_parameters_with_index_and_id
+ @clean_urls.each do |url|
+ assert_equal(
+ "http://www.singlefile.com/identity/index/25?name=David",
+ url.rewrite(
+ :path_params => { "id" => "25" },
+ :params => { "name" => "David" }
+ )
+ )
+ end
+ end
+
+ def test_action_going_away_from_id
+ assert_equal(
+ "http://www.singlefile.com/identity/list",
+ @clean_url_with_id.rewrite(
+ :action => "list"
+ )
+ )
+ end
+
+ def test_parameters_with_direct_id_and_away
+ assert_equal(
+ "http://www.singlefile.com/identity/show/25?name=David",
+ @clean_url_with_id.rewrite(
+ :id => "25",
+ :params => { "name" => "David" }
+ )
+ )
+ end
+
+ def test_parameters_with_direct_id_and_away
+ assert_equal(
+ "http://www.singlefile.com/store/open/25?name=David",
+ @clean_url_with_id.rewrite(
+ :controller => "store",
+ :action => "open",
+ :id => "25",
+ :params => { "name" => "David" }
+ )
+ )
+ end
+
+ def test_parameters_to_id
+ @clean_urls.each do |url|
+ %w(show index).each do |action|
+ assert_equal(
+ "http://www.singlefile.com/identity/#{action}/25?name=David",
+ url.rewrite(
+ :action => action,
+ :path_params => { "id" => "25" },
+ :params => { "name" => "David" }
+ )
+ )
+ end
+ end
+ end
+
+ def test_parameters_from_id
+ assert_equal(
+ "http://www.singlefile.com/identity/",
+ @clean_url_with_id.rewrite(
+ :action => "index"
+ )
+ )
+ end
+
+ def test_id_as_char_and_part_of_controller
+ assert_equal(
+ "http://www.singlefile.com/teachers/skill/5",
+ @clean_url_with_id_as_char.rewrite(
+ :action => "skill",
+ :id => 5
+ )
+ )
+ end
+
+ def test_from_clean_to_library
+ @clean_urls.each do |url|
+ assert_equal(
+ "http://www.singlefile.com/library/books/ISBN/0743536703/show?delete=1&name=David",
+ url.rewrite(
+ :controller_prefix => "library",
+ :controller => "books",
+ :action_prefix => "ISBN/0743536703",
+ :action => "show",
+ :params => { "delete" => "1", "name" => "David" }
+ )
+ )
+ end
+ end
+
+ def test_from_library_to_clean
+ assert_equal(
+ "http://www.singlefile.com/identity/",
+ @library_url.rewrite(
+ :controller => "identity", :controller_prefix => ""
+ )
+ )
+ end
+
+ def test_from_another_port
+ @library_url = ActionController::UrlRewriter.old_new(MockRequest.new(
+ "http://",
+ "www.singlefile.com",
+ 8080,
+ "/library/books/ISBN/0743536703/show",
+ { "type" => "ISBN", "code" => "0743536703" }
+ ), "books", "show")
+
+ assert_equal(
+ "http://www.singlefile.com:8080/identity/",
+ @library_url.rewrite(
+ :controller => "identity", :controller_prefix => ""
+ )
+ )
+ end
+
+ def test_basecamp
+ basecamp_url = ActionController::UrlRewriter.old_new(MockRequest.new(
+ "http://",
+ "projects.basecamp",
+ 80,
+ "/clients/disarray/1/msg/transcripts/",
+ {"category_name"=>"transcripts", "client_name"=>"disarray", "action"=>"index", "controller"=>"msg", "project_name"=>"1"}
+ ), "msg", "index")
+
+ assert_equal(
+ "http://projects.basecamp/clients/disarray/1/msg/transcripts/1/comments",
+ basecamp_url.rewrite(:action_prefix => "transcripts/1", :action => "comments")
+ )
+ end
+
+ def test_on_explicit_index_page # My index page is very modest, thank you...
+ url = ActionController::UrlRewriter.old_new(
+ MockRequest.new(
+ "http://", "example.com", 80, "/controller/index",
+ {"controller"=>"controller", "action"=>"index"}
+ ), "controller", "index"
+ )
+ assert_equal("http://example.com/controller/foo", url.rewrite(:action => 'foo'))
+ end
+
+ def test_rewriting_on_similar_fragments
+ url = UrlMockFactory.create("/advertisements/advert/", {"controller"=>"advert", "action"=>"index"})
+ assert_equal("http://example.com/advertisements/advert/news", url.rewrite(:action => 'news'))
+ end
+
+ def test_rewriting_on_similar_fragments_with_action_prefixes
+ url = UrlMockFactory.create(
+ "/clients/prall/1/msg/all/",
+ { "category_name"=>"all", "client_name"=>"prall", "action"=>"index", "controller"=>"msg", "project_name"=>"1"}
+ )
+
+ assert_equal(
+ "http://example.com/clients/prall/1/msg/all/new",
+ url.rewrite({ :controller => "msg", :action_prefix => "all", :action => "new" })
+ )
+
+ url = UrlMockFactory.create(
+ "/clients/prall/1/msg/all/",
+ { "category_name"=>"all", "client_name"=>"prall", "action"=>"index", "controller"=>"msg", "project_name"=>"1"}
+ )
+
+ assert_equal(
+ "http://example.com/clients/prall/1/msg/allous/new",
+ url.rewrite({ :controller => "msg", :action_prefix => "allous", :action => "new" })
+ )
+ end
+
+ def test_clean_application_prefix
+ assert_equal "http://www.singlefile.com/namespace/library/books/ISBN/0743536703/show",
+ @library_url.rewrite(:application_prefix => "/namespace")
+ end
+
+ def test_clean_application_prefix_with_controller_prefix
+ assert_equal "http://www.singlefile.com/namespace/shop/",
+ @library_url.rewrite(:application_prefix => "/namespace",
+ :controller_prefix => "shop" )
+ end
+
+ def test_blank_application_prefix
+ assert_equal "http://www.singlefile.com/library/books/ISBN/0743536703/show",
+ @library_url.rewrite(:application_prefix => "")
+ end
+
+ def test_nil_application_prefix
+ assert_equal "http://www.singlefile.com/library/books/ISBN/0743536703/show",
+ @library_url.rewrite(:application_prefix => nil)
+ end
+end
diff --git a/actionpack/test/fixtures/fun/games/hello_world.rhtml b/actionpack/test/fixtures/fun/games/hello_world.rhtml
new file mode 100644
index 0000000000..1ebfbe2539
--- /dev/null
+++ b/actionpack/test/fixtures/fun/games/hello_world.rhtml
@@ -0,0 +1 @@
+Living in a nested world \ No newline at end of file
diff --git a/actionpack/test/fixtures/helpers/fun/games_helper.rb b/actionpack/test/fixtures/helpers/fun/games_helper.rb
new file mode 100644
index 0000000000..bf60d9db0c
--- /dev/null
+++ b/actionpack/test/fixtures/helpers/fun/games_helper.rb
@@ -0,0 +1,3 @@
+module Fun::GamesHelper
+ def stratego() "Iz guuut!" end
+end \ No newline at end of file