usher 0.5.10 → 0.5.11

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.
data/VERSION.yml CHANGED
@@ -1,4 +1,5 @@
1
1
  ---
2
- :patch: 10
2
+ :build:
3
+ :patch: 11
3
4
  :major: 0
4
5
  :minor: 5
data/lib/usher.rb CHANGED
@@ -5,10 +5,12 @@ require File.join(File.dirname(__FILE__), 'usher', 'interface')
5
5
  require File.join(File.dirname(__FILE__), 'usher', 'splitter')
6
6
  require File.join(File.dirname(__FILE__), 'usher', 'exceptions')
7
7
  require File.join(File.dirname(__FILE__), 'usher', 'util')
8
+ require File.join(File.dirname(__FILE__), 'usher', 'spinoffs', 'strscan_additions')
9
+ require File.join(File.dirname(__FILE__), 'usher', 'delimiters')
8
10
 
9
11
  class Usher
10
12
  attr_reader :root, :named_routes, :routes, :splitter,
11
- :delimiters, :delimiter_chars, :delimiters_regex,
13
+ :delimiters, :delimiters_regex,
12
14
  :parent_route, :generator, :grapher
13
15
 
14
16
  # Returns whether the route set is empty
@@ -50,9 +52,10 @@ class Usher
50
52
  # <tt>:request_methods</tt>: Array of Symbols. (default <tt>[:protocol, :domain, :port, :query_string, :remote_ip, :user_agent, :referer, :method, :subdomains]</tt>)
51
53
  # Array of methods called against the request object for the purposes of matching route requirements.
52
54
  def initialize(options = nil)
53
- self.generator = options && options.delete(:generator)
54
- self.delimiters = options && options.delete(:delimiters) || ['/', '.']
55
- self.valid_regex = options && options.delete(:valid_regex) || '[0-9A-Za-z\$\-_\+!\*\',]+'
55
+ self.generator = options && options.delete(:generator)
56
+ delimiters_array = options && options.delete(:delimiters) || ['/', '.']
57
+ self.delimiters = Delimiters.new(delimiters_array)
58
+ self.valid_regex = options && options.delete(:valid_regex) || '[0-9A-Za-z\$\-_\+!\*\',]+'
56
59
  self.request_methods = options && options.delete(:request_methods)
57
60
  reset!
58
61
  end
@@ -258,14 +261,13 @@ class Usher
258
261
 
259
262
  def delimiters=(delimiters)
260
263
  @delimiters = delimiters
261
- @delimiter_chars = @delimiters.collect{|d| d[0]}
262
264
  @delimiters_regex = @delimiters.collect{|d| Regexp.quote(d)} * '|'
263
265
  @delimiters
264
266
  end
265
267
 
266
268
  def valid_regex=(valid_regex)
267
269
  @valid_regex = valid_regex
268
- @splitter = Splitter.for_delimiters(self, @valid_regex)
270
+ @splitter = Splitter.for_delimiters(self.delimiters)
269
271
  @valid_regex
270
272
  end
271
273
 
@@ -0,0 +1,22 @@
1
+ class Delimiters < Array
2
+ # TODO: caching
3
+
4
+ def unescaped
5
+ self.map do |delimiter|
6
+ (delimiter[0] == ?\\) ?
7
+ delimiter[1..-1] :
8
+ delimiter
9
+ end
10
+ end
11
+
12
+ def first_in(array)
13
+ # TODO: should we optimize this O(n*m)? hash or modified or KNP or at leaset sort + b-search. But they are so short
14
+
15
+ array.each do |element|
16
+ return element if self.unescaped.any? { |delimiter| delimiter == element }
17
+ end
18
+ nil
19
+ end
20
+
21
+ # TODO: Delimiters#regex and so on
22
+ end
@@ -71,16 +71,19 @@ class Usher
71
71
  "#{request.path_parameters[:controller].camelize}Controller".constantize
72
72
  end
73
73
 
