rack-rewrite 0.2.0 → 0.2.1
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.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
|