usher 0.4.8 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -12,8 +12,7 @@ class Usher
12
12
  end
13
13
 
14
14
  def reset!
15
- @usher ||= Usher.new
16
- @url_generator ||= Usher::Generators::URL.new(@usher)
15
+ @usher ||= Usher.new(:generator => Usher::Util::Generators::URL.new)
17
16
  @module ||= Module.new
18
17
  @module.instance_methods.each do |selector|
19
18
  @module.class_eval { remove_method selector }
@@ -38,7 +37,7 @@ class Usher
38
37
 
39
38
  path[0, 0] = '/' unless path[0] == ?/
40
39
  route = @usher.add_route(path, options)
41
- raise "your route must include a controller" unless route.paths.first.dynamic_keys.include?(:controller) || route.destination.include?(:controller)
40
+ raise "your route must include a controller" unless (route.paths.first.dynamic_keys && route.paths.first.dynamic_keys.include?(:controller)) || route.destination.include?(:controller)
42
41
  route
43
42
  end
44
43
 
@@ -86,7 +85,7 @@ class Usher
86
85
  end
87
86
 
88
87
  def generate_url(route, params)
89
- @url_generator.generate(route, params)
88
+ @usher.generator.generate(route, params)
90
89
  end
91
90
 
92
91
  def path_for_options(options)
@@ -28,7 +28,7 @@ class Usher
28
28
  path[0, 0] = '/' unless path[0] == ?/
29
29
  route = @router.add_route(path, options).to(options)
30
30
 
31
- raise "your route must include a controller" unless (route.paths.first.dynamic_keys.include?(:controller) || route.destination.include?(:controller))
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
 
@@ -72,8 +72,7 @@ class Usher
72
72
  end
73
73
 
74
74
  def reset!
75
- @router = Usher.new
76
- @url_generator = Usher::Generators::URL.new(@router)
75
+ @router = Usher.new(:generator => Usher::Util::Generators::URL.new)
77
76
  @configuration_files = []
78
77
  @module ||= Module.new
79
78
  @controller_route_added = false
@@ -123,7 +122,7 @@ class Usher
123
122
  end
124
123
 
125
124
  def generate_url(route, params)
126
- @url_generator.generate(route, params)
125
+ @router.generator.generate(route, params)
127
126
  end
128
127
 
129
128
  def path_for_options(options)
@@ -0,0 +1,57 @@
1
+ class Usher
2
+ module Interface
3
+ class Rails3Interface
4
+
5
+ @@instance = nil
6
+
7
+ def initialize
8
+ @usher = Usher.new
9
+ @controller_paths = []
10
+ @configurations_files = []
11
+
12
+ @@instance = self
13
+ end
14
+
15
+ def self.instance
16
+ @@instance
17
+ end
18
+
19
+ def draw(&blk)
20
+ @usher.instance_eval(&blk)
21
+ end
22
+
23
+ attr_accessor :controller_paths
24
+
25
+ def add_configuration_file(file)
26
+ @configurations_files << file
27
+ end
28
+
29
+ def reload!
30
+ @usher.reset!
31
+ @configurations_files.each do |c|
32
+ Kernel.load(c)
33
+ end
34
+ end
35
+
36
+ def call(env)
37
+ request = ActionDispatch::Request.new(env)
38
+ response = @usher.recognize(request, request.path_info)
39
+ request.parameters.merge!(response.path.route.default_values) if response.path.route.default_values
40
+ response.params.each{ |hk| request.parameters[hk.first] = hk.last}
41
+ controller = "#{request.parameters[:controller].to_s.camelize}Controller".constantize
42
+ controller.action(request.parameters[:action] || 'index').call(env)
43
+ end
44
+
45
+ def recognize(request)
46
+ params = recognize_path(request.path, extract_request_environment(request))
47
+ request.path_parameters = params.with_indifferent_access
48
+ "#{params[:controller].to_s.camelize}Controller".constantize
49
+ end
50
+
51
+ def load(app)
52
+ @app = app
53
+ end
54
+
55
+ end
56
+ end
57
+ end
data/lib/usher/node.rb CHANGED
@@ -4,15 +4,16 @@ class Usher
4
4
 
5
5
  class Node
6
6
 
7
- Response = Struct.new(:path, :params)
7
+ Response = Struct.new(:path, :params, :remaining_path, :matched_path)
8
8
 
