joshbuddy-usher 0.5.4 → 0.5.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,44 @@
1
+ class Usher
2
+ module Interface
3
+ class TextInterface
4
+
5
+ def initialize(&blk)
6
+ @usher = Usher.new(:delimiters => [' '], :generator => Usher::Util::Generators::Generic.new)
7
+ instance_eval(&blk) if blk
8
+ end
9
+
10
+ def generate(name, params = nil)
11
+ @usher.generator.generate(name, params)
12
+ end
13
+
14
+ def on(text, name = nil, &blk)
15
+ r = @usher.add_route(text).to(:block => blk, :arg_type => :array)
16
+ r.name(name) if name
17
+ end
18
+
19
+ def unrecognized(&blk)
20
+ @unrecognize_block = blk
21
+ end
22
+
23
+ def on_with_hash(text, name = nil, &blk)
24
+ r = @usher.add_route(text).to(:block => blk, :arg_type => :hash)
25
+ r.name(name) if name
26
+ end
27
+
28
+ def run(text)
29
+ response = @usher.recognize_path(text.strip)
30
+ if response
31
+ case response.path.route.destination[:arg_type]
32
+ when :hash
33
+ response.path.route.destination[:block].call(response.params.inject({}){|h,(k,v)| h[k]=v; h })
34
+ when :array
35
+ response.path.route.destination[:block].call(*response.params.collect{|p| p.last})
36
+ end
37
+ else
38
+ @unrecognize_block ? @unrecognize_block.call(text) : nil
39
+ end
40
+ end
41
+
42
+ end
43
+ end
44
+ end
data/lib/usher/node.rb CHANGED
@@ -4,7 +4,11 @@ class Usher
4
4
 
5
5
  class Node
6
6
 
7
- Response = Struct.new(:path, :params, :remaining_path, :matched_path)
7
+ class Response < Struct.new(:path, :params, :remaining_path, :matched_path)
8
+ def partial_match?
9
+ !remaining_path.nil?
10
+ end
11
+ end
8
12
 
9
13
  attr_reader :normal, :greedy, :request
10
14
  attr_accessor :terminates, :request_method_type, :parent, :value, :request_methods
@@ -108,15 +112,7 @@ class Usher
108
112
  end
109
113
 
110
114
  def find(usher, request_object, original_path, path, params = [], position = 0)
111
- if request_method_type
112
- if (specific_node = request[request_object.send(request_method_type)]) && (ret = specific_node.find(usher, request_object, original_path, path.dup, params.dup, position))
113
- ret
114
- elsif (general_node = request[nil]) && (ret = general_node.find(usher, request_object, original_path, path.dup, params.dup, position))
115
- ret
116
- else
117
- nil
118
- end
119
- elsif terminates? && (path.empty? || terminates.route.partial_match?)
115
+ if terminates? && (path.empty? || terminates.route.partial_match?)
120
116
  terminates.route.partial_match? ?
121
117
  Response.new(terminates, params, original_path[position, original_path.size], original_path[0, position]) :
122
118
  Response.new(terminates, params, nil, original_path)
@@ -130,7 +126,7 @@ class Usher
130
126
  case next_part.value
131
127
  when Route::Variable::Glob
132
128
  params << [next_part.value.name, []] unless params.last && params.last.first == next_part.value.name
133
- loop do
129
+ while true
134
130
  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)))
135
131
  path.unshift(part)
136
132
  position -= part.size
@@ -153,13 +149,21 @@ class Usher
153
149
  var = next_part.value
154
150
  var.valid!(part)
155
151
  params << [var.name, part]
156
- until (var.look_ahead === path.first) || path.empty?
152
+ until path.empty? || (var.look_ahead === path.first)
157
153
  next_path_part = path.shift
158
154
  position += next_path_part.size
159
155
  params.last.last << next_path_part
160
- end
156
+ end if var.look_ahead && usher.delimiter_chars.size > 1
161
157
  end
162
158
  next_part.find(usher, request_object, original_path, path, params, position)
159
+ elsif request_method_type
160
+ if (specific_node = request[request_object.send(request_method_type)]) && (ret = specific_node.find(usher, request_object, original_path, path.dup, params.dup, position))
161
+ ret
162
+ elsif (general_node = request[nil]) && (ret = general_node.find(usher, request_object, original_path, path.dup, params.dup, position))
163
+ ret
164
+ else
165
+ nil
166
+ end
163
167
  else
