usher 0.4.8
Sign up to get free protection for your applications and to get access to all the features.
- 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
data/History.txt
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
README.rdoc
|
4
|
+
Rakefile
|
5
|
+
lib/compat.rb
|
6
|
+
lib/usher.rb
|
7
|
+
lib/usher/exceptions.rb
|
8
|
+
lib/usher/grapher.rb
|
9
|
+
lib/usher/interface.rb
|
10
|
+
lib/usher/interface/merb_interface.rb
|
11
|
+
lib/usher/interface/rack_interface.rb
|
12
|
+
lib/usher/interface/rack_interface/mapper.rb
|
13
|
+
lib/usher/interface/rack_interface/route.rb
|
14
|
+
lib/usher/interface/rails2_interface.rb
|
15
|
+
lib/usher/interface/rails2_interface/mapper.rb
|
16
|
+
lib/usher/node.rb
|
17
|
+
lib/usher/node/lookup.rb
|
18
|
+
lib/usher/route.rb
|
19
|
+
lib/usher/route/http.rb
|
20
|
+
lib/usher/route/path.rb
|
21
|
+
lib/usher/route/separator.rb
|
22
|
+
lib/usher/route/splitter.rb
|
23
|
+
lib/usher/route/variable.rb
|
24
|
+
rails/init.rb
|
25
|
+
spec/generate_spec.rb
|
26
|
+
spec/node/lookup_spec.rb
|
27
|
+
spec/path_spec.rb
|
28
|
+
spec/rack/dispatch_spec.rb
|
29
|
+
spec/rails/generate_spec.rb
|
30
|
+
spec/rails/path_spec.rb
|
31
|
+
spec/rails/recognize_spec.rb
|
32
|
+
spec/recognize_spec.rb
|
33
|
+
spec/spec.opts
|
34
|
+
spec/split_spec.rb
|
35
|
+
usher.gemspec
|
data/README.rdoc
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
= Usher
|
2
|
+
|
3
|
+
Tree-based router library. Useful for (specifically) for Rails and Rack, but probably generally useful for anyone interested in doing routing. Based on Ilya Grigorik suggestion, turns out looking up in a hash and following a tree is faster than Krauter's massive regex approach.
|
4
|
+
|
5
|
+
== Features
|
6
|
+
|
7
|
+
* Understands single and path-globbing variables
|
8
|
+
* Arbitrary HTTP header requirements
|
9
|
+
* No optimization phase, so routes are always alterable after the fact
|
10
|
+
* Understands Proc and Regex transformations, validations
|
11
|
+
* Really, really fast
|
12
|
+
* Relatively light and happy code-base, should be easy and fun to alter
|
13
|
+
* Interface and implementation are separate, encouraging cross-pollination
|
14
|
+
|
15
|
+
== Route format
|
16
|
+
|
17
|
+
From the rdoc:
|
18
|
+
|
19
|
+
Creates a route from +path+ and +options+
|
20
|
+
|
21
|
+
=== +path+
|
22
|
+
A path consists a mix of dynamic and static parts delimited by <tt>/</tt>
|
23
|
+
|
24
|
+
==== Dynamic
|
25
|
+
Dynamic parts are prefixed with either :, *. :variable matches only one part of the path, whereas *variable can match one or
|
26
|
+
more parts.
|
27
|
+
|
28
|
+
<b>Example:</b>
|
29
|
+
<tt>/path/:variable/path</tt> would match
|
30
|
+
|
31
|
+
* <tt>/path/test/path</tt>
|
32
|
+
* <tt>/path/something_else/path</tt>
|
33
|
+
* <tt>/path/one_more/path</tt>
|
34
|
+
|
35
|
+
In the above examples, 'test', 'something_else' and 'one_more' respectively would be bound to the key <tt>:variable</tt>.
|
36
|
+
However, <tt>/path/test/one_more/path</tt> would not be matched.
|
37
|
+
|
38
|
+
<b>Example:</b>
|
39
|
+
<tt>/path/*variable/path</tt> would match
|
40
|
+
|
41
|
+
* <tt>/path/one/two/three/path</tt>
|
42
|
+
* <tt>/path/four/five/path</tt>
|
43
|
+
|
44
|
+
In the above examples, ['one', 'two', 'three'] and ['four', 'five'] respectively would be bound to the key :variable.
|
45
|
+
|
46
|
+
As well, variables can have a regex matcher.
|
47
|
+
|
48
|
+
<b>Example:</b>
|
49
|
+
<tt>/product/{:id,\d+}</tt> would match
|
50
|
+
|
51
|
+
* <tt>/product/123</tt>
|
52
|
+
* <tt>/product/4521</tt>
|
53
|
+
|
54
|
+
But not
|
55
|
+
* <tt>/product/AE-35</tt>
|
56
|
+
|
57
|
+
As well, the same logic applies for * variables as well, where only parts matchable by the supplied regex will
|
58
|
+
actually be bound to the variable
|
59
|
+
|
60
|
+
==== Static
|
61
|
+
|
62
|
+
Static parts of literal character sequences. For instance, <tt>/path/something.html</tt> would match only the same path.
|
63
|
+
As well, static parts can have a regex pattern in them as well, such as <tt>/path/something.{html|xml}</tt> which would match only
|
64
|
+
<tt>/path/something.html</tt> and <tt>/path/something.xml</tt>
|
65
|
+
|
66
|
+
==== Optional sections
|
67
|
+
|
68
|
+
Sections of a route can be marked as optional by surrounding it with brackets. For instance, in the above static example, <tt>/path/something(.html)</tt> would match both <tt>/path/something</tt> and <tt>/path/something.html</tt>.
|
69
|
+
|
70
|
+
==== One and only one sections
|
71
|
+
|
72
|
+
Sections of a route can be marked as "one and only one" by surrounding it with brackets and separating parts of the route with pipes.
|
73
|
+
For instance, the path, <tt>/path/something(.xml|.html)</tt> would only match <tt>/path/something.xml</tt> and
|
74
|
+
<tt>/path/something.html</tt>. Generally its more efficent to use one and only sections over using regex.
|
75
|
+
|
76
|
+
=== +options+
|
77
|
+
* +requirements+ - After transformation, tests the condition using ===. If it returns false, it raises an <tt>Usher::ValidationException</tt>
|
78
|
+
* +conditions+ - Accepts any of the +request_methods+ specificied in the construction of Usher. This can be either a <tt>string</tt> or a regular expression.
|
79
|
+
* Any other key is interpreted as a requirement for the variable of its name.
|
80
|
+
|
81
|
+
== Rails
|
82
|
+
|
83
|
+
script/plugin install git://github.com/joshbuddy/usher.git
|
84
|
+
|
85
|
+
== Rack
|
86
|
+
|
87
|
+
=== config.ru
|
88
|
+
|
89
|
+
require 'usher'
|
90
|
+
app = proc do |env|
|
91
|
+
body = "Hi there #{env['usher.params'][:name]}"
|
92
|
+
[
|
93
|
+
200, # Status code
|
94
|
+
{ # Response headers
|
95
|
+
'Content-Type' => 'text/plain',
|
96
|
+
'Content-Length' => body.size.to_s,
|
97
|
+
},
|
98
|
+
[body] # Response body
|
99
|
+
]
|
100
|
+
end
|
101
|
+
|
102
|
+
routes = Usher::Interface.for(:rack) do
|
103
|
+
add('/hello/:name').to(app)
|
104
|
+
end
|
105
|
+
|
106
|
+
run routes
|
107
|
+
|
108
|
+
------------
|
109
|
+
|
110
|
+
>> curl http://127.0.0.1:3000/hello/samueltanders
|
111
|
+
<< Hi there samueltanders
|
112
|
+
|
113
|
+
== DONE
|
114
|
+
|
115
|
+
* add support for () optional parts
|
116
|
+
* Add support for arbitrary HTTP header checks
|
117
|
+
* Emit exceptions inline with relevant interfaces
|
118
|
+
* More RDoc! (optionally cowbell)
|
119
|
+
|
120
|
+
== TODO
|
121
|
+
|
122
|
+
* Make it integrate with merb
|
123
|
+
* Make it integrate with rails3
|
124
|
+
* Create decent DSL for use with rack
|
125
|
+
|
126
|
+
(Let me show you to your request)
|
data/Rakefile
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'lib/usher'
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'jeweler'
|
5
|
+
Jeweler::Tasks.new do |s|
|
6
|
+
s.name = "usher"
|
7
|
+
s.description = s.summary = "A general purpose routing library"
|
8
|
+
s.email = "joshbuddy@gmail.com"
|
9
|
+
s.homepage = "http://github.com/joshbuddy/usher"
|
10
|
+
s.authors = ["Joshua Hull"]
|
11
|
+
s.files = FileList["[A-Z]*", "{lib,spec,rails}/**/*"]
|
12
|
+
s.add_dependency 'fuzzyhash', '>=0.0.3'
|
13
|
+
s.rubyforge_project = 'joshbuddy-usher' # This line would be new
|
14
|
+
end
|
15
|
+
rescue LoadError
|
16
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
17
|
+
end
|
18
|
+
|
19
|
+
require 'spec'
|
20
|
+
require 'spec/rake/spectask'
|
21
|
+
task :spec => 'spec:all'
|
22
|
+
namespace(:spec) do
|
23
|
+
Spec::Rake::SpecTask.new(:all) do |t|
|
24
|
+
t.spec_opts ||= []
|
25
|
+
t.spec_opts << "-rubygems"
|
26
|
+
t.spec_opts << "--options" << "spec/spec.opts"
|
27
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
desc "Run all examples with RCov"
|
33
|
+
Spec::Rake::SpecTask.new('spec_with_rcov') do |t|
|
34
|
+
t.spec_files = FileList['spec/**/*.rb']
|
35
|
+
t.rcov = true
|
36
|
+
t.rcov_opts = ['--exclude', 'spec']
|
37
|
+
end
|
38
|
+
|
39
|
+
require 'rake/rdoctask'
|
40
|
+
desc "Generate documentation"
|
41
|
+
Rake::RDocTask.new do |rd|
|
42
|
+
rd.main = "README.rdoc"
|
43
|
+
rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
|
44
|
+
rd.rdoc_dir = 'rdoc'
|
45
|
+
end
|
46
|
+
|
47
|
+
# These are new tasks
|
48
|
+
begin
|
49
|
+
require 'rake/contrib/sshpublisher'
|
50
|
+
namespace :rubyforge do
|
51
|
+
|
52
|
+
desc "Release gem and RDoc documentation to RubyForge"
|
53
|
+
task :release => ["rubyforge:release:gem", "rubyforge:release:docs"]
|
54
|
+
|
55
|
+
namespace :release do
|
56
|
+
desc "Publish RDoc to RubyForge."
|
57
|
+
task :docs => [:rdoc] do
|
58
|
+
config = YAML.load(
|
59
|
+
File.read(File.expand_path('~/.rubyforge/user-config.yml'))
|
60
|
+
)
|
61
|
+
|
62
|
+
host = "#{config['username']}@rubyforge.org"
|
63
|
+
remote_dir = "/var/www/gforge-projects/joshbuddy-usher/"
|
64
|
+
local_dir = 'rdoc'
|
65
|
+
|
66
|
+
Rake::SshDirPublisher.new(host, remote_dir, local_dir).upload
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
rescue LoadError
|
71
|
+
puts "Rake SshDirPublisher is unavailable or your rubyforge environment is not configured."
|
72
|
+
end
|
data/VERSION.yml
ADDED
@@ -0,0 +1,131 @@
|
|
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
|
@@ -0,0 +1,65 @@
|
|
1
|
+
class Usher
|
2
|
+
class Grapher
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
reset!
|
6
|
+
end
|
7
|
+
|
8
|
+
def reset!
|
9
|
+
@significant_keys = nil
|
10
|
+
@orders = Hash.new{|h,k| h[k] = Hash.new{|h2, k2| h2[k2] = []}}
|
11
|
+
@key_count = Hash.new(0)
|
12
|
+
@cache = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def add_route(route)
|
16
|
+
route.paths.each do |path|
|
17
|
+
unless path.dynamic_keys.size.zero?
|
18
|
+
path.dynamic_keys.each do |k|
|
19
|
+
@orders[path.dynamic_keys.size][k] << path
|
20
|
+
@key_count[k] += 1
|
21
|
+
end
|
22
|
+
|
23
|
+
dynamic_parts_with_defaults = path.dynamic_parts.select{|part| part.default_value }.map{|dp| dp.name}
|
24
|
+
dynamic_parts_without_defaults = path.dynamic_parts.select{|part| !part.default_value }.map{|dp| dp.name}
|
25
|
+
|
26
|
+
(1...(2 ** (dynamic_parts_with_defaults.size))).each do |i|
|
27
|
+
current_set = dynamic_parts_without_defaults.dup
|
28
|
+
dynamic_parts_with_defaults.each_with_index do |dp, index|
|
29
|
+
current_set << dp unless (index & i) == 0
|
30
|
+
end
|
31
|
+
|
32
|
+
current_set.each do |k|
|
33
|
+
@orders[current_set.size][k] << path
|
34
|
+
@key_count[k] += 1
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def significant_keys
|
42
|
+
@significant_keys ||= @key_count.keys.uniq
|
43
|
+
end
|
44
|
+
|
45
|
+
def find_matching_path(params)
|
46
|
+
unless params.empty?
|
47
|
+
set = params.keys & significant_keys
|
48
|
+
if cached = @cache[set]
|
49
|
+
return cached
|
50
|
+
end
|
51
|
+
set.size.downto(1) do |o|
|
52
|
+
set.each do |k|
|
53
|
+
@orders[o][k].each { |r|
|
54
|
+
if r.can_generate_from?(set)
|
55
|
+
@cache[set] = r
|
56
|
+
return r
|
57
|
+
end
|
58
|
+
}
|
59
|
+
end
|
60
|
+
end
|
61
|
+
nil
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class Usher
|
2
|
+
module Interface
|
3
|
+
class EmailInterface
|
4
|
+
|
5
|
+
def initialize(&blk)
|
6
|
+
@routes = Usher.new(:delimiters => ['@', '-', '.'], :valid_regex => '[\+a-zA-Z0-9]+', :globs_capture_separators => true)
|
7
|
+
instance_eval(&blk) if blk
|
8
|
+
end
|
9
|
+
|
10
|
+
def for(path, &block)
|
11
|
+
@routes.add_route(path).to(block)
|
12
|
+
end
|
13
|
+
|
14
|
+
def reset!
|
15
|
+
@routes.reset!
|
16
|
+
end
|
17
|
+
|
18
|
+
def act(email)
|
19
|
+
response = @routes.recognize(email, email)
|
20
|
+
if response.path
|
21
|
+
response.path.route.destination.call(response.params.inject({}){|h,(k,v)| h[k]=v.to_s; h })
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'merb-core'
|
2
|
+
require 'merb-core/dispatch/router/behavior'
|
3
|
+
|
4
|
+
class Usher
|
5
|
+
module Interface
|
6
|
+
class MerbInterface
|
7
|
+
|
8
|
+
# merb does everything with class methods.
|
9
|
+
|
10
|
+
@root_behavior = ::Merb::Router::Behavior.new.defaults(:action => "index")
|
11
|
+
|
12
|
+
class << self
|
13
|
+
attr_accessor :root_behavior
|
14
|
+
|
15
|
+
UsherRoutes = Usher.new
|
16
|
+
|
17
|
+
def prepare(first = [], last = [], &block)
|
18
|
+
@routes = []
|
19
|
+
root_behavior._with_proxy(&block)
|
20
|
+
@routes = first + @routes + last
|
21
|
+
compile
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
def compile
|
26
|
+
routes.each do |r|
|
27
|
+
r.segments
|
28
|
+
end
|
29
|
+
|
30
|
+
#puts r.inspect; UsherRoutes.add_route(r) }
|
31
|
+
#routes.each {|r| }
|
32
|
+
end
|
33
|
+
|
34
|
+
def named_routes
|
35
|
+
UsherRoutes.named_routes
|
36
|
+
end
|
37
|
+
|
38
|
+
def routes
|
39
|
+
UsherRoutes.routes
|
40
|
+
end
|
41
|
+
|
42
|
+
def route_for(request)
|
43
|
+
p request
|
44
|
+
p UsherRoutes.tree
|
45
|
+
UsherRoutes.recognize(request)
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
#class BootLoader < ::Merb::BootLoader
|
51
|
+
#end
|
52
|
+
|
53
|
+
def load_into_merb!
|
54
|
+
::Merb.send(:remove_const, "Router")
|
55
|
+
::Merb.const_set("Router", Usher::Interface::MerbInterface)
|
56
|
+
#::Merb::BootLoader.const_set("Router", Usher::Interface::Merb::BootLoader)
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
File without changes
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'rack'
|
2
|
+
|
3
|
+
class Usher
|
4
|
+
module Interface
|
5
|
+
class RackInterface
|
6
|
+
|
7
|
+
attr_accessor :routes
|
8
|
+
|
9
|
+
def initialize(&blk)
|
10
|
+
@routes = Usher.new(:request_methods => [:method, :host, :port, :scheme])
|
11
|
+
@generator = Usher::Generators::URL.new(@routes)
|
12
|
+
instance_eval(&blk) if blk
|
13
|
+
end
|
14
|
+
|
15
|
+
def add(path, options = nil)
|
16
|
+
@routes.add_route(path, options)
|
17
|
+
end
|
18
|
+
|
19
|
+
def reset!
|
20
|
+
@routes.reset!
|
21
|
+
end
|
22
|
+
|
23
|
+
def call(env)
|
24
|
+
response = @routes.recognize(Rack::Request.new(env))
|
25
|
+
params = {}
|
26
|
+
response.params.each{ |hk| params[hk.first] = hk.last}
|
27
|
+
env['usher.params'] = params
|
28
|
+
response.path.route.destination.call(env)
|
29
|
+
end
|
30
|
+
|
31
|
+
def generate(route, params = nil, options = nil)
|
32
|
+
@generator.generate(route, params, options)
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
class Usher
|
2
|
+
module Interface
|
3
|
+
class Rails2_2Interface
|
4
|
+
|
5
|
+
class Mapper #:doc:
|
6
|
+
def initialize(set) #:nodoc:
|
7
|
+
@set = set
|
8
|
+
end
|
9
|
+
|
10
|
+
def connect(path, options = nil)
|
11
|
+
@set.add_route(path, options)
|
12
|
+
end
|
13
|
+
|
14
|
+
def root(options = {})
|
15
|
+
if options.is_a?(Symbol)
|
16
|
+
if source_route = @set.named_routes[options]
|
17
|
+
options = source_route.conditions.blank? ?
|
18
|
+
source_route.options.merge({ :conditions => source_route.conditions }) : source_route.options
|
19
|
+
end
|
20
|
+
end
|
21
|
+
named_route(:root, '/', options)
|
22
|
+
end
|
23
|
+
|
24
|
+
def named_route(name, path, options = nil)
|
25
|
+
@set.add_named_route(name, path, options)
|
26
|
+
end
|
27
|
+
|
28
|
+
def namespace(name, options = {}, &block)
|
29
|
+
if options[:namespace]
|
30
|
+
with_options({:path_prefix => "#{options.delete(:path_prefix)}/#{name}", :name_prefix => "#{options.delete(:name_prefix)}#{name}_", :namespace => "#{options.delete(:namespace)}#{name}/" }.merge(options), &block)
|
31
|
+
else
|
32
|
+
with_options({:path_prefix => name, :name_prefix => "#{name}_", :namespace => "#{name}/" }.merge(options), &block)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def method_missing(route_name, *args, &proc) #:nodoc:
|
37
|
+
super unless args.length >= 1 && proc.nil?
|
38
|
+
@set.add_named_route(route_name, *args)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|