usher 0.6.3 → 0.6.4

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