9
- attr_reader :lookup
10
- attr_accessor :terminates, :exclusive_type, :parent, :value, :request_methods, :globs_capture_separators
9
+ attr_reader :lookup, :greedy_lookup
10
+ attr_accessor :terminates, :exclusive_type, :parent, :value, :request_methods
11
11
 
12
12
  def initialize(parent, value)
13
13
  @parent = parent
14
14
  @value = value
15
15
  @lookup = Hash.new
16
+ @greedy_lookup = Hash.new
16
17
  @exclusive_type = nil
17
18
  end
18
19
 
@@ -20,14 +21,21 @@ class Usher
20
21
  @lookup = FuzzyHash.new(@lookup)
21
22
  end
22
23
 
24
+ def upgrade_greedy_lookup
25
+ @greedy_lookup = FuzzyHash.new(@greedy_lookup)
26
+ end
27
+
23
28
  def depth
24
- @depth ||= @parent && @parent.is_a?(Node) ? @parent.depth + 1 : 0
29
+ @depth ||= @parent.is_a?(Node) ? @parent.depth + 1 : 0
25
30
  end
26
31
 
27
- def self.root(route_set, request_methods, globs_capture_separators)
32
+ def greedy?
33
+ !@greedy_lookup.empty?
34
+ end
35
+
36
+ def self.root(route_set, request_methods)
28
37
  root = self.new(route_set, nil)
29
38
  root.request_methods = request_methods
30
- root.globs_capture_separators = globs_capture_separators
31
39
  root
32
40
  end
33
41
 
@@ -47,92 +55,132 @@ class Usher
47
55
 
48
56
  def add(route)
49
57
  route.paths.each do |path|
50
- parts = path.parts.dup
51
- request_methods.each do |type|
52
- parts.push(Route::RequestMethod.new(type, route.conditions[type])) if route.conditions && route.conditions.key?(type)
53
- end
54
-
55
- current_node = self
56
- until parts.size.zero?
57
- key = parts.shift
58
- target_node = case key
59
- when Route::RequestMethod
60
- current_node.upgrade_lookup if key.value.is_a?(Regexp)
61
- if current_node.exclusive_type == key.type
62
- current_node.lookup[key.value] ||= Node.new(current_node, key)
63
- elsif current_node.lookup.empty?
64
- current_node.exclusive_type = key.type
65
- current_node.lookup[key.value] ||= Node.new(current_node, key)
66
- else
67
- parts.unshift(key)
68
- current_node.lookup[nil] ||= Node.new(current_node, Route::RequestMethod.new(current_node.exclusive_type, nil))
69
- end
70
- else
71
- key.globs_capture_separators = globs_capture_separators if key.is_a?(Route::Variable)
72
-
73
- if !key.is_a?(Route::Variable)
74
- current_node.upgrade_lookup if key.is_a?(Regexp)
75
- current_node.lookup[key] ||= Node.new(current_node, key)
76
- elsif key.regex_matcher
77
- current_node.upgrade_lookup
78
- current_node.lookup[key.regex_matcher] ||= Node.new(current_node, key)
79
- else
80
- current_node.lookup[nil] ||= Node.new(current_node, key)
81
- end
82
- end
83
- current_node = target_node
84
- end
85
- current_node.terminates = path
58
+ set_path_with_destination(path)
59
+ end
60
+ end
61
+
62
+ def delete(route)
63
+ route.paths.each do |path|
64
+ set_path_with_destination(path, nil)
65
+ end
66
+ end
67
+
68
+ def unique_routes(node = self, routes = [])
69
+ routes << node.terminates.route if node.terminates
70
+ node.lookup.values.each do |v|
71
+ unique_routes(v, routes)
86
72
  end
87
- route
73
+ node.greedy_lookup.values.each do |v|
74
+ unique_routes(v, routes)
75
+ end
76
+ routes.uniq!
77
+ routes
88
78
  end
89
79
 
90
- def find(usher, request, path, params = [])
80
+ def find(usher, request, original_path, path, params = [], position = 0)
91
81
  if exclusive_type
92
82
  [lookup[request.send(exclusive_type)], lookup[nil]].each do |n|
93
- if n && (ret = n.find(usher, request, path.dup, params.dup))
83
+ if n && (ret = n.find(usher, request, original_path, path.dup, params.dup, position))
94
84
  return ret
95
85
  end
96
86
  end
