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 ADDED
@@ -0,0 +1,11 @@
1
+ source :rubygems
2
+
3
+ gem 'rack'
4
+
5
+ group :development do
6
+ gem 'rake'
7
+ end
8
+
9
+ group :test do
10
+ gem 'shoulda'
11
+ end
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 to_s(opts = {})
52
- opts = options.merge(opts)
53
- requirements_met = (required_variables - (opts.keys & required_variables)).empty?
63
+ def url(env = {}, opts = {})
64
+ unless env.key?('rack.version')
65
+ opts = env
66
+ env = nil
67
+ end
54
68
 
55
- raise Ungeneratable, "Missing required variables" unless requirements_met
56
- File.join(local_segments.inject([]){|url, segment| str = segment.to_s(opts); url << str if str; url}) =~ /(.*?)\/?$/
57
- result = $1
58
- url_mount.nil? ? result : File.join(url_mount.to_s(opts), result)
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, true, options)
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, required, options)
125
- @name, @required = name.to_sym, true
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
- attr_reader :segments
143
- def initialize(path, options)
144
- @url_mount = UrlMount.new(path, options)
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).map{|s| s.name}
190
+ (@url_mount.required_variable_segments + @url_mount.optional_variable_segments)
149
191
  end
150
192
 
151
- def required_variable_segments; []; end
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
- if (opts.keys & @url_mount.required_variables) == @url_mount.required_variables
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
@@ -1,6 +1,7 @@
1
1
  require 'rubygems'
2
2
  require 'test/unit'
3
3
  require 'shoulda'
4
+ require 'rack'
4
5
 
5
6
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
7
  $LOAD_PATH.unshift(File.dirname(__FILE__))
@@ -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.to_s
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.to_s(:bar => "bar", :barry => "sue")
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
- u.to_s(:bar => "baz")
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.to_s(:bar => "gary")
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.to_s(:baz => "sue")
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.to_s
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.to_s
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.to_s
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.to_s
88
- assert_equal "/foo/clue/sue/bazz", u.to_s(:baz => "sue")
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.to_s(:bar => "sue", :baz => "larry")
94
- assert_equal "/foo/barr/gary", u.to_s(:baz => "gary")
95
- assert_equal "/foo/harry/bazz", u.to_s(:bar => "harry")
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.to_s(:gary => "gary")
103
- assert_equal "/foo/bar/gary", u.to_s(:gary => "gary", :bar => "bar")
104
- assert_equal "/foo/bar/baz/gary", u.to_s(:gary => "gary", :bar => "bar", :baz => "baz")
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
- u1.url_mount = u2
122
- assert "/root/bar", u1.to_s
123
- assert "/root/bar/baz/barry", u2.to_s
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.1.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-02-18}
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.extra_rdoc_files = [
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
- if s.respond_to? :specification_version then
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
- version: 0.1.0
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-02-18 00:00:00 +11:00
18
+ date: 2010-06-07 00:00:00 +10:00
13
19
  default_executable:
14
20
  dependencies:
15
21
  - !ruby/object:Gem::Dependency
16
- name: thoughtbot-shoulda
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
- version_requirement:
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
- version:
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
- - LICENSE
33
- - README.rdoc
55
+ extra_rdoc_files: []
56
+
34
57
  files:
35
- - .document
36
- - .gitignore
58
+ - Gemfile
59
+ - Gemfile.lock
60
+ - lib/url_mount.rb
37
61
  - LICENSE
38
- - README.rdoc
62
+ - pkg/url_mount-0.1.0.gem
39
63
  - Rakefile
40
- - VERSION
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.5
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
- - test/helper.rb
75
- - test/test_url_mount.rb
103
+ test_files: []
104
+
data/.document DELETED
@@ -1,5 +0,0 @@
1
- README.rdoc
2
- lib/**/*.rb
3
- bin/*
4
- features/**/*.feature
5
- LICENSE
data/.gitignore DELETED
@@ -1,22 +0,0 @@
1
- ## MAC OS
2
- .DS_Store
3
-
4
- ## TEXTMATE
5
- *.tmproj
6
- tmtags
7
-
8
- ## EMACS
9
- *~
10
- \#*
11
- .\#*
12
-
13
- ## VIM
14
- *.swp
15
-
16
- ## PROJECT::GENERAL
17
- coverage
18
- rdoc
19
- pkg
20
-
21
- ## PROJECT::SPECIFIC
22
- *.gem
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