Skip to content
This repository
tree: 6cad407623
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

file 132 lines (108 sloc) 4.417 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
require 'action_dispatch/http/request'
require 'active_support/core_ext/uri'
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
    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

      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:
      #
      # match "/stories" => redirect("/posts")
      #
      # You can also use interpolation in the supplied redirect argument:
      #
      # match 'docs/:article', :to => redirect('/wiki/%{article}')
      #
      # Alternatively you can use one of the other syntaxes:
      #
      # The block version of redirect allows for the easy encapsulation of any logic associated with
      # the redirect in question. Either the params and request are supplied as arguments, or just
      # 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")
      # "http://#{request.host_with_port}/#{path}"
      # end
      #
      # 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.
      #
      # match 'stores/:name', :to => redirect(:subdomain => 'stores', :path => '/%{name}')
      # match 'stores/:name(*all)', :to => redirect(:subdomain => 'stores', :path => '/%{name}%{all}')
      #
      # Finally, an object which responds to call can be supplied to redirect, allowing you to reuse
      # common redirect routes. The call method must accept two arguments, params and request, and return
      # a string.
      #
      # match 'accounts/:name' => redirect(SubdomainRedirector.new('api'))
      #
      def redirect(*args, &block)
        options = args.last.is_a?(Hash) ? args.pop : {}
        status = options.delete(:status) || 301

        return OptionRedirect.new(status, options) if options.any?

        path = args.shift

        block = lambda { |params, request|
          (params.empty? || !path.match(/%\{\w*\}/)) ? path : (path % escape(params))
        } if String === path

        block = path if path.respond_to? :call

        # :FIXME: remove in Rails 4.0
        if block && block.respond_to?(:arity) && block.arity < 2
          msg = "redirect blocks with arity of #{block.arity} are deprecated. Your block must take 2 parameters: the environment, and a request object"
          ActiveSupport::Deprecation.warn msg
          deprecated_block = block
          block = lambda { |params, _| deprecated_block.call(params) }
        end

        raise ArgumentError, "redirection argument not supported" unless block

        Redirect.new status, block
      end

      private
        def escape(params)
          Hash[params.map{ |k,v| [k, Rack::Utils.escape(v)] }]
        end
    end
  end
end
Something went wrong with that request. Please try again.