Class: Usher

Inherits:
Object
  • Object
show all
Defined in:
lib/usher.rb,
lib/usher/node.rb,
lib/usher/util.rb,
lib/usher/route.rb,
lib/usher/grapher.rb,
lib/usher/splitter.rb,
lib/usher/interface.rb,
lib/usher/node/root.rb,
lib/usher/delimiters.rb,
lib/usher/exceptions.rb,
lib/usher/route/path.rb,
lib/usher/route/util.rb,
lib/usher/util/rails.rb,
lib/usher/util/parser.rb,
lib/usher/route/static.rb,
lib/usher/node/response.rb,
lib/usher/util/generate.rb,
lib/usher/interface/rack.rb,
lib/usher/interface/text.rb,
lib/usher/route/variable.rb,
lib/usher/interface/rails3.rb,
lib/usher/interface/rails20.rb,
lib/usher/interface/rails22.rb,
lib/usher/interface/rails23.rb,
lib/usher/interface/sinatra.rb,
lib/usher/interface/rack/route.rb,
lib/usher/node/failed_response.rb,
lib/usher/route/request_method.rb,
lib/usher/interface/rack/builder.rb,
lib/usher/interface/rails22/mapper.rb,
lib/usher/interface/rails23/mapper.rb,
lib/usher/interface/rack/middleware.rb,
lib/usher/node/root_ignoring_trailing_delimiters.rb

Overview

Main class for routing. If you’re going to be routing for a specific context, like rails or rack, you probably want to use an interface. Otherwise, this is the main class that actually does all the work.

Examples:

u = Usher.new
u.add_route('one/two').to(:one)
u.add_route('two/three').to(:two)
u.add_route('two/:variable').to(:variable)
u.recognize_path('one/two').destination
==> :one
u.recognize_path('two/whatwasthat').params_as_hash
==> {:variable => 'whatwasthat'}

Defined Under Namespace

Modules: Interface, Util Classes: Delimiters, DoubleRegexpException, Grapher, MissingParameterException, MultipleParameterException, Node, Route, Splitter, UnrecognizedException, ValidationException

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = nil) ⇒ Usher

Creates a route set, with options

Parameters:

  • options (Hash) (defaults to: nil)

    the options to create a router with

Options Hash (options):

  • :delimiters (Array<String>) — default: ['/', '.']

    Delimiters used in path separation. Array must be single character strings.

  • :valid_regex (String) — default: '[0-9A-Za-z\$\-_\+!\*\', ]+'

    String that can be interpolated into regex to match valid character sequences within path.

  • :request_methods (Array<Symbol>) — default: [:protocol, :domain, :port, :query_string, :remote_ip, :user_agent, :referer, :method, :subdomains]

    Array of methods called against the request object for the purposes of matching route requirements.

  • :generator (nil or Generator) — default: nil

    Take a look at ‘Usher::Util::Generators for examples.`.

  • :ignore_trailing_delimiters (Boolean) — default: false

    Ignore trailing delimiters in recognizing paths.

  • :consider_destination_keys (Boolean) — default: false

    When generating, and using hash destinations, you can have Usher use the destination hash to match incoming params.

  • :detailed_failure (Boolean) — default: false

    When a route fails to match, return a Usher::Node::FailedResponse instead of a ‘nil` Example, you create a route with a destination of :controller => ’test’, :action => ‘action’. If you made a call to generator with :controller => ‘test’, :action => ‘action’, it would pick that route to use for generation.

  • :allow_identical_variable_names (Boolean) — default: true

    When adding routes, allow identical variable names to be used.



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/usher.rb', line 74

def initialize(options = nil)
  self.route_class                     = Usher::Route
  self.generator                       = options && options.delete(:generator)
  self.delimiters                      = Delimiters.new(options && options.delete(:delimiters) || ['/', '.'])
  self.valid_regex                     = options && options.delete(:valid_regex) || '[0-9A-Za-z\$\-_\+!\*\',]+'
  self.request_methods                 = options && options.delete(:request_methods)
  self.ignore_trailing_delimiters      = options && options.key?(:ignore_trailing_delimiters) ? options.delete(:ignore_trailing_delimiters) : false
  self.consider_destination_keys       = options && options.key?(:consider_destination_keys) ? options.delete(:consider_destination_keys) : false
  self.allow_identical_variable_names  = options && options.key?(:allow_identical_variable_names) ? options.delete(:allow_identical_variable_names) : true
  self.detailed_failure                = options && options.key?(:detailed_failure) ? options.delete(:detailed_failure) : false

  unless options.nil? || options.empty?
    raise "unrecognized options -- #{options.keys.join(', ')}"
  end
  reset!