97
- elsif path.size.zero? && terminates?
98
- Response.new(terminates, params)
87
+ elsif terminates? && (path.size.zero? || terminates.route.partial_match?)
88
+ if terminates.route.partial_match?
89
+ Response.new(terminates, params, original_path[position, original_path.size], original_path[0, position])
90
+ else
91
+ Response.new(terminates, params, nil, original_path)
92
+ end
93
+
94
+ elsif !path.size.zero? && (greedy? && (match_with_result_output = greedy_lookup.match_with_result(whole_path = original_path[position, original_path.size])))
95
+ next_path, matched_part = match_with_result_output
96
+ position += matched_part.size
97
+ params << [next_path.value.name, whole_path.slice!(0, matched_part.size)]
98
+ next_path.find(usher, request, original_path, whole_path.size.zero? ? whole_path : usher.splitter.url_split(whole_path), params, position)
99
99
  elsif !path.size.zero? && (next_part = lookup[part = path.shift] || lookup[nil])
100
+ position += part.size
100
101
  case next_part.value
101
- when Route::Variable
102
- case next_part.value.type
103
- when :*
104
- params << [next_part.value.name, []] unless params.last && params.last.first == next_part.value.name
105
- loop do
106
- if (next_part.value.look_ahead === part || (!usher.splitter.delimiter_chars.include?(part[0]) && next_part.value.regex_matcher && !next_part.value.regex_matcher.match(part)))
107
- path.unshift(part)
108
- path.unshift(next_part.parent.value) if usher.splitter.delimiter_chars.include?(next_part.parent.value[0])
109
- break
110
- elsif next_part.value.globs_capture_separators
111
- params.last.last << part
112
- elsif !usher.splitter.delimiter_chars.include?(part[0])
113
- next_part.value.valid!(part)
114
- params.last.last << part
115
- end
116
- if path.size.zero?
117
- break
118
- else
119
- part = path.shift
102
+ when Route::Variable::Glob
103
+ params << [next_part.value.name, []] unless params.last && params.last.first == next_part.value.name
104
+ loop do
105
+ if (next_part.value.look_ahead === part || (!usher.delimiter_chars.include?(part[0]) && next_part.value.regex_matcher && !next_part.value.regex_matcher.match(part)))
106
+ path.unshift(part)
107
+ position -= part.size
108
+ if usher.delimiter_chars.include?(next_part.parent.value[0])
109
+ path.unshift(next_part.parent.value)
110
+ position -= next_part.parent.value.size
120
111
  end
112
+ break
113
+ elsif !usher.delimiter_chars.include?(part[0])
114
+ next_part.value.valid!(part)
115
+ params.last.last << part
121
116
  end
122
- when :':'
123
- var = next_part.value
124
- var.valid!(part)
125
- params << [var.name, part]
126
- until (var.look_ahead === path.first) || path.empty?
127
- params.last.last << path.shift
117
+ if path.size.zero?
118
+ break
119
+ else
120
+ part = path.shift
128
121
  end
129
122
  end
123
+ when Route::Variable::Single
124
+ var = next_part.value
125
+ var.valid!(part)
126
+ params << [var.name, part]
127
+ until (var.look_ahead === path.first) || path.empty?
128
+ next_path_part = path.shift
129
+ position += next_path_part.size
130
+ params.last.last << next_path_part
131
+ end
130
132
  end
131
- next_part.find(usher, request, path, params)
133
+ next_part.find(usher, request, original_path, path, params, position)
132
134
  else
133
135
  nil
134
136
  end
135
137
  end
136
138
 
139
+ private
140
+ def set_path_with_destination(path, destination = path)
141
+ parts = path.parts.dup
142
+ request_methods.each do |type|
143
+ parts.push(Route::RequestMethod.new(type, path.route.conditions[type])) if path.route.conditions && path.route.conditions.key?(type)
144
+ end
145
+
146
+ current_node = self
147
+ until parts.size.zero?
148
+ key = parts.shift
149
+ target_node = case key
150
+ when Route::RequestMethod
151
+ current_node.upgrade_lookup if key.value.is_a?(Regexp)
152
+ if current_node.exclusive_type == key.type
153
+ current_node.lookup[key.value] ||= Node.new(current_node, key)
154
+ elsif current_node.lookup.empty?
155
+ current_node.exclusive_type = key.type
156
+ current_node.lookup[key.value] ||= Node.new(current_node, key)
157
+ else
158
+ parts.unshift(key)
159
+ current_node.lookup[nil] ||= Node.new(current_node, Route::RequestMethod.new(current_node.exclusive_type, nil))
160
+ end
161
+ when Route::Variable
162
+ upgrade_method, lookup_method = case key
163
+ when Route::Variable::Greedy
164
+ [:upgrade_greedy_lookup, :greedy_lookup]
165
+ else
166
+ [:upgrade_lookup, :lookup]
167
+ end
168
+
169
+ if key.regex_matcher
170
+ current_node.send(upgrade_method)
171
+ current_node.send(lookup_method)[key.regex_matcher] ||= Node.new(current_node, key)
172
+ else
173
+ current_node.send(lookup_method)[nil] ||= Node.new(current_node, key)
174
+ end
175
+ else
176
+ current_node.upgrade_lookup if key.is_a?(Regexp)
177
+ current_node.lookup[key] ||= Node.new(current_node, key)
178
+ end
179
+ current_node = target_node
180
+ end
181
+ current_node.terminates = destination
182
+ end
183
+
184
+
137
185
  end
