url_mount 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +11 -0
- data/Gemfile.lock +25 -0
- data/README.textile +131 -0
- data/Rakefile +0 -19
- data/lib/url_mount.rb +71 -28
- data/pkg/url_mount-0.1.0.gem +0 -0
- data/test/helper.rb +1 -0
- data/test/test_url_mount.rb +142 -28
- data/url_mount.gemspec +5 -37
- metadata +49 -20
- data/.document +0 -5
- data/.gitignore +0 -22
- data/README.rdoc +0 -94
- data/VERSION +0 -1
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
---
|
2
|
+
dependencies:
|
3
|
+
rake:
|
4
|
+
group:
|
5
|
+
- :development
|
6
|
+
version: ">= 0"
|
7
|
+
shoulda:
|
8
|
+
group:
|
9
|
+
- :test
|
10
|
+
version: ">= 0"
|
11
|
+
rack:
|
12
|
+
group:
|
13
|
+
- :default
|
14
|
+
version: ">= 0"
|
15
|
+
specs:
|
16
|
+
- rake:
|
17
|
+
version: 0.8.7
|
18
|
+
- rack:
|
19
|
+
version: 1.1.0
|
20
|
+
- shoulda:
|
21
|
+
version: 2.10.3
|
22
|
+
hash: 20813f28addb840e72440df2793e52ace4717691
|
23
|
+
sources:
|
24
|
+
- Rubygems:
|
25
|
+
uri: http://gemcutter.org
|
data/README.textile
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
h1. UrlMount
|
2
|
+
|
3
|
+
UrlMount is a universal mount point designed for use in rack applications.
|
4
|
+
|
5
|
+
It provides a simple way to pass a url mounting point to the mounted application.
|
6
|
+
|
7
|
+
This means that when you mount an application in the url space, it's a simple call to url to get the mount point of where the application is.
|
8
|
+
|
9
|
+
h2. Example
|
10
|
+
|
11
|
+
Say you mount an application at "/foo/bar"
|
12
|
+
|
13
|
+
<pre><code>router.mount("/foo/bar").to(my_app)
|
14
|
+
|
15
|
+
# behind the scenes, the routers mount method should do this:
|
16
|
+
|
17
|
+
if my_app.respond_to?(:url_mount=)
|
18
|
+
mount_point = UrlMount.new("/foo/bar")
|
19
|
+
my_app.url_mount = mount_point
|
20
|
+
end
|
21
|
+
</code></pre>
|
22
|
+
|
23
|
+
This means that if an application can handle a url_mount, you give it one.
|
24
|
+
|
25
|
+
This then allows the +my_app+ applciation, to know where it's mounted. Generating a url is now as simple as
|
26
|
+
<pre><code>File.join(url_mount.url, "/local/url/path")</code></pre>
|
27
|
+
|
28
|
+
The benefit of this is that all routers or applciations can make use of this. If used, it can act as a universal glue between rack applications where parent apps can let the child applications know where they're mounted so the child applciations can have a chance of generating full routes, even when they're not in the request path.
|
29
|
+
|
30
|
+
h3. Show me the code
|
31
|
+
|
32
|
+
url_mount is made to be used with many routers that are currently available.
|
33
|
+
|
34
|
+
<pre><code>
|
35
|
+
# simple string mounting point
|
36
|
+
mount = UrlMount.new("/foo/bar")
|
37
|
+
mount.url == "/foo/bar"
|
38
|
+
|
39
|
+
# Mount Point including variables
|
40
|
+
mount = UrlMount.new("/foo/:bar", :bar => "bar")
|
41
|
+
mount.url(:bar => "something") == "/foo/something"
|
42
|
+
mount.url #=> Raises UrlMount::Ungeneratable because a required variable was not found
|
43
|
+
mount.required_variables == [:bar]
|
44
|
+
|
45
|
+
# UrlMount::Ungeneratable raised when a route cannot be generated without options
|
46
|
+
no_mount = UrlMount.new("/foo/:bar") # fails because the mount point cannot be generated with no options
|
47
|
+
|
48
|
+
# Mount Point including optional variables
|
49
|
+
mount = UrlMount.new("/foo/:bar(/:baz)", :bar => "bar")
|
50
|
+
mount.url(:bar => "doh") == "/foo/doh"
|
51
|
+
mount.url(:bar => "doh", :baz => "hah") == "/foo/doh/hah"
|
52
|
+
mount.required_variables == [:bar]
|
53
|
+
mount.optional_variables == [:baz]
|
54
|
+
|
55
|
+
# Mount Point with defaults
|
56
|
+
mount = UrlMount.new("/foo/:bar(/:baz)", :bar => "default_bar")
|
57
|
+
mount.url == "/foo/default_bar"
|
58
|
+
mount.url(:baz => "baz_value") == "/foo/default_bar/baz_value"
|
59
|
+
mount.url(:bar => "other_bar") == "/foo/other_bar"
|
60
|
+
|
61
|
+
# Using procs for mount point defaults
|
62
|
+
mount = UrlMount.new("/foo/:bar", :bar => proc{"some_bar"})
|
63
|
+
mount.url == "/foo/some_bar"
|
64
|
+
|
65
|
+
# Nested mounting point
|
66
|
+
mount_parent = UrlMount.new("/foo/bar")
|
67
|
+
mount_child = UrlMount.new("/baz/:barry)
|
68
|
+
|
69
|
+
mount_child.url_mount = mount_parent
|
70
|
+
|
71
|
+
mount_parent.url == "/foo/bar"
|
72
|
+
mount_child.url(:barry =>"barry_value") == "/foo/bar/baz/barry_value"
|
73
|
+
</code></pre>
|
74
|
+
|
75
|
+
Considering that UrlMounts can be nested, when you mount an application you should do it something like this.
|
76
|
+
|
77
|
+
<pre><code>
|
78
|
+
if mounted_app.respond_to?(:url_mount=)
|
79
|
+
mount = UrlMount.new(path, deafult_options)
|
80
|
+
|
81
|
+
# If this app is mounted, add this apps mount point to
|
82
|
+
# the mount point for the child app
|
83
|
+
mount.url_mount = self.url_mount if self.url_mount
|
84
|
+
|
85
|
+
mounted_app.url_mount = mount
|
86
|
+
end
|
87
|
+
</code></pre>
|
88
|
+
|
89
|
+
h2. Generating routes
|
90
|
+
|
91
|
+
When you generate routes, you can see which variables will be used by the mount point, so that you can remove these options from the options hash. otherwise, depending on the generating application, you may get query string options.
|
92
|
+
|
93
|
+
<pre><code>
|
94
|
+
url = UrlMount.new(/"foo/:bar(/:baz)", :bar => "some_bar")
|
95
|
+
|
96
|
+
generation_options = {:baz => "baz", :bar => "bar"}
|
97
|
+
mount_point = url.url(generation_options)
|
98
|
+
|
99
|
+
# We can now at this point ask the mount point what variables it will use if present.
|
100
|
+
url.variables.each{|v| generation_options.delete(v)}
|
101
|
+
|
102
|
+
# use the generation_options now without the mount_point variables to interfere with a query string
|
103
|
+
</code></pre>
|
104
|
+
|
105
|
+
h2. Callbacks
|
106
|
+
|
107
|
+
When generating the routes, you can provide one or more callbacks to the mount point. Inside the callbacks you can set options for the routes based on the Rack request.
|
108
|
+
|
109
|
+
<pre><code>mount = UrlMount.new("/foo/:bar") do |env, opts|
|
110
|
+
opts[:bar] ||= "my_bar"
|
111
|
+
end
|
112
|
+
|
113
|
+
mount.url(env) # Rack::Request === env
|
114
|
+
#=> /foo/my_bar
|
115
|
+
</code></pre>
|
116
|
+
|
117
|
+
NOTE: callbacks are only run when a rack environment is given to the url method
|
118
|
+
|
119
|
+
h2. Note on Patches/Pull Requests
|
120
|
+
|
121
|
+
* Fork the project.
|
122
|
+
* Make your feature addition or bug fix.
|
123
|
+
* Add tests for it. This is important so I don't break it in a
|
124
|
+
future version unintentionally.
|
125
|
+
* Commit, do not mess with rakefile, version, or history.
|
126
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
127
|
+
* Send me a pull request. Bonus points for topic branches.
|
128
|
+
|
129
|
+
h2. Copyright
|
130
|
+
|
131
|
+
Copyright (c) 2010 Daniel Neighman. See LICENSE for details.
|
data/Rakefile
CHANGED
@@ -1,23 +1,6 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'rake'
|
3
3
|
|
4
|
-
begin
|
5
|
-
require 'jeweler'
|
6
|
-
Jeweler::Tasks.new do |gem|
|
7
|
-
gem.name = "url_mount"
|
8
|
-
gem.summary = %Q{Universal mounting points for rack applications}
|
9
|
-
gem.description = %Q{Glue to allow mounted rack applications to know where they're mounted}
|
10
|
-
gem.email = "has.sox@gmail.com"
|
11
|
-
gem.homepage = "http://github.com/hassox/url_mount"
|
12
|
-
gem.authors = ["Daniel Neighman"]
|
13
|
-
gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
|
14
|
-
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
15
|
-
end
|
16
|
-
Jeweler::GemcutterTasks.new
|
17
|
-
rescue LoadError
|
18
|
-
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
19
|
-
end
|
20
|
-
|
21
4
|
require 'rake/testtask'
|
22
5
|
Rake::TestTask.new(:test) do |test|
|
23
6
|
test.libs << 'lib' << 'test'
|
@@ -38,8 +21,6 @@ rescue LoadError
|
|
38
21
|
end
|
39
22
|
end
|
40
23
|
|
41
|
-
task :test => :check_dependencies
|
42
|
-
|
43
24
|
task :default => :test
|
44
25
|
|
45
26
|
require 'rake/rdoctask'
|
data/lib/url_mount.rb
CHANGED
@@ -3,18 +3,30 @@ class UrlMount
|
|
3
3
|
# Inspiration for this is taken straight from Usher. http://github.com/joshbuddy/usher
|
4
4
|
DELIMETERS = ['/', '(', ')']
|
5
5
|
|
6
|
-
attr_accessor :raw_path, :options, :url_mount
|
6
|
+
attr_accessor :raw_path, :options, :url_mount, :host, :scheme
|
7
7
|
alias_method :defaults, :options
|
8
8
|
|
9
|
-
def initialize(path, opts = {})
|
9
|
+
def initialize(path, opts = {}, &blk)
|
10
10
|
@raw_path, @options = path, opts
|
11
11
|
@url_split_regex = Regexp.new("[^#{DELIMETERS.collect{|d| Regexp.quote(d)}.join}]+|[#{DELIMETERS.collect{|d| Regexp.quote(d)}.join}]")
|
12
|
+
@host, @scheme = opts[:host], opts[:scheme]
|
13
|
+
@callbacks = []
|
14
|
+
@callbacks << blk if blk
|
15
|
+
end
|
16
|
+
|
17
|
+
def callback(&blk)
|
18
|
+
@callbacks << blk if blk
|
19
|
+
@callbacks
|
12
20
|
end
|
13
21
|
|
14
22
|
def local_segments
|
15
23
|
@local_segments || parse_local_segments
|
16
24
|
end
|
17
25
|
|
26
|
+
def variables
|
27
|
+
required_variables + local_variables
|
28
|
+
end
|
29
|
+
|
18
30
|
def required_variables
|
19
31
|
@required_variables ||= begin
|
20
32
|
required_variable_segments.map{|s| s.name}
|
@@ -48,17 +60,49 @@ class UrlMount
|
|
48
60
|
end
|
49
61
|
end
|
50
62
|
|
51
|
-
def
|
52
|
-
|
53
|
-
|
63
|
+
def url(env = {}, opts = {})
|
64
|
+
unless env.key?('rack.version')
|
65
|
+
opts = env
|
66
|
+
env = nil
|
67
|
+
end
|
54
68
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
69
|
+
@callbacks.each{|blk| blk.call(env,opts)} if env
|
70
|
+
|
71
|
+
requirements_met = (local_required_variables - (opts.keys + options.keys)).empty?
|
72
|
+
|
73
|
+
if !required_to_generate? && !requirements_met
|
74
|
+
nil
|
75
|
+
else
|
76
|
+
raise Ungeneratable, "Missing required variables" if !requirements_met
|
77
|
+
File.join(local_segments.inject([]){|url, segment| str = segment.to_s(opts); url << str if str; url}) =~ /(.*?)\/?$/
|
78
|
+
path = local_segments.inject([]){|url, segment| str = segment.to_s(opts); url << str if str; url}.join
|
79
|
+
match = /(.*?)\/?$/.match(path)
|
80
|
+
result = match[1]
|
81
|
+
path = url_mount.nil? ? result : File.join(url_mount.to_s(opts), result)
|
82
|
+
if opts[:host] || host || opts[:scheme] || scheme
|
83
|
+
_host = opts[:host] || host
|
84
|
+
_scheme = opts[:scheme] || scheme || "http"
|
85
|
+
raise Ungeneratable, "Missing host when generating absolute url" if _scheme && !_host
|
86
|
+
uri = URI.parse(path)
|
87
|
+
uri.host = _host
|
88
|
+
uri.scheme = _scheme || "http"
|
89
|
+
uri.to_s
|
90
|
+
else
|
91
|
+
path
|
92
|
+
end
|
93
|
+
end
|
59
94
|
end
|
95
|
+
alias_method :to_s, :url
|
60
96
|
|
61
97
|
private
|
98
|
+
def local_required_variables
|
99
|
+
local_segments.select{|s| s.instance_of?(Segment::Variable)}.map{|s| s.name}
|
100
|
+
end
|
101
|
+
|
102
|
+
def required_to_generate?
|
103
|
+
true
|
104
|
+
end
|
105
|
+
|
62
106
|
def parse_local_segments
|
63
107
|
stack = []
|
64
108
|
@local_segments = []
|
@@ -84,7 +128,7 @@ class UrlMount
|
|
84
128
|
end
|
85
129
|
when /^\:(.*)/
|
86
130
|
if stack.empty?
|
87
|
-
@local_segments << Segment::Variable.new($1,
|
131
|
+
@local_segments << Segment::Variable.new($1, options)
|
88
132
|
else
|
89
133
|
buffer << segment
|
90
134
|
end
|
@@ -104,9 +148,6 @@ class UrlMount
|
|
104
148
|
class Base
|
105
149
|
attr_accessor :name
|
106
150
|
|
107
|
-
def required!; @required = true; end
|
108
|
-
def required?; !!@required; end
|
109
|
-
|
110
151
|
def optional_variable_segments; []; end
|
111
152
|
def required_variable_segments; []; end
|
112
153
|
end
|
@@ -121,12 +162,8 @@ class UrlMount
|
|
121
162
|
end
|
122
163
|
|
123
164
|
class Variable < Base
|
124
|
-
def initialize(name,
|
125
|
-
@name, @
|
126
|
-
end
|
127
|
-
|
128
|
-
def optional_variable_segments
|
129
|
-
[]
|
165
|
+
def initialize(name, options)
|
166
|
+
@name, @options = name.to_sym, (options || {})
|
130
167
|
end
|
131
168
|
|
132
169
|
def required_variable_segments
|
@@ -134,26 +171,32 @@ class UrlMount
|
|
134
171
|
end
|
135
172
|
|
136
173
|
def to_s(opts = {})
|
137
|
-
opts[name]
|
174
|
+
item = opts[name] || @options[name]
|
175
|
+
item.respond_to?(:call) ? item.call : item
|
138
176
|
end
|
139
177
|
end
|
140
178
|
|
141
179
|
class Conditional < Base
|
142
|
-
|
143
|
-
|
144
|
-
|
180
|
+
class UnrequiredUrlMount < UrlMount
|
181
|
+
private
|
182
|
+
def required_to_generate?; false; end
|
183
|
+
end
|
184
|
+
|
185
|
+
def initialize(*args)
|
186
|
+
@url_mount = UnrequiredUrlMount.new(*args)
|
145
187
|
end
|
146
188
|
|
147
189
|
def optional_variable_segments
|
148
|
-
(@url_mount.required_variable_segments + @url_mount.optional_variable_segments)
|
190
|
+
(@url_mount.required_variable_segments + @url_mount.optional_variable_segments)
|
149
191
|
end
|
150
192
|
|
151
|
-
def
|
193
|
+
def required_variables; []; end
|
194
|
+
def optional_variables
|
195
|
+
(@url_mount.required_variables + @url_mount.optional_variables)
|
196
|
+
end
|
152
197
|
|
153
198
|
def to_s(opts = {})
|
154
|
-
|
155
|
-
@url_mount.to_s(opts)
|
156
|
-
end
|
199
|
+
@url_mount.to_s(opts)
|
157
200
|
end
|
158
201
|
end
|
159
202
|
end
|
Binary file
|
data/test/helper.rb
CHANGED
data/test/test_url_mount.rb
CHANGED
@@ -8,7 +8,6 @@ class TestUrlMount < Test::Unit::TestCase
|
|
8
8
|
assert_equal({:some => "options"}, u.defaults)
|
9
9
|
end
|
10
10
|
|
11
|
-
|
12
11
|
context "required variables" do
|
13
12
|
should "calculate the required variables of the mount as an emtpy array when there are none" do
|
14
13
|
u = UrlMount.new("/foo")
|
@@ -21,27 +20,44 @@ class TestUrlMount < Test::Unit::TestCase
|
|
21
20
|
end
|
22
21
|
|
23
22
|
should "calculate the required variables when there are some" do
|
24
|
-
u = UrlMount.new("/foo/:bar/baz/:homer")
|
23
|
+
u = UrlMount.new("/foo/:bar/baz/:homer", :bar => "bar", :homer => "homer")
|
25
24
|
assert_equal [:bar, :homer], u.required_variables
|
26
25
|
assert_equal( {:required => [:bar, :homer], :optional => []}, u.variables )
|
27
26
|
end
|
28
27
|
|
28
|
+
should "calculate required variables from procs" do
|
29
|
+
u = UrlMount.new("/foo/:bar/:baz", :bar => "a_bar", :baz => proc{"baz_in_proc"})
|
30
|
+
assert_equal "/foo/a_bar/baz_in_proc", u.url
|
31
|
+
end
|
32
|
+
|
29
33
|
should "generate a static url mount" do
|
30
34
|
u = UrlMount.new("/foo/bar")
|
31
|
-
assert_equal "/foo/bar", u.
|
35
|
+
assert_equal "/foo/bar", u.url
|
32
36
|
end
|
33
37
|
|
34
38
|
should "generate a dynamic url with static and variable segments" do
|
35
|
-
u = UrlMount.new("/foo/:bar/baz/:barry")
|
36
|
-
assert_equal "/foo/bar/baz/sue", u.
|
39
|
+
u = UrlMount.new("/foo/:bar/baz/:barry", :bar => "bar", :barry => "sue")
|
40
|
+
assert_equal "/foo/bar/baz/sue", u.url
|
37
41
|
end
|
38
42
|
|
39
43
|
should "raise an exception when a required variable is missing" do
|
40
|
-
u = UrlMount.new("/foo/:bar/:baz")
|
41
44
|
assert_raises UrlMount::Ungeneratable do
|
42
|
-
|
45
|
+
UrlMount.new("/foo/:bar/:baz").url
|
43
46
|
end
|
44
47
|
end
|
48
|
+
|
49
|
+
should "consume the options so the router does not use them" do
|
50
|
+
opts = {:bar => "bar", :other => "other"}
|
51
|
+
u = UrlMount.new("/foo/:bar", :bar => "some_default_bar")
|
52
|
+
u.url(opts)
|
53
|
+
assert_equal( {:bar => "bar", :other => "other"}, opts )
|
54
|
+
end
|
55
|
+
|
56
|
+
should "alias to_s to url" do
|
57
|
+
u = UrlMount.new "/foo/bar"
|
58
|
+
assert_equal u.to_s, u.url
|
59
|
+
end
|
60
|
+
|
45
61
|
end
|
46
62
|
|
47
63
|
context "optional variables" do
|
@@ -57,60 +73,63 @@ class TestUrlMount < Test::Unit::TestCase
|
|
57
73
|
|
58
74
|
should "calculate optional variables when there are some" do
|
59
75
|
u = UrlMount.new("/foo(/:bar(/:baz))")
|
60
|
-
assert_equal "/foo/gary", u.
|
76
|
+
assert_equal "/foo/gary", u.url(:bar => "gary")
|
61
77
|
end
|
62
78
|
|
63
79
|
should "skip nested optional variables when the optional parent is not present" do
|
64
80
|
u = UrlMount.new("/foo(/:bar(/:baz))")
|
65
|
-
assert_equal "/foo", u.
|
81
|
+
assert_equal "/foo", u.url(:baz => "sue")
|
66
82
|
end
|
67
83
|
end
|
68
84
|
|
69
85
|
context "default variables" do
|
70
86
|
should "generate a simple url with a variable with a default" do
|
71
87
|
u = UrlMount.new("/foo/:bar", :bar => "default")
|
72
|
-
assert_equal "/foo/default", u.
|
88
|
+
assert_equal "/foo/default", u.url
|
73
89
|
end
|
74
90
|
|
75
91
|
should "generate urls with multiple varilables using defaults" do
|
76
92
|
u = UrlMount.new("/foo/:bar/:baz", :bar => "bar", :baz => "baz")
|
77
|
-
assert_equal "/foo/bar/baz", u.
|
93
|
+
assert_equal "/foo/bar/baz", u.url
|
78
94
|
end
|
79
95
|
|
80
96
|
should "generate urls with optional variables" do
|
81
97
|
u = UrlMount.new("/foo(/:bar)", :bar => "bar")
|
82
|
-
assert_equal "/foo/bar", u.
|
98
|
+
assert_equal "/foo/bar", u.url
|
83
99
|
end
|
84
100
|
|
85
101
|
should "generate urls with mixed variables" do
|
86
102
|
u = UrlMount.new("/foo/:bar(/:baz(/:barry))", :barry => "bazz", :bar => "clue")
|
87
|
-
assert_equal "/foo/clue", u.
|
88
|
-
assert_equal "/foo/clue/sue/bazz", u.
|
103
|
+
assert_equal "/foo/clue", u.url
|
104
|
+
assert_equal "/foo/clue/sue/bazz", u.url(:baz => "sue")
|
89
105
|
end
|
90
106
|
|
91
107
|
should "generate urls with overwritten defaults" do
|
92
108
|
u = UrlMount.new("/foo/:bar(/:baz)", :bar => "barr", :baz => "bazz")
|
93
|
-
assert_equal "/foo/sue/larry", u.
|
94
|
-
assert_equal "/foo/barr/gary", u.
|
95
|
-
assert_equal "/foo/harry/bazz", u.
|
109
|
+
assert_equal "/foo/sue/larry", u.url(:bar => "sue", :baz => "larry")
|
110
|
+
assert_equal "/foo/barr/gary", u.url(:baz => "gary")
|
111
|
+
assert_equal "/foo/harry/bazz", u.url(:bar => "harry")
|
112
|
+
end
|
113
|
+
|
114
|
+
should "generate optional and fixed paths with procs" do
|
115
|
+
u = UrlMount.new("/foo/:bar(/:baz)", :bar => proc{"the_bar"}, :baz => proc{"the_baz"})
|
116
|
+
assert_equal "/foo/the_bar/the_baz", u.url
|
117
|
+
assert_equal "/foo/bar/other_baz", u.url(:bar => "bar", :baz => proc{"other_baz"})
|
96
118
|
end
|
97
119
|
end
|
98
120
|
|
99
121
|
context "complex compound urls" do
|
100
122
|
should "generate complex urls containing multiple nested conditionals and multiple required variables" do
|
101
|
-
u = UrlMount.new("/foo(/:bar(/:baz))/:gary")
|
102
|
-
assert_equal "/foo/gary", u.
|
103
|
-
assert_equal "/foo/bar/gary", u.
|
104
|
-
assert_equal "/foo/bar/baz/gary", u.
|
105
|
-
assert_raises UrlMount::Ungeneratable do
|
106
|
-
u.to_s(:bar => "bar")
|
107
|
-
end
|
123
|
+
u = UrlMount.new("/foo(/:bar(/:baz))/:gary", :gary => "gary")
|
124
|
+
assert_equal "/foo/gary", u.url
|
125
|
+
assert_equal "/foo/bar/gary", u.url(:bar => "bar")
|
126
|
+
assert_equal "/foo/bar/baz/gary", u.url(:bar => "bar", :baz => "baz")
|
108
127
|
end
|
109
128
|
end
|
110
129
|
|
111
130
|
context "nested url mounts" do
|
112
131
|
should "allow a mount to accept a mount" do
|
113
|
-
u1 = UrlMount.new("/root/:bar")
|
132
|
+
u1 = UrlMount.new("/root/:bar", :bar => "bar")
|
114
133
|
u2 = UrlMount.new("/baz/barry")
|
115
134
|
u1.url_mount = u2
|
116
135
|
end
|
@@ -118,9 +137,104 @@ class TestUrlMount < Test::Unit::TestCase
|
|
118
137
|
should "generate the mount" do
|
119
138
|
u1 = UrlMount.new("/root/bar")
|
120
139
|
u2 = UrlMount.new("/baz/barry")
|
121
|
-
|
122
|
-
|
123
|
-
|
140
|
+
u2.url_mount = u1
|
141
|
+
assert_equal "/root/bar", u1.url
|
142
|
+
assert_equal "/root/bar/baz/barry", u2.url
|
143
|
+
end
|
144
|
+
|
145
|
+
should "overwrite a parents options" do
|
146
|
+
u1 = UrlMount.new("/root/:bar", :bar => "bar")
|
147
|
+
u2 = UrlMount.new("/baz/barry")
|
148
|
+
u2.url_mount = u1
|
149
|
+
assert_equal "/root/different/baz/barry", u2.url(:bar => "different")
|
150
|
+
end
|
151
|
+
|
152
|
+
should "not consume params to nested routes" do
|
153
|
+
u1 = UrlMount.new("/root/:bar", :bar => "bar")
|
154
|
+
u2 = UrlMount.new("/baz/:barry", :barry => "barry")
|
155
|
+
u2.url_mount = u1
|
156
|
+
opts = {:bar => "sue", :barry => "wendy"}
|
157
|
+
assert_equal "/root/sue/baz/wendy", u2.url(opts)
|
158
|
+
assert_equal({:bar => "sue", :barry => "wendy"}, opts)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
context "host options" do
|
163
|
+
should "generate an absolute url when given the host option" do
|
164
|
+
u = UrlMount.new("/foo/bar")
|
165
|
+
assert_equal "http://example.com/foo/bar", u.url(:host => "example.com")
|
166
|
+
end
|
167
|
+
|
168
|
+
should "generate an absolute url when the host option is set in the route" do
|
169
|
+
u = UrlMount.new("/foo/bar", :host => "example.com")
|
170
|
+
assert_equal "http://example.com/foo/bar", u.url
|
171
|
+
end
|
172
|
+
|
173
|
+
should "overwrite the host" do
|
174
|
+
u = UrlMount.new("/foo/bar", :host => "example.com")
|
175
|
+
assert_equal "http://foo.com/foo/bar", u.url(:host => "foo.com")
|
176
|
+
end
|
177
|
+
|
178
|
+
should "allow me to set the scheme" do
|
179
|
+
u = UrlMount.new("/foo/bar", :host => "example.com", :scheme => "https")
|
180
|
+
assert_equal "https://example.com/foo/bar", u.url
|
181
|
+
end
|
182
|
+
|
183
|
+
should "raise an exception if the scheme is set and not the host" do
|
184
|
+
u = UrlMount.new("/foo/bar")
|
185
|
+
assert_raises(UrlMount::Ungeneratable) do
|
186
|
+
u.url(:scheme => "https")
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
context "callbacks" do
|
192
|
+
should "let me add a callback" do
|
193
|
+
captures = []
|
194
|
+
u = UrlMount.new("/foo/bar") do |env, opts|
|
195
|
+
captures << :block
|
196
|
+
req = Rack::Request.new(env)
|
197
|
+
assert_equal "example.com", req.host
|
198
|
+
opts[:host] = req.host
|
199
|
+
opts[:scheme] = req.scheme
|
200
|
+
end
|
201
|
+
env = Rack::MockRequest.env_for("/", 'HTTP_HOST' => "example.com")
|
202
|
+
assert_equal "http://example.com/foo/bar", u.url(env)
|
203
|
+
assert_equal :block, captures.first
|
204
|
+
end
|
205
|
+
|
206
|
+
should "let me add many callbacks" do
|
207
|
+
captures = []
|
208
|
+
u = UrlMount.new("/foo/bar")
|
209
|
+
u.callback do |env, opts|
|
210
|
+
captures << :one
|
211
|
+
opts[:one] = :one
|
212
|
+
end
|
213
|
+
u.callback do |env, opts|
|
214
|
+
captures << :two
|
215
|
+
captures << opts[:one]
|
216
|
+
end
|
217
|
+
env = Rack::MockRequest.env_for("/", :host => "example.com")
|
218
|
+
assert_equal "/foo/bar", u.url(env)
|
219
|
+
assert_equal [:one, :two, :one], captures
|
220
|
+
end
|
221
|
+
|
222
|
+
should "not run the callbacks if the env is not available" do
|
223
|
+
captures = []
|
224
|
+
u = UrlMount.new("/foo/bar")
|
225
|
+
u.callback do |env, opts|
|
226
|
+
captures << :here
|
227
|
+
end
|
228
|
+
assert_equal "/foo/bar", u.url
|
229
|
+
assert_equal [], captures
|
230
|
+
end
|
231
|
+
|
232
|
+
should "let me generate a required option url via callbacks" do
|
233
|
+
env = Rack::MockRequest.env_for "/"
|
234
|
+
u = UrlMount.new("/foo/:bar") do |env, opts|
|
235
|
+
opts[:bar] ||= "here_is_my_bar"
|
236
|
+
end
|
237
|
+
assert_equal "/foo/here_is_my_bar", u.url(env)
|
124
238
|
end
|
125
239
|
end
|
126
240
|
end
|
data/url_mount.gemspec
CHANGED
@@ -1,54 +1,22 @@
|
|
1
|
-
# Generated by jeweler
|
2
|
-
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
1
|
# -*- encoding: utf-8 -*-
|
2
|
+
require 'bundler'
|
5
3
|
|
6
4
|
Gem::Specification.new do |s|
|
7
5
|
s.name = %q{url_mount}
|
8
|
-
s.version = "0.
|
6
|
+
s.version = "0.2.0"
|
9
7
|
|
10
8
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
9
|
s.authors = ["Daniel Neighman"]
|
12
|
-
s.date = %q{2010-
|
10
|
+
s.date = %q{2010-06-07}
|
13
11
|
s.description = %q{Glue to allow mounted rack applications to know where they're mounted}
|
14
12
|
s.email = %q{has.sox@gmail.com}
|
15
|
-
s.
|
16
|
-
"LICENSE",
|
17
|
-
"README.rdoc"
|
18
|
-
]
|
19
|
-
s.files = [
|
20
|
-
".document",
|
21
|
-
".gitignore",
|
22
|
-
"LICENSE",
|
23
|
-
"README.rdoc",
|
24
|
-
"Rakefile",
|
25
|
-
"VERSION",
|
26
|
-
"lib/url_mount.rb",
|
27
|
-
"test/helper.rb",
|
28
|
-
"test/test_url_mount.rb",
|
29
|
-
"url_mount.gemspec"
|
30
|
-
]
|
13
|
+
s.files = Dir["**/*"]
|
31
14
|
s.homepage = %q{http://github.com/hassox/url_mount}
|
32
15
|
s.rdoc_options = ["--charset=UTF-8"]
|
33
16
|
s.require_paths = ["lib"]
|
34
17
|
s.rubygems_version = %q{1.3.5}
|
35
18
|
s.summary = %q{Universal mounting points for rack applications}
|
36
|
-
s.test_files = [
|
37
|
-
"test/helper.rb",
|
38
|
-
"test/test_url_mount.rb"
|
39
|
-
]
|
40
19
|
|
41
|
-
|
42
|
-
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
43
|
-
s.specification_version = 3
|
44
|
-
|
45
|
-
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
46
|
-
s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
47
|
-
else
|
48
|
-
s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
49
|
-
end
|
50
|
-
else
|
51
|
-
s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
52
|
-
end
|
20
|
+
s.add_bundler_dependencies
|
53
21
|
end
|
54
22
|
|
metadata
CHANGED
@@ -1,7 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: url_mount
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
hash: 23
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 2
|
9
|
+
- 0
|
10
|
+
version: 0.2.0
|
5
11
|
platform: ruby
|
6
12
|
authors:
|
7
13
|
- Daniel Neighman
|
@@ -9,38 +15,56 @@ autorequire:
|
|
9
15
|
bindir: bin
|
10
16
|
cert_chain: []
|
11
17
|
|
12
|
-
date: 2010-
|
18
|
+
date: 2010-06-07 00:00:00 +10:00
|
13
19
|
default_executable:
|
14
20
|
dependencies:
|
15
21
|
- !ruby/object:Gem::Dependency
|
16
|
-
|
22
|
+
version_requirements: &id001 !ruby/object:Gem::Requirement
|
23
|
+
none: false
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
hash: 3
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
version: "0"
|
31
|
+
name: rake
|
32
|
+
requirement: *id001
|
33
|
+
prerelease: false
|
17
34
|
type: :development
|
18
|
-
|
19
|
-
version_requirements: !ruby/object:Gem::Requirement
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
version_requirements: &id002 !ruby/object:Gem::Requirement
|
37
|
+
none: false
|
20
38
|
requirements:
|
21
39
|
- - ">="
|
22
40
|
- !ruby/object:Gem::Version
|
41
|
+
hash: 3
|
42
|
+
segments:
|
43
|
+
- 0
|
23
44
|
version: "0"
|
24
|
-
|
45
|
+
name: rack
|
46
|
+
requirement: *id002
|
47
|
+
prerelease: false
|
48
|
+
type: :runtime
|
25
49
|
description: Glue to allow mounted rack applications to know where they're mounted
|
26
50
|
email: has.sox@gmail.com
|
27
51
|
executables: []
|
28
52
|
|
29
53
|
extensions: []
|
30
54
|
|
31
|
-
extra_rdoc_files:
|
32
|
-
|
33
|
-
- README.rdoc
|
55
|
+
extra_rdoc_files: []
|
56
|
+
|
34
57
|
files:
|
35
|
-
-
|
36
|
-
- .
|
58
|
+
- Gemfile
|
59
|
+
- Gemfile.lock
|
60
|
+
- lib/url_mount.rb
|
37
61
|
- LICENSE
|
38
|
-
-
|
62
|
+
- pkg/url_mount-0.1.0.gem
|
39
63
|
- Rakefile
|
40
|
-
-
|
41
|
-
- lib/url_mount.rb
|
64
|
+
- README.textile
|
42
65
|
- test/helper.rb
|
43
66
|
- test/test_url_mount.rb
|
67
|
+
- url_mount-0.2.0.gem
|
44
68
|
- url_mount.gemspec
|
45
69
|
has_rdoc: true
|
46
70
|
homepage: http://github.com/hassox/url_mount
|
@@ -52,24 +76,29 @@ rdoc_options:
|
|
52
76
|
require_paths:
|
53
77
|
- lib
|
54
78
|
required_ruby_version: !ruby/object:Gem::Requirement
|
79
|
+
none: false
|
55
80
|
requirements:
|
56
81
|
- - ">="
|
57
82
|
- !ruby/object:Gem::Version
|
83
|
+
hash: 3
|
84
|
+
segments:
|
85
|
+
- 0
|
58
86
|
version: "0"
|
59
|
-
version:
|
60
87
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
88
|
+
none: false
|
61
89
|
requirements:
|
62
90
|
- - ">="
|
63
91
|
- !ruby/object:Gem::Version
|
92
|
+
hash: 3
|
93
|
+
segments:
|
94
|
+
- 0
|
64
95
|
version: "0"
|
65
|
-
version:
|
66
96
|
requirements: []
|
67
97
|
|
68
98
|
rubyforge_project:
|
69
|
-
rubygems_version: 1.3.
|
99
|
+
rubygems_version: 1.3.7
|
70
100
|
signing_key:
|
71
101
|
specification_version: 3
|
72
102
|
summary: Universal mounting points for rack applications
|
73
|
-
test_files:
|
74
|
-
|
75
|
-
- test/test_url_mount.rb
|
103
|
+
test_files: []
|
104
|
+
|
data/.document
DELETED
data/.gitignore
DELETED
data/README.rdoc
DELETED
@@ -1,94 +0,0 @@
|
|
1
|
-
= UrlMount
|
2
|
-
|
3
|
-
UrlMount is a universal mount point designed for use in rack applications.
|
4
|
-
|
5
|
-
It provides a simple way to pass a url mounting point to the mounted application.
|
6
|
-
|
7
|
-
This means that when you mount an application in the url space, it's a simple to_s to get the mount point of where the application is.
|
8
|
-
|
9
|
-
== Example
|
10
|
-
|
11
|
-
Say you mount an application at "/foo/bar"
|
12
|
-
|
13
|
-
<pre><code>router.mount("/foo/bar").to(my_app)
|
14
|
-
|
15
|
-
# behind the scenes, the routers mount method should do this:
|
16
|
-
|
17
|
-
if my_app.respond_to?(:url_mount=)
|
18
|
-
mount_point = UrlMount.new("/foo/bar")
|
19
|
-
my_app.url_mount = mount_point
|
20
|
-
end
|
21
|
-
</code></pre>
|
22
|
-
|
23
|
-
This means that if an application can handle a url_mount, you give it one.
|
24
|
-
|
25
|
-
This then allows the +my_app+ applciation, to know where it's mounted. Generating a url is now as simple as
|
26
|
-
<pre><code>File.join(url_mount.to_s, "/local/url/path")</code></pre>
|
27
|
-
|
28
|
-
The benefit of this is that all routers or applciations can make use of this. If used, it can act as a universal glue between rack applications where parent apps can let the child applications know where they're mounted.
|
29
|
-
|
30
|
-
=== Show me the code
|
31
|
-
|
32
|
-
urlmount is made to be used with many routers that are currently available.
|
33
|
-
|
34
|
-
<pre><code>
|
35
|
-
# simple string mounting point
|
36
|
-
mount = urlmount.new("/foo/bar")
|
37
|
-
mount.to_s == "/foo/bar"
|
38
|
-
|
39
|
-
# Mount Point including variables
|
40
|
-
mount = UrlMount.new("/foo/:bar")
|
41
|
-
mount.to_s(:bar => "something") == "/foo/something"
|
42
|
-
mount.to_s #=> Raises UrlMount::Ungeneratable because a required variable was not found
|
43
|
-
mount.required_variables == [:bar]
|
44
|
-
|
45
|
-
# Mount Point including optional variables
|
46
|
-
mount = UrlMount.new("/foo/:bar(/:baz)")
|
47
|
-
mount.to_s(:bar => "doh") == "/foo/doh"
|
48
|
-
mount.to_s(:bar => "doh", :baz => "hah") == "/foo/doh/hah"
|
49
|
-
mount.required_variables == [:bar]
|
50
|
-
mount.optional_variables == [:baz]
|
51
|
-
|
52
|
-
# Mount Point with defaults
|
53
|
-
mount = UrlMount.new("/foo/:bar(/:baz)", :bar => "default_bar")
|
54
|
-
mount.to_s == "/foo/default_bar"
|
55
|
-
mount.to_s(:baz => "baz_value") == "/foo/default_bar/baz_value"
|
56
|
-
mount.to_s(:bar => "other_bar") == "/foo/other_bar"
|
57
|
-
|
58
|
-
# Nested mounting point
|
59
|
-
mount_parent = UrlMount.new("/foo/bar")
|
60
|
-
mount_child = UrlMount.new("/baz/:barry)
|
61
|
-
|
62
|
-
mount_child.url_mount = mount_parent
|
63
|
-
|
64
|
-
mount_parent.to_s == "/foo/bar"
|
65
|
-
mount_child.to_s(:barry =>"barry_value") == "/foo/bar/baz/barry_value"
|
66
|
-
</code></pre>
|
67
|
-
|
68
|
-
Considering that UrlMounts can be nested, when you mount an application you should do it something like this.
|
69
|
-
|
70
|
-
<pre><code>
|
71
|
-
if mounted_app.respond_to?(:url_mount=)
|
72
|
-
mount = UrlMount.new(path, deafult_options)
|
73
|
-
|
74
|
-
# If this app is mounted, add this apps mount point to
|
75
|
-
# the mount point for the child app
|
76
|
-
mount.url_mount = self.url_mount if self.url_mount
|
77
|
-
|
78
|
-
mounted_app.url_mount = mount
|
79
|
-
end
|
80
|
-
</code></pre>
|
81
|
-
|
82
|
-
== Note on Patches/Pull Requests
|
83
|
-
|
84
|
-
* Fork the project.
|
85
|
-
* Make your feature addition or bug fix.
|
86
|
-
* Add tests for it. This is important so I don't break it in a
|
87
|
-
future version unintentionally.
|
88
|
-
* Commit, do not mess with rakefile, version, or history.
|
89
|
-
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
90
|
-
* Send me a pull request. Bonus points for topic branches.
|
91
|
-
|
92
|
-
== Copyright
|
93
|
-
|
94
|
-
Copyright (c) 2010 Daniel Neighman. See LICENSE for details.
|
data/VERSION
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
0.1.0
|