aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_dispatch/routing/redirection.rb
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib/action_dispatch/routing/redirection.rb')
-rw-r--r--actionpack/lib/action_dispatch/routing/redirection.rb165
1 files changed, 97 insertions, 68 deletions
diff --git a/actionpack/lib/action_dispatch/routing/redirection.rb b/actionpack/lib/action_dispatch/routing/redirection.rb
index 804991ad5f..b3823bb496 100644
--- a/actionpack/lib/action_dispatch/routing/redirection.rb
+++ b/actionpack/lib/action_dispatch/routing/redirection.rb
@@ -1,7 +1,90 @@
require 'action_dispatch/http/request'
+require 'active_support/core_ext/uri'
+require 'active_support/core_ext/array/extract_options'
+require 'rack/utils'
module ActionDispatch
module Routing
+ class Redirect # :nodoc:
+ attr_reader :status, :block
+
+ def initialize(status, block)
+ @status = status
+ @block = block
+ end
+
+ def call(env)
+ req = Request.new(env)
+
+ uri = URI.parse(path(req.symbolized_path_parameters, req))
+ uri.scheme ||= req.scheme
+ uri.host ||= req.host
+ uri.port ||= req.port unless req.standard_port?
+
+ body = %(<html><body>You are being <a href="#{ERB::Util.h(uri.to_s)}">redirected</a>.</body></html>)
+
+ headers = {
+ 'Location' => uri.to_s,
+ 'Content-Type' => 'text/html',
+ 'Content-Length' => body.length.to_s
+ }
+
+ [ status, headers, [body] ]
+ end
+
+ def path(params, request)
+ block.call params, request
+ end
+
+ def inspect
+ "redirect(#{status})"
+ end
+ end
+
+ class PathRedirect < Redirect
+ def path(params, request)
+ (params.empty? || !block.match(/%\{\w*\}/)) ? block : (block % escape(params))
+ end
+
+ def inspect
+ "redirect(#{status}, #{block})"
+ end
+
+ private
+ def escape(params)
+ Hash[params.map{ |k,v| [k, Rack::Utils.escape(v)] }]
+ end
+ end
+
+ class OptionRedirect < Redirect # :nodoc:
+ alias :options :block
+
+ def path(params, request)
+ url_options = {
+ :protocol => request.protocol,
+ :host => request.host,
+ :port => request.optional_port,
+ :path => request.path,
+ :params => request.query_parameters
+ }.merge options
+
+ if !params.empty? && url_options[:path].match(/%\{\w*\}/)
+ url_options[:path] = (url_options[:path] % escape_path(params))
+ end
+
+ ActionDispatch::Http::URL.url_for url_options
+ end
+
+ def inspect
+ "redirect(#{status}, #{options.map{ |k,v| "#{k}: #{v}" }.join(', ')})"
+ end
+
+ private
+ def escape_path(params)
+ Hash[params.map{ |k,v| [k, URI.parser.escape(v)] }]
+ end
+ end
+
module Redirection
# Redirect any path to another path:
@@ -19,10 +102,13 @@ module ActionDispatch
# params, depending of how many arguments your block accepts. A string is required as a
# return value.
#
- # match 'jokes/:number', :to => redirect do |params, request|
- # path = (params[:number].to_i.even? ? "/wheres-the-beef" : "/i-love-lamp")
+ # match 'jokes/:number', :to => redirect { |params, request|
+ # path = (params[:number].to_i.even? ? "wheres-the-beef" : "i-love-lamp")
# "http://#{request.host_with_port}/#{path}"
- # end
+ # }
+ #
+ # Note that the +do end+ syntax for the redirect block wouldn't work, as Ruby would pass
+ # the block to +match+ instead of +redirect+. Use <tt>{ ... }</tt> instead.
#
# The options version of redirect allows you to supply only the parts of the url which need
# to change, it also supports interpolation of the path similar to the first example.
@@ -37,74 +123,17 @@ module ActionDispatch
# match 'accounts/:name' => redirect(SubdomainRedirector.new('api'))
#
def redirect(*args, &block)
- options = args.last.is_a?(Hash) ? args.pop : {}
+ options = args.extract_options!
status = options.delete(:status) || 301
+ path = args.shift
- path = args.shift
-
- path_proc = if path.is_a?(String)
- proc { |params| (params.empty? || !path.match(/%\{\w*\}/)) ? path : (path % params) }
- elsif options.any?
- options_proc(options)
- elsif path.respond_to?(:call)
- proc { |params, request| path.call(params, request) }
- elsif block
- block
- else
- raise ArgumentError, "redirection argument not supported"
- end
+ return OptionRedirect.new(status, options) if options.any?
+ return PathRedirect.new(status, path) if String === path
- redirection_proc(status, path_proc)
+ block = path if path.respond_to? :call
+ raise ArgumentError, "redirection argument not supported" unless block
+ Redirect.new status, block
end
-
- private
-
- def options_proc(options)
- proc do |params, request|
- path = if options[:path].nil?
- request.path
- elsif params.empty? || !options[:path].match(/%\{\w*\}/)
- options.delete(:path)
- else
- (options.delete(:path) % params)
- end
-
- default_options = {
- :protocol => request.protocol,
- :host => request.host,
- :port => request.optional_port,
- :path => path,
- :params => request.query_parameters
- }
-
- ActionDispatch::Http::URL.url_for(options.reverse_merge(default_options))
- end
- end
-
- def redirection_proc(status, path_proc)
- lambda do |env|
- req = Request.new(env)
-
- params = [req.symbolized_path_parameters]
- params << req if path_proc.arity > 1
-
- uri = URI.parse(path_proc.call(*params))
- uri.scheme ||= req.scheme
- uri.host ||= req.host
- uri.port ||= req.port unless req.standard_port?
-
- body = %(<html><body>You are being <a href="#{ERB::Util.h(uri.to_s)}">redirected</a>.</body></html>)
-
- headers = {
- 'Location' => uri.to_s,
- 'Content-Type' => 'text/html',
- 'Content-Length' => body.length.to_s
- }
-
- [ status, headers, [body] ]
- end
- end
-
end
end
-end \ No newline at end of file
+end