138
186
  end
data/lib/usher/route.rb CHANGED
@@ -1,20 +1,20 @@
1
1
  require File.join(File.dirname(__FILE__), 'route', 'path')
2
+ require File.join(File.dirname(__FILE__), 'route', 'util')
2
3
  require File.join(File.dirname(__FILE__), 'route', 'variable')
3
4
  require File.join(File.dirname(__FILE__), 'route', 'request_method')
4
5
 
5
6
  class Usher
6
7
  class Route
7
- attr_reader :paths, :original_path, :requirements, :conditions, :destination, :named, :generate_with
8
+ attr_reader :paths, :requirements, :conditions,
9
+ :destination, :named, :generate_with,
10
+ :default_values, :match_partially
11
+ attr_accessor :parent_route
8
12
 
9
13
  GenerateWith = Struct.new(:scheme, :port, :host)
10
14
 
11
- def initialize(original_path, router, conditions, requirements, default_values, generate_with) # :nodoc:
12
- @original_path = original_path
13
- @router = router
14
- @requirements = requirements
15
- @conditions = conditions
16
- @default_values = default_values
17
- @paths = @router.splitter.split(@original_path, @requirements, @default_values).collect {|path| Path.new(self, path)}
15
+ def initialize(parsed_paths, router, conditions, requirements, default_values, generate_with, match_partially)
16
+ @paths = parsed_paths.collect {|path| Path.new(self, path)}
17
+ @router, @requirements, @conditions, @default_values, @match_partially = router, requirements, conditions, default_values, match_partially
18
18
  @generate_with = GenerateWith.new(generate_with[:scheme], generate_with[:port], generate_with[:host]) if generate_with
19
19
  end
20
20
 
@@ -26,11 +26,27 @@ class Usher
26
26
  @grapher
27
27
  end
28
28
 
29
+ def dup
30
+ result = super
31
+ result.grapher = nil
32
+ result
33
+ end
34
+
29
35
  def find_matching_path(params)
30
- @paths.size == 1 ? @paths.first : grapher.find_matching_path(params)
36
+ if params.nil? || params.empty?
37
+ matching_path = @paths.first
38
+ else
39
+ matching_path = @paths.size == 1 ? @paths.first : grapher.find_matching_path(params)
40
+ end
41
+
42
+ if parent_route
43
+ matching_path = parent_route.find_matching_path(params).merge(matching_path)
44
+ matching_path.route = self
45
+ end
46
+
47
+ matching_path
31
48
  end
32
49
 
33
-
34
50
  # Sets +options+ on a route. Returns +self+.
35
51
  #
36
52
  # Request = Struct.new(:path)
@@ -39,7 +55,13 @@ class Usher
39
55
  # route.to(:controller => 'testing', :action => 'index')
40
56
  # set.recognize(Request.new('/test')).first.params => {:controller => 'testing', :action => 'index'}
41
57
  def to(options = nil, &block)
42
- @destination = (block_given? ? block : options)
58
+ raise "cannot set destintaion as block and argument" if block_given? && options
59
+ @destination = if block_given?
60
+ block
61
+ else
62
+ options.parent_route = self if options.respond_to?(:parent_route=)
63
+ options
64
+ end
43
65
  self
44
66
  end
45
67
 
@@ -53,6 +75,18 @@ class Usher
53
75
  @router.name(name, self)
54
76
  self
55
77
  end
78
+
79
+ def match_partially!
80
+ @match_partially = true
81
+ self
82
+ end
83
+
84
+ def partial_match?
85
+ @match_partially
86
+ end
87
+
88
+ private
89
+ attr_writer :grapher
56
90
 
57
91
  end
58
92
  end