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/delimiters.rb,
lib/usher/exceptions.rb,
lib/usher/route/path.rb,
lib/usher/route/util.rb,
lib/usher/util/graph.rb,
lib/usher/util/parser.rb,
lib/usher/route/static.rb,
lib/usher/util/generate.rb,
lib/usher/interface/merb.rb,
lib/usher/interface/rack.rb,
lib/usher/interface/text.rb,
lib/usher/route/variable.rb,
lib/usher/interface/email.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/rails22/mapper.rb,
lib/usher/interface/rails23/mapper.rb

Defined Under Namespace

Modules: Interface, Util Classes: Delimiters, Grapher, MissingParameterException, 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

:delimiters: Array of Strings. (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 of Symbols. (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 instance. (default: nil) Take a look at Usher::Util::Generators for examples..

:ignore_trailing_delimiters: true or false. (default: false) Ignore trailing delimiters in recognizing paths.

:consider_destination_keys: true or false. (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.



69
70
71
72
73
74
75
76
77
# File 'lib/usher.rb', line 69

def initialize(options = nil)
  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
  reset!
end

Instance Attribute Details

#delimitersObject

Returns the value of attribute delimiters.



12
13
14
# File 'lib/usher.rb', line 12

def delimiters
  @delimiters
end

#delimiters_regexObject (readonly)

Returns the value of attribute delimiters_regex.



12
13
14
# File 'lib/usher.rb', line 12

def delimiters_regex
  @delimiters_regex
end

#generatorObject

Returns the value of attribute generator.



12
13
14
# File 'lib/usher.rb', line 12

def generator
  @generator
end

#grapherObject (readonly)

Returns the value of attribute grapher.



12
13
14
# File 'lib/usher.rb', line 12

def grapher
  @grapher
end

#named_routesObject (readonly)

Returns the value of attribute named_routes.



12
13
14
# File 'lib/usher.rb', line 12

def named_routes
  @named_routes
end

#parent_routeObject

Returns the value of attribute parent_route.



12
13
14
# File 'lib/usher.rb', line 12

def parent_route
  @parent_route
end

#priority_lookupsObject (readonly) Also known as: priority_lookups?

Returns the value of attribute priority_lookups.



12
13
14
# File 'lib/usher.rb', line 12

def priority_lookups
  @priority_lookups
end

#rootObject (readonly)

Returns the value of attribute root.



12
13
14
# File 'lib/usher.rb', line 12

def root
  @root
end

#routesObject (readonly)

Returns the value of attribute routes.



12
13
14
# File 'lib/usher.rb', line 12

def routes
  @routes
end

#splitterObject (readonly)

Returns the value of attribute splitter.



12
13
14
# File 'lib/usher.rb', line 12

def splitter
  @splitter
end

Instance Method Details

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

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

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


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

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

#add_route(path, options = nil) ⇒ Object

Creates a route from path and options

path

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

  • requirements - After transformation, tests the condition using ===. If it returns false, it raises an Usher::ValidationException

  • conditions - Accepts any of the request_methods specificied in the construction of Usher. This can be either a string or a regular expression.

  • default_values - 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 - If there are two routes which equally match, the route with the highest priority will match first.

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



198
199
200
201
202
203
204
205
# File 'lib/usher.rb', line 198

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

#can_generate?Boolean

Returns:

  • (Boolean)


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

def can_generate?
  !@generator.nil?
end

#consider_destination_keys?Boolean

Returns:

  • (Boolean)


83
84
85
# File 'lib/usher.rb', line 83

def consider_destination_keys?
  @consider_destination_keys
end

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

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

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


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

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

#delete_route(path, options = nil) ⇒ Object

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

set = Usher.new
set.delete_route('/test')


211
212
213
214
215
216
217
# File 'lib/usher.rb', line 211

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

#dupObject



253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
# File 'lib/usher.rb', line 253

def dup
  replacement = super
  original = self
  inverted_named_routes = original.named_routes.invert
  replacement.instance_eval do
    @parser = nil
    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?
    rebuild_grapher!
  end
  replacement
end

#empty?Boolean

Returns whether the route set is empty

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

Returns:

  • (Boolean)


24
25
26
# File 'lib/usher.rb', line 24

def empty?
  @routes.empty?
end

#ignore_trailing_delimiters?Boolean

Returns:

  • (Boolean)


79
80
81
# File 'lib/usher.rb', line 79

def ignore_trailing_delimiters?
  @ignore_trailing_delimiters
end

#name(name, route) ⇒ Object

Attaches a route to a name

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


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

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

#parserObject



87
88
89
# File 'lib/usher.rb', line 87

def parser
  @parser ||= Util::Parser.for_delimiters(self, valid_regex)
end

#path_for_options(options) ⇒ Object

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

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


244
245
246
# File 'lib/usher.rb', line 244

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

#recognize(request, path = request.path) ⇒ Object

Recognizes a request and returns nil or an Usher::Node::Response, which is a struct containing a Usher::Route::Path and an array of arrays containing the extracted parameters.

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


225
226
227
# File 'lib/usher.rb', line 225

def recognize(request, path = request.path)
  @root.find(self, request, path, @splitter.url_split(path))
end

#recognize_path(path) ⇒ Object

Recognizes a path and returns nil or an Usher::Node::Response, which is a struct containing a Usher::Route::Path and an array of arrays containing the extracted parameters. Convenience method for when recognizing on the request object is unneeded.

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


235
236
237
# File 'lib/usher.rb', line 235

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

#reset!Object Also known as: clear!

Resets the route set back to its initial state

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


41
42
43
44
45
46
47
# File 'lib/usher.rb', line 41

def reset!
  @root = Node.root(self, request_methods)
  @named_routes = {}
  @routes = []
  @grapher = Grapher.new(self)
  @priority_lookups = false
end

#route_countObject

Returns the number of routes



30
31
32
# File 'lib/usher.rb', line 30

def route_count
  @routes.size
end