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/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/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, 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. 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.



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

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
  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_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



121
122
123
# File 'lib/usher.rb', line 121

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



224
225
226
227
228
229
230
231
# File 'lib/usher.rb', line 224

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.



89
90
91
# File 'lib/usher.rb', line 89

def allow_identical_variable_names?
  @allow_identical_variable_names
end

#can_generate?Boolean

Returns Able to generate.

Returns:

  • (Boolean)

    Able to generate



109
110
111
# File 'lib/usher.rb', line 109

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.



99
100
101
# File 'lib/usher.rb', line 99

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



133
134
135
136
# File 'lib/usher.rb', line 133

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



239
240
241
242
243
244
245
# File 'lib/usher.rb', line 239

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

#dupUsher

Duplicates the router.

Returns:

  • (Usher)

    The duplicated router



292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
# File 'lib/usher.rb', line 292

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.



94
95
96
# File 'lib/usher.rb', line 94

def ignore_trailing_delimiters?
  @ignore_trailing_delimiters
end

#inspectObject



313
314
315
# File 'lib/usher.rb', line 313

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



146
147
148
149
# File 'lib/usher.rb', line 146

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.



279
280
281
# File 'lib/usher.rb', line 279

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.



104
105
106
# File 'lib/usher.rb', line 104

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



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

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



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

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.for_delimiters(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



317
318
319
# File 'lib/usher.rb', line 317

def to_s
  inspect
end