74
- def reset!
75
- @router = Usher.new(:generator => Usher::Util::Generators::URL.new, :request_methods => [:protocol, :domain, :port, :query_string, :remote_ip, :user_agent, :referer, :method, :subdomains])
74
+ def reset!(options={})
75
+ options[:generator] = options[:generator] || Usher::Util::Generators::URL.new
76
+ options[:request_methods] = options[:request_methods] || [:protocol, :domain, :port, :query_string, :remote_ip, :user_agent, :referer, :method, :subdomains]
77
+
78
+ @router = Usher.new(options)
76
79
  @configuration_files = []
77
80
  @module ||= Module.new
78
81
  @controller_route_added = false
79
82
  @controller_action_route_added = false
80
83
  end
81
84
 
82
- def draw
83
- reset!
85
+ def draw(options={})
86
+ reset!(options)
84
87
  yield ActionController::Routing::RouteSet::Mapper.new(self)
85
88
  install_helpers
86
89
  end
data/lib/usher/node.rb CHANGED
@@ -61,7 +61,7 @@ class Usher
61
61
  end
62
62
 
63
63
  def terminates?
64
- @terminates
64
+ @terminates && @terminates.route.recognizable?
65
65
  end
66
66
 
67
67
  def pp
@@ -128,15 +128,15 @@ class Usher
128
128
  when Route::Variable::Glob
129
129
  params << [next_part.value.name, []] unless params.last && params.last.first == next_part.value.name
130
130
  while true
131
- 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)))
131
+ if (next_part.value.look_ahead === part || (!usher.delimiters.unescaped.include?(part) && next_part.value.regex_matcher && !next_part.value.regex_matcher.match(part)))
132
132
  path.unshift(part)
133
133
  position -= part.size
134
- if usher.delimiter_chars.include?(next_part.parent.value[0])
134
+ if usher.delimiters.unescaped.include?(next_part.parent.value)
135
135
  path.unshift(next_part.parent.value)
136
136
  position -= next_part.parent.value.size
137
137
  end
138
138
  break
139
- elsif !usher.delimiter_chars.include?(part[0])
139
+ elsif !usher.delimiters.unescaped.include?(part)
140
140
  next_part.value.valid!(part)
141
141
  params.last.last << part
142
142
  end
@@ -154,7 +154,7 @@ class Usher
154
154
  next_path_part = path.shift
155
155
  position += next_path_part.size
156
156
  params.last.last << next_path_part
157
- end if var.look_ahead && usher.delimiter_chars.size > 1
157
+ end if var.look_ahead && usher.delimiters.size > 1
158
158
  end
159
159
  next_part.find(usher, request_object, original_path, path, params, position)
160
160
  elsif request_method_type
data/lib/usher/route.rb CHANGED
@@ -5,16 +5,15 @@ require File.join(File.dirname(__FILE__), 'route', 'request_method')
5
5
 
6
6
  class Usher
7
7
  class Route
8
- attr_reader :paths, :requirements, :conditions,
9
- :destination, :named, :generate_with,
10
- :default_values, :match_partially
11
- attr_accessor :parent_route, :router
8
+ attr_reader :paths, :requirements, :conditions, :named, :generate_with, :default_values, :match_partially, :destination
9
+ attr_accessor :parent_route, :router, :recognizable
12
10
 
13
11
  GenerateWith = Struct.new(:scheme, :port, :host)
14
12
 
15
13
  def initialize(parsed_paths, router, conditions, requirements, default_values, generate_with, match_partially)
16
- @paths = parsed_paths.collect {|path| Path.new(self, path)}
17
14
  @router, @requirements, @conditions, @default_values, @match_partially = router, requirements, conditions, default_values, match_partially
15
+ @recognizable = true
16
+ @paths = parsed_paths.collect {|path| Path.new(self, path)}
18
17
  @generate_with = GenerateWith.new(generate_with[:scheme], generate_with[:port], generate_with[:host]) if generate_with
19
18
  end
20
19
 
@@ -25,7 +24,21 @@ class Usher
25
24
  end
26
25
  @grapher
27
26
  end
27
+
28
+ def unrecognizable!
29
+ self.recognizable = false
30
+ self
31
+ end
28
32
 