164
168
  nil
165
169
  end
data/lib/usher/route.rb CHANGED
@@ -8,16 +8,16 @@ class Usher
8
8
  attr_reader :paths, :requirements, :conditions,
9
9
  :destination, :named, :generate_with,
10
10
  :default_values, :match_partially
11
- attr_accessor :parent_route
12
-
11
+ attr_accessor :parent_route, :router
12
+
13
13
  GenerateWith = Struct.new(:scheme, :port, :host)
14
-
15
- def initialize(parsed_paths, router, conditions, requirements, default_values, generate_with, match_partially)
14
+
15
+ def initialize(parsed_paths, router, conditions, requirements, default_values, generate_with, match_partially)
16
16
  @paths = parsed_paths.collect {|path| Path.new(self, path)}
17
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
+
21
21
  def grapher
22
22
  unless @grapher
23
23
  @grapher = Grapher.new
@@ -28,7 +28,9 @@ class Usher
28
28
 
29
29
  def dup
30
30
  result = super
31
- result.grapher = nil
31
+ result.instance_eval do
32
+ @grapher = nil
33
+ end
32
34
  result
33
35
  end
34
36
 
@@ -38,17 +40,17 @@ class Usher
38
40
  else
39
41
  matching_path = @paths.size == 1 ? @paths.first : grapher.find_matching_path(params)
40
42
  end
41
-
43
+
42
44
  if parent_route
43
45
  matching_path = parent_route.find_matching_path(params).merge(matching_path)
44
46
  matching_path.route = self
45
47
  end
46
-
48
+
47
49
  matching_path
48
50
  end
49
-
51
+
50
52
  # Sets +options+ on a route. Returns +self+.
51
- #
53
+ #
52
54
  # Request = Struct.new(:path)
53
55
  # set = Usher.new
54
56
  # route = set.add_route('/test')
@@ -66,7 +68,7 @@ class Usher
66
68
  end
67
69
 
68
70
  # Sets route as referenceable from +name+. Returns +self+.
69
- #
71
+ #
70
72
  # set = Usher.new
71
73
  # route = set.add_route('/test').name(:route)
72
74
  # set.generate_url(:route) => '/test'
@@ -75,16 +77,16 @@ class Usher
75
77
  @router.name(name, self)
76
78
  self
77
79
  end
78
-
80
+
79
81
  def match_partially!
80
82
  @match_partially = true
81
83
  self
82
84
  end
83
-
85
+
84
86
  def partial_match?
85
87
  @match_partially
86
88
  end
87
-
89
+
88
90
  private
89
91
  attr_writer :grapher
90
92
 
@@ -1,35 +1,46 @@
1
- require 'rack'
1
+ class Usher
2
+ module Util
3
+ class Generators
2
4
 
3
- unless Rack::Utils.respond_to?(:uri_escape)
4
- module Rack
5
+ class Generic
5
6
 
6
- module Utils
7
+ attr_accessor :usher
7
8
 
