usher 0.4.8 → 0.5.1

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.
@@ -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