usher 0.7.1 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,66 +1,125 @@
1
1
  class Usher
2
2
  module Interface
3
3
  class Sinatra
4
-
4
+
5
+ def initialize
6
+ ::Sinatra.send(:include, Extension)
7
+ end
8
+
5
9
  module Extension
6
-
7
- def self.included(cls)
8
- cls::Base.class_eval(<<-HERE_DOC, __FILE__, __LINE__)
9
- def self.route(verb, path, options={}, &block)
10
- @router ||= Usher.new(:request_methods => [:request_method, :host, :port, :scheme], :generator => Usher::Util::Generators::URL.new)
11
-
12
- name = options.delete(:name)
13
- options[:conditions] ||= {}
14
- options[:conditions][:request_method] = verb
15
- options[:conditions][:host] = options.delete(:host) if options.key?(:host)
16
-
17
- # Because of self.options.host
18
- host_name(options.delete(:host)) if options.key?(:host)
19
-
20
- define_method "\#{verb} \#{path}", &block
21
- unbound_method = instance_method("\#{verb} \#{path}")
22
- block =
23
- if block.arity != 0
24
- lambda { unbound_method.bind(self).call(*@block_params) }
25
- else
26
- lambda { unbound_method.bind(self).call }
27
- end
28
-
29
- invoke_hook(:route_added, verb, path, block)
30
-
31
- route = @router.add_route(path, options).to(block)
32
- route.name(name) if name
33
- route
34
- end
35
-
36
- def self.router
37
- @router
38
- end
39
10
 
40
- def route!(base = self.class)
41
- if self.class.router and match = self.class.router.recognize(@request, @request.path_info)
42
- @block_params = match.params.map{|p| p.last}
43
- @params = @params ? @params.merge(match.params_as_hash) : match.params_as_hash
11
+ def self.registered(app)
12
+ app.send(:include, Extension)
13
+ end
14
+
15
+ def self.included(base)
16
+ base.extend ClassMethods
17
+ end
18
+
19
+ def generate(name, *params)
20
+ self.class.generate(name, *params)
21
+ end
22
+
23
+ private
24
+ def route!(base=self.class, pass_block=nil)
25
+ if base.router and match = base.router.recognize(@request, @request.path_info)
26
+ @block_params = match.params.map { |p| p.last }
27
+ (@params ||= {}).merge!(match.params_as_hash)
28
+ pass_block = catch(:pass) do
44
29
  route_eval(&match.destination)
45
- elsif base.superclass.respond_to?(:routes)
46
- route! base.superclass
47
- else
48
- route_missing
49
30
  end
50
31
  end
51
-
52
- def generate(name, *params)
53
- self.class.router.generator.generate(name, *params)
32
+
33
+ # Run routes defined in superclass.
34
+ if base.superclass.respond_to?(:router)
35
+ route! base.superclass, pass_block
36
+ return
54
37
  end
55
38
 
