Class: Usher

Inherits:
Object
  • Object
show all
Defined in:
lib/usher/interface.rb,
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/exceptions.rb,
lib/usher/route/path.rb,
lib/usher/route/util.rb,
lib/usher/util/parser.rb,
lib/usher/util/generate.rb,
lib/usher/route/variable.rb,
lib/usher/route/request_method.rb,
lib/usher/interface/merb_interface.rb,
lib/usher/interface/rack_interface.rb,
lib/usher/interface/text_interface.rb,
lib/usher/interface/email_interface.rb,
lib/usher/interface/rails3_interface.rb,
lib/usher/interface/rails2_2_interface.rb,
lib/usher/interface/rails2_3_interface.rb,
lib/usher/interface/rack_interface/route.rb,
lib/usher/interface/rails2_2_interface/mapper.rb

Overview

TODO: refactoring: I suggest to use usher/interfaces/rack.rb instead of usher/interface/rack_interface.rb, it will enable me to simplify this code

Defined Under Namespace

Modules: Interface, Util Classes: 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.



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

def initialize(options = nil)
  self.generator = options && options.delete(:generator)
  self.delimiters = options && options.delete(:delimiters) || ['/', '.']
  self.valid_regex = options && options.delete(:valid_regex) || '[0-9A-Za-z\$\-_\+!\*\',]+'
  self.request_methods = options && options.delete(:request_methods) || [:protocol, :domain, :port, :query_string, :remote_ip, :user_agent, :referer, :method, :subdomains]
  reset!
end

Instance Attribute Details

#delimiter_charsObject (readonly)

Returns the value of attribute delimiter_chars.



10
11
12
# File 'lib/usher.rb', line 10

def delimiter_chars
  @delimiter_chars
end

#delimitersObject

Returns the value of attribute delimiters.



10
11
12
# File 'lib/usher.rb', line 10

def delimiters
  @delimiters
end

#delimiters_regexObject (readonly)

Returns the value of attribute delimiters_regex.



10
11
12
# File 'lib/usher.rb', line 10

def delimiters_regex
  @delimiters_regex
end

#generatorObject

Returns the value of attribute generator.



10
11
12
# File 'lib/usher.rb', line 10

def generator
  @generator
end

#grapherObject (readonly)

Returns the value of attribute grapher.



10
11
12
# File 'lib/usher.rb', line 10

def grapher
  @grapher
end

#named_routesObject (readonly)

Returns the value of attribute named_routes.



10
11
12
# File 'lib/usher.rb', line 10

def named_routes
  @named_routes
end

#parent_routeObject

Returns the value of attribute parent_route.



10
11
12
# File 'lib/usher.rb', line 10

def parent_route
  @parent_route
end

#rootObject (readonly)

Returns the value of attribute root.



10
11
12
# File 'lib/usher.rb', line 10

def root
  @root
end

#routesObject (readonly)

Returns the value of attribute routes.



10
11
12
# File 'lib/usher.rb', line 10

def routes
  @routes
end

#splitterObject (readonly)

Returns the value of attribute splitter.



10
11
12
# File 'lib/usher.rb', line 10

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')


76
77
78
# File 'lib/usher.rb', line 76

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.

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



169
170
171
172
173
174
175
176
# File 'lib/usher.rb', line 169

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)


64
65
66
# File 'lib/usher.rb', line 64

def can_generate?
  !@generator.nil?
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')


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

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')


182
183
184
185
186
187
188
# File 'lib/usher.rb', line 182

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

#dupObject



224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
# File 'lib/usher.rb', line 224

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)


20
21
22
# File 'lib/usher.rb', line 20

def empty?
  @routes.empty?
end

#name(name, route) ⇒ Object

Attaches a route to a name

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


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

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

#parserObject



60
61
62
# File 'lib/usher.rb', line 60

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


215
216
217
# File 'lib/usher.rb', line 215

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


196
197
198
# File 'lib/usher.rb', line 196

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


206
207
208
# File 'lib/usher.rb', line 206

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


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

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

#route_countObject



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

def route_count
  @routes.size
end