usher 0.4.8
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/History.txt +3 -0
- data/Manifest.txt +35 -0
- data/README.rdoc +126 -0
- data/Rakefile +72 -0
- data/VERSION.yml +4 -0
- data/lib/usher/exceptions.rb +5 -0
- data/lib/usher/generate.rb +131 -0
- data/lib/usher/grapher.rb +65 -0
- data/lib/usher/interface/email_interface.rb +27 -0
- data/lib/usher/interface/merb_interface.rb +61 -0
- data/lib/usher/interface/rack_interface/mapper.rb +0 -0
- data/lib/usher/interface/rack_interface/route.rb +9 -0
- data/lib/usher/interface/rack_interface.rb +37 -0
- data/lib/usher/interface/rails2_2_interface/mapper.rb +44 -0
- data/lib/usher/interface/rails2_2_interface.rb +135 -0
- data/lib/usher/interface/rails2_3_interface.rb +135 -0
- data/lib/usher/interface.rb +27 -0
- data/lib/usher/node.rb +138 -0
- data/lib/usher/route/path.rb +24 -0
- data/lib/usher/route/request_method.rb +22 -0
- data/lib/usher/route/variable.rb +37 -0
- data/lib/usher/route.rb +58 -0
- data/lib/usher/splitter.rb +159 -0
- data/lib/usher.rb +184 -0
- data/rails/init.rb +8 -0
- data/spec/private/email/recognize_spec.rb +38 -0
- data/spec/private/generate_spec.rb +141 -0
- data/spec/private/grapher_spec.rb +41 -0
- data/spec/private/path_spec.rb +68 -0
- data/spec/private/rack/dispatch_spec.rb +29 -0
- data/spec/private/rails2_2/compat.rb +1 -0
- data/spec/private/rails2_2/generate_spec.rb +28 -0
- data/spec/private/rails2_2/path_spec.rb +16 -0
- data/spec/private/rails2_2/recognize_spec.rb +79 -0
- data/spec/private/rails2_3/compat.rb +1 -0
- data/spec/private/rails2_3/generate_spec.rb +28 -0
- data/spec/private/rails2_3/path_spec.rb +16 -0
- data/spec/private/rails2_3/recognize_spec.rb +79 -0
- data/spec/private/recognize_spec.rb +178 -0
- data/spec/private/request_method_spec.rb +15 -0
- data/spec/private/split_spec.rb +76 -0
- data/spec/spec.opts +7 -0
- metadata +120 -0
@@ -0,0 +1,135 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'rails2_2_interface', 'mapper')
|
2
|
+
|
3
|
+
class Usher
|
4
|
+
module Interface
|
5
|
+
class Rails2_2Interface
|
6
|
+
|
7
|
+
attr_reader :usher
|
8
|
+
attr_accessor :configuration_file
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
reset!
|
12
|
+
end
|
13
|
+
|
14
|
+
def reset!
|
15
|
+
@usher ||= Usher.new
|
16
|
+
@url_generator ||= Usher::Generators::URL.new(@usher)
|
17
|
+
@module ||= Module.new
|
18
|
+
@module.instance_methods.each do |selector|
|
19
|
+
@module.class_eval { remove_method selector }
|
20
|
+
end
|
21
|
+
@controller_action_route_added = false
|
22
|
+
@controller_route_added = false
|
23
|
+
@usher.reset!
|
24
|
+
end
|
25
|
+
|
26
|
+
def add_route(path, options = {})
|
27
|
+
if !@controller_action_route_added && path =~ %r{^/?:controller/:action/:id$}
|
28
|
+
add_route('/:controller/:action', options.dup)
|
29
|
+
@controller_action_route_added = true
|
30
|
+
end
|
31
|
+
|
32
|
+
if !@controller_route_added && path =~ %r{^/?:controller/:action$}
|
33
|
+
add_route('/:controller', options.merge({:action => 'index'}))
|
34
|
+
@controller_route_added = true
|
35
|
+
end
|
36
|
+
|
37
|
+
options[:action] = 'index' unless options[:action]
|
38
|
+
|
39
|
+
path[0, 0] = '/' unless path[0] == ?/
|
40
|
+
route = @usher.add_route(path, options)
|
41
|
+
raise "your route must include a controller" unless route.paths.first.dynamic_keys.include?(:controller) || route.destination.include?(:controller)
|
42
|
+
route
|
43
|
+
end
|
44
|
+
|
45
|
+
def recognize(request)
|
46
|
+
node = @usher.recognize(request)
|
47
|
+
params = node.params.inject({}){|h,(k,v)| h[k]=v; h }
|
48
|
+
request.path_parameters = (node.params.empty? ? node.path.route.destination : node.path.route.destination.merge(params)).with_indifferent_access
|
49
|
+
"#{request.path_parameters[:controller].camelize}Controller".constantize
|
50
|
+
rescue
|
51
|
+
raise ActionController::RoutingError, "No route matches #{request.path.inspect} with #{request.inspect}"
|
52
|
+
end
|
53
|
+
|
54
|
+
def add_named_route(name, route, options = {})
|
55
|
+
@usher.add_route(route, options).name(name)
|
56
|
+
end
|
57
|
+
|
58
|
+
def route_count
|
59
|
+
@usher.route_count
|
60
|
+
end
|
61
|
+
|
62
|
+
def empty?
|
63
|
+
@usher.route_count.zero?
|
64
|
+
end
|
65
|
+
|
66
|
+
def generate(options, recall = {}, method = :generate, route_name = nil)
|
67
|
+
route = if(route_name)
|
68
|
+
@usher.named_routes[route_name]
|
69
|
+
else
|
70
|
+
merged_options = options
|
71
|
+
merged_options[:controller] = recall[:controller] unless options.key?(:controller)
|
72
|
+
unless options.key?(:action)
|
73
|
+
options[:action] = ''
|
74
|
+
end
|
75
|
+
path_for_options(merged_options)
|
76
|
+
end
|
77
|
+
case method
|
78
|
+
when :generate
|
79
|
+
merged_options ||= recall.merge(options)
|
80
|
+
url = generate_url(route, merged_options)
|
81
|
+
url.slice!(-1) if url[-1] == ?/
|
82
|
+
url
|
83
|
+
else
|
84
|
+
raise "method #{method} not recognized"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def generate_url(route, params)
|
89
|
+
@url_generator.generate(route, params)
|
90
|
+
end
|
91
|
+
|
92
|
+
def path_for_options(options)
|
93
|
+
@usher.path_for_options(options)
|
94
|
+
end
|
95
|
+
|
96
|
+
def named_routes
|
97
|
+
@usher.named_routes
|
98
|
+
end
|
99
|
+
|
100
|
+
def reload
|
101
|
+
@usher.reset!
|
102
|
+
if @configuration_file
|
103
|
+
Kernel.load(@configuration_file)
|
104
|
+
else
|
105
|
+
@usher.add_route ":controller/:action/:id"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def load_routes!
|
110
|
+
reload
|
111
|
+
end
|
112
|
+
|
113
|
+
def draw
|
114
|
+
reset!
|
115
|
+
yield Mapper.new(self)
|
116
|
+
install_helpers
|
117
|
+
end
|
118
|
+
|
119
|
+
def install_helpers(destinations = [ActionController::Base, ActionView::Base], regenerate_code = false)
|
120
|
+
#*_url and hash_for_*_url
|
121
|
+
Array(destinations).each do |d| d.module_eval { include Helpers }
|
122
|
+
@usher.named_routes.keys.each do |name|
|
123
|
+
@module.module_eval <<-end_eval # We use module_eval to avoid leaks
|
124
|
+
def #{name}_url(options = {})
|
125
|
+
ActionController::Routing::UsherRoutes.generate(options, {}, :generate, :#{name})
|
126
|
+
end
|
127
|
+
end_eval
|
128
|
+
end
|
129
|
+
d.__send__(:include, @module)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
class Usher
|
2
|
+
module Interface
|
3
|
+
class Rails2_3Interface
|
4
|
+
|
5
|
+
attr_reader :configuration_files
|
6
|
+
|
7
|
+
def named_routes
|
8
|
+
@router.named_routes
|
9
|
+
end
|
10
|
+
|
11
|
+
def add_named_route(name, route, options = {})
|
12
|
+
@router.add_route(route, options).name(name)
|
13
|
+
end
|
14
|
+
|
15
|
+
def add_route(path, options = {})
|
16
|
+
if !@controller_action_route_added && path =~ %r{^/?:controller/:action/:id$}
|
17
|
+
add_route('/:controller/:action', options.dup)
|
18
|
+
@controller_action_route_added = true
|
19
|
+
end
|
20
|
+
|
21
|
+
if !@controller_route_added && path =~ %r{^/?:controller/:action$}
|
22
|
+
add_route('/:controller', options.merge({:action => 'index'}))
|
23
|
+
@controller_route_added = true
|
24
|
+
end
|
25
|
+
|
26
|
+
options[:action] = 'index' unless options[:action]
|
27
|
+
|
28
|
+
path[0, 0] = '/' unless path[0] == ?/
|
29
|
+
route = @router.add_route(path, options).to(options)
|
30
|
+
|
31
|
+
raise "your route must include a controller" unless (route.paths.first.dynamic_keys.include?(:controller) || route.destination.include?(:controller))
|
32
|
+
route
|
33
|
+
end
|
34
|
+
|
35
|
+
def initialize
|
36
|
+
reset!
|
37
|
+
end
|
38
|
+
|
39
|
+
def add_configuration_file(file)
|
40
|
+
@configuration_files << file
|
41
|
+
end
|
42
|
+
|
43
|
+
def reload!
|
44
|
+
if configuration_files.any?
|
45
|
+
configuration_files.each { |config| load(config) }
|
46
|
+
else
|
47
|
+
add_route ":controller/:action/:id"
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
alias_method :reload, :reload!
|
52
|
+
|
53
|
+
def route_count
|
54
|
+
routes.size
|
55
|
+
end
|
56
|
+
|
57
|
+
def routes
|
58
|
+
@router.routes
|
59
|
+
end
|
60
|
+
|
61
|
+
def call(env)
|
62
|
+
request = ActionController::Request.new(env)
|
63
|
+
app = recognize(request)
|
64
|
+
app.call(env).to_a
|
65
|
+
end
|
66
|
+
|
67
|
+
def recognize(request)
|
68
|
+
response = @router.recognize(request)
|
69
|
+
request.path_parameters = (response.params.empty? ? response.path.route.destination : response.path.route.destination.merge(response.params.inject({}){|h,(k,v)| h[k]=v; h })).with_indifferent_access
|
70
|
+
response.params.each { |pair| request.path_parameters[pair.first] = pair.last }
|
71
|
+
"#{request.path_parameters[:controller].camelize}Controller".constantize
|
72
|
+
end
|
73
|
+
|
74
|
+
def reset!
|
75
|
+
@router = Usher.new
|
76
|
+
@url_generator = Usher::Generators::URL.new(@router)
|
77
|
+
@configuration_files = []
|
78
|
+
@module ||= Module.new
|
79
|
+
@controller_route_added = false
|
80
|
+
@controller_action_route_added = false
|
81
|
+
end
|
82
|
+
|
83
|
+
def draw
|
84
|
+
reset!
|
85
|
+
yield ActionController::Routing::RouteSet::Mapper.new(self)
|
86
|
+
install_helpers
|
87
|
+
end
|
88
|
+
|
89
|
+
def install_helpers(destinations = [ActionController::Base, ActionView::Base], regenerate_code = false)
|
90
|
+
#*_url and hash_for_*_url
|
91
|
+
Array(destinations).each do |d| d.module_eval { include Helpers }
|
92
|
+
@router.named_routes.keys.each do |name|
|
93
|
+
@module.module_eval <<-end_eval # We use module_eval to avoid leaks
|
94
|
+
def #{name}_url(options = {})
|
95
|
+
ActionController::Routing::UsherRoutes.generate(options, {}, :generate, :#{name})
|
96
|
+
end
|
97
|
+
end_eval
|
98
|
+
end
|
99
|
+
d.__send__(:include, @module)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def generate(options, recall = {}, method = :generate, route_name = nil)
|
104
|
+
route = if(route_name)
|
105
|
+
@router.named_routes[route_name]
|
106
|
+
else
|
107
|
+
merged_options = options
|
108
|
+
merged_options[:controller] = recall[:controller] unless options.key?(:controller)
|
109
|
+
unless options.key?(:action)
|
110
|
+
options[:action] = ''
|
111
|
+
end
|
112
|
+
path_for_options(merged_options)
|
113
|
+
end
|
114
|
+
case method
|
115
|
+
when :generate
|
116
|
+
merged_options ||= recall.merge(options)
|
117
|
+
url = generate_url(route, merged_options)
|
118
|
+
url.slice!(-1) if url[-1] == ?/
|
119
|
+
url
|
120
|
+
else
|
121
|
+
raise "method #{method} not recognized"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def generate_url(route, params)
|
126
|
+
@url_generator.generate(route, params)
|
127
|
+
end
|
128
|
+
|
129
|
+
def path_for_options(options)
|
130
|
+
@router.path_for_options(options)
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class Usher
|
2
|
+
module Interface
|
3
|
+
autoload :Rails2_2Interface, File.join(File.dirname(__FILE__), 'interface', 'rails2_2_interface')
|
4
|
+
autoload :Rails2_3Interface, File.join(File.dirname(__FILE__), 'interface', 'rails2_3_interface')
|
5
|
+
autoload :MerbInterface, File.join(File.dirname(__FILE__), 'interface', 'merb_interface')
|
6
|
+
autoload :RackInterface, File.join(File.dirname(__FILE__), 'interface', 'rack_interface')
|
7
|
+
autoload :EmailInterface, File.join(File.dirname(__FILE__), 'interface', 'email_interface')
|
8
|
+
|
9
|
+
def self.for(type, &blk)
|
10
|
+
case type
|
11
|
+
when :rails2_2
|
12
|
+
Rails2_2Interface.new(&blk)
|
13
|
+
when :rails2_3
|
14
|
+
Rails2_3Interface.new(&blk)
|
15
|
+
when :merb
|
16
|
+
MerbInterface.new(&blk)
|
17
|
+
when :rack
|
18
|
+
RackInterface.new(&blk)
|
19
|
+
when :email
|
20
|
+
EmailInterface.new(&blk)
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
data/lib/usher/node.rb
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
require 'fuzzy_hash'
|
2
|
+
|
3
|
+
class Usher
|
4
|
+
|
5
|
+
class Node
|
6
|
+
|
7
|
+
Response = Struct.new(:path, :params)
|
8
|
+
|
9
|
+
attr_reader :lookup
|
10
|
+
attr_accessor :terminates, :exclusive_type, :parent, :value, :request_methods, :globs_capture_separators
|
11
|
+
|
12
|
+
def initialize(parent, value)
|
13
|
+
@parent = parent
|
14
|
+
@value = value
|
15
|
+
@lookup = Hash.new
|
16
|
+
@exclusive_type = nil
|
17
|
+
end
|
18
|
+
|
19
|
+
def upgrade_lookup
|
20
|
+
@lookup = FuzzyHash.new(@lookup)
|
21
|
+
end
|
22
|
+
|
23
|
+
def depth
|
24
|
+
@depth ||= @parent && @parent.is_a?(Node) ? @parent.depth + 1 : 0
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.root(route_set, request_methods, globs_capture_separators)
|
28
|
+
root = self.new(route_set, nil)
|
29
|
+
root.request_methods = request_methods
|
30
|
+
root.globs_capture_separators = globs_capture_separators
|
31
|
+
root
|
32
|
+
end
|
33
|
+
|
34
|
+
def terminates?
|
35
|
+
@terminates
|
36
|
+
end
|
37
|
+
|
38
|
+
def pp
|
39
|
+
$stdout << " " * depth
|
40
|
+
$stdout << "#{depth}: #{value.inspect} #{!!terminates?}\n"
|
41
|
+
@lookup.each do |k,v|
|
42
|
+
$stdout << " " * (depth + 1)
|
43
|
+
$stdout << "#{k} ==> \n"
|
44
|
+
v.pp
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def add(route)
|
49
|
+
route.paths.each do |path|
|
50
|
+
parts = path.parts.dup
|
51
|
+
request_methods.each do |type|
|
52
|
+
parts.push(Route::RequestMethod.new(type, route.conditions[type])) if route.conditions && route.conditions.key?(type)
|
53
|
+
end
|
54
|
+
|
55
|
+
current_node = self
|
56
|
+
until parts.size.zero?
|
57
|
+
key = parts.shift
|
58
|
+
target_node = case key
|
59
|
+
when Route::RequestMethod
|
60
|
+
current_node.upgrade_lookup if key.value.is_a?(Regexp)
|
61
|
+
if current_node.exclusive_type == key.type
|
62
|
+
current_node.lookup[key.value] ||= Node.new(current_node, key)
|
63
|
+
elsif current_node.lookup.empty?
|
64
|
+
current_node.exclusive_type = key.type
|
65
|
+
current_node.lookup[key.value] ||= Node.new(current_node, key)
|
66
|
+
else
|
67
|
+
parts.unshift(key)
|
68
|
+
current_node.lookup[nil] ||= Node.new(current_node, Route::RequestMethod.new(current_node.exclusive_type, nil))
|
69
|
+
end
|
70
|
+
else
|
71
|
+
key.globs_capture_separators = globs_capture_separators if key.is_a?(Route::Variable)
|
72
|
+
|
73
|
+
if !key.is_a?(Route::Variable)
|
74
|
+
current_node.upgrade_lookup if key.is_a?(Regexp)
|
75
|
+
current_node.lookup[key] ||= Node.new(current_node, key)
|
76
|
+
elsif key.regex_matcher
|
77
|
+
current_node.upgrade_lookup
|
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
|
82
|
+
end
|
83
|
+
current_node = target_node
|
84
|
+
end
|
85
|
+
current_node.terminates = path
|
86
|
+
end
|
87
|
+
route
|
88
|
+
end
|
89
|
+
|
90
|
+
def find(usher, request, path, params = [])
|
91
|
+
if exclusive_type
|
92
|
+
[lookup[request.send(exclusive_type)], lookup[nil]].each do |n|
|
93
|
+
if n && (ret = n.find(usher, request, path.dup, params.dup))
|
94
|
+
return ret
|
95
|
+
end
|
96
|
+
end
|
97
|
+
elsif path.size.zero? && terminates?
|
98
|
+
Response.new(terminates, params)
|
99
|
+
elsif !path.size.zero? && (next_part = lookup[part = path.shift] || lookup[nil])
|
100
|
+
case next_part.value
|
101
|
+
when Route::Variable
|
102
|
+
case next_part.value.type
|
103
|
+
when :*
|
104
|
+
params << [next_part.value.name, []] unless params.last && params.last.first == next_part.value.name
|
105
|
+
loop do
|
106
|
+
if (next_part.value.look_ahead === part || (!usher.splitter.delimiter_chars.include?(part[0]) && next_part.value.regex_matcher && !next_part.value.regex_matcher.match(part)))
|
107
|
+
path.unshift(part)
|
108
|
+
path.unshift(next_part.parent.value) if usher.splitter.delimiter_chars.include?(next_part.parent.value[0])
|
109
|
+
break
|
110
|
+
elsif next_part.value.globs_capture_separators
|
111
|
+
params.last.last << part
|
112
|
+
elsif !usher.splitter.delimiter_chars.include?(part[0])
|
113
|
+
next_part.value.valid!(part)
|
114
|
+
params.last.last << part
|
115
|
+
end
|
116
|
+
if path.size.zero?
|
117
|
+
break
|
118
|
+
else
|
119
|
+
part = path.shift
|
120
|
+
end
|
121
|
+
end
|
122
|
+
when :':'
|
123
|
+
var = next_part.value
|
124
|
+
var.valid!(part)
|
125
|
+
params << [var.name, part]
|
126
|
+
until (var.look_ahead === path.first) || path.empty?
|
127
|
+
params.last.last << path.shift
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
next_part.find(usher, request, path, params)
|
132
|
+
else
|
133
|
+
nil
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class Usher
|
2
|
+
class Route
|
3
|
+
class Path
|
4
|
+
|
5
|
+
attr_reader :dynamic_parts, :dynamic_map, :dynamic_indicies, :route, :parts, :dynamic_required_keys, :dynamic_keys
|
6
|
+
|
7
|
+
def initialize(route, parts)
|
8
|
+
@route = route
|
9
|
+
@parts = parts
|
10
|
+
@dynamic_indicies = []
|
11
|
+
@parts.each_index{|i| @dynamic_indicies << i if @parts[i].is_a?(Variable)}
|
12
|
+
@dynamic_parts = @parts.values_at(*@dynamic_indicies)
|
13
|
+
@dynamic_map = {}
|
14
|
+
@dynamic_parts.each{|p| @dynamic_map[p.name] = p }
|
15
|
+
@dynamic_keys = @dynamic_map.keys
|
16
|
+
@dynamic_required_keys = @dynamic_parts.select{|dp| !dp.default_value}.map{|dp| dp.name}
|
17
|
+
end
|
18
|
+
|
19
|
+
def can_generate_from?(keys)
|
20
|
+
(@dynamic_required_keys - keys).size.zero?
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class Usher
|
2
|
+
class Route
|
3
|
+
class RequestMethod
|
4
|
+
|
5
|
+
attr_reader :type, :value
|
6
|
+
|
7
|
+
def initialize(type, value)
|
8
|
+
@type = type
|
9
|
+
@value = value
|
10
|
+
end
|
11
|
+
|
12
|
+
def hash
|
13
|
+
type.hash + value.hash
|
14
|
+
end
|
15
|
+
|
16
|
+
def eql?(o)
|
17
|
+
o.is_a?(self.class) && o.type == type && o.value == value
|
18
|
+
end
|
19
|
+
alias == eql?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class Usher
|
2
|
+
class Route
|
3
|
+
class Variable
|
4
|
+
attr_reader :type, :name, :validator, :regex_matcher
|
5
|
+
attr_accessor :look_ahead, :globs_capture_separators, :default_value
|
6
|
+
|
7
|
+
def initialize(type, name, validator = nil, regex_matcher = nil, globs_capture_separators = false)
|
8
|
+
@type = type
|
9
|
+
@name = :"#{name}"
|
10
|
+
@validator = validator
|
11
|
+
@regex_matcher = regex_matcher
|
12
|
+
@globs_capture_separators = globs_capture_separators
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_s
|
16
|
+
"#{type}#{name}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def valid!(val)
|
20
|
+
case @validator
|
21
|
+
when Proc
|
22
|
+
begin
|
23
|
+
@validator.call(val)
|
24
|
+
rescue Exception => e
|
25
|
+
raise ValidationException.new("#{val} does not conform to #{@validator}, root cause #{e.inspect}")
|
26
|
+
end
|
27
|
+
else
|
28
|
+
@validator === val or raise(ValidationException.new("#{val} does not conform to #{@validator}, root cause #{e.inspect}"))
|
29
|
+
end if @validator
|
30
|
+
end
|
31
|
+
|
32
|
+
def ==(o)
|
33
|
+
o && (o.type == @type && o.name == @name && o.validator == @validator)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/usher/route.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'route', 'path')
|
2
|
+
require File.join(File.dirname(__FILE__), 'route', 'variable')
|
3
|
+
require File.join(File.dirname(__FILE__), 'route', 'request_method')
|
4
|
+
|
5
|
+
class Usher
|
6
|
+
class Route
|
7
|
+
attr_reader :paths, :original_path, :requirements, :conditions, :destination, :named, :generate_with
|
8
|
+
|
9
|
+
GenerateWith = Struct.new(:scheme, :port, :host)
|
10
|
+
|
11
|
+
def initialize(original_path, router, conditions, requirements, default_values, generate_with) # :nodoc:
|
12
|
+
@original_path = original_path
|
13
|
+
@router = router
|
14
|
+
@requirements = requirements
|
15
|
+
@conditions = conditions
|
16
|
+
@default_values = default_values
|
17
|
+
@paths = @router.splitter.split(@original_path, @requirements, @default_values).collect {|path| Path.new(self, path)}
|
18
|
+
@generate_with = GenerateWith.new(generate_with[:scheme], generate_with[:port], generate_with[:host]) if generate_with
|
19
|
+
end
|
20
|
+
|
21
|
+
def grapher
|
22
|
+
unless @grapher
|
23
|
+
@grapher = Grapher.new
|
24
|
+
@grapher.add_route(self)
|
25
|
+
end
|
26
|
+
@grapher
|
27
|
+
end
|
28
|
+
|
29
|
+
def find_matching_path(params)
|
30
|
+
@paths.size == 1 ? @paths.first : grapher.find_matching_path(params)
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
# Sets +options+ on a route. Returns +self+.
|
35
|
+
#
|
36
|
+
# Request = Struct.new(:path)
|
37
|
+
# set = Usher.new
|
38
|
+
# route = set.add_route('/test')
|
39
|
+
# route.to(:controller => 'testing', :action => 'index')
|
40
|
+
# set.recognize(Request.new('/test')).first.params => {:controller => 'testing', :action => 'index'}
|
41
|
+
def to(options = nil, &block)
|
42
|
+
@destination = (block_given? ? block : options)
|
43
|
+
self
|
44
|
+
end
|
45
|
+
|
46
|
+
# Sets route as referenceable from +name+. Returns +self+.
|
47
|
+
#
|
48
|
+
# set = Usher.new
|
49
|
+
# route = set.add_route('/test').name(:route)
|
50
|
+
# set.generate_url(:route) => '/test'
|
51
|
+
def name(name)
|
52
|
+
@named = name
|
53
|
+
@router.name(name, self)
|
54
|
+
self
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|