33
+ def recognizable!
34
+ self.recognizable = true
35
+ self
36
+ end
37
+
38
+ def recognizable?
39
+ self.recognizable
40
+ end
41
+
29
42
  def dup
30
43
  result = super
31
44
  result.instance_eval do
@@ -0,0 +1,31 @@
1
+ require 'strscan'
2
+
3
+ #TODO: rewrite this in C and commit this addition back to community
4
+ class StringScanner
5
+ # scan_before(pattern)
6
+ #
7
+ #
8
+ # Scans the string until the +pattern+ is matched. As opposed to #scan_until,
9
+ # it does not include a matching pattern into a returning value and sets
10
+ # pointer just _before_ the pattern position.
11
+ # If there is no match, +nil+ is returned.
12
+ #
13
+ # s = StringScanner.new("Fri Dec 12 1975 14:39")
14
+ # s.scan_until(/1/) # -> "Fri Dec "
15
+ # s.scan(/1/) # -> "1"
16
+ # s.scan_until(/1/) # -> nil
17
+ #
18
+ def scan_before(pattern)
19
+ return nil unless self.exist?(pattern)
20
+
21
+ pattern_size = self.matched_size
22
+ result = self.scan_until(pattern)
23
+
24
+ self.pos = self.pos - pattern_size
25
+ (result.length == pattern_size) ?
26
+ (result = '') :
27
+ (result = result[0..(result.length - pattern_size - 1)])
28
+
29
+ result
30
+ end
31
+ end
@@ -1,22 +1,44 @@
1
- require 'strscan'
2
-
3
1
  class Usher
4
2
  class Splitter
5
-
6
- def self.for_delimiters(router, valid_regex)
7
- SplitterInstance.new(Regexp.new("[#{router.delimiters.collect{|d| Regexp.quote(d)}.join}]|[^#{router.delimiters.collect{|d| Regexp.quote(d)}.join}]+"))
3
+
4
+ def self.for_delimiters(delimiters_array)
5
+ delimiters = Delimiters.new(delimiters_array)
6
+ delimiters_array.any?{|d| d.size > 1} ?
7
+ MultiCharacterSplitterInstance.new(delimiters) :
8
+ SingleCharacterSplitterInstance.new(delimiters)
8
9
  end
9
10
 
10
- class SplitterInstance
11
+ class SingleCharacterSplitterInstance
11
12
 
12
- def initialize(url_split_regex)
13
- @url_split_regex = url_split_regex
13
+ def initialize(delimiters)
14
+ @url_split_regex = Regexp.new("[#{delimiters.collect{|d| Regexp.quote(d)}.join}]|[^#{delimiters.collect{|d| Regexp.quote(d)}.join}]+")
14
15
  end
15
16
 
16
17
  def url_split(path)
17
18
  path.scan(@url_split_regex)
18
19
  end
20
+ alias split url_split
19
21
  end
20
22
 
23
+ class MultiCharacterSplitterInstance
24
+
25
+ def initialize(delimiters)
26
+ @delimiters = delimiters
27
+ end
28
+
29
+ def url_split(path)
30
+ split_path = path.split(delimiters_regexp)
31
+ split_path.reject!{|s| s.size.zero? }
32
+ split_path
33
+ end
34
+ alias split url_split
35
+
36
+ protected
37
+
38
+ def delimiters_regexp
39
+ Regexp.new("(#{@delimiters.unescaped.collect{|d| Regexp.quote(d)}.join('|')})")
40
+ end
41
+
42
+ end
21
43
  end
22
44
  end
@@ -59,7 +59,7 @@ class Usher
59
59
  generate_path(path_for_routing_lookup(routing_lookup, params), params)
60
60
  end
61
61
 
62
- def generate_path(path, params = nil)
62
+ def generate_path(path, params = nil, generate_extra = true)
63
63
  params = Array(params) if params.is_a?(String)
64
64
  if params.is_a?(Array)
65
65
  given_size = params.size
@@ -69,7 +69,7 @@ class Usher
69
69
  end
70
70
 
71
71
  result = Rack::Utils.uri_escape(generate_path_for_base_params(path, params))
