usher 0.6.3 → 0.6.4

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.
data/Rakefile CHANGED
@@ -1,12 +1,10 @@
1
1
  # encoding: utf-8
2
2
 
3
- require File.expand_path(File.join(File.dirname(__FILE__), 'lib', 'usher'))
4
-
5
3
  begin
6
4
  require 'jeweler'
7
5
  Jeweler::Tasks.new do |s|
8
6
  s.name = "usher"
9
- s.description = s.summary = "A general purpose routing library"
7
+ s.description = s.summary = "Pure ruby general purpose router with interfaces for rails, rack, email or choose your own adventure"
10
8
  s.email = "joshbuddy@gmail.com"
11
9
  s.homepage = "http://github.com/joshbuddy/usher"
12
10
  s.authors = ["Joshua Hull", 'Jakub Šťastný', 'Daniel Neighman', 'Daniel Vartanov'].sort
data/VERSION.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  ---
2
- :patch: 3
3
2
  :build:
4
3
  :major: 0
5
4
  :minor: 6
5
+ :patch: 4
@@ -1,12 +1,12 @@
1
1
  class Usher
2
2
  module Interface
3
3
  class Email
4
-
4
+
5
5
  def initialize(&blk)
6
6
  @routes = Usher.new(:delimiters => ['@', '-', '.'], :valid_regex => '[\+a-zA-Z0-9]+')
7
7
  instance_eval(&blk) if blk
8
8
  end
9
-
9
+
10
10
  def for(path, &block)
11
11
  @routes.add_route(path).to(block)
12
12
  end
@@ -18,10 +18,10 @@ class Usher
18
18
  def act(email)
19
19
  response = @routes.recognize(email, email)
20
20
  if response.path
21
- response.path.route.destination.call(response.params.inject({}){|h,(k,v)| h[k]=v.to_s; h })
21
+ response.path.route.destination.call(response.params_as_hash)
22
22
  end
23
23
  end
24
24
 
25
25
  end
26
26
  end
27
- end
27
+ end
@@ -4,24 +4,24 @@ require File.join(File.dirname(__FILE__), 'rack', 'route')
4
4
  class Usher
5
5
  module Interface
6
6
  class Rack
7
-
7
+
8
8
  ENV_KEY_RESPONSE = 'usher.response'
9
9
  ENV_KEY_PARAMS = 'usher.params'
10
-
10
+
11
11
  class Middleware
12
-
12
+
13
13
  def initialize(app, router)
14
14
  @app = app
15
15
  @router = router
16
16
  end
17
-
17
+
18
18
  def call(env)
19
19
  @router.call(env)
20
20
  @app.call(env)
21
21
  end
22
-
22
+
23
23
  end
24
-
24
+
25
25
  class Builder < ::Rack::Builder
26
26
  def initialize(&block)
27
27
  @usher = Usher::Interface::Rack.new
@@ -79,7 +79,7 @@ class Usher
79
79
  @router.add_route(path, options)
80
80
  end
81
81
  alias_method :path, :add
82
-
82
+
83
83
  # default { |env| ... }
84
84
  # default DefaultApp
85
85
  def default(app = nil, &block)
@@ -138,14 +138,14 @@ class Usher
138
138
  # @api plugin
139
139
  def after_match(request, response)
140
140
  params = response.path.route.default_values ? response.path.route.default_values.merge(response.params_as_hash) : response.params_as_hash
141
-
141
+
142
142
  request.env[ENV_KEY_RESPONSE] ||= []
143
143
  request.env[ENV_KEY_RESPONSE] << response
144
144
 
145
145
  request.env[ENV_KEY_PARAMS] ?
146
146
  request.env[ENV_KEY_PARAMS].merge!(params) :
147
147
  (request.env[ENV_KEY_PARAMS] = params)
148
-
148
+
149
149
  # consume the path_info to the script_name
150
150
  # response.remaining_path
151
151
  consume_path!(request, response) if response.partial_match?
@@ -159,7 +159,11 @@ class Usher
159
159
  #
160
160
  # @api private
161
161
  def determine_respondant(response)
162
- use_destinations? && response && response.destination || _app
162
+ if use_destinations? && response && response.destination && response.destination.respond_to?(:call)
163
+ response.destination
164
+ else
165
+ _app
166
+ end
163
167
  end
164
168
 
165
169
  # Consume the path from path_info to script_name
@@ -167,14 +171,14 @@ class Usher
167
171
  request.env["SCRIPT_NAME"] = (request.env["SCRIPT_NAME"] + response.matched_path) || ""
168
172
  request.env["PATH_INFO"] = response.remaining_path || ""
169
173
  end
170
-
174
+
171
175
  def default_app
172
176
  _app
173
177
  end
174
-
178
+
175
179
  private
176
180
  attr_reader :_app
177
-
181
+
178
182
  end
179
183
  end
180
184
  end
@@ -46,7 +46,7 @@ class Usher
46
46
 
47
47
  def recognize(request)
48
48
  node = @usher.recognize(request)
49
- params = node.params.inject({}){|h,(k,v)| h[k]=v; h }
49
+ params = node.params_as_hash
50
50
  request.path_parameters = (node.params.empty? ? node.path.route.destination : node.path.route.destination.merge(params)).with_indifferent_access
51
51
  "#{request.path_parameters[:controller].camelize}Controller".constantize
52
52
  rescue
@@ -1,13 +1,13 @@
1
1
  class Usher
2
2
  module Interface
3
3
  class Rails23
4
-
4
+
5
5
  attr_reader :configuration_files
6
-
6
+
7
7
  def named_routes
8
8
  @router.named_routes
9
9
  end
10
-
10
+
11
11
  def add_named_route(name, route, options = {})
12
12
  @router.add_route(route, options).name(name)
13
13
  end
@@ -15,38 +15,38 @@ class Usher
15
15
  def add_route(path, options = {})