end

Instance Attribute Details

#delimitersObject

Returns the value of attribute delimiters.



26
27
28
# File 'lib/usher.rb', line 26

def delimiters
  @delimiters
end

#delimiters_regexObject (readonly)

Returns the value of attribute delimiters_regex.



26
27
28
# File 'lib/usher.rb', line 26

def delimiters_regex
  @delimiters_regex
end

#generatorObject

Returns the value of attribute generator.



26
27
28
# File 'lib/usher.rb', line 26

def generator
  @generator
end

#grapherObject (readonly)

Returns the value of attribute grapher.



26
27
28
# File 'lib/usher.rb', line 26

def grapher
  @grapher
end

#named_routesObject (readonly)

Returns the value of attribute named_routes.



26
27
28
# File 'lib/usher.rb', line 26

def named_routes
  @named_routes
end

#parent_routeObject

Returns the value of attribute parent_route.



26
27
28
# File 'lib/usher.rb', line 26

def parent_route
  @parent_route
end

#parserObject

Returns the value of attribute parser.



26
27
28
# File 'lib/usher.rb', line 26

def parser
  @parser
end

#rootObject (readonly)

Returns the value of attribute root.



26
27
28
# File 'lib/usher.rb', line 26

def root
  @root
end

#route_classObject

Returns the value of attribute route_class.



28
29
30
# File 'lib/usher.rb', line 28

def route_class
  @route_class
end

#routesObject (readonly)

Returns the value of attribute routes.



26
27
28
# File 'lib/usher.rb', line 26

def routes
  @routes
end

#splitterObject (readonly)

Returns the value of attribute splitter.



26
27
28
# File 'lib/usher.rb', line 26

def splitter
  @splitter
end

Instance Method Details

#add_meta(meta, path, options = nil) ⇒ Object



255
256
257
258
# File 'lib/usher.rb', line 255

def add_meta(meta, path, options = nil)
  route = get_route(path, options)
  root.add_meta(route, meta)
end

#add_named_route(name, path, options = nil) ⇒ Route

Adds a route referencable by ‘name`. See #add_route for format `path` and `options`.

Examples:

set = Usher.new
set.add_named_route(:test_route, '/test')

Parameters:

  • name

    Name of route

  • path

    Path of route

  • options (defaults to: nil)

    Options for route

Returns:

  • (Route)

    Route added



129
130
131
# File 'lib/usher.rb', line 129

def add_named_route(name, path, options = nil)
  add_route(path, options).name(name)
end

#add_route(path, options = nil) ⇒ Route

Creates a route from ‘path` and `options`

Parameters:

  • path (String)

    A path consists a mix of dynamic and static parts delimited by ‘/` ## Dynamic Dynamic parts are prefixed with either :, *. :variable matches only one part of the path, whereas *variable can match one or more parts.

    ### Example ‘/path/:variable/path` would match

    *  `/path/test/path`
    *  `/path/something_else/path`
    *  `/path/one_more/path`
    

    In the above examples, ‘test’, ‘something_else’ and ‘one_more’ respectively would be bound to the key ‘:variable`. However, `/path/test/one_more/path` would not be matched.

    ### example ‘/path/*variable/path` would match

    *  `/path/one/two/three/path`
    *  `/path/four/five/path`
    

    In the above examples, ‘[’one’, ‘two’, ‘three’]‘ and `[’four’, ‘five’]‘ respectively would be bound to the key `:variable`.

    As well, variables can have a regex matcher.

    ### Example ‘/product/:id,d+` would match

    • ‘/product/123`

    • ‘/product/4521`

    But not

    • ‘/product/AE-35`

    As well, the same logic applies for * variables as well, where only parts matchable by the supplied regex will actually be bound to the variable

    Variables can also have a greedy regex matcher. These matchers ignore all delimiters, and continue matching for as long as much as their regex allows.

    ### Example ‘/product/!id,hello/world|hello` would match

    • ‘/product/hello/world`

    • ‘/product/hello`

    ## Static

    Static parts of literal character sequences. For instance, ‘/path/something.html` would match only the same path. As well, static parts can have a regex pattern in them as well, such as `/path/something.html|xml` which would match only `/path/something.html` and `/path/something.xml`

    ## Optional sections

    Sections of a route can be marked as optional by surrounding it with brackets. For instance, in the above static example, ‘/path/something(.html)` would match both `/path/something` and `/path/something.html`.

    ## One and only one sections

    Sections of a route can be marked as “one and only one” by surrounding it with brackets and separating parts of the route with pipes. For instance, the path, ‘/path/something(.xml|.html)` would only match `/path/something.xml` and `/path/something.html`. Generally its more efficent to use one and only sections over using regex.

  • options (Hash) (defaults to: nil)

    Any other key is interpreted as a requirement for the variable of its name.

Options Hash (options):

  • :requirements (Object)

    After transformation, tests the condition using ===. If it returns false, it raises an ValidationException

  • :conditions (String, Regexp)

    Accepts any of the ‘request_methods` specificied in the construction of Usher. This can be either a `String` or a regular expression.

  • :default_values (Hash<Symbol, String>)

    Provides values for variables in your route for generation. If you’re using URL generation, then any values supplied here that aren’t included in your path will be appended to the query string.

  • :priority (Number)

    If there are two routes which equally match, the route with the highest priority will match first.

