joshbuddy-usher 0.4.8 → 0.4.10
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/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
|