joshbuddy-usher 0.4.8 → 0.4.10
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +1 -1
- data/VERSION.yml +2 -2
- data/lib/usher/interface/rack_interface.rb +1 -1
- data/lib/usher/interface/rails2_2_interface.rb +1 -1
- data/lib/usher/interface/rails2_3_interface.rb +1 -1
- data/lib/usher/node.rb +49 -16
- data/lib/usher/route/variable.rb +4 -0
- data/lib/usher/route.rb +3 -4
- data/lib/usher/splitter.rb +4 -141
- data/lib/usher/util/generate.rb +133 -0
- data/lib/usher/util/parser.rb +148 -0
- data/lib/usher/util.rb +6 -0
- data/lib/usher.rb +22 -5
- data/spec/private/generate_spec.rb +1 -1
- data/spec/private/grapher_spec.rb +1 -1
- data/spec/private/parser_spec.rb +75 -0
- data/spec/private/recognize_spec.rb +12 -0
- metadata +8 -6
- data/lib/usher/generate.rb +0 -131
- data/spec/private/split_spec.rb +0 -76
data/Rakefile
CHANGED
@@ -9,7 +9,7 @@ begin
|
|
9
9
|
s.homepage = "http://github.com/joshbuddy/usher"
|
10
10
|
s.authors = ["Joshua Hull"]
|
11
11
|
s.files = FileList["[A-Z]*", "{lib,spec,rails}/**/*"]
|
12
|
-
s.add_dependency 'fuzzyhash', '>=0.0.
|
12
|
+
s.add_dependency 'fuzzyhash', '>=0.0.5'
|
13
13
|
end
|
14
14
|
rescue LoadError
|
15
15
|
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
data/VERSION.yml
CHANGED
@@ -13,7 +13,7 @@ class Usher
|
|
13
13
|
|
14
14
|
def reset!
|
15
15
|
@usher ||= Usher.new
|
16
|
-
@url_generator ||= Usher::Generators::URL.new(@usher)
|
16
|
+
@url_generator ||= Usher::Util::Generators::URL.new(@usher)
|
17
17
|
@module ||= Module.new
|
18
18
|
@module.instance_methods.each do |selector|
|
19
19
|
@module.class_eval { remove_method selector }
|
data/lib/usher/node.rb
CHANGED
@@ -6,13 +6,14 @@ class Usher
|
|
6
6
|
|
7
7
|
Response = Struct.new(:path, :params)
|
8
8
|
|
9
|
-
attr_reader :lookup
|
9
|
+
attr_reader :lookup, :greedy_lookup
|
10
10
|
attr_accessor :terminates, :exclusive_type, :parent, :value, :request_methods, :globs_capture_separators
|
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,10 +21,18 @@ 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
29
|
@depth ||= @parent && @parent.is_a?(Node) ? @parent.depth + 1 : 0
|
25
30
|
end
|
26
31
|
|
32
|
+
def greedy?
|
33
|
+
!@greedy_lookup.empty?
|
34
|
+
end
|
35
|
+
|
27
36
|
def self.root(route_set, request_methods, globs_capture_separators)
|
28
37
|
root = self.new(route_set, nil)
|
29
38
|
root.request_methods = request_methods
|
@@ -69,16 +78,29 @@ class Usher
|
|
69
78
|
end
|
70
79
|
else
|
71
80
|
key.globs_capture_separators = globs_capture_separators if key.is_a?(Route::Variable)
|
72
|
-
|
73
|
-
|
81
|
+
|
82
|
+
case key
|
83
|
+
when Route::Variable
|
84
|
+
if key.greedy?
|
85
|
+
if key.regex_matcher
|
86
|
+
current_node.upgrade_greedy_lookup
|
87
|
+
current_node.greedy_lookup[key.regex_matcher] ||= Node.new(current_node, key)
|
88
|
+
else
|
89
|
+
current_node.greedy_lookup[nil] ||= Node.new(current_node, key)
|
90
|
+
end
|
91
|
+
else
|
92
|
+
if key.regex_matcher
|
93
|
+
current_node.upgrade_lookup
|
94
|
+
current_node.lookup[key.regex_matcher] ||= Node.new(current_node, key)
|
95
|
+
else
|
96
|
+
current_node.lookup[nil] ||= Node.new(current_node, key)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
else
|
74
100
|
current_node.upgrade_lookup if key.is_a?(Regexp)
|
75
101
|
current_node.lookup[key] ||= Node.new(current_node, key)
|
76
|
-
|
77
|
-
|
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
|
102
|
+
end
|
103
|
+
|
82
104
|
end
|
83
105
|
current_node = target_node
|
84
106
|
end
|
@@ -87,29 +109,38 @@ class Usher
|
|
87
109
|
route
|
88
110
|
end
|
89
111
|
|
90
|
-
def find(usher, request, path, params = [])
|
112
|
+
def find(usher, request, original_path, path, params = [], position = 0)
|
91
113
|
if exclusive_type
|
92
114
|
[lookup[request.send(exclusive_type)], lookup[nil]].each do |n|
|
93
|
-
if n && (ret = n.find(usher, request, path.dup, params.dup))
|
115
|
+
if n && (ret = n.find(usher, request, original_path, path.dup, params.dup, position))
|
94
116
|
return ret
|
95
117
|
end
|
96
118
|
end
|
97
119
|
elsif path.size.zero? && terminates?
|
98
120
|
Response.new(terminates, params)
|
121
|
+
elsif !path.size.zero? && (greedy? && ((next_path, matched_part) = greedy_lookup.match_with_result(whole_path = original_path[position, original_path.size])))
|
122
|
+
position += matched_part.size
|
123
|
+
params << [next_path.value.name, whole_path.slice!(0, matched_part.size)]
|
124
|
+
next_path.find(usher, request, original_path, whole_path.size.zero? ? whole_path : usher.splitter.url_split(whole_path), params, position)
|
99
125
|
elsif !path.size.zero? && (next_part = lookup[part = path.shift] || lookup[nil])
|
126
|
+
position += part.size
|
100
127
|
case next_part.value
|
101
128
|
when Route::Variable
|
102
129
|
case next_part.value.type
|
103
130
|
when :*
|
104
131
|
params << [next_part.value.name, []] unless params.last && params.last.first == next_part.value.name
|
105
132
|
loop do
|
106
|
-
if (next_part.value.look_ahead === part || (!usher.
|
133
|
+
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)))
|
107
134
|
path.unshift(part)
|
108
|
-
|
135
|
+
position -= part.size
|
136
|
+
if usher.delimiter_chars.include?(next_part.parent.value[0])
|
137
|
+
path.unshift(next_part.parent.value)
|
138
|
+
position -= next_part.parent.value.size
|
139
|
+
end
|
109
140
|
break
|
110
141
|
elsif next_part.value.globs_capture_separators
|
111
142
|
params.last.last << part
|
112
|
-
elsif !usher.
|
143
|
+
elsif !usher.delimiter_chars.include?(part[0])
|
113
144
|
next_part.value.valid!(part)
|
114
145
|
params.last.last << part
|
115
146
|
end
|
@@ -124,11 +155,13 @@ class Usher
|
|
124
155
|
var.valid!(part)
|
125
156
|
params << [var.name, part]
|
126
157
|
until (var.look_ahead === path.first) || path.empty?
|
127
|
-
|
158
|
+
next_path_part = path.shift
|
159
|
+
position += next_path_part.size
|
160
|
+
params.last.last << next_path_part
|
128
161
|
end
|
129
162
|
end
|
130
163
|
end
|
131
|
-
next_part.find(usher, request, path, params)
|
164
|
+
next_part.find(usher, request, original_path, path, params, position)
|
132
165
|
else
|
133
166
|
nil
|
134
167
|
end
|
data/lib/usher/route/variable.rb
CHANGED
data/lib/usher/route.rb
CHANGED
@@ -4,18 +4,17 @@ require File.join(File.dirname(__FILE__), 'route', 'request_method')
|
|
4
4
|
|
5
5
|
class Usher
|
6
6
|
class Route
|
7
|
-
attr_reader :paths, :
|
7
|
+
attr_reader :paths, :requirements, :conditions, :destination, :named, :generate_with
|
8
8
|
|
9
9
|
GenerateWith = Struct.new(:scheme, :port, :host)
|
10
10
|
|
11
|
-
def initialize(
|
12
|
-
@original_path = original_path
|
11
|
+
def initialize(parsed_paths, router, conditions, requirements, default_values, generate_with) # :nodoc:
|
13
12
|
@router = router
|
14
13
|
@requirements = requirements
|
15
14
|
@conditions = conditions
|
16
15
|
@default_values = default_values
|
17
|
-
@paths = @router.splitter.split(@original_path, @requirements, @default_values).collect {|path| Path.new(self, path)}
|
18
16
|
@generate_with = GenerateWith.new(generate_with[:scheme], generate_with[:port], generate_with[:host]) if generate_with
|
17
|
+
@paths = parsed_paths.collect {|path| Path.new(self, path)}
|
19
18
|
end
|
20
19
|
|
21
20
|
def grapher
|
data/lib/usher/splitter.rb
CHANGED
@@ -3,156 +3,19 @@ require 'strscan'
|
|
3
3
|
class Usher
|
4
4
|
class Splitter
|
5
5
|
|
6
|
-
def self.for_delimiters(
|
7
|
-
|
8
|
-
SplitterInstance.new(
|
9
|
-
delimiters,
|
10
|
-
Regexp.new('((:|\*)?' + valid_regex + '|' + delimiters_regex + '|\(|\)|\||\{)'),
|
11
|
-
Regexp.new("[#{delimiters.collect{|d| Regexp.quote(d)}}]|[^#{delimiters.collect{|d| Regexp.quote(d)}}]+")
|
12
|
-
)
|
6
|
+
def self.for_delimiters(router, valid_regex)
|
7
|
+
SplitterInstance.new(Regexp.new("[#{router.delimiters.collect{|d| Regexp.quote(d)}}]|[^#{router.delimiters.collect{|d| Regexp.quote(d)}}]+"))
|
13
8
|
end
|
14
|
-
|
15
|
-
attr_reader :paths
|
16
9
|
|
17
10
|
class SplitterInstance
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
def initialize(delimiters, split_regex, url_split_regex)
|
22
|
-
@delimiters = delimiters
|
23
|
-
@delimiter_chars = delimiters.collect{|d| d[0]}
|
24
|
-
@split_regex = split_regex
|
11
|
+
|
12
|
+
def initialize(url_split_regex)
|
25
13
|
@url_split_regex = url_split_regex
|
26
14
|
end
|
27
15
|
|
28
16
|
def url_split(path)
|
29
17
|
path.scan(@url_split_regex)
|
30
18
|
end
|
31
|
-
|
32
|
-
def split(path, requirements = nil, default_values = nil)
|
33
|
-
parts = Group.new(:all, nil)
|
34
|
-
ss = StringScanner.new(path)
|
35
|
-
current_group = parts
|
36
|
-
while !ss.eos?
|
37
|
-
part = ss.scan(@split_regex)
|
38
|
-
case part[0]
|
39
|
-
when ?*, ?:
|
40
|
-
type = part.slice!(0).chr.to_sym
|
41
|
-
current_group << Usher::Route::Variable.new(type, part, requirements && requirements[part.to_sym])
|
42
|
-
when ?{
|
43
|
-
pattern = ''
|
44
|
-
count = 1
|
45
|
-
variable = ss.scan(/[:\*]([^,]+),/)
|
46
|
-
until count.zero?
|
47
|
-
regex_part = ss.scan(/\{|\}|[^\{\}]+/)
|
48
|
-
case regex_part[0]
|
49
|
-
when ?{
|
50
|
-
count += 1
|
51
|
-
when ?}
|
52
|
-
count -= 1
|
53
|
-
end
|
54
|
-
pattern << regex_part
|
55
|
-
end
|
56
|
-
pattern.slice!(pattern.length - 1)
|
57
|
-
regex = Regexp.new(pattern)
|
58
|
-
if variable
|
59
|
-
variable_type = variable.slice!(0).chr.to_sym
|
60
|
-
variable_name = variable[0, variable.size - 1].to_sym
|
61
|
-
current_group << Usher::Route::Variable.new(variable_type, variable_name, requirements && requirements[variable_name], regex)
|
62
|
-
else
|
63
|
-
current_group << regex
|
64
|
-
end
|
65
|
-
when ?(
|
66
|
-
new_group = Group.new(:any, current_group)
|
67
|
-
current_group << new_group
|
68
|
-
current_group = new_group
|
69
|
-
when ?)
|
70
|
-
current_group = current_group.parent.type == :one ? current_group.parent.parent : current_group.parent
|
71
|
-
when ?|
|
72
|
-
unless current_group.parent.type == :one
|
73
|
-
detached_group = current_group.parent.pop
|
74
|
-
new_group = Group.new(:one, detached_group.parent)
|
75
|
-
detached_group.parent = new_group
|
76
|
-
detached_group.type = :all
|
77
|
-
new_group << detached_group
|
78
|
-
new_group.parent << new_group
|
79
|
-
end
|
80
|
-
current_group.parent << Group.new(:all, current_group.parent)
|
81
|
-
current_group = current_group.parent.last
|
82
|
-
else
|
83
|
-
current_group << part
|
84
|
-
end
|
85
|
-
end unless !path || path.empty?
|
86
|
-
paths = calc_paths(parts)
|
87
|
-
paths.each do |path|
|
88
|
-
path.each_with_index do |part, index|
|
89
|
-
if part.is_a?(Usher::Route::Variable)
|
90
|
-
part.default_value = default_values[part.name] if default_values
|
91
|
-
|
92
|
-
case part.type
|
93
|
-
when :*
|
94
|
-
part.look_ahead = path[index + 1, path.size].find{|p| !p.is_a?(Usher::Route::Variable) && !@delimiter_chars.include?(p[0])} || nil
|
95
|
-
when :':'
|
96
|
-
part.look_ahead = path[index + 1, path.size].find{|p| @delimiter_chars.include?(p[0])} || @delimiters.first
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
paths
|
102
|
-
end
|
103
|
-
|
104
|
-
private
|
105
|
-
|
106
|
-
def cartesian_product!(lval, rval)
|
107
|
-
product = []
|
108
|
-
(lval.size * rval.size).times do |index|
|
109
|
-
val = []
|
110
|
-
val.push(*lval[index % lval.size])
|
111
|
-
val.push(*rval[index % rval.size])
|
112
|
-
product << val
|
113
|
-
end
|
114
|
-
lval.replace(product)
|
115
|
-
end
|
116
|
-
|
117
|
-
def calc_paths(parts)
|
118
|
-
if parts.is_a?(Group)
|
119
|
-
paths = [[]]
|
120
|
-
case parts.type
|
121
|
-
when :all
|
122
|
-
parts.each do |p|
|
123
|
-
cartesian_product!(paths, calc_paths(p))
|
124
|
-
end
|
125
|
-
when :any
|
126
|
-
parts.each do |p|
|
127
|
-
cartesian_product!(paths, calc_paths(p))
|
128
|
-
end
|
129
|
-
paths.unshift([])
|
130
|
-
when :one
|
131
|
-
cartesian_product!(paths, parts.collect do |p|
|
132
|
-
calc_paths(p)
|
133
|
-
end)
|
134
|
-
end
|
135
|
-
paths.each{|p| p.compact!; p.flatten! }
|
136
|
-
paths
|
137
|
-
else
|
138
|
-
[[parts]]
|
139
|
-
end
|
140
|
-
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
class Group < Array
|
145
|
-
attr_accessor :type
|
146
|
-
attr_accessor :parent
|
147
|
-
|
148
|
-
def inspect
|
149
|
-
"#{type}->#{super}"
|
150
|
-
end
|
151
|
-
|
152
|
-
def initialize(type, parent)
|
153
|
-
@type = type
|
154
|
-
@parent = parent
|
155
|
-
end
|
156
19
|
end
|
157
20
|
|
158
21
|
end
|
@@ -0,0 +1,133 @@
|
|
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
|
25
|
+
|
26
|
+
class Usher
|
27
|
+
module Util
|
28
|
+
class Generators
|
29
|
+
|
30
|
+
class URL
|
31
|
+
|
32
|
+
def initialize(usher)
|
33
|
+
@usher = usher
|
34
|
+
end
|
35
|
+
|
36
|
+
def generate_full(routing_lookup, request, params = nil)
|
37
|
+
path = path_for_routing_lookup(routing_lookup, params)
|
38
|
+
result = generate_start(path, request)
|
39
|
+
result << generate_path(path, params)
|
40
|
+
end
|
41
|
+
|
42
|
+
def generate(routing_lookup, params = nil)
|
43
|
+
generate_path(path_for_routing_lookup(routing_lookup, params), params)
|
44
|
+
end
|
45
|
+
|
46
|
+
def generate_start(path, request)
|
47
|
+
result = (path.route.generate_with && path.route.generate_with.scheme || request.scheme).dup
|
48
|
+
result << '://'
|
49
|
+
result << (path.route.generate_with && path.route.generate_with.host) ? path.route.generate_with.host : request.host
|
50
|
+
port = path.route.generate_with && path.route.generate_with.port || request.port
|
51
|
+
if result[4] == ?s
|
52
|
+
result << ':' << port.to_s if port != 443
|
53
|
+
else
|
54
|
+
result << ':' << port.to_s if port != 80
|
55
|
+
end
|
56
|
+
result
|
57
|
+
end
|
58
|
+
|
59
|
+
def path_for_routing_lookup(routing_lookup, params)
|
60
|
+
path = case routing_lookup
|
61
|
+
when Symbol
|
62
|
+
route = @usher.named_routes[routing_lookup]
|
63
|
+
params.is_a?(Hash) ? route.find_matching_path(params) : route.paths.first
|
64
|
+
when Route
|
65
|
+
params.is_a?(Hash) ? routing_lookup.find_matching_path(params) : routing_lookup.paths.first
|
66
|
+
when nil
|
67
|
+
params.is_a?(Hash) ? @usher.path_for_options(params) : raise
|
68
|
+
when Route::Path
|
69
|
+
routing_lookup
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Generates a completed URL based on a +route+ or set of optional +params+
|
74
|
+
#
|
75
|
+
# set = Usher.new
|
76
|
+
# route = set.add_named_route(:test_route, '/:controller/:action')
|
77
|
+
# set.generate_url(nil, {:controller => 'c', :action => 'a'}) == '/c/a' => true
|
78
|
+
# set.generate_url(:test_route, {:controller => 'c', :action => 'a'}) == '/c/a' => true
|
79
|
+
# set.generate_url(route.primary_path, {:controller => 'c', :action => 'a'}) == '/c/a' => true
|
80
|
+
def generate_path(path, params = nil)
|
81
|
+
raise UnrecognizedException.new unless path
|
82
|
+
|
83
|
+
params = Array(params) if params.is_a?(String)
|
84
|
+
if params.is_a?(Array)
|
85
|
+
given_size = params.size
|
86
|
+
extra_params = params.last.is_a?(Hash) ? params.pop : nil
|
87
|
+
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}]
|
88
|
+
params.merge!(extra_params) if extra_params
|
89
|
+
end
|
90
|
+
|
91
|
+
result = ''
|
92
|
+
path.parts.each do |part|
|
93
|
+
case part
|
94
|
+
when Route::Variable
|
95
|
+
value = (params && params.delete(part.name)) || part.default_value || raise(MissingParameterException.new)
|
96
|
+
case part.type
|
97
|
+
when :*
|
98
|
+
value.each_with_index do |current_value, index|
|
99
|
+
current_value = current_value.to_s unless current_value.is_a?(String)
|
100
|
+
part.valid!(current_value)
|
101
|
+
result << current_value
|
102
|
+
result << '/' if index != value.size - 1
|
103
|
+
end
|
104
|
+
when :':'
|
105
|
+
value = value.to_s unless value.is_a?(String)
|
106
|
+
part.valid!(value)
|
107
|
+
result << value
|
108
|
+
end
|
109
|
+
else
|
110
|
+
result << part
|
111
|
+
end
|
112
|
+
end
|
113
|
+
result = Rack::Utils.uri_escape(result)
|
114
|
+
|
115
|
+
if params && !params.empty?
|
116
|
+
has_query = result[??]
|
117
|
+
params.each do |k,v|
|
118
|
+
case v
|
119
|
+
when Array
|
120
|
+
v.each do |v_part|
|
121
|
+
result << (has_query ? '&' : has_query = true && '?') << Rack::Utils.escape("#{k.to_s}[]") << '=' << Rack::Utils.escape(v_part.to_s)
|
122
|
+
end
|
123
|
+
else
|
124
|
+
result << (has_query ? '&' : has_query = true && '?') << Rack::Utils.escape(k.to_s) << '=' << Rack::Utils.escape(v.to_s)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
result
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
require 'strscan'
|
2
|
+
|
3
|
+
class Usher
|
4
|
+
module Util
|
5
|
+
class Parser
|
6
|
+
|
7
|
+
def self.for_delimiters(router, valid_regex)
|
8
|
+
ParserInstance.new(
|
9
|
+
router,
|
10
|
+
Regexp.new('((:|\*)?' + valid_regex + '|' + router.delimiters_regex + '|\(|\)|\||\{)')
|
11
|
+
)
|
12
|
+
end
|
13
|
+
|
14
|
+
class ParserInstance
|
15
|
+
|
16
|
+
def initialize(router, split_regex)
|
17
|
+
@router = router
|
18
|
+
@split_regex = split_regex
|
19
|
+
end
|
20
|
+
|
21
|
+
def parse(path, requirements = nil, default_values = nil)
|
22
|
+
parts = Group.new(:all, nil)
|
23
|
+
ss = StringScanner.new(path)
|
24
|
+
current_group = parts
|
25
|
+
while !ss.eos?
|
26
|
+
part = ss.scan(@split_regex)
|
27
|
+
case part[0]
|
28
|
+
when ?*, ?:
|
29
|
+
type = part.slice!(0).chr.to_sym
|
30
|
+
current_group << Usher::Route::Variable.new(type, part, requirements && requirements[part.to_sym])
|
31
|
+
when ?{
|
32
|
+
pattern = ''
|
33
|
+
count = 1
|
34
|
+
variable = ss.scan(/[!:\*]([^,]+),/)
|
35
|
+
until count.zero?
|
36
|
+
regex_part = ss.scan(/\{|\}|[^\{\}]+/)
|
37
|
+
case regex_part[0]
|
38
|
+
when ?{
|
39
|
+
count += 1
|
40
|
+
when ?}
|
41
|
+
count -= 1
|
42
|
+
end
|
43
|
+
pattern << regex_part
|
44
|
+
end
|
45
|
+
pattern.slice!(pattern.length - 1)
|
46
|
+
regex = Regexp.new(pattern)
|
47
|
+
if variable
|
48
|
+
variable_type = variable.slice!(0).chr.to_sym
|
49
|
+
variable_name = variable[0, variable.size - 1].to_sym
|
50
|
+
current_group << Usher::Route::Variable.new(variable_type, variable_name, requirements && requirements[variable_name], regex)
|
51
|
+
else
|
52
|
+
current_group << regex
|
53
|
+
end
|
54
|
+
when ?(
|
55
|
+
new_group = Group.new(:any, current_group)
|
56
|
+
current_group << new_group
|
57
|
+
current_group = new_group
|
58
|
+
when ?)
|
59
|
+
current_group = current_group.parent.type == :one ? current_group.parent.parent : current_group.parent
|
60
|
+
when ?|
|
61
|
+
unless current_group.parent.type == :one
|
62
|
+
detached_group = current_group.parent.pop
|
63
|
+
new_group = Group.new(:one, detached_group.parent)
|
64
|
+
detached_group.parent = new_group
|
65
|
+
detached_group.type = :all
|
66
|
+
new_group << detached_group
|
67
|
+
new_group.parent << new_group
|
68
|
+
end
|
69
|
+
current_group.parent << Group.new(:all, current_group.parent)
|
70
|
+
current_group = current_group.parent.last
|
71
|
+
else
|
72
|
+
current_group << part
|
73
|
+
end
|
74
|
+
end unless !path || path.empty?
|
75
|
+
paths = calc_paths(parts)
|
76
|
+
paths.each do |path|
|
77
|
+
path.each_with_index do |part, index|
|
78
|
+
if part.is_a?(Usher::Route::Variable)
|
79
|
+
part.default_value = default_values[part.name] if default_values
|
80
|
+
|
81
|
+
case part.type
|
82
|
+
when :*
|
83
|
+
part.look_ahead = path[index + 1, path.size].find{|p| !p.is_a?(Usher::Route::Variable) && !@router.delimiter_chars.include?(p[0])} || nil
|
84
|
+
when :':'
|
85
|
+
part.look_ahead = path[index + 1, path.size].find{|p| @router.delimiter_chars.include?(p[0])} || @router.delimiters.first
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
paths
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def cartesian_product!(lval, rval)
|
96
|
+
product = []
|
97
|
+
(lval.size * rval.size).times do |index|
|
98
|
+
val = []
|
99
|
+
val.push(*lval[index % lval.size])
|
100
|
+
val.push(*rval[index % rval.size])
|
101
|
+
product << val
|
102
|
+
end
|
103
|
+
lval.replace(product)
|
104
|
+
end
|
105
|
+
|
106
|
+
def calc_paths(parts)
|
107
|
+
if parts.is_a?(Group)
|
108
|
+
paths = [[]]
|
109
|
+
case parts.type
|
110
|
+
when :all
|
111
|
+
parts.each do |p|
|
112
|
+
cartesian_product!(paths, calc_paths(p))
|
113
|
+
end
|
114
|
+
when :any
|
115
|
+
parts.each do |p|
|
116
|
+
cartesian_product!(paths, calc_paths(p))
|
117
|
+
end
|
118
|
+
paths.unshift([])
|
119
|
+
when :one
|
120
|
+
cartesian_product!(paths, parts.collect do |p|
|
121
|
+
calc_paths(p)
|
122
|
+
end)
|
123
|
+
end
|
124
|
+
paths.each{|p| p.compact!; p.flatten! }
|
125
|
+
paths
|
126
|
+
else
|
127
|
+
[[parts]]
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
class Group < Array
|
134
|
+
attr_accessor :type
|
135
|
+
attr_accessor :parent
|
136
|
+
|
137
|
+
def inspect
|
138
|
+
"#{type}->#{super}"
|
139
|
+
end
|
140
|
+
|
141
|
+
def initialize(type, parent)
|
142
|
+
@type = type
|
143
|
+
@parent = parent
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
data/lib/usher/util.rb
ADDED
data/lib/usher.rb
CHANGED
@@ -4,12 +4,12 @@ require File.join(File.dirname(__FILE__), 'usher', 'grapher')
|
|
4
4
|
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
|
+
require File.join(File.dirname(__FILE__), 'usher', 'util')
|
7
8
|
|
8
9
|
class Usher
|
9
10
|
|
10
|
-
autoload :Generators, File.join(File.dirname(__FILE__), 'usher', 'generate')
|
11
11
|
|
12
|
-
attr_reader :tree, :named_routes, :route_count, :routes, :splitter, :delimiters
|
12
|
+
attr_reader :tree, :named_routes, :route_count, :routes, :splitter, :delimiters, :delimiter_chars, :delimiters_regex
|
13
13
|
|
14
14
|
SymbolArraySorter = proc {|a,b| a.hash <=> b.hash} #:nodoc:
|
15
15
|
|
@@ -54,12 +54,18 @@ class Usher
|
|
54
54
|
def initialize(options = nil)
|
55
55
|
@globs_capture_separators = options && options.key?(:globs_capture_separators) ? options.delete(:globs_capture_separators) : false
|
56
56
|
@delimiters = options && options.delete(:delimiters) || ['/', '.']
|
57
|
+
@delimiter_chars = @delimiters.collect{|d| d[0]}
|
58
|
+
@delimiters_regex = @delimiters.collect{|d| Regexp.quote(d)} * '|'
|
57
59
|
@valid_regex = options && options.delete(:valid_regex) || '[0-9A-Za-z\$\-_\+!\*\',]+'
|
58
60
|
@request_methods = options && options.delete(:request_methods) || [:protocol, :domain, :port, :query_string, :remote_ip, :user_agent, :referer, :method, :subdomains]
|
59
|
-
@splitter = Splitter.for_delimiters(
|
61
|
+
@splitter = Splitter.for_delimiters(self, @valid_regex)
|
60
62
|
reset!
|
61
63
|
end
|
62
64
|
|
65
|
+
def parser
|
66
|
+
@parser ||= Util::Parser.for_delimiters(self, @valid_regex)
|
67
|
+
end
|
68
|
+
|
63
69
|
# Adds a route referencable by +name+. Sett add_route for format +path+ and +options+.
|
64
70
|
#
|
65
71
|
# set = Usher.new
|
@@ -152,7 +158,18 @@ class Usher
|
|
152
158
|
end
|
153
159
|
end
|
154
160
|
end
|
155
|
-
|
161
|
+
|
162
|
+
path = parser.parse(path, requirements, default_values) if path.is_a?(String)
|
163
|
+
|
164
|
+
route = Route.new(
|
165
|
+
path,
|
166
|
+
self,
|
167
|
+
conditions,
|
168
|
+
requirements,
|
169
|
+
default_values,
|
170
|
+
generate_with
|
171
|
+
)
|
172
|
+
|
156
173
|
route.to(options) if options && !options.empty?
|
157
174
|
|
158
175
|
@tree.add(route)
|
@@ -169,7 +186,7 @@ class Usher
|
|
169
186
|
# route = set.add_route('/test')
|
170
187
|
# set.recognize(Request.new('/test')).path.route == route => true
|
171
188
|
def recognize(request, path = request.path)
|
172
|
-
@tree.find(self, request, @splitter.url_split(path))
|
189
|
+
@tree.find(self, request, path, @splitter.url_split(path))
|
173
190
|
end
|
174
191
|
|
175
192
|
# Recognizes a set of +parameters+ and gets the closest matching Usher::Route::Path or +nil+ if no route exists.
|
@@ -6,7 +6,7 @@ describe "Usher URL generation" do
|
|
6
6
|
before(:each) do
|
7
7
|
@route_set = Usher.new
|
8
8
|
@route_set.reset!
|
9
|
-
@url_generator = Usher::Generators::URL.new(@route_set)
|
9
|
+
@url_generator = Usher::Util::Generators::URL.new(@route_set)
|
10
10
|
end
|
11
11
|
|
12
12
|
it "should generate a simple URL" do
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'lib/usher'
|
2
|
+
|
3
|
+
describe "Usher route tokenizing" do
|
4
|
+
|
5
|
+
|
6
|
+
it "should split / delimited routes" do
|
7
|
+
Usher.new(:delimiters => ['/', '.'], :valid_regex => '[0-9A-Za-z\$\-_\+!\*\',]+').parser.parse('/test/this/split').should == [['/', 'test', '/','this', '/', 'split']]
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should split / delimited routes with a regex in it" do
|
11
|
+
Usher.new(:delimiters => ['/', '.'], :valid_regex => '[0-9A-Za-z\$\-_\+!\*\',]+').parser.parse('/test/{this}/split').should == [['/', 'test', '/', /this/, '/', 'split']]
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should split on ' ' delimited routes as well" do
|
15
|
+
Usher.new(:delimiters => [' '], :valid_regex => '[0-9A-Za-z\$\-_\+!\*\',]+').parser.parse('test this split').should == [['test', ' ', 'this', ' ', 'split']]
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should split on email delimiters as well" do
|
19
|
+
Usher.new(:delimiters => ['@', '+', '-', '.'], :valid_regex => '[a-zA-Z0-9]+').parser.parse('one+more.12345-09876-alphanum3ric5@domain.com').should == [["one", '+', "more", ".", "12345", '-', "09876", '-', "alphanum3ric5", "@", "domain", ".", "com"]]
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should split on ' ' delimited routes for more complex routes as well" do
|
23
|
+
Usher.new(:delimiters => [' '], :valid_regex => '[0-9A-Za-z\$\-_\+!\*\',]+').parser.parse('(test|this) split').should == [['test', ' ', 'split'], ['this', ' ', 'split']]
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should group optional parts with brackets" do
|
27
|
+
Usher.new(:delimiters => ['/', '.'], :valid_regex => '[0-9A-Za-z\$\-_\+!\*\',]+').parser.parse('/test/this(/split)').should == [
|
28
|
+
['/', 'test', '/', 'this'],
|
29
|
+
['/', 'test', '/', 'this', '/', 'split']
|
30
|
+
]
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should group exclusive optional parts with brackets and pipes" do
|
34
|
+
Usher.new(:delimiters => ['/', '.'], :valid_regex => '[0-9A-Za-z\$\-_\+!\*\',]+').parser.parse('/test/this(/split|/split2)').should == [
|
35
|
+
['/', 'test', '/', 'this','/', 'split'],
|
36
|
+
['/', 'test', '/', 'this','/', 'split2']
|
37
|
+
]
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should group exclusive optional-optional parts with brackets and pipes" do
|
41
|
+
Usher.new(:delimiters => ['/', '.'], :valid_regex => '[0-9A-Za-z\$\-_\+!\*\',]+').parser.parse('/test/this((/split|/split2))').should == [
|
42
|
+
['/', 'test','/', 'this'],
|
43
|
+
['/', 'test','/', 'this', '/', 'split'],
|
44
|
+
['/', 'test','/', 'this', '/', 'split2']
|
45
|
+
]
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should group optional parts with brackets (for non overlapping groups)" do
|
49
|
+
Usher.new(:delimiters => ['/', '.'], :valid_regex => '[0-9A-Za-z\$\-_\+!\*\',]+').parser.parse('/test/this(/split)(/split2)') == [
|
50
|
+
['/', "test", '/', "this"],
|
51
|
+
['/', "test", '/', "this", '/', "split"],
|
52
|
+
['/', "test", '/', "this", '/', "split2"],
|
53
|
+
['/', "test", '/', "this", '/', "split", '/', "split2"]
|
54
|
+
]
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should group nested-optional parts with brackets" do
|
58
|
+
Usher.new(:delimiters => ['/', '.'], :valid_regex => '[0-9A-Za-z\$\-_\+!\*\',]+').parser.parse('/test/this(/split(.:format))') == [
|
59
|
+
['/', "test", '/', "this"],
|
60
|
+
['/', "test", '/', "this", '/', "split"],
|
61
|
+
['/', "test", '/', "this", '/', "split", '.', Usher::Route::Variable.new(:':', :format)]
|
62
|
+
]
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should to_s all different variable types" do
|
66
|
+
Usher.new(:delimiters => ['/', '.'], :valid_regex => '[0-9A-Za-z\$\-_\+!\*\',]+').parser.parse('/:split/*splitter').first.collect{|v| v.to_s} ==
|
67
|
+
[ ':split', '*splitter' ]
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should == variable types" do
|
71
|
+
parts = Usher.new(:delimiters => ['/', '.'], :valid_regex => '[0-9A-Za-z\$\-_\+!\*\',]+').parser.parse('/:split/:split').first
|
72
|
+
parts[1].should == parts[3]
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
@@ -93,6 +93,18 @@ describe "Usher route recognition" do
|
|
93
93
|
route_set.recognize(build_request({:method => 'get', :path => '/test/part/hello/again/123/hello/again/onemore'})).params.should == [[:test, ['hello', 'again', '123', 'hello', 'again']], [:party, 'onemore']]
|
94
94
|
end
|
95
95
|
|
96
|
+
it "should recgonize a greedy regex single variable" do
|
97
|
+
target_route = route_set.add_route('/test/part/{!test,one/more/time}')
|
98
|
+
route_set.recognize(build_request({:method => 'get', :path => '/test/part/one/more/time'})).path.route.should == target_route
|
99
|
+
route_set.recognize(build_request({:method => 'get', :path => '/test/part/one/more/time'})).params.should == [[:test, 'one/more/time']]
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should recgonize a greedy regex single variable with static parts after" do
|
103
|
+
target_route = route_set.add_route('/test/part/{!test,one/more/time}/help')
|
104
|
+
route_set.recognize(build_request({:method => 'get', :path => '/test/part/one/more/time/help'})).path.route.should == target_route
|
105
|
+
route_set.recognize(build_request({:method => 'get', :path => '/test/part/one/more/time/help'})).params.should == [[:test, 'one/more/time']]
|
106
|
+
end
|
107
|
+
|
96
108
|
it "should recgonize two glob-style variables separated by a static part" do
|
97
109
|
target_route = route_set.add_route('/*format/innovate/*onemore')
|
98
110
|
response = route_set.recognize(build_request({:method => 'get', :path => '/sample/html/innovate/apple'}))
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: joshbuddy-usher
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.10
|
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-
|
12
|
+
date: 2009-07-22 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -20,7 +20,7 @@ dependencies:
|
|
20
20
|
requirements:
|
21
21
|
- - ">="
|
22
22
|
- !ruby/object:Gem::Version
|
23
|
-
version: 0.0.
|
23
|
+
version: 0.0.5
|
24
24
|
version:
|
25
25
|
description: A general purpose routing library
|
26
26
|
email: joshbuddy@gmail.com
|
@@ -38,7 +38,6 @@ files:
|
|
38
38
|
- VERSION.yml
|
39
39
|
- lib/usher.rb
|
40
40
|
- lib/usher/exceptions.rb
|
41
|
-
- lib/usher/generate.rb
|
42
41
|
- lib/usher/grapher.rb
|
43
42
|
- lib/usher/interface.rb
|
44
43
|
- lib/usher/interface/email_interface.rb
|
@@ -55,10 +54,14 @@ files:
|
|
55
54
|
- lib/usher/route/request_method.rb
|
56
55
|
- lib/usher/route/variable.rb
|
57
56
|
- lib/usher/splitter.rb
|
57
|
+
- lib/usher/util.rb
|
58
|
+
- lib/usher/util/generate.rb
|
59
|
+
- lib/usher/util/parser.rb
|
58
60
|
- rails/init.rb
|
59
61
|
- spec/private/email/recognize_spec.rb
|
60
62
|
- spec/private/generate_spec.rb
|
61
63
|
- spec/private/grapher_spec.rb
|
64
|
+
- spec/private/parser_spec.rb
|
62
65
|
- spec/private/path_spec.rb
|
63
66
|
- spec/private/rack/dispatch_spec.rb
|
64
67
|
- spec/private/rails2_2/compat.rb
|
@@ -71,7 +74,6 @@ files:
|
|
71
74
|
- spec/private/rails2_3/recognize_spec.rb
|
72
75
|
- spec/private/recognize_spec.rb
|
73
76
|
- spec/private/request_method_spec.rb
|
74
|
-
- spec/private/split_spec.rb
|
75
77
|
- spec/spec.opts
|
76
78
|
has_rdoc: false
|
77
79
|
homepage: http://github.com/joshbuddy/usher
|
@@ -103,6 +105,7 @@ test_files:
|
|
103
105
|
- spec/private/email/recognize_spec.rb
|
104
106
|
- spec/private/generate_spec.rb
|
105
107
|
- spec/private/grapher_spec.rb
|
108
|
+
- spec/private/parser_spec.rb
|
106
109
|
- spec/private/path_spec.rb
|
107
110
|
- spec/private/rack/dispatch_spec.rb
|
108
111
|
- spec/private/rails2_2/compat.rb
|
@@ -115,4 +118,3 @@ test_files:
|
|
115
118
|
- spec/private/rails2_3/recognize_spec.rb
|
116
119
|
- spec/private/recognize_spec.rb
|
117
120
|
- spec/private/request_method_spec.rb
|
118
|
-
- spec/private/split_spec.rb
|
data/lib/usher/generate.rb
DELETED
@@ -1,131 +0,0 @@
|
|
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
|
25
|
-
|
26
|
-
class Usher
|
27
|
-
class Generators
|
28
|
-
|
29
|
-
class URL
|
30
|
-
|
31
|
-
def initialize(usher)
|
32
|
-
@usher = usher
|
33
|
-
end
|
34
|
-
|
35
|
-
def generate_full(routing_lookup, request, params = nil)
|
36
|
-
path = path_for_routing_lookup(routing_lookup, params)
|
37
|
-
result = generate_start(path, request)
|
38
|
-
result << generate_path(path, params)
|
39
|
-
end
|
40
|
-
|
41
|
-
def generate(routing_lookup, params = nil)
|
42
|
-
generate_path(path_for_routing_lookup(routing_lookup, params), params)
|
43
|
-
end
|
44
|
-
|
45
|
-
def generate_start(path, request)
|
46
|
-
result = (path.route.generate_with && path.route.generate_with.scheme || request.scheme).dup
|
47
|
-
result << '://'
|
48
|
-
result << (path.route.generate_with && path.route.generate_with.host) ? path.route.generate_with.host : request.host
|
49
|
-
port = path.route.generate_with && path.route.generate_with.port || request.port
|
50
|
-
if result[4] == ?s
|
51
|
-
result << ':' << port.to_s if port != 443
|
52
|
-
else
|
53
|
-
result << ':' << port.to_s if port != 80
|
54
|
-
end
|
55
|
-
result
|
56
|
-
end
|
57
|
-
|
58
|
-
def path_for_routing_lookup(routing_lookup, params)
|
59
|
-
path = case routing_lookup
|
60
|
-
when Symbol
|
61
|
-
route = @usher.named_routes[routing_lookup]
|
62
|
-
params.is_a?(Hash) ? route.find_matching_path(params) : route.paths.first
|
63
|
-
when Route
|
64
|
-
params.is_a?(Hash) ? routing_lookup.find_matching_path(params) : routing_lookup.paths.first
|
65
|
-
when nil
|
66
|
-
params.is_a?(Hash) ? @usher.path_for_options(params) : raise
|
67
|
-
when Route::Path
|
68
|
-
routing_lookup
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
# Generates a completed URL based on a +route+ or set of optional +params+
|
73
|
-
#
|
74
|
-
# set = Usher.new
|
75
|
-
# route = set.add_named_route(:test_route, '/:controller/:action')
|
76
|
-
# set.generate_url(nil, {:controller => 'c', :action => 'a'}) == '/c/a' => true
|
77
|
-
# set.generate_url(:test_route, {:controller => 'c', :action => 'a'}) == '/c/a' => true
|
78
|
-
# set.generate_url(route.primary_path, {:controller => 'c', :action => 'a'}) == '/c/a' => true
|
79
|
-
def generate_path(path, params = nil)
|
80
|
-
raise UnrecognizedException.new unless path
|
81
|
-
|
82
|
-
params = Array(params) if params.is_a?(String)
|
83
|
-
if params.is_a?(Array)
|
84
|
-
given_size = params.size
|
85
|
-
extra_params = params.last.is_a?(Hash) ? params.pop : nil
|
86
|
-
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}]
|
87
|
-
params.merge!(extra_params) if extra_params
|
88
|
-
end
|
89
|
-
|
90
|
-
result = ''
|
91
|
-
path.parts.each do |part|
|
92
|
-
case part
|
93
|
-
when Route::Variable
|
94
|
-
value = (params && params.delete(part.name)) || part.default_value || raise(MissingParameterException.new)
|
95
|
-
case part.type
|
96
|
-
when :*
|
97
|
-
value.each_with_index do |current_value, index|
|
98
|
-
current_value = current_value.to_s unless current_value.is_a?(String)
|
99
|
-
part.valid!(current_value)
|
100
|
-
result << current_value
|
101
|
-
result << '/' if index != value.size - 1
|
102
|
-
end
|
103
|
-
when :':'
|
104
|
-
value = value.to_s unless value.is_a?(String)
|
105
|
-
part.valid!(value)
|
106
|
-
result << value
|
107
|
-
end
|
108
|
-
else
|
109
|
-
result << part
|
110
|
-
end
|
111
|
-
end
|
112
|
-
result = Rack::Utils.uri_escape(result)
|
113
|
-
|
114
|
-
if params && !params.empty?
|
115
|
-
has_query = result[??]
|
116
|
-
params.each do |k,v|
|
117
|
-
case v
|
118
|
-
when Array
|
119
|
-
v.each do |v_part|
|
120
|
-
result << (has_query ? '&' : has_query = true && '?') << Rack::Utils.escape("#{k.to_s}[]") << '=' << Rack::Utils.escape(v_part.to_s)
|
121
|
-
end
|
122
|
-
else
|
123
|
-
result << (has_query ? '&' : has_query = true && '?') << Rack::Utils.escape(k.to_s) << '=' << Rack::Utils.escape(v.to_s)
|
124
|
-
end
|
125
|
-
end
|
126
|
-
end
|
127
|
-
result
|
128
|
-
end
|
129
|
-
end
|
130
|
-
end
|
131
|
-
end
|
data/spec/private/split_spec.rb
DELETED
@@ -1,76 +0,0 @@
|
|
1
|
-
require 'lib/usher'
|
2
|
-
|
3
|
-
describe "Usher route tokenizing" do
|
4
|
-
|
5
|
-
|
6
|
-
it "should split / delimited routes" do
|
7
|
-
Usher::Splitter.for_delimiters(['/', '.'], '[0-9A-Za-z\$\-_\+!\*\',]+').split('/test/this/split').should == [['/', 'test', '/','this', '/', 'split']]
|
8
|
-
end
|
9
|
-
|
10
|
-
it "should split / delimited routes with a regex in it" do
|
11
|
-
Usher::Splitter.for_delimiters(['/', '.'], '[0-9A-Za-z\$\-_\+!\*\',]+').
|
12
|
-
split('/test/{this}/split').should == [['/', 'test', '/', /this/, '/', 'split']]
|
13
|
-
end
|
14
|
-
|
15
|
-
it "should split on ' ' delimited routes as well" do
|
16
|
-
Usher::Splitter.for_delimiters([' '], '[0-9A-Za-z\$\-_\+!\*\',]+').split('test this split').should == [['test', ' ', 'this', ' ', 'split']]
|
17
|
-
end
|
18
|
-
|
19
|
-
it "should split on email delimiters as well" do
|
20
|
-
Usher::Splitter.for_delimiters(['@', '+', '-', '.'], '[a-zA-Z0-9]+').split('one+more.12345-09876-alphanum3ric5@domain.com').should == [["one", '+', "more", ".", "12345", '-', "09876", '-', "alphanum3ric5", "@", "domain", ".", "com"]]
|
21
|
-
end
|
22
|
-
|
23
|
-
it "should split on ' ' delimited routes for more complex routes as well" do
|
24
|
-
Usher::Splitter.for_delimiters([' '], '[0-9A-Za-z\$\-_\+!\*\',]+').split('(test|this) split').should == [['test', ' ', 'split'], ['this', ' ', 'split']]
|
25
|
-
end
|
26
|
-
|
27
|
-
it "should group optional parts with brackets" do
|
28
|
-
Usher::Splitter.for_delimiters(['/', '.'], '[0-9A-Za-z\$\-_\+!\*\',]+').split('/test/this(/split)').should == [
|
29
|
-
['/', 'test', '/', 'this'],
|
30
|
-
['/', 'test', '/', 'this', '/', 'split']
|
31
|
-
]
|
32
|
-
end
|
33
|
-
|
34
|
-
it "should group exclusive optional parts with brackets and pipes" do
|
35
|
-
Usher::Splitter.for_delimiters(['/', '.'], '[0-9A-Za-z\$\-_\+!\*\',]+').split('/test/this(/split|/split2)').should == [
|
36
|
-
['/', 'test', '/', 'this','/', 'split'],
|
37
|
-
['/', 'test', '/', 'this','/', 'split2']
|
38
|
-
]
|
39
|
-
end
|
40
|
-
|
41
|
-
it "should group exclusive optional-optional parts with brackets and pipes" do
|
42
|
-
Usher::Splitter.for_delimiters(['/', '.'], '[0-9A-Za-z\$\-_\+!\*\',]+').split('/test/this((/split|/split2))').should == [
|
43
|
-
['/', 'test','/', 'this'],
|
44
|
-
['/', 'test','/', 'this', '/', 'split'],
|
45
|
-
['/', 'test','/', 'this', '/', 'split2']
|
46
|
-
]
|
47
|
-
end
|
48
|
-
|
49
|
-
it "should group optional parts with brackets (for non overlapping groups)" do
|
50
|
-
Usher::Splitter.for_delimiters(['/', '.'], '[0-9A-Za-z\$\-_\+!\*\',]+').split('/test/this(/split)(/split2)') == [
|
51
|
-
['/', "test", '/', "this"],
|
52
|
-
['/', "test", '/', "this", '/', "split"],
|
53
|
-
['/', "test", '/', "this", '/', "split2"],
|
54
|
-
['/', "test", '/', "this", '/', "split", '/', "split2"]
|
55
|
-
]
|
56
|
-
end
|
57
|
-
|
58
|
-
it "should group nested-optional parts with brackets" do
|
59
|
-
Usher::Splitter.for_delimiters(['/', '.'], '[0-9A-Za-z\$\-_\+!\*\',]+').split('/test/this(/split(.:format))') == [
|
60
|
-
['/', "test", '/', "this"],
|
61
|
-
['/', "test", '/', "this", '/', "split"],
|
62
|
-
['/', "test", '/', "this", '/', "split", '.', Usher::Route::Variable.new(:':', :format)]
|
63
|
-
]
|
64
|
-
end
|
65
|
-
|
66
|
-
it "should to_s all different variable types" do
|
67
|
-
Usher::Splitter.for_delimiters(['/', '.'], '[0-9A-Za-z\$\-_\+!\*\',]+').split('/:split/*splitter').first.collect{|v| v.to_s} ==
|
68
|
-
[ ':split', '*splitter' ]
|
69
|
-
end
|
70
|
-
|
71
|
-
it "should == variable types" do
|
72
|
-
parts = Usher::Splitter.for_delimiters(['/', '.'], '[0-9A-Za-z\$\-_\+!\*\',]+').split('/:split/:split').first
|
73
|
-
parts[1].should == parts[3]
|
74
|
-
end
|
75
|
-
|
76
|
-
end
|