16
16
  if !@controller_action_route_added && path =~ %r{^/?:controller/:action/:id$}
17
17
  add_route('/:controller/:action', options.dup)
18
- @controller_action_route_added = true
18
+ @controller_action_route_added = true
19
19
  end
20
20
 
21
21
  if !@controller_route_added && path =~ %r{^/?:controller/:action$}
22
22
  add_route('/:controller', options.merge({:action => 'index'}))
23
- @controller_route_added = true
23
+ @controller_route_added = true
24
24
  end
25
-
25
+
26
26
  options[:action] = 'index' unless options[:action]
27
27
 
28
28
  path[0, 0] = '/' unless path[0] == ?/
29
29
  route = @router.add_route(path, options).to(options)
30
-
30
+
31
31
  raise "your route must include a controller" unless (route.paths.first.dynamic_keys && route.paths.first.dynamic_keys.include?(:controller)) || route.destination.include?(:controller)
32
32
  route
33
33
  end
34
-
34
+
35
35
  def initialize
36
36
  reset!
37
37
  end
38
-
38
+
39
39
  def add_configuration_file(file)
40
40
  @configuration_files << file
41
41
  end
42
-
42
+
43
43
  def reload!
44
44
  if configuration_files.any?
45
45
  configuration_files.each { |config| load(config) }
46
46
  else
47
47
  add_route ":controller/:action/:id"
48
48
  end
49
-
49
+
50
50
  end
51
51
  alias_method :reload, :reload!
52
52
 
@@ -63,75 +63,74 @@ class Usher
63
63
  app = recognize(request)
64
64
  app.call(env).to_a
65
65
  end
66
-
66
+
67
67
  def recognize(request)
68
68
  response = @router.recognize(request)
69
- request.path_parameters = (response.params.empty? ? response.path.route.destination : response.path.route.destination.merge(response.params.inject({}){|h,(k,v)| h[k]=v; h })).with_indifferent_access
70
- response.params.each { |pair| request.path_parameters[pair.first] = pair.last }
69
+ request.path_parameters.merge!(response.params_as_hash)
71
70
  "#{request.path_parameters[:controller].camelize}Controller".constantize
72
71
  end
73
-
72
+
74
73
  def reset!(options={})
75
74
  options[:generator] = options[:generator] || Usher::Util::Generators::URL.new
76
75
  options[:request_methods] = options[:request_methods] || [:protocol, :domain, :port, :query_string, :remote_ip, :user_agent, :referer, :method, :subdomains]
77
-
76
+
78
77
  @router = Usher.new(options)
79
78
  @configuration_files = []
80
79
  @module ||= Module.new
81
80
  @controller_route_added = false
82
81
  @controller_action_route_added = false
83
82
  end
84
-
83
+
85
84
  def draw(options={})
86
85
  reset!(options)
87
86
  yield ActionController::Routing::RouteSet::Mapper.new(self)
88
87
  install_helpers
89
88
  end
90
-
89
+
91
90
  def install_helpers(destinations = [ActionController::Base, ActionView::Base], regenerate_code = false)
92
91
  #*_url and hash_for_*_url
93
- Array(destinations).each do |d| d.module_eval { include Helpers }
92
+ Array(destinations).each do |d| d.module_eval { include Helpers }
94
93
  @router.named_routes.keys.each do |name|
95
94
  @module.module_eval <<-end_eval # We use module_eval to avoid leaks