72
- unless params.nil? || params.empty?
72
+ unless !generate_extra || params.nil? || params.empty?
73
73
  extra_params = generate_extra_params(params, result[??])
74
74
  result << extra_params
75
75
  end
@@ -117,6 +117,16 @@ class Usher
117
117
  end
118
118
  end
119
119
 
120
+ def generate_base_url(params = nil)
121
+ if usher.parent_route
122
+ usher.parent_route.router.generator.generate_path(usher.parent_route.paths.first, params, false)
123
+ elsif params && params.key?(:default)
124
+ params[:default].to_s
125
+ else
126
+ '/'
127
+ end
128
+ end
129
+
120
130
  def generate_start(path, request)
121
131
  result = (path.route.generate_with && path.route.generate_with.scheme || request.scheme).dup
122
132
  result << '://'
@@ -12,10 +12,10 @@ class Usher
12
12
  end
13
13
 
14
14
  class ParserInstance
15
-
16
15
  def initialize(router, split_regex)
17
16
  @router = router
18
17
  @split_regex = split_regex
18
+ @delimiters_regex = Regexp.new(router.delimiters_regex)
19
19
  end
20
20
 
21
21
  def generate_route(unprocessed_path, conditions, requirements, default_values, generate_with)
@@ -48,14 +48,14 @@ class Usher
48
48
  part.look_ahead = nil
49
49
  part.look_ahead_priority = true
50
50
  else
51
- part.look_ahead = path[index + 1, path.size].find{|p| !p.is_a?(Usher::Route::Variable) && !router.delimiter_chars.include?(p[0])} || nil
51
+ part.look_ahead = path[index + 1, path.size].find{|p| !p.is_a?(Usher::Route::Variable) && !router.delimiters.unescaped.include?(p)} || nil
52
52
  end
53
53
  when Usher::Route::Variable
54
54
  if part.look_ahead && !part.look_ahead_priority
55
55
  part.look_ahead = nil
56
56
  part.look_ahead_priority = true
57
57
  else
58
- part.look_ahead = path[index + 1, path.size].find{|p| router.delimiter_chars.include?(p[0])} || router.delimiters.first
58
+ part.look_ahead = router.delimiters.first_in(path[index + 1, path.size]) || router.delimiters.unescaped.first
59
59
  end
60
60
  end
61
61
  end
@@ -73,17 +73,27 @@ class Usher
73
73
 
74
74
  end
75
75
 
76
-
77
76
  def parse_and_expand(path, requirements = nil, default_values = nil)
78
77
  Usher::Route::Util.expand_path(parse(path, requirements, default_values))
79
78
  end
80
79
 
81
80
  def parse(path, requirements = nil, default_values = nil)
82
81
  parts = Usher::Route::Util::Group.new(:all, nil)
83
- ss = StringScanner.new(path)
82
+ scanner = StringScanner.new(path)
83
+
84
84
  current_group = parts
85
- while !ss.eos?
86
- part = ss.scan(@split_regex)
85
+ part = nil
86
+ while !scanner.eos?
87
+ part ?
88
+ (part << scanner.scan(@split_regex)) :
89
+ (part = scanner.scan(@split_regex))
90
+
91
+ if scanner.match?(/\\/) and !scanner.match?(@delimiters_regex)
92
+ scanner.skip(/\\/)
93
+ part << scanner.getch
94
+ next
95
+ end
96
+
87
97
  case part[0]
88
98
  when ?*
89
99
  var_name = part[1, part.size - 1].to_sym
