joshbuddy-usher 0.5.4 → 0.5.6

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