96
- def #{name}_url(options = {})
97
- ActionController::Routing::UsherRoutes.generate(options, {}, :generate, :#{name})
98
- end
99
- end_eval
100
- end
101
- d.__send__(:include, @module)
102
- end
103
- end
95
+ def #{name}_url(options = {})
96
+ ActionController::Routing::UsherRoutes.generate(options, {}, :generate, :#{name})
97
+ end
98
+ end_eval
99
+ end
100
+ d.__send__(:include, @module)
101
+ end
102
+ end
104
103
 
105
- def generate(options, recall = {}, method = :generate, route_name = nil)
106
- route = if(route_name)
107
- @router.named_routes[route_name]
108
- else
109
- merged_options = options
110
- merged_options[:controller] = recall[:controller] unless options.key?(:controller)
111
- unless options.key?(:action)
112
- options[:action] = ''
113
- end
114
- path_for_options(merged_options)
115
- end
116
- case method
117
- when :generate
118
- merged_options ||= recall.merge(options)
119
- url = generate_url(route, merged_options)
120
- url.slice!(-1) if url[-1] == ?/
121
- url
122
- else
123
- raise "method #{method} not recognized"
124
- end
125
- end
126
-
127
- def generate_url(route, params)
128
- @router.generator.generate(route, params)
129
- end
130
-
131
- def path_for_options(options)
132
- @router.path_for_options(options)
133
- end
134
-
135
- end
136
- end
137
- end
104
+ def generate(options, recall = {}, method = :generate, route_name = nil)
105
+ route = if(route_name)
106
+ @router.named_routes[route_name]
107
+ else
108
+ merged_options = options
109
+ merged_options[:controller] = recall[:controller] unless options.key?(:controller)
110
+ unless options.key?(:action)
111
+ options[:action] = ''
112
+ end
113
+ path_for_options(merged_options)
114
+ end
115
+ case method
116
+ when :generate
117
+ merged_options ||= recall.merge(options)
118
+ url = generate_url(route, merged_options)
119
+ url.slice!(-1) if url[-1] == ?/
120
+ url
121
+ else
122
+ raise "method #{method} not recognized"
123
+ end
124
+ end
125
+
126
+ def generate_url(route, params)
127
+ @router.generator.generate(route, params)
128
+ end
129
+
130
+ def path_for_options(options)
131
+ @router.path_for_options(options)
132
+ end
133
+
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,22 @@
1
+ class Usher
2
+ class Node
3
+ class Response < Struct.new(:path, :params_as_array, :remaining_path, :matched_path)
4
+
5
+ def params
6
+ @params ||= params_as_array.nil? ? [] : path.convert_params_array(params_as_array)
7
+ end
8
+
9
+ def partial_match?
10
+ !remaining_path.nil?
11
+ end
12
+
13
+ def params_as_hash
14
+ @params_as_hash ||= params_as_array.nil? ? {} : params_as_array.inject({}){|hash, val| hash[path.dynamic_keys[hash.size]] = val; hash}
15
+ end
16
+
17
+ def destination
18
+ path && path.route.destination
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,140 @@
1
+ class Usher
2
+ class Node
3
+ class Root < Node
4
+
5
+ def initialize(route_set, request_methods)
6
+ super(route_set, nil)
7
+ self.request_methods = request_methods
8
+ end
9
+
10
+ def add(route)
11
+ route.paths.each do |path|
12
+ set_path_with_destination(path)
13
+ end
14
+ end
15
+
16
+ def delete(route)
17
+ route.paths.each do |path|
18
+ set_path_with_destination(path, nil)
19
+ end
20
+ end
21
+
22
+ def unique_routes(node = self, routes = [])
23
+ routes << node.terminates.route if node.terminates
24
+ node.normal.values.each do |v|
25
+ unique_routes(v, routes)
26
+ end if node.normal
27
+ node.greedy.values.each do |v|
28
+ unique_routes(v, routes)
29
+ end if node.greedy
30
+ node.request.values.each do |v|
31
+ unique_routes(v, routes)
32
+ end if node.request
33
+ routes.uniq!
34
+ routes
35
+ end
36
+
37
+ private
38
+
39
+ def set_path_with_destination(path, destination = path)
40
+ nodes = [path.parts.inject(self){ |node, key| process_path_part(node, key) }]
41
+ nodes = process_request_parts(nodes, request_methods_for_path(path)) if request_methods
42
+
43
+ nodes.each do |node|
44
+ while node.request_method_type
45
+ node = (node.request[nil] ||= Node.new(node, Route::RequestMethod.new(node.request_method_type, nil)))
46
+ end
47
+ node.terminates = destination
48
+ end
49
+ end
50
+
51
+ def request_method_index(type)
52
+ request_methods.index(type)
53
+ end
54
+
55
+ def process_request_parts(nodes, parts)
56
+ while parts.any?{ |p| !p.trivial? }
57
+ key = parts.shift
58
+
59
+ next if key.trivial?
60
+ nodes.map! do |node|
61
+ node.activate_request!
62
+ if node.request_method_type.nil?
63
+ node.request_method_type = key.type
64
+ node.upgrade_request! if key.value.is_a?(Regexp)
65
+ Array(key.value).map{|k| node.request[k] ||= Node.new(node, key) }
66
+ else
67
+ case request_method_index(node.request_method_type) <=> request_method_index(key.type)
68
+ when -1
69
+ parts.unshift(key)
70
+ Array(key.value).map{|k| node.request[nil] ||= Node.new(node, Route::RequestMethod.new(node.request_method_type, nil)) }
71
+ when 0
72
+ node.upgrade_request! if key.value.is_a?(Regexp)
73
+ Array(key.value).map{|k| node.request[k] ||= Node.new(node, key) }
74
+ when 1
75
+ previous_node = node.parent
76
+ current_node_entry_key = nil
77
+ current_node_entry_lookup = nil
78
+ [previous_node.normal, previous_node.greedy, previous_node.request].compact.each do |l|
79
+ current_node_entry_key = l.each{|k,v| break k if node == v}
80
+ current_node_entry_lookup = l and break if current_node_entry_key
81
+ end
82
+
83
+ current_node_entry_lookup.respond_to?(:delete_value) ?
84
+ current_node_entry_lookup.delete_value(node) : current_node_entry_lookup.delete_if{|k,v| v == node}
85
+
86
+ new_node = Node.new(previous_node, Route::RequestMethod.new(key.type, nil))
87
+ new_node.activate_request!
88
+ new_node.request_method_type = key.type
89
+ current_node_entry_lookup[current_node_entry_key] = new_node
90
+ node.parent = new_node
91
+ new_node.request[nil] = node
92
+ parts.unshift(key)
93
+ new_node
94
+ end
95
+ end
96
+ end
97
+ nodes.flatten!
98
+ end
99
+ nodes
100
+ end
101
+
102
+ def process_path_part(node, key)
103
+ case key
104
+ when Route::Variable::Greedy
105
+ node.activate_greedy!
106
+ if key.regex_matcher
107
+ node.upgrade_greedy!
108
+ node.greedy[key.regex_matcher] ||= Node.new(node, key)
109
+ else
110
+ node.greedy[nil] ||= Node.new(node, key)
111
+ end
112
+ when Route::Variable
113
+ node.activate_normal!
114
+ if key.regex_matcher
115
+ node.upgrade_normal!
116
+ node.normal[key.regex_matcher] ||= Node.new(node, key)
117
+ else
118
+ node.normal[nil] ||= Node.new(node, key)
119
+ end
120
+ when Route::Static::Greedy
121
+ node.activate_greedy!
122
+ node.upgrade_greedy!
123
+ node.greedy[key] ||= Node.new(node, key)
124
+ else
125
+ node.activate_normal!
126
+ node.upgrade_normal! if key.is_a?(Regexp)
127
+ node.normal[key] ||= Node.new(node, key)
128
+ end
129
+ end
130
+
131
+ def request_methods_for_path(path)
132
+ request_methods.collect do |type|
133
+ Route::RequestMethod.new(type, path.route.conditions && path.route.conditions[type])
134
+ end
135
+ end
136
+
137
+ end
138
+ end
139
+ end
140
+
data/lib/usher/node.rb CHANGED
@@ -1,23 +1,9 @@
1
- require 'fuzzy_hash'
1
+ require File.join('usher', 'node', 'root')
2
+ require File.join('usher', 'node', 'response')
2
3
 
3
4
  class Usher
4
-
5
5
  class Node
6
-
7
- class Response < Struct.new(:path, :params, :remaining_path, :matched_path)
8
- def partial_match?
9
- !remaining_path.nil?
10
- end
11
-
12
- def params_as_hash
13
- params.inject({}){|hash, pair| hash[pair.first] = pair.last; hash}
14
- end
15
-
16
- def destination
17
- path && path.route.destination
18
- end
19
- end
20
-
6
+
21
7
  attr_reader :normal, :greedy, :request
22
8
  attr_accessor :terminates, :request_method_type, :parent, :value, :request_methods
23
9
 
@@ -29,7 +15,7 @@ class Usher
29
15
  @greedy = nil
30
16
  @request_method_type = nil
31
17
  end
32
-
18
+
33
19
  def activate_normal!
34
20
  @normal ||= Hash.new
35
21
  end
@@ -57,16 +43,10 @@ class Usher
57
43
  def depth
58
44
  @depth ||= @parent.is_a?(Node) ? @parent.depth + 1 : 0
59
45
  end
60
-
46
+
61
47
  def greedy?
62
48
  @greedy
63
49
  end
64
-
65
- def self.root(route_set, request_methods)
66
- root = self.new(route_set, nil)
67
- root.request_methods = request_methods
68
- root
69
- end
70
50
 
71
51
  def terminates?
72
52
  @terminates && @terminates.route.recognizable?
@@ -91,95 +71,68 @@ class Usher
91
71
  v.pp
92
72
  end if request
93
73
  end
94
-
95
- def add(route)
96
- route.paths.each do |path|
97
- set_path_with_destination(path)
98
- end
99
- end
100
-
101
- def delete(route)
102
- route.paths.each do |path|
103
- set_path_with_destination(path, nil)
104
- end
105
- end
106
-
107
- def unique_routes(node = self, routes = [])
108
- routes << node.terminates.route if node.terminates
109
- node.normal.values.each do |v|
110
- unique_routes(v, routes)
111
- end if node.normal
112
- node.greedy.values.each do |v|
113
- unique_routes(v, routes)
114
- end if node.greedy
115
- node.request.values.each do |v|
116
- unique_routes(v, routes)
117
- end if node.request
118
- routes.uniq!
119
- routes
120
- end
121
-
122
- def find(usher, request_object, original_path, path, params = [], position = 0)
74
+
75
+ def find(usher, request_object, original_path, path, params = nil, position = 0)
123
76
  if terminates? && (path.empty? || terminates.route.partial_match? || (usher.ignore_trailing_delimiters? && path.all?{|p| usher.delimiters.include?(p)}))
124
77
  terminates.route.partial_match? ?
125
- Response.new(terminates, terminates.convert_params_array(params), original_path[position, original_path.size], original_path[0, position]) :
126
- Response.new(terminates, terminates.convert_params_array(params), nil, original_path)
78
+ Response.new(terminates, params, original_path[position, original_path.size], original_path[0, position]) :
79
+ Response.new(terminates, params, nil, original_path)
127
80
  elsif !path.empty? and greedy and match_with_result_output = greedy.match_with_result(whole_path = original_path[position, original_path.size])
128
- next_path, matched_part = match_with_result_output
81
+ next_path, matched_part = match_with_result_output
129
82
  position += matched_part.size
130
83
  whole_path.slice!(0, matched_part.size)
131
- params << matched_part if next_path.value.is_a?(Route::Variable)
84
+ (params ||= []) << matched_part if next_path.value.is_a?(Route::Variable)
132
85
  next_path.find(usher, request_object, original_path, whole_path.empty? ? whole_path : usher.splitter.split(whole_path), params, position)
133
86
  elsif !path.empty? and normal and next_part = normal[path.first] || normal[nil]
134
87
  part = path.shift
135
88
  position += part.size
136
89
  case next_part.value
137
- when String
138
- # do nothing
139
- when Route::Variable::Single
140
- # get the variable
141
- var = next_part.value
142
- # do a validity check
143
- var.valid!(part)
144
- # because its a variable, we need to add it to the params array
145
- params << part
146
- until path.empty? || (var.look_ahead === path.first) # variables have a look ahead notion,
147
- next_path_part = path.shift # and until they are satified,
148
- position += next_path_part.size # keep appending to the value in params
149
- params.last << next_path_part
150
- end if var.look_ahead && usher.delimiters.size > 1
151
- when Route::Variable::Glob
152
- params << []
153
- while true
154
- if (next_part.value.look_ahead === part || (!usher.delimiters.unescaped.include?(part) && next_part.value.regex_matcher && !next_part.value.regex_matcher.match(part)))
155
- path.unshift(part)
156
- position -= part.size
157
- if usher.delimiters.unescaped.include?(next_part.parent.value)
158
- path.unshift(next_part.parent.value)
159
- position -= next_part.parent.value.size
90
+ when String
91
+ # do nothing
92
+ when Route::Variable::Single
93
+ # get the variable
94
+ var = next_part.value
95
+ # do a validity check
96
+ var.valid!(part)
97
+ # because its a variable, we need to add it to the params array
98
+ (params ||= []) << part
99
+ until path.empty? || (var.look_ahead === path.first) # variables have a look ahead notion,
100
+ next_path_part = path.shift # and until they are satified,
101
+ position += next_path_part.size # keep appending to the value in params
102
+ params.last << next_path_part
103
+ end if var.look_ahead && usher.delimiters.size > 1
104
+ when Route::Variable::Glob
105
+ (params ||= []) << []
106
+ while true
107
+ if (next_part.value.look_ahead === part || (!usher.delimiters.unescaped.include?(part) && next_part.value.regex_matcher && !next_part.value.regex_matcher.match(part)))
108
+ path.unshift(part)
109
+ position -= part.size
110
+ if usher.delimiters.unescaped.include?(next_part.parent.value)
111
+ path.unshift(next_part.parent.value)
112
+ position -= next_part.parent.value.size
113
+ end
114
+ break
115
+ elsif !usher.delimiters.unescaped.include?(part)
116
+ next_part.value.valid!(part)
117
+ params.last << part
118
+ end
119
+ if path.empty?
120
+ break
121
+ else
122
+ part = path.shift
160
123
  end
161
- break
162
- elsif !usher.delimiters.unescaped.include?(part)
163
- next_part.value.valid!(part)
164
- params.last << part
165
- end
166
- if path.empty?
167
- break
168
- else
169
- part = path.shift
170
124
  end
171
- end
172
125
  end
173
126
  next_part.find(usher, request_object, original_path, path, params, position)
174
127
  elsif request_method_type
175
- return_value = if (specific_node = request[request_object.send(request_method_type)] and ret = specific_node.find(usher, request_object, original_path, path.dup, params.dup, position))
128
+ return_value = if (specific_node = request[request_object.send(request_method_type)] and ret = specific_node.find(usher, request_object, original_path, path.dup, params && params.dup, position))
176
129
  usher.priority_lookups? ? [ret] : ret
177
130
  end
178
-
179
- if usher.priority_lookups? || return_value.nil? and general_node = request[nil] and ret = general_node.find(usher, request_object, original_path, path.dup, params.dup, position)
131
+
132
+ if usher.priority_lookups? || return_value.nil? and general_node = request[nil] and ret = general_node.find(usher, request_object, original_path, path.dup, params && params.dup, position)
180
133
  return_value = usher.priority_lookups? && return_value ? [return_value, ret] : ret
181
134
  end
182
-
135
+
183
136
  unless usher.priority_lookups?
184
137
  return_value
185
138
  else
@@ -192,106 +145,5 @@ class Usher
192
145
  end
193
146
  end
194
147
 
195
- private
196
-
197
- def set_path_with_destination(path, destination = path)
198
- nodes = [path.parts.inject(self){ |node, key| process_path_part(node, key) }]
199
- nodes = process_request_parts(nodes, request_methods_for_path(path)) if request_methods
200
-
201
- nodes.each do |node|
202
- while node.request_method_type
203
- node = (node.request[nil] ||= Node.new(node, Route::RequestMethod.new(node.request_method_type, nil)))
204
- end
205
- node.terminates = destination
206
- end
207
- end
208
-
209
- def request_method_index(type)
210
- request_methods.index(type)
211
- end
212
-
213
- def process_request_parts(nodes, parts)
214
- while parts.any?{ |p| !p.trivial? }
215
- key = parts.shift
216
-
217
- next if key.trivial?
218
- nodes.map! do |node|
219
- node.activate_request!
220
- if node.request_method_type.nil?
221
- node.request_method_type = key.type
222
- node.upgrade_request! if key.value.is_a?(Regexp)
223
- Array(key.value).map{|k| node.request[k] ||= Node.new(node, key) }
224
- else
225
- case request_method_index(node.request_method_type) <=> request_method_index(key.type)
226
- when -1
227
- parts.unshift(key)
228
- Array(key.value).map{|k| node.request[nil] ||= Node.new(node, Route::RequestMethod.new(node.request_method_type, nil)) }
229
- when 0
230
- node.upgrade_request! if key.value.is_a?(Regexp)
231
- Array(key.value).map{|k| node.request[k] ||= Node.new(node, key) }
232
- when 1
233
- previous_node = node.parent
234
- current_node_entry_key = nil
235
- current_node_entry_lookup = nil
236
- [previous_node.normal, previous_node.greedy, previous_node.request].compact.each do |l|
237
- current_node_entry_key = l.each{|k,v| break k if node == v}
238
- current_node_entry_lookup = l and break if current_node_entry_key
239
- end
240
-
241
- current_node_entry_lookup.respond_to?(:delete_value) ?
242
- current_node_entry_lookup.delete_value(node) : current_node_entry_lookup.delete_if{|k,v| v == node}
243
-
244
- new_node = Node.new(previous_node, Route::RequestMethod.new(key.type, nil))
245
- new_node.activate_request!
246
- new_node.request_method_type = key.type
247
- current_node_entry_lookup[current_node_entry_key] = new_node
248
- node.parent = new_node
249
- new_node.request[nil] = node
250
- parts.unshift(key)
251
- new_node
252
- end
253
- end
254
- end
255
- nodes.flatten!
256
- end
257
- nodes
258
- end
259
-
260
-
261
- def process_path_part(node, key)
262
- case key
263
- when Route::Variable::Greedy
264
- node.activate_greedy!
265
- if key.regex_matcher
266
- node.upgrade_greedy!
267
- node.greedy[key.regex_matcher] ||= Node.new(node, key)
268
- else
269
- node.greedy[nil] ||= Node.new(node, key)
270
- end
271
- when Route::Variable
272
- node.activate_normal!
273
- if key.regex_matcher
274
- node.upgrade_normal!
275
- node.normal[key.regex_matcher] ||= Node.new(node, key)
276
- else
277
- node.normal[nil] ||= Node.new(node, key)
278
- end
279
- when Route::Static::Greedy
280
- node.activate_greedy!
281
- node.upgrade_greedy!
282
- node.greedy[key] ||= Node.new(node, key)
283
- else
284
- node.activate_normal!
285
- node.upgrade_normal! if key.is_a?(Regexp)
286
- node.normal[key] ||= Node.new(node, key)
287
- end
288
- end
289
-
290
- def request_methods_for_path(path)
291
- request_methods.collect do |type|
292
- Route::RequestMethod.new(type, path.route.conditions && path.route.conditions[type])
293
- end
294
- end
295
-
296
148
  end
297
149
  end
@@ -1,19 +1,19 @@
1
1
  class Usher
2
2
  class Route
3
3
  class Path
4
-
4
+
5
5
  attr_accessor :route
6
6
  attr_reader :parts
7
-
7
+
8
8
  def initialize(route, parts)
9
9
  self.route = route
10
10
  self.parts = parts
11
11
  end
12
-
12
+
13
13
  def convert_params_array(ary)
14
14
  ary.empty? ? ary : dynamic_keys.zip(ary)
15
15
  end
16
-
16
+
17
17
  def dynamic_indicies
18
18
  unless dynamic? && @dynamic_indicies
19
19
  @dynamic_indicies = []
@@ -33,15 +33,15 @@ class Usher
33
33
  end
34
34
  @dynamic_map
35
35
  end
36
-
36
+
37
37
  def dynamic_keys
38
38
  @dynamic_keys ||= dynamic_parts.map{|dp| dp.name} if dynamic?
39
39
  end
40
-
40
+
41
41
  def dynamic_required_keys
42
42
  @dynamic_required_keys ||= dynamic_parts.select{|dp| !dp.default_value}.map{|dp| dp.name} if dynamic?
43
43
  end
44
-
44
+
45
45
  def dynamic?
46
46
  @dynamic
47
47
  end
@@ -51,26 +51,26 @@ class Usher
51
51
  (dynamic_required_keys - keys).size.zero? ? keys : nil
52
52
  end
53
53
  end
54
-
54
+
55
55
  def can_generate_from_params?(params)
56
56
  if route.router.consider_destination_keys?
57
57
  route.destination.to_a - params.to_a
58
58
  (route.destination.to_a - params.to_a).size.zero?
59
59
  end
60
60
  end
61
-
61
+
62
62
  # Merges paths for use in generation
63
63
  def merge(other_path)
64
64
  new_parts = parts + other_path.parts
65
65
  Path.new(route, new_parts)
66
66
  end
67
-
68
- private
69
- def parts=(parts)
70
- @parts = parts
71
- @dynamic = @parts.any?{|p| p.is_a?(Variable)}
72
- end
73
-
67
+
68
+ private
69
+ def parts=(parts)
70
+ @parts = parts
71
+ @dynamic = @parts.any?{|p| p.is_a?(Variable)}
72
+ end
73
+
74
74
  end
75
75
  end
76
- end
76
+ end
data/lib/usher/route.rb CHANGED
@@ -24,12 +24,12 @@ class Usher
24
24
 
25
25
  def destination_keys
26
26
  @destination_keys ||= case
27
- when Hash
28
- destination.keys
29
- when CompoundDestination
30
- destination.options.keys
31
- else
32
- nil
27
+ when Hash
28
+ destination.keys
29
+ when CompoundDestination
30
+ destination.options.keys
31
+ else
32
+ nil
33
33
  end
34
34
  end
35
35
 
@@ -48,7 +48,7 @@ class Usher
48
48
  end
49
49
  @grapher
50
50
  end
51
-
51
+
52
52
  def unrecognizable!
53
53
  self.recognizable = false
54
54
  self
@@ -62,7 +62,7 @@ class Usher
62
62
  def recognizable?
63
63
  self.recognizable
64
64
  end
65
-
65
+
66
66
  def dup
67
67
  result = super
68
68
  result.instance_eval do
@@ -78,11 +78,11 @@ class Usher
78
78
  else
79
79
  @paths.size == 1 ? @paths.first : grapher.find_matching_path(params)
80
80
  end
81
-
81
+
82
82
  if matching_path.nil? and router.find_matching_paths_based_on_destination_keys?
83
83
  # do something
84
84
  end
85
-
85
+
86
86
  if parent_route
87
87
  matching_path = parent_route.find_matching_path(params).merge(matching_path)
88
88
  matching_path.route = self
@@ -105,19 +105,19 @@ class Usher
105
105
  # route.to(:controller => 'testing', :action => 'index')
106
106
  # set.recognize(Request.new('/test')).first.params => {:controller => 'testing', :action => 'index'}
107
107
  #
108
- #
108
+ #
109
109
  #
110
110
  def to(*args, &block)
111
111
  if !args.empty? && block
112
112
  @destination = CompoundDestination.new(args, block, args.last.is_a?(Hash) ? args.pop : {})
113
113
  elsif block.nil?
114
114
  case args.size
115
- when 0
116
- raise "destination should be set as something"
117
- when 1
118
- @destination = args.first
119
- else
120
- @destination = CompoundDestination.new(args, nil, args.last.is_a?(Hash) ? args.pop : {})
115
+ when 0
116
+ raise "destination should be set as something"
117
+ when 1
118
+ @destination = args.first
119
+ else
120
+ @destination = CompoundDestination.new(args, nil, args.last.is_a?(Hash) ? args.pop : {})
121
121
  end
122
122
  else
123
123
  @destination = block
@@ -147,7 +147,7 @@ class Usher
147
147
  end
148
148
 
149
149
  private
150
- attr_writer :grapher
150
+ attr_writer :grapher
151
151
 
152
152
  end
153
153
  end
@@ -0,0 +1,9 @@
1
+ class Usher
2
+ module Util
3
+ class Mapper
4
+
5
+ def
6
+
7
+ end
8
+ end
9
+ end
data/lib/usher/util.rb CHANGED
@@ -3,5 +3,6 @@ class Usher
3
3
  autoload :Generators, File.join('usher', 'util', 'generate')
4
4
  autoload :Parser, File.join('usher', 'util', 'parser')
5
5
  autoload :Graph, File.join('usher', 'util', 'graph')
6
+ autoload :Mapper, File.join('usher', 'util', 'mapper')
6
7
  end
7
8
  end
data/lib/usher.rb CHANGED
@@ -1,3 +1,5 @@
1
+ require 'fuzzy_hash'
2
+
1
3
  $LOAD_PATH << File.expand_path(File.dirname(__FILE__))
2
4
  require File.join('usher', 'node')
3
5
  require File.join('usher', 'route')
@@ -39,7 +41,7 @@ class Usher
39
41
  # set.reset!
40
42
  # set.empty? => true
41
43
  def reset!
42
- @root = Node.root(self, request_methods)
44
+ @root = Node::Root.new(self, request_methods)
43
45
  @named_routes = {}
44
46
  @routes = []
45
47
  @grapher = Grapher.new(self)
@@ -64,6 +66,8 @@ class Usher
64
66
  # <tt>:consider_destination_keys</tt>: +true+ or +false+. (default: +false+) When generating, and using hash destinations, you can have
65
67
  # Usher use the destination hash to match incoming params.
66
68
  #
69
+ # <tt>:allow_identical_variable_names</tt>: +true+ or +false+. (default: +true+) When adding routes, allow identical variable names to be used.
70
+ #
67
71
  # Example, you create a route with a destination of :controller => 'test', :action => 'action'. If you made a call to generator with :controller => 'test',
68
72
  # :action => 'action', it would pick that route to use for generation.
69
73
  def initialize(options = nil)
@@ -75,6 +75,21 @@ describe "Usher (for rack) route dispatching" do
75
75
  end
76
76
  end
77
77
 
78
+ describe "non rack app destinations" do
79
+ it "should route to a default application when using a hash" do
80
+ $captures = []
81
+ @default_app = lambda do |e|
82
+ $captures << :default
83
+ Rack::Response.new("Default").finish
84
+ end
85
+ @router = Usher::Interface.for(:rack)
86
+ @router.default(@default_app)
87
+ @router.add("/default").to(:action => "default")
88
+ response = @router.call(Rack::MockRequest.env_for("/default"))
89
+ $captures.should == [:default]
90
+ end
91
+ end
92
+
78
93
  describe "mounted rack instances" do
79
94
  before do
80
95
  @bad_app = mock("bad_app")
@@ -10,7 +10,7 @@ def build_request_mock(path, method, params)
10
10
  request.should_receive(:path).any_number_of_times.and_return(path)
11
11
  request.should_receive(:method).any_number_of_times.and_return(method)
12
12
  params = params.with_indifferent_access
13
- request.should_receive(:path_parameters=).any_number_of_times.with(params)
13
+ request.should_receive(:path_parameters=).any_number_of_times.with(hash_including(params))
14
14
  request.should_receive(:path_parameters).any_number_of_times.and_return(params)
15
15
  request
16
16
  end
@@ -45,36 +45,36 @@ describe "Usher (for rails 2.3) route recognition" do
45
45
  route_set.add_route('/sample/test', :controller => 'sample', :action => 'action')
46
46
  route_set.recognize(build_request_mock('/sample/test', 'get', {:controller => 'sample', :action => 'action'})).should == SampleController
47
47
  end
48
-
48
+
49
49
  it "should raise based upon an invalid param" do
50
50
  route_set.add_named_route(:sample, '/sample/:action', :controller => 'sample', :requirements => {:action => /\d+/})
51
51
  proc { route_set.recognize(build_request_mock('/sample/asdqwe', :post, {})) }.should raise_error
52
52
  end
53
-
53
+
54
54
  it "should raise based upon an invalid route" do
55
55
  route_set.add_named_route(:sample, '/sample', :controller => 'sample', :action => 'test')
56
56
  proc { route_set.recognize(build_request_mock('/test/asdqwe', :post, {})) }.should raise_error
57
57
  end
58
-
58
+
59
59
  it "should add /:controller and /:controller/:action if /:controller/:action/:id is added" do
60
60
  route_set.add_route('/:controller/:action/:id')
61
61
  route_set.route_count.should == 3
62
62
  end
63
-
63
+
64
64
  it "should correctly recognize a format (dynamic path path with . delimiter)" do
65
65
  route_set.add_route('/:controller/:action/:id.:format')
66
66
  route_set.recognize(build_request_mock('/sample/test/123.html', 'get', {:controller => 'sample', :action => 'test', :id => '123', :format => 'html'})).should == SampleController
67
67
  end
68
-
68
+
69
69
  it "should support root nodes" do
70
70
  route_set.add_route('/', :controller => 'sample')
71
71
  route_set.recognize(build_request_mock('/', :get, {:controller => 'sample', :action => 'index'})).should == SampleController
72
72
  end
73
-
73
+
74
74
  it "should default action to 'index' when controller (and not index) is specified" do
75
75
  route_set.add_route('/:controller/:action')
76
76
  route_set.recognize(build_request_mock('/sample', :get, {:controller => 'sample', :action => 'index'})).should == SampleController
77
77
  end
78
-
79
-
78
+
79
+
80
80
  end
@@ -47,18 +47,18 @@ describe "Usher route recognition" do
47
47
  @route_set.recognize(build_request({:method => 'put', :path => '/sample', :protocol => 'wacky', :domain => 'admin.spec.com', :user_agent => nil})).path.route.should == target_route_http_admin_generic
48
48
 
49
49
  end
50
-
50
+
51
51
  it "should recognize an empty path" do
52
52
  @route_set.add_route('').to(:test)
53
53
  @route_set.recognize(build_request({:path => ''})).path.route.destination.should == :test
54
54
  end
55
-
55
+
56
56
  it "should recognize an optionally empty path" do
57
57
  @route_set.add_route('(/)').to(:test)
58
58
  @route_set.recognize(build_request({:path => ''})).path.route.destination.should == :test
59
59
  @route_set.recognize(build_request({:path => '/'})).path.route.destination.should == :test
60
60
  end
61
-
61
+
62
62
  it "should allow adding a pure regex" do
63
63
  @route_set.add_route(/\/test\/(testing|gold)/).to(:test)
64
64
  @route_set.recognize(build_request({:path => '/test/testing'})).path.route.destination.should == :test
@@ -92,7 +92,7 @@ describe "Usher route recognition" do
92
92
 
93
93
  it "should recognize path with a trailing slash" do
94
94
  @route_set = Usher.new(:request_methods => [:protocol, :domain, :port, :query_string, :remote_ip, :user_agent, :referer, :method, :subdomains], :ignore_trailing_delimiters => true)
95
-
95
+
96
96
  target_route = @route_set.add_route('/path', :controller => 'sample', :action => 'action')
97
97
 
98
98
  response = @route_set.recognize(build_request({:method => 'get', :path => '/path/'}))
@@ -101,7 +101,7 @@ describe "Usher route recognition" do
101
101
 
102
102
  it "should recognize a format-style variable" do
103
103
  target_route = @route_set.add_route('/sample.:format', :controller => 'sample', :action => 'action')
104
- @route_set.recognize(build_request({:method => 'get', :path => '/sample.html', :domain => 'admin.host.com'})).should == Usher::Node::Response.new(target_route.paths.first, [[:format , 'html']], nil, "/sample.html")
104
+ @route_set.recognize(build_request({:method => 'get', :path => '/sample.html', :domain => 'admin.host.com'})).should == Usher::Node::Response.new(target_route.paths.first, ['html'], nil, "/sample.html")
105
105
  end
106
106
 
107
107
  it "should recognize a glob-style variable" do
@@ -236,12 +236,12 @@ describe "Usher route recognition" do
236
236
 
237
237
  it "should recognize a format-style literal" do
238
238
  target_route = @route_set.add_route('/:action.html', :controller => 'sample', :action => 'action')
239
- @route_set.recognize(build_request({:method => 'get', :path => '/sample.html', :domain => 'admin.host.com'})).should == Usher::Node::Response.new(target_route.paths.first, [[:action , 'sample']], nil, "/sample.html")
239
+ @route_set.recognize(build_request({:method => 'get', :path => '/sample.html', :domain => 'admin.host.com'})).should == Usher::Node::Response.new(target_route.paths.first, ['sample'], nil, "/sample.html")
240
240
  end
241
241
 
242
242
  it "should recognize a format-style variable along side another variable" do
243
243
  target_route = @route_set.add_route('/:action.:format', :controller => 'sample', :action => 'action')
244
- @route_set.recognize(build_request({:method => 'get', :path => '/sample.html', :domain => 'admin.host.com'})).should == Usher::Node::Response.new(target_route.paths.first, [[:action , 'sample'], [:format, 'html']], nil, '/sample.html')
244
+ @route_set.recognize(build_request({:method => 'get', :path => '/sample.html', :domain => 'admin.host.com'})).should == Usher::Node::Response.new(target_route.paths.first, ['sample', 'html'], nil, '/sample.html')
245
245
  end
246
246
 
247
247
  it "should use a requirement (proc) on incoming variables" do
@@ -336,7 +336,11 @@ describe "Usher route recognition" do
336
336
  it "should partially match a route" do
337
337
  route = @route_set.add_route("/foo")
338
338
  route.match_partially!
339
- @route_set.recognize(build_request(:method => "get", :path => "/foo/bar")).should == Usher::Node::Response.new(route.paths.first, [], "/bar", '/foo')
339
+ response = @route_set.recognize(build_request(:method => "get", :path => "/foo/bar"))
340
+ response.partial_match?.should be_true
341
+ response.params.should == []
342
+ response.remaining_path.should == '/bar'
343
+ response.matched_path.should == '/foo'
340
344
  end
341
345
 
342
346
  it "should partially match a route and use request conditions" do
@@ -401,5 +405,5 @@ describe "Usher route recognition" do
401
405
  end
402
406
 
403
407
  end
404
-
408
+
405
409
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: usher
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.3
4
+ version: 0.6.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Neighman
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-01-09 00:00:00 -05:00
17
+ date: 2010-01-12 00:00:00 -05:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -27,7 +27,7 @@ dependencies:
27
27
  - !ruby/object:Gem::Version
28
28
  version: 0.0.11
29
29
  version:
30
- description: A general purpose routing library
30
+ description: Pure ruby general purpose router with interfaces for rails, rack, email or choose your own adventure
31
31
  email: joshbuddy@gmail.com
32
32
  executables: []
33
33
 
@@ -60,6 +60,8 @@ files:
60
60
  - lib/usher/interface/sinatra.rb
61
61
  - lib/usher/interface/text.rb
62
62
  - lib/usher/node.rb
63
+ - lib/usher/node/response.rb
64
+ - lib/usher/node/root.rb
63
65
  - lib/usher/route.rb
64
66
  - lib/usher/route/path.rb
65
67
  - lib/usher/route/request_method.rb
@@ -70,6 +72,7 @@ files:
70
72
  - lib/usher/util.rb
71
73
  - lib/usher/util/generate.rb
72
74
  - lib/usher/util/graph.rb
75
+ - lib/usher/util/mapper.rb
73
76
  - lib/usher/util/parser.rb
74
77
  - lib/usher/util/rack-mixins.rb
75
78
  - rails/init.rb
@@ -125,7 +128,7 @@ rubyforge_project: joshbuddy-usher
125
128
  rubygems_version: 1.3.5
126
129
  signing_key:
127
130
  specification_version: 3
128
- summary: A general purpose routing library
131
+ summary: Pure ruby general purpose router with interfaces for rails, rack, email or choose your own adventure
129
132
  test_files:
130
133
  - spec/private/delimiters_spec.rb
131
134
  - spec/private/destination_spec.rb