rack-rewrite 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.rdoc +9 -0
- data/README.rdoc +24 -24
- data/Rakefile +1 -0
- data/VERSION +1 -1
- data/lib/rack-rewrite/rule.rb +18 -12
- data/rack-rewrite.gemspec +4 -3
- data/test/rack-rewrite_test.rb +34 -4
- data/test/rule_test.rb +38 -9
- metadata +3 -2
data/History.rdoc
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
=== 0.2.1 / 2010-01-06
|
2
|
+
* API
|
3
|
+
* Implement $& substitution pattern (thanks to {Ben Brinckerhoff}[http://github.com/bhb])
|
4
|
+
|
5
|
+
* Maintenance
|
6
|
+
* Ignore empty captures instead of failing during subsitution (thanks to {Ben Brinckerhoff}[http://github.com/bhb])
|
7
|
+
* Play nice with Rack::Test requests which only set PATH_INFO and not REQUEST_URI (thanks to {@docunext}[http://github.com/docunext])
|
8
|
+
* Use QUERY_STRING instead of QUERYSTRING as per Rack spec. Closes Issue #1.
|
9
|
+
|
1
10
|
=== 0.2.0 / 2009-11-14
|
2
11
|
* API
|
3
12
|
* Allow Proc's to be be passed as the 'to' argument to rule declarations
|
data/README.rdoc
CHANGED
@@ -3,11 +3,18 @@
|
|
3
3
|
A rack middleware for defining and applying rewrite rules. In many cases you
|
4
4
|
can get away with rack-rewrite instead of writing Apache mod_rewrite rules.
|
5
5
|
|
6
|
+
== References
|
7
|
+
|
8
|
+
* Source[http://github.com/jtrupiano/rack-rewrite]
|
9
|
+
* Documentation[http://johntrupiano.rubyforge.org/rack-rewrite]
|
10
|
+
* {Rack::Rewrite for Site Maintenance and Downtime}[http://blog.smartlogicsolutions.com/2009/11/16/rack-rewrite-for-site-maintenance-and-downtime/]
|
11
|
+
* {Rack::Rewrite + Google Analytics Makes Site Transitions Seamless}[http://blog.smartlogicsolutions.com/2009/11/24/rack-rewrite-google-analytics-makes-site-transitions-seamless/]
|
12
|
+
|
6
13
|
== Usage
|
7
14
|
|
8
15
|
=== Sample rackup file
|
9
16
|
|
10
|
-
gem 'rack-rewrite', '~> 0.1
|
17
|
+
gem 'rack-rewrite', '~> 0.2.1'
|
11
18
|
require 'rack-rewrite
|
12
19
|
use Rack::Rewrite do
|
13
20
|
rewrite '/wiki/John_Trupiano', '/john'
|
@@ -17,7 +24,7 @@ can get away with rack-rewrite instead of writing Apache mod_rewrite rules.
|
|
17
24
|
end
|
18
25
|
|
19
26
|
=== Sample usage in a rails app
|
20
|
-
config.gem 'rack-rewrite', '~> 0.1
|
27
|
+
config.gem 'rack-rewrite', '~> 0.2.1'
|
21
28
|
require 'rack-rewrite
|
22
29
|
config.middleware.insert_before(Rack::Lock, Rack::Rewrite) do
|
23
30
|
rewrite '/wiki/John_Trupiano', '/john'
|
@@ -53,6 +60,16 @@ to these routes and keep your routes.rb clean.
|
|
53
60
|
|
54
61
|
rewrite %r{/features(.*)}, '/facial_features$1'
|
55
62
|
|
63
|
+
=== CNAME alternative
|
64
|
+
|
65
|
+
In the event that you do not control your DNS, you can leverage Rack::Rewrite
|
66
|
+
to redirect to a canonical domain. In the following rule we utilize the
|
67
|
+
$& substitution operator to capture the entire request URI.
|
68
|
+
|
69
|
+
r301 %r{.*}, 'http://mynewdomain.com$&', :if => Proc.new {|rack_env|
|
70
|
+
rack_env['SERVER_NAME'] != 'mynewdomain.com'
|
71
|
+
}
|
72
|
+
|
56
73
|
=== Site Maintenance
|
57
74
|
|
58
75
|
Most capistrano users will be familiar with the following Apache rewrite rules:
|
@@ -72,10 +89,10 @@ We can replace the mod_rewrite rules with the following Rack::Rewrite rule:
|
|
72
89
|
|
73
90
|
maintenance_file = File.join(RAILS_ROOT, 'public', 'system', 'maintenance.html')
|
74
91
|
send_file /.*/, maintenance_file, :if => Proc.new { |rack_env|
|
75
|
-
File.exists?(maintenance_file) &&
|
92
|
+
File.exists?(maintenance_file) && rack_env['REQUEST_URI'] !~ /\.(css|jpg|png)/
|
76
93
|
}
|
77
94
|
|
78
|
-
If you're running Ruby 1.9, this rule is
|
95
|
+
If you're running Ruby 1.9, this rule is simplified:
|
79
96
|
|
80
97
|
maintenance_file = File.join(RAILS_ROOT, 'public', 'system', 'maintenance.html')
|
81
98
|
send_file /(.*)$(?<!css|png|jpg)/, maintenance_file, :if => Proc.new { |rack_env|
|
@@ -83,19 +100,13 @@ If you're running Ruby 1.9, this rule is highly simplified:
|
|
83
100
|
}
|
84
101
|
|
85
102
|
For those using the oniguruma gem with their ruby 1.8 installation, you can
|
86
|
-
get away with
|
103
|
+
get away with:
|
87
104
|
|
88
105
|
maintenance_file = File.join(RAILS_ROOT, 'public', 'system', 'maintenance.html')
|
89
|
-
send_file Oniguruma::ORegexp.new("(.*)$(?<!css|png|jpg)"),
|
106
|
+
send_file Oniguruma::ORegexp.new("(.*)$(?<!css|png|jpg)"), maintenance_file, :if => Proc.new { |rack_env|
|
90
107
|
File.exists?(maintenance_file)
|
91
108
|
}
|
92
109
|
|
93
|
-
NOTE: These replacement rules actually issue redirects. The apache config
|
94
|
-
snippet referenced above actually performs a rewrite. With Rack::Rewrite
|
95
|
-
we currently have to issue a redirect because rails will not recognize the
|
96
|
-
maintenance route. If you have any ideas on how to fix this, I would love
|
97
|
-
to discuss it with you.
|
98
|
-
|
99
110
|
== Rewrite Rules
|
100
111
|
|
101
112
|
=== :rewrite
|
@@ -179,17 +190,6 @@ received between 12AM and 8AM to an unavailable page.
|
|
179
190
|
Time.now.hour < 8 ? "/unavailable.html" : match[1]
|
180
191
|
}
|
181
192
|
|
182
|
-
Note also that the rack environment is yielded to the lambda. The
|
183
|
-
following redirect rule will send all visitors coming from a
|
184
|
-
|
185
|
-
rewrite %r{(.*)}, lambda { |match, rack_env|
|
186
|
-
if rack_env['HTTP_REFERER'] =~ /microsoft\.com/
|
187
|
-
|
188
|
-
Time.now.hour < 8 ? "/unavailable.html" : match[1]
|
189
|
-
}
|
190
|
-
|
191
|
-
|
192
|
-
|
193
193
|
== Copyright
|
194
194
|
|
195
|
-
Copyright (c) 2009 John Trupiano. See LICENSE for details.
|
195
|
+
Copyright (c) 2009-2010 John Trupiano. See LICENSE for details.
|
data/Rakefile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.2.
|
1
|
+
0.2.1
|
data/lib/rack-rewrite/rule.rb
CHANGED
@@ -13,7 +13,7 @@ module Rack
|
|
13
13
|
# using method_missing
|
14
14
|
|
15
15
|
# Creates a rewrite rule that will simply rewrite the REQUEST_URI,
|
16
|
-
# PATH_INFO, and
|
16
|
+
# PATH_INFO, and QUERY_STRING headers of the Rack environment. The
|
17
17
|
# user's browser will continue to show the initially requested URL.
|
18
18
|
#
|
19
19
|
# rewrite '/wiki/John_Trupiano', '/john'
|
@@ -45,7 +45,7 @@ module Rack
|
|
45
45
|
# Creates a rule that will render a file if matched.
|
46
46
|
#
|
47
47
|
# send_file /*/, 'public/system/maintenance.html',
|
48
|
-
# :if => { File.exists?('public/system/maintenance.html') }
|
48
|
+
# :if => Proc.new { File.exists?('public/system/maintenance.html') }
|
49
49
|
def send_file(from, to, *args)
|
50
50
|
options = args.last.is_a?(Hash) ? args.last : {}
|
51
51
|
@rules << Rule.new(:send_file, from, to, options[:if])
|
@@ -55,7 +55,7 @@ module Rack
|
|
55
55
|
# if matched.
|
56
56
|
#
|
57
57
|
# x_send_file /*/, 'public/system/maintenance.html',
|
58
|
-
# :if => { File.exists?('public/system/maintenance.html') }
|
58
|
+
# :if => Proc.new { File.exists?('public/system/maintenance.html') }
|
59
59
|
def x_send_file(from, to, *args)
|
60
60
|
options = args.last.is_a?(Hash) ? args.last : {}
|
61
61
|
@rules << Rule.new(:x_send_file, from, to, options[:if])
|
@@ -71,8 +71,8 @@ module Rack
|
|
71
71
|
|
72
72
|
def matches?(rack_env) #:nodoc:
|
73
73
|
return false if !guard.nil? && !guard.call(rack_env)
|
74
|
-
path = rack_env['REQUEST_URI']
|
75
|
-
if self.
|
74
|
+
path = rack_env['REQUEST_URI'].nil? ? rack_env['PATH_INFO'] : rack_env['REQUEST_URI']
|
75
|
+
if self.is_a_regexp?(self.from)
|
76
76
|
path =~ self.from
|
77
77
|
elsif self.from.is_a?(String)
|
78
78
|
path == self.from
|
@@ -84,7 +84,7 @@ module Rack
|
|
84
84
|
# Either (a) return a Rack response (short-circuiting the Rack stack), or
|
85
85
|
# (b) alter env as necessary and return true
|
86
86
|
def apply!(env) #:nodoc:
|
87
|
-
interpreted_to = self.
|
87
|
+
interpreted_to = self.interpret_to(env['REQUEST_URI'], env)
|
88
88
|
case self.rule_type
|
89
89
|
when :r301
|
90
90
|
[301, {'Location' => interpreted_to, 'Content-Type' => 'text/html'}, ['Redirecting...']]
|
@@ -95,10 +95,10 @@ module Rack
|
|
95
95
|
env['REQUEST_URI'] = interpreted_to
|
96
96
|
if q_index = interpreted_to.index('?')
|
97
97
|
env['PATH_INFO'] = interpreted_to[0..q_index-1]
|
98
|
-
env['
|
98
|
+
env['QUERY_STRING'] = interpreted_to[q_index+1..interpreted_to.size-1]
|
99
99
|
else
|
100
100
|
env['PATH_INFO'] = interpreted_to
|
101
|
-
env['
|
101
|
+
env['QUERY_STRING'] = ''
|
102
102
|
end
|
103
103
|
true
|
104
104
|
when :send_file
|
@@ -117,20 +117,25 @@ module Rack
|
|
117
117
|
end
|
118
118
|
end
|
119
119
|
|
120
|
-
|
120
|
+
protected
|
121
121
|
def interpret_to(path, env={}) #:nodoc:
|
122
122
|
return interpret_to_proc(path, env) if self.to.is_a?(Proc)
|
123
123
|
return computed_to(path) if compute_to?(path)
|
124
124
|
self.to
|
125
125
|
end
|
126
|
+
|
127
|
+
def is_a_regexp?(obj)
|
128
|
+
obj.is_a?(Regexp) || (Object.const_defined?(:Oniguruma) && obj.is_a?(Oniguruma::ORegexp))
|
129
|
+
end
|
126
130
|
|
131
|
+
private
|
127
132
|
def interpret_to_proc(path, env)
|
128
133
|
return self.to.call(match(path), env) if self.from.is_a?(Regexp)
|
129
134
|
self.to.call(self.from, env)
|
130
135
|
end
|
131
136
|
|
132
137
|
def compute_to?(path)
|
133
|
-
self.
|
138
|
+
self.is_a_regexp?(from) && match(path)
|
134
139
|
end
|
135
140
|
|
136
141
|
def match(path)
|
@@ -140,11 +145,12 @@ module Rack
|
|
140
145
|
def computed_to(path)
|
141
146
|
# is there a better way to do this?
|
142
147
|
computed_to = self.to.dup
|
148
|
+
computed_to.gsub!("$&",match(path).to_s)
|
143
149
|
(match(path).size - 1).downto(1) do |num|
|
144
|
-
computed_to.gsub!("$#{num}", match(path)[num])
|
150
|
+
computed_to.gsub!("$#{num}", match(path)[num].to_s)
|
145
151
|
end
|
146
152
|
return computed_to
|
147
|
-
end
|
153
|
+
end
|
148
154
|
end
|
149
155
|
end
|
150
156
|
end
|
data/rack-rewrite.gemspec
CHANGED
@@ -5,16 +5,17 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{rack-rewrite}
|
8
|
-
s.version = "0.2.
|
8
|
+
s.version = "0.2.1"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["John Trupiano"]
|
12
|
-
s.date = %q{
|
12
|
+
s.date = %q{2010-01-06}
|
13
13
|
s.description = %q{A rack middleware for enforcing rewrite rules. In many cases you can get away with rack-rewrite instead of writing Apache mod_rewrite rules.}
|
14
14
|
s.email = %q{jtrupiano@gmail.com}
|
15
15
|
s.extra_rdoc_files = [
|
16
16
|
"LICENSE",
|
17
|
-
"README.rdoc"
|
17
|
+
"README.rdoc",
|
18
|
+
"TODO"
|
18
19
|
]
|
19
20
|
s.files = [
|
20
21
|
".document",
|
data/test/rack-rewrite_test.rb
CHANGED
@@ -3,7 +3,11 @@ require 'test_helper'
|
|
3
3
|
class RackRewriteTest < Test::Unit::TestCase
|
4
4
|
|
5
5
|
def call_args(overrides={})
|
6
|
-
{'REQUEST_URI' => '/wiki/Yair_Flicker', 'PATH_INFO' => '/wiki/Yair_Flicker', '
|
6
|
+
{'REQUEST_URI' => '/wiki/Yair_Flicker', 'PATH_INFO' => '/wiki/Yair_Flicker', 'QUERY_STRING' => ''}.merge(overrides)
|
7
|
+
end
|
8
|
+
|
9
|
+
def call_args_no_req(overrides={})
|
10
|
+
{'PATH_INFO' => '/wiki/Yair_Flicker', 'QUERY_STRING' => ''}.merge(overrides)
|
7
11
|
end
|
8
12
|
|
9
13
|
def self.should_not_halt
|
@@ -90,10 +94,36 @@ class RackRewriteTest < Test::Unit::TestCase
|
|
90
94
|
assert_equal '/john', @initial_args['PATH_INFO']
|
91
95
|
end
|
92
96
|
should "set REQUEST_URI to '/john'" do
|
93
|
-
assert_equal '/john', @initial_args['REQUEST_URI']
|
97
|
+
assert_equal '/john', @initial_args['REQUEST_URI']
|
98
|
+
end
|
99
|
+
should "set QUERY_STRING to ''" do
|
100
|
+
assert_equal '', @initial_args['QUERY_STRING']
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
context 'when a rewrite rule matches but there is no REQUEST_URI set' do
|
106
|
+
setup {
|
107
|
+
@rack = Rack::Rewrite.new(@app) do
|
108
|
+
rewrite '/wiki/Yair_Flicker', '/john'
|
109
|
+
end
|
110
|
+
}
|
111
|
+
should_not_halt
|
112
|
+
|
113
|
+
context 'the env' do
|
114
|
+
setup do
|
115
|
+
@initial_args = call_args_no_req.dup
|
116
|
+
@rack.call(@initial_args)
|
117
|
+
end
|
118
|
+
|
119
|
+
should "set PATH_INFO to '/john'" do
|
120
|
+
assert_equal '/john', @initial_args['PATH_INFO']
|
121
|
+
end
|
122
|
+
should "set REQUEST_URI to '/john'" do
|
123
|
+
assert_equal '/john', @initial_args['REQUEST_URI']
|
94
124
|
end
|
95
|
-
should "set
|
96
|
-
assert_equal '', @initial_args['
|
125
|
+
should "set QUERY_STRING to ''" do
|
126
|
+
assert_equal '', @initial_args['QUERY_STRING']
|
97
127
|
end
|
98
128
|
end
|
99
129
|
end
|
data/test/rule_test.rb
CHANGED
@@ -38,27 +38,27 @@ class RuleTest < Test::Unit::TestCase
|
|
38
38
|
assert_equal rule.send(:interpret_to, '/abc'), rule.apply!(env)[1]['Location']
|
39
39
|
end
|
40
40
|
|
41
|
-
should 'keep the
|
41
|
+
should 'keep the QUERY_STRING when a 301 rule matches a URL with a querystring' do
|
42
42
|
rule = Rack::Rewrite::Rule.new(:r301, %r{/john(.*)}, '/yair$1')
|
43
|
-
env = {'REQUEST_URI' => '/john?show_bio=1', 'PATH_INFO' => '/john', '
|
43
|
+
env = {'REQUEST_URI' => '/john?show_bio=1', 'PATH_INFO' => '/john', 'QUERY_STRING' => 'show_bio=1'}
|
44
44
|
assert_equal '/yair?show_bio=1', rule.apply!(env)[1]['Location']
|
45
45
|
end
|
46
46
|
|
47
|
-
should 'keep the
|
47
|
+
should 'keep the QUERY_STRING when a rewrite rule that requires a querystring matches a URL with a querystring' do
|
48
48
|
rule = Rack::Rewrite::Rule.new(:rewrite, %r{/john(\?.*)}, '/yair$1')
|
49
|
-
env = {'REQUEST_URI' => '/john?show_bio=1', 'PATH_INFO' => '/john', '
|
49
|
+
env = {'REQUEST_URI' => '/john?show_bio=1', 'PATH_INFO' => '/john', 'QUERY_STRING' => 'show_bio=1'}
|
50
50
|
rule.apply!(env)
|
51
51
|
assert_equal '/yair', env['PATH_INFO']
|
52
|
-
assert_equal 'show_bio=1', env['
|
52
|
+
assert_equal 'show_bio=1', env['QUERY_STRING']
|
53
53
|
assert_equal '/yair?show_bio=1', env['REQUEST_URI']
|
54
54
|
end
|
55
55
|
|
56
|
-
should 'update the
|
56
|
+
should 'update the QUERY_STRING when a rewrite rule changes its value' do
|
57
57
|
rule = Rack::Rewrite::Rule.new(:rewrite, %r{/(\w+)\?show_bio=(\d)}, '/$1?bio=$2')
|
58
|
-
env = {'REQUEST_URI' => '/john?show_bio=1', 'PATH_INFO' => '/john', '
|
58
|
+
env = {'REQUEST_URI' => '/john?show_bio=1', 'PATH_INFO' => '/john', 'QUERY_STRING' => 'show_bio=1'}
|
59
59
|
rule.apply!(env)
|
60
60
|
assert_equal '/john', env['PATH_INFO']
|
61
|
-
assert_equal 'bio=1', env['
|
61
|
+
assert_equal 'bio=1', env['QUERY_STRING']
|
62
62
|
assert_equal '/john?bio=1', env['REQUEST_URI']
|
63
63
|
end
|
64
64
|
|
@@ -206,7 +206,7 @@ class RuleTest < Test::Unit::TestCase
|
|
206
206
|
setup do
|
207
207
|
@rule = Rack::Rewrite::Rule.new(:rewrite, /.*/, '/system/maintenance.html', lambda { |rack_env|
|
208
208
|
maintenance_file = File.join('system', 'maintenance.html')
|
209
|
-
File.exists?(maintenance_file) &&
|
209
|
+
File.exists?(maintenance_file) && rack_env['REQUEST_URI'] !~ /\.(css|jpg|png)/
|
210
210
|
})
|
211
211
|
end
|
212
212
|
should_pass_maintenance_tests
|
@@ -222,6 +222,25 @@ class RuleTest < Test::Unit::TestCase
|
|
222
222
|
should_pass_maintenance_tests
|
223
223
|
end
|
224
224
|
end
|
225
|
+
|
226
|
+
context 'Given the CNAME alternative rewrite rule in our README' do
|
227
|
+
setup do
|
228
|
+
@rule = Rack::Rewrite::Rule.new(:r301, %r{.*}, 'http://mynewdomain.com$&', lambda {|rack_env|
|
229
|
+
rack_env['SERVER_NAME'] != 'mynewdomain.com'
|
230
|
+
})
|
231
|
+
end
|
232
|
+
|
233
|
+
should 'match requests for domain myolddomain.com and redirect to mynewdomain.com' do
|
234
|
+
env = {'REQUEST_URI' => '/anything?abc=1', 'PATH_INFO' => '/anything', 'QUERY_STRING' => 'abc=1', 'SERVER_NAME' => 'myolddomain.com'}
|
235
|
+
assert @rule.matches?(env)
|
236
|
+
rack_response = @rule.apply!(env)
|
237
|
+
assert_equal 'http://mynewdomain.com/anything?abc=1', rack_response[1]['Location']
|
238
|
+
end
|
239
|
+
|
240
|
+
should 'not match requests for domain mynewdomain.com' do
|
241
|
+
assert !@rule.matches?({'REQUEST_URI' => '/anything', 'SERVER_NAME' => 'mynewdomain.com'})
|
242
|
+
end
|
243
|
+
end
|
225
244
|
end
|
226
245
|
|
227
246
|
context 'Rule#interpret_to' do
|
@@ -246,6 +265,16 @@ class RuleTest < Test::Unit::TestCase
|
|
246
265
|
assert_equal 'jihgfedcba', rule.send(:interpret_to, "abcdefghij")
|
247
266
|
end
|
248
267
|
|
268
|
+
should 'replace $& on a match' do
|
269
|
+
rule = Rack::Rewrite::Rule.new(:rewrite, %r{.*}, 'http://example.org$&')
|
270
|
+
assert_equal 'http://example.org/person/1', rule.send(:interpret_to, "/person/1")
|
271
|
+
end
|
272
|
+
|
273
|
+
should 'ignore empty captures' do
|
274
|
+
rule = Rack::Rewrite::Rule.new(:rewrite, %r{/person(_\d+)?}, '/people/$1')
|
275
|
+
assert_equal '/people/', rule.send(:interpret_to, "/person")
|
276
|
+
end
|
277
|
+
|
249
278
|
should 'call to with from when it is a lambda' do
|
250
279
|
rule = Rack::Rewrite::Rule.new(:rewrite, 'a', lambda { |from, env| from * 2 })
|
251
280
|
assert_equal 'aa', rule.send(:interpret_to, 'a')
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-rewrite
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Trupiano
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date:
|
12
|
+
date: 2010-01-06 00:00:00 -05:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -31,6 +31,7 @@ extensions: []
|
|
31
31
|
extra_rdoc_files:
|
32
32
|
- LICENSE
|
33
33
|
- README.rdoc
|
34
|
+
- TODO
|
34
35
|
files:
|
35
36
|
- .document
|
36
37
|
- .gitignore
|