56
- HERE_DOC
57
- end
58
-
59
- end
60
-
61
- def initialize
62
- ::Sinatra.send(:include, Extension)
63
- end
64
- end
65
- end
66
- end
39
+ route_eval(&pass_block) if pass_block
40
+
41
+ route_missing
42
+ end
43
+
44
+ module ClassMethods
45
+
46
+ def new(*args, &bk)
47
+ configure! unless @_configured
48
+ super(*args, &bk)
49
+ end
50
+
51
+ def route(verb, path, options={}, &block)
52
+ path.gsub!(/\/\?$/, "(/)")
53
+ name = options.delete(:name)
54
+ options[:conditions] ||= {}
55
+ options[:conditions][:request_method] = verb
56
+ options[:conditions][:host] = options.delete(:host) if options.key?(:host)
57
+
58
+ define_method "#{verb} #{path}", &block
59
+ unbound_method = instance_method("#{verb} #{path}")
60
+ block =
61
+ if block.arity != 0
62
+ lambda { unbound_method.bind(self).call(*@block_params) }
63
+ else
64
+ lambda { unbound_method.bind(self).call }
65
+ end
66
+
67
+ invoke_hook(:route_added, verb, path, block)
68
+
69
+ route = router.add_route(path, options).to(block)
70
+ route.name(name) if name
71
+ route
72
+ end
73
+
74
+ def router
75
+ @router ||= Usher.new(:request_methods => [:request_method, :host, :port, :scheme],
76
+ :ignore_trailing_delimiters => true,
77
+ :generator => Usher::Util::Generators::URL.new,
78
+ :delimiters => ['/', '.', '-'],
79
+ :valid_regex => '[0-9A-Za-z\$_\+!\*\',]+')
80
+ block_given? ? yield(@router) : @router
81
+ end
82
+
83
+ def generate(name, *params)
84
+ router.generator.generate(name, *params)
85
+ end
86
+
87
+ def reset!
88
+ router.reset!
89
+ super
90
+ end
91
+
92
+ def configure!
93
+ configure :development do
94
+ error 404 do
95
+ content_type 'text/html'
96
+
97
+ (<<-HTML).gsub(/^ {17}/, '')
98
+ <!DOCTYPE html>
99
+ <html>
100
+ <head>
101
+ <style type="text/css">
102
+ body { text-align:center;font-family:helvetica,arial;font-size:22px;
103
+ color:#888;margin:20px}
104
+ #c {margin:0 auto;width:500px;text-align:left}
105
+ </style>
106
+ </head>
107
+ <body>
108
+ <h2>Sinatra doesn't know this ditty.</h2>
109
+ <div id="c">
110
+ Try this:
111
+ <pre>#{request.request_method.downcase} '#{request.path_info}' do\n "Hello World"\nend</pre>
112
+ </div>
113
+ </body>
114
+ </html>
115
+ HTML
116
+ end
117
+ end
118
+
119
+ @_configured = true
120
+ end
121
+ end # ClassMethods
122
+ end # Extension
123
+ end # Sinatra
124
+ end # Interface
125
+ end # Usher
@@ -1,30 +1,20 @@
1
1
  class Usher
2
+ # Various interfaces for Usher.
2
3
  module Interface
3
4
 
4
- InterfaceRegistry = {}
5
-
6
- def self.register(name, cls)
7
- InterfaceRegistry[name] = cls
8
- end
9
-
10
- register(:email, File.join(File.dirname(__FILE__), 'interface', 'email'))
11
- register(:merb, File.join(File.dirname(__FILE__), 'interface', 'merb'))
12
- register(:rails20, File.join(File.dirname(__FILE__), 'interface', 'rails20'))
13
- register(:rails22, File.join(File.dirname(__FILE__), 'interface', 'rails22'))
14
- register(:rails23, File.join(File.dirname(__FILE__), 'interface', 'rails23'))
15
- register(:rack, File.join(File.dirname(__FILE__), 'interface', 'rack'))
16
- register(:rails3, File.join(File.dirname(__FILE__), 'interface', 'rails3'))
17
- register(:text, File.join(File.dirname(__FILE__), 'interface', 'text'))
18
- register(:sinatra, File.join(File.dirname(__FILE__), 'interface', 'sinatra'))
5
+ autoload(:Email, File.join(File.dirname(__FILE__), 'interface', 'email'))
6
+ autoload(:Merb, File.join(File.dirname(__FILE__), 'interface', 'merb'))
7
+ autoload(:Rails20, File.join(File.dirname(__FILE__), 'interface', 'rails20'))
8
+ autoload(:Rails22, File.join(File.dirname(__FILE__), 'interface', 'rails22'))
9
+ autoload(:Rails23, File.join(File.dirname(__FILE__), 'interface', 'rails23'))
10
+ autoload(:Rack, File.join(File.dirname(__FILE__), 'interface', 'rack'))
11
+ autoload(:Rails3, File.join(File.dirname(__FILE__), 'interface', 'rails3'))
12
+ autoload(:Text, File.join(File.dirname(__FILE__), 'interface', 'text'))
13
+ autoload(:Sinatra, File.join(File.dirname(__FILE__), 'interface', 'sinatra'))
19
14
 
20
15
  def self.class_for(name)
21
- name = name.to_sym
22
- if InterfaceRegistry[name]
23
- require InterfaceRegistry[name]
24
- Usher::Interface.const_get(File.basename(InterfaceRegistry[name]).to_s.split(/_/).map{|e| e.capitalize}.join)
25
- else
26
- raise ArgumentError, "Interface #{name.inspect} doesn't exist. Choose one of: #{InterfaceRegistry.keys.inspect}"
27
- end
16
+ Usher::Interface.const_get(name.to_s.split(/_/).map{|e| e.capitalize}.join) or
17
+ raise ArgumentError, "Interface #{name.inspect} doesn't exist."
28
18
  end
29
19
 
30
20
  # Usher::Interface.for(:rack, &block)
@@ -1,7 +1,9 @@
1
1
  class Usher
2
2
  class Node
3
- class Response < Struct.new(:path, :params_as_array, :remaining_path, :matched_path, :only_trailing_delimiters)
3
+ class Response < Struct.new(:path, :params_as_array, :remaining_path, :matched_path)
4
4
 
5
+ attr_accessor :only_trailing_delimiters
6
+
5
7
  def params
6
8
  @params ||= path.convert_params_array(params_as_array)
7
9
  end
data/lib/usher/node.rb CHANGED
@@ -9,44 +9,12 @@ class Usher
9
9
 
10
10
  def initialize(parent, value)
11
11
  @parent, @value = parent, value
12
- @request = nil
13
- @normal = nil
14
- @greedy = nil
15
- @request_method_type = nil
16
- end
17
-
18
- def activate_normal!
19
- @normal ||= Hash.new
20
- end
21
-
22
- def activate_greedy!
23
- @greedy ||= Hash.new
24
- end
25
-
26
- def activate_request!
27
- @request ||= Hash.new
28
- end
29
-
30
- def upgrade_normal!
31
- @normal = FuzzyHash.new(@normal)
32
- end
33
-
34
- def upgrade_greedy!
35
- @greedy = FuzzyHash.new(@greedy)
36
- end
37
-
38
- def upgrade_request!
39
- @request = FuzzyHash.new(@request)
40
12
  end
41
13
 
42
14
  def depth
43
15
  @depth ||= parent.is_a?(Node) ? parent.depth + 1 : 0
44
16
  end
45
17
 
46
- def greedy?
47
- @greedy
48
- end
49
-
50
18
  def terminates?
51
19
  @terminates && @terminates.route.recognizable?
52
20
  end
@@ -66,68 +34,57 @@ class Usher
66
34
  def root
67
35
  @root ||= ancestors.last
68
36
  end
69
-
37
+
70
38
  def route_set
71
39
  @route_set ||= root.route_set
72
40
  end
73
41
 
74
- def pp
75
- $stdout << " " * depth
76
- $stdout << "#{terminates? ? '* ' : ''}#{depth}: #{value.inspect}\n"
77
- normal.each do |k,v|
78
- $stdout << " " * (depth + 1)
79
- $stdout << ". #{k.inspect} ==> \n"
80
- v.pp
81
- end if normal
82
- greedy.each do |k,v|
83
- $stdout << " " * (depth + 1)
84
- $stdout << "g #{k.inspect} ==> \n"
85
- v.pp
86
- end if greedy
87
- request.each do |k,v|
88
- $stdout << " " * (depth + 1)
89
- $stdout << "r #{k.inspect} ==> \n"
90
- v.pp
91
- end if request
42
+ def inspect
43
+ out = ''
44
+ out << " " * depth
45
+ out << "#{terminates? ? '* ' : ''}#{depth}: #{value.inspect}\n"
46
+ [:normal, :greedy, :request].each do |node_type|
47
+ send(node_type).each do |k,v|
48
+ out << (" " * (depth + 1)) << "#{node_type.to_s[0].chr} #{k.inspect} ==> \n" << v.inspect
49
+ end if send(node_type)
50
+ end
51
+ out
92
52
  end
93
53
 
94
54
  def find(request_object, original_path, path, params = [])
95
55
  # terminates or is partial
96
- if terminates? && (path.nil? || path.empty? || terminates.route.partial_match? || (route_set.ignore_trailing_delimiters? and only_trailing_delimiters = path.all?{|p| route_set.delimiters.include?(p)}))
97
- terminates.route.partial_match? ?
98
- Response.new(terminates, params, path.join, original_path[0, original_path.size - path.join.size], only_trailing_delimiters) :
99
- Response.new(terminates, params, nil, original_path, only_trailing_delimiters)
56
+
57
+ if terminates? && (path.empty? || terminates.route.partial_match?)
58
+ response = terminates.route.partial_match? ?
59
+ Response.new(terminates, params, path.join, original_path[0, original_path.size - path.join.size]) :
60
+ Response.new(terminates, params, nil, original_path)
100
61
  # terminates or is partial
101
- elsif greedy && !path.empty? and match_with_result_output = greedy.match_with_result(whole_path = path.join)
102
- next_path, matched_part = match_with_result_output
62
+ elsif !path.empty? and greedy and match_with_result_output = greedy.match_with_result(whole_path = path.join)
63
+ child_node, matched_part = match_with_result_output
103
64
  whole_path.slice!(0, matched_part.size)
104
- params << matched_part if next_path.value.is_a?(Route::Variable)
105
- next_path.find(request_object, original_path, whole_path.empty? ? whole_path : route_set.splitter.split(whole_path), params)
106
- elsif normal && !path.empty? and next_part = normal[path.first] || normal[nil]
65
+ params << matched_part if child_node.value.is_a?(Route::Variable)
66
+ child_node.find(request_object, original_path, whole_path.empty? ? whole_path : route_set.splitter.split(whole_path), params)
67
+ elsif !path.empty? and normal and child_node = normal[path.first] || normal[nil]
107
68
  part = path.shift
108
- case next_part.value
69
+ case child_node.value
109
70
  when String
110
71
  when Route::Variable::Single
111
- variable = next_part.value # get the variable
112
- variable.valid!(part) # do a validity check
113
- if variable.look_ahead
114
- until (variable.look_ahead === path.first) || path.empty? # variables have a look ahead notion,
115
- next_path_part = path.shift # and until they are satified,
116
- part << next_path_part
117
- end
118
- end
72
+ variable = child_node.value # get the variable
73
+ variable.valid!(part) # do a validity check
74
+ until path.empty? || (variable.look_ahead === path.first) # variables have a look ahead notion,
75
+ next_path_part = path.shift # and until they are satified,
76
+ part << next_path_part
77
+ end if variable.look_ahead
119
78
  params << part # because its a variable, we need to add it to the params array
120
79
  when Route::Variable::Glob
121
80
  params << []
122
81
  loop do
123
- if (next_part.value.look_ahead === part || (!route_set.delimiters.unescaped.include?(part) && next_part.value.regex_matcher && !next_part.value.regex_matcher.match(part)))
82
+ if (child_node.value.look_ahead === part || (!route_set.delimiters.unescaped.include?(part) && child_node.value.regex_matcher && !child_node.value.regex_matcher.match(part)))
124
83
  path.unshift(part)
125
- if route_set.delimiters.unescaped.include?(next_part.parent.value)
126
- path.unshift(next_part.parent.value)
127
- end
84
+ path.unshift(child_node.parent.value) if route_set.delimiters.unescaped.include?(child_node.parent.value)
128
85
  break
129
86
  elsif !route_set.delimiters.unescaped.include?(part)
130
- next_part.value.valid!(part)
87
+ child_node.value.valid!(part)
131
88
  params.last << part
132
89
  end
133
90
  unless part = path.shift
@@ -135,7 +92,7 @@ class Usher
135
92
  end
136
93
  end
137
94
  end
138
- next_part.find(request_object, original_path, path, params)
95
+ child_node.find(request_object, original_path, path, params)
139
96
  elsif request_method_type
140
97
  if route_set.priority_lookups?
141
98
  route_candidates = []
@@ -159,5 +116,29 @@ class Usher
159
116
  end
160
117
  end
161
118
 
119
+ def activate_normal!
120
+ @normal ||= {}
121
+ end
122
+
123
+ def activate_greedy!
124
+ @greedy ||= {}
125
+ end
126
+
127
+ def activate_request!
128
+ @request ||= {}
129
+ end
130
+
131
+ def upgrade_normal!
132
+ @normal = FuzzyHash.new(@normal)
133
+ end
134
+
135
+ def upgrade_greedy!
136
+ @greedy = FuzzyHash.new(@greedy)
137
+ end
138
+
139
+ def upgrade_request!
140
+ @request = FuzzyHash.new(@request)
141
+ end
142
+
162
143
  end
163
144
  end
@@ -3,6 +3,8 @@ class Usher
3
3
  class Static
4
4
  class Greedy < Regexp
5
5
 
6
+ attr_accessor :generate_with
7
+
6
8
  end
7
9
  end
8
10
  end
@@ -14,7 +14,7 @@ class Usher
14
14
 
15
15
  module CaseEqualsValidator
16
16
  def valid!(val)
17
- @validator === val or raise(ValidationException.new("#{val} does not conform to #{@validator}"))
17
+ @validator === val.to_s or raise(ValidationException.new("#{val} does not conform to #{@validator}"))
18
18
  end
19
19
  end
20
20
 
data/lib/usher/route.rb CHANGED
@@ -50,12 +50,12 @@ class Usher
50
50
  end
51
51
 
52
52
  def unrecognizable!
53
- self.recognizable = false
53
+ @recognizable = false
54
54
  self
55
55
  end
56
56
 
57
57
  def recognizable!
58
- self.recognizable = true
58
+ @recognizable = true
59
59
  self
60
60
  end
61
61
 
@@ -93,17 +93,18 @@ class Usher
93
93
 
94
94
  CompoundDestination = Struct.new(:args, :block, :options)
95
95
 
96
- # Sets destination on a route. Returns +self+.
97
- #
98
- # This method acceps varargs. If you pass in more than one variable, it will be returned to you wrapped in a +CompoundDestination+.
99
- # If you send it varargs and the last member is a Hash, it will pop off the hash, and will be stored under <tt>#options</tt>.
100
- # Otherwise, if you use send a single variable, or call it with a block, these will be returned to you by <tt>#destination</tt>.
101
- #
102
- # Request = Struct.new(:path)
103
- # set = Usher.new
104
- # route = set.add_route('/test')
105
- # route.to(:controller => 'testing', :action => 'index')
106
- # set.recognize(Request.new('/test')).first.params => {:controller => 'testing', :action => 'index'}
96
+ # Sets destination on a route.
97
+ # @return [self]
98
+ # @param [Object] args
99
+ # If you pass in more than one variable, it will be returned to you wrapped in a {CompoundDestination}
100
+ # If you send it varargs and the last member is a Hash, it will pop off the hash, and will be stored under {CompoundDestination#options}
101
+ # Otherwise, if you use send a single variable, or call it with a block, these will be returned to you by {CompoundDestination#destination}
102
+ # @example
103
+ # Request = Struct.new(:path)
104
+ # set = Usher.new
105
+ # route = set.add_route('/test')
106
+ # route.to(:controller => 'testing', :action => 'index')
107
+ # set.recognize(Request.new('/test')).first.params => {:controller => 'testing', :action => 'index'}
107
108
  #
108
109
  #
109
110
  #
@@ -126,11 +127,13 @@ class Usher
126
127
  self
127
128
  end
128
129
 
129
- # Sets route as referenceable from +name+. Returns +self+.
130
- #
131
- # set = Usher.new
132
- # route = set.add_route('/test').name(:route)
133
- # set.generate_url(:route) => '/test'
130
+ # Sets route as referenceable from `name`
131
+ # @param [Symbol] name The name of the route
132
+ # @return [self] The route named
133
+ # @example
134
+ # set = Usher.new
135
+ # route = set.add_route('/test').name(:route)
136
+ # set.generate_url(:route) => '/test'
134
137
  def name(name)
135
138
  @named = name
136
139
  @router.name(name, self)
@@ -3,21 +3,19 @@ class Usher
3
3
 
4
4
  def self.for_delimiters(delimiters_array)
5
5
  delimiters = Delimiters.new(delimiters_array)
6
- delimiters_array.any?{|d| d.size > 1} ?
6
+ delimiters.any?{|d| d.size > 1} ?
7
7
  MultiCharacterSplitterInstance.new(delimiters) :
8
8
  SingleCharacterSplitterInstance.new(delimiters)
9
9
  end
10
10
 
11
11
  class SingleCharacterSplitterInstance
12
12
 
13
- attr_reader :url_split_regex
14
-
15
13
  def initialize(delimiters)
16
- @url_split_regex = Regexp.new("[^#{delimiters.collect{|d| Regexp.quote(d)}.join}]+|[#{delimiters.collect{|d| Regexp.quote(d)}.join}]")
14
+ @url_split_regex = Regexp.new("[^#{delimiters.regexp_char_class}]+|[#{delimiters.regexp_char_class}]")
17
15
  end
18
16
 
19
17
  def split(path)
20
- path.scan(url_split_regex)
18
+ path.scan(@url_split_regex)
21
19
  end
22
20
  end
23
21
 
@@ -36,7 +34,7 @@ class Usher
36
34
  protected
37
35
 
38
36
  def delimiters_regexp
39
- Regexp.new("(#{@delimiters.unescaped.collect{|d| Regexp.quote(d)}.join('|')})")
37
+ @delimiters.regexp
40
38
  end
41
39
 
42
40
  end
@@ -12,45 +12,61 @@ class Usher
12
12
 
13
13
  def generate_path_for_base_params(path, params)
14
14
  raise UnrecognizedException.new unless path
15
- result = ''
16
-
17
15
  case params
18
16
  when nil, Hash
19
- path.parts.each do |part|
20
- case part
21
- when String
22
- result << part
23
- when Route::Variable::Glob
24
- value = (params && params.delete(part.name)) || part.default_value || raise(MissingParameterException.new("expected a value for #{part.name}"))
25
- value.each_with_index do |current_value, index|
26
- part.valid!(current_value)
27
- result << current_value.to_s
28
- result << usher.delimiters.first if index != value.size - 1
29
- end
30
- when Route::Variable
31
- value = (params && params.delete(part.name)) || part.default_value || raise(MissingParameterException.new("expected a value for #{part.name}"))
32
- part.valid!(value)
33
- result << value.to_s
17
+ generate_path_for_base_params_with_hash(path, params)
18
+ else
19
+ generate_path_for_base_params_with_array(path, Array(params))
20
+ end
21
+ end
22
+
23
+ def generate_path_for_base_params_with_hash(path, params)
24
+ result = ''
25
+ path.parts.each do |part|
26
+ case part
27
+ when String
28
+ result << part
29
+ when Route::Variable::Glob
30
+ value = (params && params.delete(part.name)) || part.default_value || raise(MissingParameterException.new("expected a value for #{part.name}"))
31
+ value.each_with_index do |current_value, index|
32
+ part.valid!(current_value)
33
+ result << current_value.to_s
34
+ result << usher.delimiters.first if index != value.size - 1
34
35
  end
36
+ when Route::Variable
37
+ value = (params && params.delete(part.name)) || part.default_value || raise(MissingParameterException.new("expected a value for #{part.name}"))
38
+ part.valid!(value)
39
+ result << value.to_s
40
+ when Route::Static::Greedy
41
+ result << part.generate_with
42
+ else
43
+ raise
35
44
  end
36
- else
37
- params = Array(params)
38
- path.parts.each do |part|
39
- case part
40
- when String
41
- result << part
42
- when Route::Variable::Glob
43
- value = (params && params.shift) || part.default_value || raise(MissingParameterException.new)
44
- value.each_with_index do |current_value, index|
45
- part.valid!(current_value)
46
- result << current_value.to_s
47
- result << usher.delimiters.first if index != value.size - 1
48
- end
49
- when Route::Variable
50
- value = (params && params.shift) || part.default_value || raise(MissingParameterException.new)
51
- part.valid!(value)
52
- result << value.to_s
45
+ end
46
+ result
47
+ end
48
+
49
+ def generate_path_for_base_params_with_array(path, params)
50
+ result = ''
51
+ path.parts.each do |part|
52
+ case part
53
+ when String
54
+ result << part
55
+ when Route::Variable::Glob
56
+ value = (params && params.shift) || part.default_value || raise(MissingParameterException.new)
57
+ value.each_with_index do |current_value, index|
58
+ part.valid!(current_value)
59
+ result << current_value.to_s
60
+ result << usher.delimiters.first if index != value.size - 1
53
61
  end
62
+ when Route::Variable
63
+ value = (params && params.shift) || part.default_value || raise(MissingParameterException.new)
64
+ part.valid!(value)
65
+ result << value.to_s
66
+ when Route::Static::Greedy
67
+ result << value.generate_with
68
+ else
69
+ raise
54
70
  end
55
71
  end
56
72
  result