8
- def uri_escape(s)
9
- s.to_s.gsub(/([^:\/?\[\]\-_~\.!\$&'\(\)\*\+,;=@a-zA-Z0-9]+)/n) {
10
- '%'<<$1.unpack('H2'*$1.size).join('%').upcase
11
- }.tr(' ', '+')
12
- end
13
- module_function :uri_escape
9
+ def generate(name, params)
10
+ generate_path_for_base_params(@usher.named_routes[name].find_matching_path(params), params)
11
+ end
12
+
13
+ def generate_path_for_base_params(path, params)
14
+ raise UnrecognizedException.new unless path
15
+
16
+ result = ''
17
+ path.parts.each do |part|
18
+ case part
19
+ when Route::Variable::Glob
20
+ value = (params && params.delete(part.name)) || part.default_value || raise(MissingParameterException.new)
21
+ value.each_with_index do |current_value, index|
22
+ part.valid!(current_value)
23
+ result << current_value.to_s
24
+ result << usher.delimiters.first if index != value.size - 1
25
+ end
26
+ when Route::Variable
27
+ value = (params && params.delete(part.name)) || part.default_value || raise(MissingParameterException.new)
28
+ part.valid!(value)
29
+ result << value.to_s
30
+ else
31
+ result << part
32
+ end
33
+ end
34
+ result
35
+ end
14
36
 
15
- def uri_unescape(s)
16
- gsub(/((?:%[0-9a-fA-F]{2})+)/n){
17
- [$1.delete('%')].pack('H*')
18
- }
19
37
  end
20
- module_function :uri_unescape
21
38
 
22
- end
23
- end
24
- end
39
+ class URL < Generic
25
40
 
26
- class Usher
27
- module Util
28
- class Generators
29
-
30
- class URL
31
-
32
- attr_accessor :usher
41
+ def initialize
42
+ require File.join(File.dirname(__FILE__), 'rack-mixins')
43
+ end
33
44
 
34
45
  def generate_full(routing_lookup, request, params = nil)
35
46
  path = path_for_routing_lookup(routing_lookup, params)
@@ -37,10 +48,75 @@ class Usher
37
48
  result << generate_path(path, params)
38
49
  end
39
50
 
51
+ # Generates a completed URL based on a +route+ or set of optional +params+
52
+ #
53
+ # set = Usher.new
54
+ # route = set.add_named_route(:test_route, '/:controller/:action')
55
+ # set.generator.generate(nil, {:controller => 'c', :action => 'a'}) == '/c/a' => true
56
+ # set.generator.generate(:test_route, {:controller => 'c', :action => 'a'}) == '/c/a' => true
57
+ # set.generator.generate(route.primary_path, {:controller => 'c', :action => 'a'}) == '/c/a' => true
40
58
  def generate(routing_lookup, params = nil)
41
59
  generate_path(path_for_routing_lookup(routing_lookup, params), params)
42
60
  end
43
61
 
62
+ def generate_path(path, params = nil)
63
+ params = Array(params) if params.is_a?(String)
64
+ if params.is_a?(Array)
65
+ given_size = params.size
66
+ extra_params = params.last.is_a?(Hash) ? params.pop : nil
67
+ params = Hash[*path.dynamic_parts.inject([]){|a, dynamic_part| a.concat([dynamic_part.name, params.shift || raise(MissingParameterException.new("got #{given_size}, expected #{path.dynamic_parts.size} parameters"))]); a}]
68
+ params.merge!(extra_params) if extra_params
69
+ end
70
+
71
+ result = Rack::Utils.uri_escape(generate_path_for_base_params(path, params))
72
+ unless params.nil? || params.empty?
73
+ extra_params = generate_extra_params(params, result[??])
74
+ result << extra_params
75
+ end
76
+ result
77
+ end
78
+
79
+ def generation_module
80
+ build_module!
81
+ @generation_module
82
+ end
83
+
84
+ def build_module!
85
+ unless @generation_module
86
+ @generation_module = Module.new
87
+ @generation_module.module_eval <<-END_EVAL
88
+ @@generator = nil
89
+ def self.generator=(generator)
90
+ @@generator = generator
91
+ end
92
+ END_EVAL
93
+ @generation_module.generator = self
94
+
95
+ @generation_module.module_eval <<-END_EVAL
96
+ def respond_to?(method_name)
97
+ if match = Regexp.new('^(.*?)_(path|url)$').match(method_name.to_s)
98
+ @@generator.usher.named_routes.key?(match.group(1))
99
+ else
100
+ super
101
+ end
102
+ end
103
+ END_EVAL
104
+
105
+
106
+ usher.named_routes.each do |name, route|
107
+ @generation_module.module_eval <<-END_EVAL
108
+ def #{name}_url(name, request, params = nil)
109
+ @@generator.generate_full(name, request, options)
110
+ end
111
+
112
+ def #{name}_path(name, params = nil)
113
+ @@generator.generate(name, options)
114
+ end
115
+ END_EVAL
116
+ end
117
+ end
118
+ end
119
+
44
120
  def generate_start(path, request)
45
121
  result = (path.route.generate_with && path.route.generate_with.scheme || request.scheme).dup
46
122
  result << '://'
@@ -53,77 +129,42 @@ class Usher
53
129
  end
54
130
  result
55
131
  end
56
-
132
+
57
133
  def path_for_routing_lookup(routing_lookup, params = {})
58
134
  path = case routing_lookup
59
135
  when Symbol
60
- route = @usher.named_routes[routing_lookup]
136
+ route = @usher.named_routes[routing_lookup]
137
+ raise UnrecognizedException unless route
61
138
  route.find_matching_path(params || {})
62
139
  when Route
63
- routing_lookup.find_matching_path(params || {})
140
+ routing_lookup.find_matching_path(params)
64
141
  when nil
65
- params.is_a?(Hash) ? @usher.path_for_options(params) : raise
142
+ params.is_a?(Hash) ? usher.path_for_options(params) : raise
66
143
  when Route::Path
67
144
  routing_lookup
68
145
  end
69
146
  end
70
-
71
- # Generates a completed URL based on a +route+ or set of optional +params+
72
- #
73
- # set = Usher.new
74
- # route = set.add_named_route(:test_route, '/:controller/:action')
75
- # set.generate_url(nil, {:controller => 'c', :action => 'a'}) == '/c/a' => true
76
- # set.generate_url(:test_route, {:controller => 'c', :action => 'a'}) == '/c/a' => true
77
- # set.generate_url(route.primary_path, {:controller => 'c', :action => 'a'}) == '/c/a' => true
78
- def generate_path(path, params = nil)
79
- raise UnrecognizedException.new unless path
80
147
 
81
- params = Array(params) if params.is_a?(String)
82
- if params.is_a?(Array)
83
- given_size = params.size
84
- extra_params = params.last.is_a?(Hash) ? params.pop : nil
85
- params = Hash[*path.dynamic_parts.inject([]){|a, dynamic_part| a.concat([dynamic_part.name, params.shift || raise(MissingParameterException.new("got #{given_size}, expected #{path.dynamic_parts.size} parameters"))]); a}]
86
- params.merge!(extra_params) if extra_params
87
- end
88
148
 
89
- result = ''
90
- path.parts.each do |part|
91
- case part
92
- when Route::Variable::Glob
93
- value = (params && params.delete(part.name)) || part.default_value || raise(MissingParameterException.new)
94
- value.each_with_index do |current_value, index|
95
- part.valid!(current_value)
96
- result << current_value.to_s
97
- result << '/' if index != value.size - 1
98
- end
99
- when Route::Variable
100
- value = (params && params.delete(part.name)) || part.default_value || raise(MissingParameterException.new)
101
- part.valid!(value)
102
- result << value.to_s
103
- else
104
- result << part
105
- end
106
- end
107
- result = Rack::Utils.uri_escape(result)
149
+ def generate_extra_params(params, has_question_mark)
150
+ extra_params_result = ''
108
151
 
109
- unless params.nil? || params.empty?
110
- has_query = result[??]
111
- params.each do |k,v|
112
- case v
113
- when Array
114
- v.each do |v_part|
115
- result << (has_query ? '&' : has_query = true && '?') << Rack::Utils.escape("#{k.to_s}[]") << '=' << Rack::Utils.escape(v_part.to_s)
116
- end
117
- else
118
- result << (has_query ? '&' : has_query = true && '?') << Rack::Utils.escape(k.to_s) << '=' << Rack::Utils.escape(v.to_s)
152
+ params.each do |k,v|
153
+ case v
154
+ when Array
155
+ v.each do |v_part|
156
+ extra_params_result << (has_question_mark ? '&' : has_question_mark = true && '?') << Rack::Utils.escape("#{k.to_s}[]") << '=' << Rack::Utils.escape(v_part.to_s)
119
157
  end
158
+ else
159
+ extra_params_result << (has_question_mark ? '&' : has_question_mark = true && '?') << Rack::Utils.escape(k.to_s) << '=' << Rack::Utils.escape(v.to_s)
120
160
  end
121
161
  end
122
- result
162
+ extra_params_result
123
163
  end
124
-
164
+
125
165
  end
126
-
166
+
127
167
  end
128
168
  end
129
- end
169
+ end
170
+
@@ -0,0 +1,24 @@
1
+ require 'rack'
2
+
3
+ unless Rack::Utils.respond_to?(:uri_escape)
4
+ module Rack
5
+
6
+ module Utils
7
+
8
+ def uri_escape(s)
9
+ s.to_s.gsub(/([^:\/?\[\]\-_~\.!\$&'\(\)\*\+,;=@a-zA-Z0-9]+)/n) {
10
+ '%'<<$1.unpack('H2'*$1.size).join('%').upcase
11
+ }.tr(' ', '+')
12
+ end
13
+ module_function :uri_escape
14
+
15
+ def uri_unescape(s)
16
+ gsub(/((?:%[0-9a-fA-F]{2})+)/n){
17
+ [$1.delete('%')].pack('H*')
18
+ }
19
+ end
20
+ module_function :uri_unescape
21
+
22
+ end
23
+ end
24
+ end