Returns:

  • (Route)

    The route added



232
233
234
235
236
237
238
239
# File 'lib/usher.rb', line 232

def add_route(path, options = nil)
  route = get_route(path, options)
  root.add(route)
  routes << route
  grapher.add_route(route)
  route.parent_route = parent_route if parent_route
  route
end

#allow_identical_variable_names?Boolean

Returns State of allow_identical_variable_names feature.

Returns:

  • (Boolean)

    State of allow_identical_variable_names feature.



92
93
94
# File 'lib/usher.rb', line 92

def allow_identical_variable_names?
  @allow_identical_variable_names
end

#can_generate?Boolean

Returns Able to generate.

Returns:

  • (Boolean)

    Able to generate



117
118
119
# File 'lib/usher.rb', line 117

def can_generate?
  !generator.nil?
end

#consider_destination_keys?Boolean

Returns State of consider_destination_keys feature.

Returns:

  • (Boolean)

    State of consider_destination_keys feature.



107
108
109
# File 'lib/usher.rb', line 107

def consider_destination_keys?
  @consider_destination_keys
end

#delete_named_route(name, path, options = nil) ⇒ Route

Deletes a route referencable by ‘name`. At least the path and conditions have to match the route you intend to delete.

Examples:

set = Usher.new
set.delete_named_route(:test_route, '/test')

Parameters:

  • name

    Name of route

  • path

    Path of route

  • options (defaults to: nil)

    Options for route

Returns:

  • (Route)

    Route added



141
142
143
144
# File 'lib/usher.rb', line 141

def delete_named_route(name, path, options = nil)
  delete_route(path, options)
  named_routes.delete(name)
end

#delete_route(path, options = nil) ⇒ Route

Deletes a route. At least the path and conditions have to match the route you intend to delete.

Examples:

set.delete_route('/test')

Parameters:

  • path (String)

    The path to delete

  • options (Hash) (defaults to: nil)

    The options used to identify the path

Returns:

  • (Route)

    The route deleted



247
248
249
250
251
252
253
# File 'lib/usher.rb', line 247

def delete_route(path, options = nil)
  route = get_route(path, options)
  root.delete(route)
  routes.replace(root.unique_routes)
  build_grapher!
  route
end

#detailed_failure?Boolean

Returns State of detailed_failure feature.

Returns:

  • (Boolean)

    State of detailed_failure feature.



97
98
99
# File 'lib/usher.rb', line 97

def detailed_failure?
  @detailed_failure
end

#dupUsher

Duplicates the router.

Returns:

  • (Usher)

    The duplicated router



305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
# File 'lib/usher.rb', line 305

def dup
  replacement = super
  original = self
  inverted_named_routes = original.named_routes.invert
  replacement.instance_eval do
    reset!
    original.routes.each do |route|
      new_route = route.dup
      new_route.router = self
      root.add(new_route)
      routes << new_route
      if name = inverted_named_routes[route]
        named_routes[name] = new_route
      end
    end
    send(:generator=, original.generator.class.new) if original.can_generate?
    build_grapher!
  end
  replacement
end

#empty?Boolean

Returns Whether the route set is empty.

Examples:

set = Usher.new
set.empty? => true
set.add_route('/test')
set.empty? => false

Returns:

  • (Boolean)

    Whether the route set is empty



36
37
38
# File 'lib/usher.rb', line 36

def empty?
  routes.empty?
end

#ignore_trailing_delimiters?Boolean

Returns State of ignore_trailing_delimiters feature.

Returns:

  • (Boolean)

    State of ignore_trailing_delimiters feature.



102
103
104
# File 'lib/usher.rb', line 102

def ignore_trailing_delimiters?
  @ignore_trailing_delimiters
end

#inspectObject



326
327
328
# File 'lib/usher.rb', line 326

def inspect
  "#<Usher:0x%x route_count=%d delimiters=%s request_methods=%s ignore_trailing_delimiters? %s consider_destination_keys? %s can_generate? %s priority_lookups? %s>" % [self.object_id, route_count, self.delimiters.inspect, request_methods.inspect, ignore_trailing_delimiters?.inspect, consider_destination_keys?.inspect, can_generate?.inspect, priority_lookups?.inspect]
end

#name(name, route) ⇒ Route

Attaches a ‘route` to a `name`