@@ -94,9 +104,9 @@ class Usher
94
104
  when ?{
95
105
  pattern = ''
96
106
  count = 1
97
- variable = ss.scan(/[!:\*]([^,]+),/)
107
+ variable = scanner.scan(/[!:\*]([^,]+),/)
98
108
  until count.zero?
99
- regex_part = ss.scan(/\{|\}|[^\{\}]+/)
109
+ regex_part = scanner.scan(/\{|\}|[^\{\}]+/)
100
110
  case regex_part[0]
101
111
  when ?{
102
112
  count += 1
@@ -137,19 +147,20 @@ class Usher
137
147
  end
138
148
  current_group.parent << Usher::Route::Util::Group.new(:all, current_group.parent)
139
149
  current_group = current_group.parent.last
150
+ when ?\\
151
+ current_group << part[1..-1]
140
152
  else
141
153
  current_group << part
142
154
  end
155
+ part = nil
143
156
  end unless !path || path.empty?
144
157
  parts
145
158
  end
146
159
 
147
- private
148
- attr_reader :router
160
+ private
149
161
 
162
+ attr_reader :router
150
163
  end
151
-
152
-
153
164
  end
154
165
  end
155
166
  end
@@ -8,7 +8,7 @@ unless Rack::Utils.respond_to?(:uri_escape)
8
8
  def uri_escape(s)
9
9
  s.to_s.gsub(/([^:\/?\[\]\-_~\.!\$&'\(\)\*\+,;=@a-zA-Z0-9]+)/n) {
10
10
  '%'<<$1.unpack('H2'*$1.size).join('%').upcase
11
- }.tr(' ', '+')
11
+ }
12
12
  end
13
13
  module_function :uri_escape
14
14
 
@@ -0,0 +1,48 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper"))
2
+ require 'usher'
3
+
4
+ describe Delimiters do
5
+ describe "#unescaped" do
6
+ it "should unescape delimiters correctly" do
7
+ Delimiters.new(['/', '\)', '\\\\']).unescaped.should == ['/', ')', '\\']
8
+ end
9
+ end
10
+
11
+ describe "#first_in" do
12
+ describe "when there is a complex path with a lot of delimiters occurrences" do
13
+ before :each do
14
+ @delimiters = Delimiters.new ['@', '.', '/']
15
+ @paths = ['var', '.', 'var', '/', 'var', '@']
16
+ end
17
+
18
+ it "should find nearest delimiter correctly" do
19
+ @delimiters.first_in(@paths).should == '.'
20
+ @delimiters.first_in(@paths[2..-1]).should == '/'
21
+ @delimiters.first_in(@paths[4..-1]).should == '@'
22
+ end
23
+ end
24
+
25
+ describe "when there are delimiters with escaped charaters" do
26
+ before :each do
27
+ @delimiters = Delimiters.new ['\\(', '\\)']
28
+ @paths = ['var', '(', 'var', ')']
29
+ end
30
+
31
+ it "should find nearest delimiter in unescaped path" do
32
+ @delimiters.first_in(@paths).should == '('
33
+ end
34
+ end
35
+
36
+ describe "when there is no occurence of delimiters in path" do
37
+ before :each do
38
+ @delimiters = Delimiters.new ['-', '/']
39
+ @paths = ['e', '@', 'ma', '.', 'il']
40
+ end
41
+
42
+ it "should return nil" do
43
+ @delimiters.first_in(@paths).should be_nil
44
+ end
45
+ end
46
+
47
+ end
48
+ end
@@ -189,6 +189,33 @@ describe "Usher URL generation" do
189
189
  @route_set4.generator.generate(:nested_simple)
190
190
  end.should raise_error(Usher::MissingParameterException)
191
191
  end
192
+
193
+ describe "generate_base_url" do
194
+ it "should generate a base url for a non nested router" do
195
+ @route_set.generator.generate_base_url.should == "/"
196
+ end
197
+
198
+ it "should generate a base url for a nested router" do
199
+ @route_set2.generator.generate_base_url.should == "/mount_point"
200
+ end
201
+
202
+ it "should generate a base url with parameters" do
203
+ @route_set4.generator.generate_base_url(:bar => "the_bar").should == "/fourth/the_bar"
204
+ end
205
+
206
+ it "should generate a base url with a default route" do
207
+ @route_set.generator.generate_base_url(:default => "/foo").should == "/foo"
208
+ end
209
+
210
+ it "should generate a base url with a default that is not a /" do
211
+ @route_set.generator.generate_base_url(:default => ":").should == ":"
212
+ end
213
+
214
+ it "should generate a base url with a default of a blank string" do
215
+ @route_set.generator.generate_base_url(:default => "").should == ""
216
+ @route_set.generator.generate_base_url(:default => nil).should == ""
217
+ end
218
+ end
192
219
  end
193
220
 
194
221
  describe "dupped generation" do
@@ -23,6 +23,10 @@ describe "Usher route tokenizing" do
23
23
  it "should split on ' ' delimited routes for more complex routes as well" do
24
24
  Usher.new(:delimiters => [' '], :valid_regex => '[0-9A-Za-z\$\-_\+!\*\',]+').parser.parse_and_expand('(test|this) split').should == [['test', ' ', 'split'], ['this', ' ', 'split']]
25
25
  end
26
+
27
+ it "should correctly handle multichar delimiters as well" do
28
+ Usher.new(:delimiters => ['%28', '%29'], :valid_regex => '[0-9A-Za-z\$\-_\+!\*\',]+').parser.parse_and_expand('cheese%28parmesan%29').should == [['cheese', '%28', 'parmesan', '%29']]
29
+ end
26
30
 
27
31
  it "should group optional parts with brackets" do
28
32
  Usher.new(:delimiters => ['/', '.'], :valid_regex => '[0-9A-Za-z\$\-_\+!\*\',]+').parser.parse_and_expand('/test/this(/split)').should == [
@@ -73,4 +77,27 @@ describe "Usher route tokenizing" do
73
77
  parts[1].should == parts[3]
74
78
  end
75
79
 
76
- end
80
+ it "should let me escape reserved characters" do
81
+ Usher.new.parser.parse_and_expand('/my\/thing/is\*lovingyou').should == [["/", "my/thing", "/", "is*lovingyou"]]
82
+ end
83
+
84
+ it "should let me use escaped characters as delimiters" do
85
+ Usher.new(:delimiters => ['/', '\(', '\)']).parser.parse_and_expand('/cheese\(:kind\)').should == [['/', 'cheese', '(', Usher::Route::Variable::Single.new(:kind), ')']]
86
+ end
87
+
88
+ describe "#generate_route" do
89
+ describe "when delimiters contain escaped characters" do
90
+ before :each do
91
+ @parser = Usher.new(:delimiters => ['/', '\(', '\)']).parser
92
+ end
93
+
94
+ it "should correctly generate route with a variable" do
95
+ route = @parser.generate_route('/cheese\(:kind\)', nil, nil, nil, nil)
96
+ variable = route.paths[0].parts[3]
97
+
98
+ variable.should be_kind_of(Usher::Route::Variable::Single)
99
+ variable.look_ahead.should == ')'
100
+ end
101
+ end
102
+ end
103
+ end
@@ -17,6 +17,11 @@ describe "Usher route recognition" do
17
17
 
18
18
  describe 'request conditions' do
19
19
 
20
+ it "should ignore an unrecognized route" do
21
+ target_route = @route_set.add_route('/sample', :controller => 'sample', :action => 'action', :conditions => {:protocol => 'http'}).unrecognizable!
22
+ @route_set.recognize(build_request({:method => 'get', :path => '/sample', :protocol => 'http'})).should be_nil
23
+ end
24
+
20
25
  it "should recognize a specific domain name" do
21
26
  target_route = @route_set.add_route('/sample', :controller => 'sample', :action => 'action', :conditions => {:protocol => 'http'})
22
27
  @route_set.add_route('/sample', :controller => 'sample', :action => 'action2', :conditions => {:protocol => 'https'})
@@ -79,6 +84,35 @@ describe "Usher route recognition" do
79
84
  @route_set.recognize(build_request({:method => 'get', :path => '/sample/html/json/apple'})).params.should == [[:format, ['html', 'json', 'apple']]]
80
85
  end
81
86
 
87
+ it "should recognize variables between multi-char delimiters" do
88
+ @route_set = Usher.new(:delimiters => ['%28', '%29', '/', '.'])
89
+ target_route = @route_set.add_route('/cheese%28:kind%29', :controller => 'sample', :action => 'action')
90
+
91
+ response = @route_set.recognize(build_request({:method => 'get', :path => '/cheese%28parmesan%29'}))
92
+ response.path.route.should == target_route
93
+ response.params.should == [[:kind , 'parmesan']]
94
+ end
95
+
96
+ it "should recognize route with escaped characters as delimiters" do
97
+ @route_set = Usher.new(:delimiters => ['/', '.', '\\(', '\\)'])
98
+
99
+ target_route = @route_set.add_route('/cheese\\(:kind\\)', :controller => 'sample', :action => 'action')
100
+
101
+ response = @route_set.recognize(build_request({:method => 'get', :path => '/cheese(parmesan)'}))
102
+ response.should_not be_nil
103
+ response.path.route.should == target_route
104
+ response.params.should == [[:kind , 'parmesan']]
105
+ end
106
+
107
+ it "should recognize route with consecutive delimiters" do
108
+ @route_set = Usher.new(:delimiters => ['!', '/'])
109
+ target_route = @route_set.add_route('/cheese/!:kind', :controller => 'sample', :action => 'action')
110
+
111
+ response = @route_set.recognize(build_request({:method => 'get', :path => '/cheese/!parmesan'}))
112
+ response.path.route.should == target_route
113
+ response.params.should == [[:kind , 'parmesan']]
114
+ end
115
+
82
116
  it "should recgonize only a glob-style variable" do
83
117
  target_route = @route_set.add_route('/*format')
84
118
  response = @route_set.recognize(build_request({:method => 'get', :path => '/sample/html/json/apple'}))
@@ -94,10 +128,10 @@ describe "Usher route recognition" do
94
128
  end
95
129
 
96
130
  it "should recgonize a two identical regex static parts distinguished by request methods" do
97
- get_route = @route_set.add_route('/{:val,test}', :conditions => {:method => 'get'})
98
- post_route = @route_set.add_route('/{:val,test}', :conditions => {:method => 'post'})
99
- @route_set.recognize(build_request({:method => 'get', :path => '/test'})).path.route.should == get_route
100
- @route_set.recognize(build_request({:method => 'post', :path => '/test'})).path.route.should == post_route
131
+ get_route = @route_set.add_route('/test1/{:val,test}', :conditions => {:method => 'get'})
132
+ post_route = @route_set.add_route('/test1/{:val,test}', :conditions => {:method => 'post'})
133
+ @route_set.recognize(build_request({:method => 'get', :path => '/test1/test'})).path.route.should == get_route
134
+ @route_set.recognize(build_request({:method => 'post', :path => '/test1/test'})).path.route.should == post_route
101
135
  end
102
136
 
103
137
  it "shouldn't accept a nil variable" do
@@ -0,0 +1,40 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper"))
2
+ require "usher"
3
+
4
+ describe Usher::Splitter, "#split" do
5
+ describe "when there are single-character delimiters" do
6
+ it "should split correctly" do
7
+ Usher::Splitter.for_delimiters(['.', '/']).split('/one/two.three/').should == ['/', 'one', '/', 'two', '.', 'three', '/']
8
+ end
9
+ end
10
+
11
+ describe "when there are multi-character delimiters" do
12
+ it "should split correctly" do
13
+ Usher::Splitter.for_delimiters(['/', '%28', '%29']).split('/one%28two%29three/').should == ['/', 'one', '%28', 'two', '%29', 'three', '/']
14
+ end
15
+ end
16
+
17
+ describe "when there is no delimiter in the end" do
18
+ it "should split correctly" do
19
+ Usher::Splitter.for_delimiters(['.', '/']).split('/one/two.three').should == ['/', 'one', '/', 'two', '.', 'three']
20
+ end
21
+ end
22
+
23
+ describe "when there is no delimiter in the beginning" do
24
+ it "should split correctly" do
25
+ Usher::Splitter.for_delimiters(['.', '/']).split('one/two.three/').should == ['one', '/', 'two', '.', 'three', '/']
26
+ end
27
+ end
28
+
29
+ describe "when delimiters are consecutive" do
30
+ it "should split correctly" do
31
+ Usher::Splitter.for_delimiters(['/', '!']).split('/cheese/!parmesan').should == ['/', 'cheese', '/', '!', 'parmesan']
32
+ end
33
+ end
34
+
35
+ describe "when delimiters contain escaped characters" do
36
+ it "should split correctly" do
37
+ Usher::Splitter.for_delimiters(['/', '\(', '\)']).split('/cheese(parmesan)').should == ['/', 'cheese', '(', 'parmesan', ')']
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,39 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper"))
2
+ require "usher"
3
+
4
+ describe StringScanner do
5
+ describe "#scan_before" do
6
+ before :each do
7
+ @scanner = StringScanner.new("/one/two..three")
8
+ end
9
+
10
+ describe "when there is a match" do
11
+ it "should return subsequent string without matching pattern in the end" do
12
+ @scanner.scan_before(/\.\./).should == "/one/two"
13
+ end
14
+
15
+ it "should set pointer right before the matching pattern" do
16
+ @scanner.scan_before(/\.\./)
17
+ @scanner.scan(/\.\./).should == '..'
18
+ end
19
+
20
+ describe "when matching pattern is right at the position" do
21
+ it "should return empty string" do
22
+ @scanner.scan_before(/\//).should == ''
23
+ end
24
+ end
25
+ end
26
+
27
+ describe "when there is no match" do
28
+ it "should return nil" do
29
+ @scanner.scan_before(/bla-bla-bla/).should == nil
30
+ end
31
+
32
+ it "should not move the pointer" do
33
+ @scanner.scan_before(/bla-bla-bla/)
34
+ @scanner.pos.should == 0
35
+ end
36
+ end
37
+ end
38
+ end
39
+
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.5.10
4
+ version: 0.5.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joshua Hull
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-10-11 00:00:00 -04:00
12
+ date: 2009-11-20 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -37,6 +37,7 @@ files:
37
37
  - Rakefile
38
38
  - VERSION.yml
39
39
  - lib/usher.rb
40
+ - lib/usher/delimiters.rb
40
41
  - lib/usher/exceptions.rb
41
42
  - lib/usher/grapher.rb
42
43
  - lib/usher/interface.rb
@@ -56,6 +57,7 @@ files:
56
57
  - lib/usher/route/request_method.rb
57
58
  - lib/usher/route/util.rb
58
59
  - lib/usher/route/variable.rb
60
+ - lib/usher/spinoffs/strscan_additions.rb
59
61
  - lib/usher/splitter.rb
60
62
  - lib/usher/util.rb
61
63
  - lib/usher/util/generate.rb
@@ -63,6 +65,7 @@ files:
63
65
  - lib/usher/util/parser.rb
64
66
  - lib/usher/util/rack-mixins.rb
65
67
  - rails/init.rb
68
+ - spec/private/delimiters_spec.rb
66
69
  - spec/private/email/recognize_spec.rb
67
70
  - spec/private/generate_spec.rb
68
71
  - spec/private/grapher_spec.rb
@@ -80,6 +83,8 @@ files:
80
83
  - spec/private/rails2_3/recognize_spec.rb
81
84
  - spec/private/recognize_spec.rb
82
85
  - spec/private/request_method_spec.rb
86
+ - spec/private/splitter_spec.rb
87
+ - spec/private/string_scanner_spec.rb
83
88
  - spec/spec.opts
84
89
  - spec/spec_helper.rb
85
90
  has_rdoc: true
@@ -111,6 +116,7 @@ signing_key:
111
116
  specification_version: 3
112
117
  summary: A general purpose routing library
113
118
  test_files:
119
+ - spec/private/delimiters_spec.rb
114
120
  - spec/private/email/recognize_spec.rb
115
121
  - spec/private/generate_spec.rb
116
122
  - spec/private/grapher_spec.rb
@@ -128,4 +134,6 @@ test_files:
128
134
  - spec/private/rails2_3/recognize_spec.rb
129
135
  - spec/private/recognize_spec.rb
130
136
  - spec/private/request_method_spec.rb
137
+ - spec/private/splitter_spec.rb
138
+ - spec/private/string_scanner_spec.rb
131
139
  - spec/spec_helper.rb