Class: Rack::Routes

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

Constant Summary collapse

VERSION =
'0.2.0'

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(app = nil) ⇒ Routes

rack app



125
126
127
128
# File 'lib/rack/routes.rb', line 125

def initialize app = nil
  self.class.compile!
  @app = app
end

Class Method Details

.call(env) ⇒ Object

convience rack interface Enables the use of either: use Rack::Routes or run Rack::Routes



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

def call env
  @app ||= new
  @app.call env
end

.compile!Object

location directives should be run in a certain order. This is needed to establish that order.



23
24
25
26
27
28
# File 'lib/rack/routes.rb', line 23

def compile!
  # longest first
  [:exact, :string, :string_break].each do |type|
    locations[type].sort_by!{|path, _| -path.length}
  end
end

.location(path, *args, &blk) ⇒ Object

Description

Main interface method. Reimplementation of nginx location directive.

Args

path

The path to match on. Can be String or Regexp.

opts

Hash of options. see below.

app

Optional object which responds to :call

block

required if app is missing

opts keys:

:exact

Type of string matching. default false Also can be :prefix (for making ‘^~’ make sense)

:method

HTTP Request Method matching. defaults nil (all)

:type

Explicetly set the type of match.

exact values can be:

false

prefix match

true

literal match

=

literal match

^~

skip regex matching

yields env

raises ArgumentError if app or block is missing or if type is invalid

Examples

# config.ru

# matches everything # but longer matches will get applied first Rack::Routes.location ‘/’ do

[200, {}, ['hi']]

end

# matches everything beginning with /asdf Rack::Routes.location ‘/asdf’ do

[200, {}, ['hi asdf']]

end

# matches /foo and not /foobar nor /foo/baz etc. Rack::Routes.location ‘/foo’, :exact => true do

[200, {}, ['hi foo']]

end

# matches anything beginning with /bar # path can be any ruby regex # matchdata is stored in env Rack::Routes.location //bar(.*)/ do |env|

m = env['routes.location.matchdata']
[200, {}, ["hi #{m[1]}"]]

end

# matches beginning of path but stops if match is found. Does not evaluate regexen Rack::Routes.location ‘/baz’, :prefix => ‘^~’ do

[200, {}, ['hi baz']]

end

run Rack::Routes

Raises:

  • (ArgumentError)


94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/rack/routes.rb', line 94

def location path, *args, &blk
  app = args.last.respond_to?(:call) ? args.pop : blk
  raise ArgumentError, 'must provide either an app or a block' unless app

  opts = Hash === args.last ? args.pop : {}

  type = opts.fetch(:type, nil)
  type = :regex if Regexp === path
  type ||= case opts.fetch(:exact, opts.fetch(:prefix, false))
           when FalseClass
             :string
           when TrueClass, '='
             :exact
           when '^~'
             :string_break
           end

  raise ArgumentError, "unknown type `#{type}'" unless
    [:regex, :string, :exact, :string_break].include? type

  locations[type] << [path, app, opts]
end

.locationsObject

accessor for @locations hash



118
119
120
# File 'lib/rack/routes.rb', line 118

def locations
  @locations ||= Hash.new{|h,k| h[k] = []}
end

Instance Method Details

#call(env) ⇒ Object

rack interface



131
132
133
# File 'lib/rack/routes.rb', line 131

def call env
  dup.call! env
end

#call!(env) ⇒ Object



135
136
137
138
139
140
# File 'lib/rack/routes.rb', line 135

def call! env
  @env  = env
  @path = URI.decode_www_form_component @env['PATH_INFO']

  (matching_app || @app).call(env)
end

#find_exactObject



150
151
152
# File 'lib/rack/routes.rb', line 150

def find_exact
  find_type(:exact){|pth| pth == @path}
end

#find_regexObject



162
163
164
165
# File 'lib/rack/routes.rb', line 162

def find_regex
  find_type(:regex){|pth| pth === @path and
    @env['routes.location.matchdata'] = Regexp.last_match }
end

#find_stringObject



154
155
156
# File 'lib/rack/routes.rb', line 154

def find_string
  find_type(:string){|pth| @path[0, pth.length] == pth}
end

#find_string_breakObject



158
159
160
# File 'lib/rack/routes.rb', line 158

def find_string_break
  find_type(:string_break){|pth| @path[0, pth.length] == pth}
end

#find_type(type) ⇒ Object



142
143
144
145
146
147
148
# File 'lib/rack/routes.rb', line 142

def find_type type
  _, app = locations[type].find do |path, _, opts|
    next if opts[:method] and opts[:method] != @env['REQUEST_METHOD']
    yield path
  end
  app
end

#locationsObject



167
168
169
# File 'lib/rack/routes.rb', line 167

def locations
  self.class.locations
end

#matching_appObject

search exact matches first then ^~ strings (not sure what else to call them) then regex then all the other strings

NOTE the docs say to run strings before regex but I don’t see why it matters as the return logic is the same.



179
180
181
182
183
184
# File 'lib/rack/routes.rb', line 179

def matching_app
  find_exact          ||
    find_string_break ||
    find_regex        ||
    find_string
end