Examples:

set = Usher.new
route = set.add_route('/test')
set.name(:test, route)

Parameters:

  • name

    Name of route

  • route

    Route to attach to

Returns:

  • (Route)

    Route named



154
155
156
157
# File 'lib/usher.rb', line 154

def name(name, route)
  named_routes[name.to_sym] = route
  route
end

#path_for_options(options) ⇒ nil, Route::Path

Recognizes a set of ‘parameters` and gets the closest matching Usher::Route::Path or `nil` if no route exists.

Examples:

set = Usher.new
route = set.add_route('/:controller/:action')
set.path_for_options({:controller => 'test', :action => 'action'}) == path.route => true

Parameters:

  • options (Hash<Symbol, String>)

    A set of parameters

Returns:

  • (nil, Route::Path)

    A path matched or ‘nil` if not found.



292
293
294
# File 'lib/usher.rb', line 292

def path_for_options(options)
  grapher.find_matching_path(options)
end

#priority_lookups?Boolean

Returns State of priority_lookups feature.

Returns:

  • (Boolean)

    State of priority_lookups feature.



112
113
114
# File 'lib/usher.rb', line 112

def priority_lookups?
  @priority_lookups
end

#recognize(request, path = request.path) ⇒ nil, Node::Response

Recognizes a ‘request`

Examples:

Request = Struct.new(:path)
set = Usher.new
route = set.add_route('/test')
set.recognize(Request.new('/test')).path.route == route => true

Parameters:

  • request (#path)

    The request object. Must minimally respond to #path if no path argument is supplied here.

  • path (String) (defaults to: request.path)

    The path to be recognized.

Returns:

  • (nil, Node::Response)

    The recognition response if the request object was recognized



269
270
271
# File 'lib/usher.rb', line 269

def recognize(request, path = request.path)
  root.lookup(request, path)
end

#recognize_path(path) ⇒ nil, Node::Response

Recognizes a ‘path`

Examples:

Request = Struct.new(:path)
set = Usher.new
route = set.add_route('/test')
set.recognize_path('/test').path.route == route => true

Parameters:

  • path (String)

    The path to be recognized.

Returns:

  • (nil, Node::Response)

    The recognition response if the request object was recognized



281
282
283
# File 'lib/usher.rb', line 281

def recognize_path(path)
  recognize(nil, path)
end

#reset!Object

Resets the route set back to its initial state

Examples:

set = Usher.new
set.add_route('/test')
set.empty? => false
set.reset!
set.empty? => true


53
54
55
56
57
58
59
60
# File 'lib/usher.rb', line 53

def reset!
  @root = class_for_root.new(self, request_methods)
  @named_routes = {}
  @routes = []
  @grapher = Grapher.new(self)
  @priority_lookups = false
  @parser = Util::Parser.new(self, valid_regex)
end

#route_countNumber

Returns The number of routes currently mapped.

Returns:

  • (Number)

    The number of routes currently mapped



42
43
44
# File 'lib/usher.rb', line 42

def route_count
  routes.size
end

#to_sObject



330
331
332
# File 'lib/usher.rb', line 330

def to_s
  inspect
end