usher 0.7.1 → 0.7.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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