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
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
|