Class: Rack::URLMap

Inherits:
Object
  • Object
show all
Defined in:
lib/rack/urlmap.rb

Overview

Rack::URLMap takes a hash mapping urls or paths to apps, and dispatches accordingly. Support for HTTP/1.1 host names exists if the URLs start with http:// or https://.

URLMap modifies the SCRIPT_NAME and PATH_INFO such that the part relevant for dispatch is in the SCRIPT_NAME, and the rest in the PATH_INFO. This should be taken care of when you need to reconstruct the URL in order to create links.

URLMap dispatches in such a way that the longest paths are tried first, since they are most specific.

Instance Method Summary collapse

Constructor Details

#initialize(map = {}) ⇒ URLMap

Returns a new instance of URLMap.



15
16
17
# File 'lib/rack/urlmap.rb', line 15

def initialize(map = {})
  remap(map)
end

Instance Method Details

#call(env) ⇒ Object



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/rack/urlmap.rb', line 36

def call(env)
  path = env["PATH_INFO"].to_s.squeeze("/")
  script_name = env['SCRIPT_NAME']
  hHost, sName, sPort = env.values_at('HTTP_HOST','SERVER_NAME','SERVER_PORT')
  @mapping.each { |host, location, app|
    next unless (hHost == host || sName == host \
      || (host.nil? && (hHost == sName || hHost == sName+':'+sPort)))
    next unless location == path[0, location.size]
    next unless path[location.size] == nil || path[location.size] == ?/

    return app.call(
      env.merge(
        'SCRIPT_NAME' => (script_name + location),
        'PATH_INFO'   => path[location.size..-1]))
  }
  [404, {"Content-Type" => "text/plain"}, ["Not Found: #{path}"]]
end

#remap(map) ⇒ Object



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/rack/urlmap.rb', line 19

def remap(map)
  @mapping = map.map { |location, app|
    if location =~ %r{\Ahttps?://(.*?)(/.*)}
      host, location = $1, $2
    else
      host = nil
    end

    unless location[0] == ?/
      raise ArgumentError, "paths need to start with /"
    end
    location = location.chomp('/')

    [host, location, app]
  }.sort_by { |(h, l, a)| [h ? -h.size : (-1.0 / 0.0), -l.size] }  # Longest path first
end