rack-rewrite 1.3.0 → 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile.lock CHANGED
@@ -1,13 +1,13 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rack-rewrite (1.3.0)
4
+ rack-rewrite (1.3.1)
5
5
 
6
6
  GEM
7
7
  remote: http://rubygems.org/
8
8
  specs:
9
9
  mocha (0.9.12)
10
- rack (1.4.1)
10
+ rack (1.2.1)
11
11
  shoulda (2.10.3)
12
12
 
13
13
  PLATFORMS
data/LICENSE CHANGED
@@ -1,4 +1,6 @@
1
- Copyright (c) 2009 John Trupiano
1
+ (The MIT License)
2
+
3
+ Copyright (c) 2012 — John Trupiano, Travis Jeffery
2
4
 
3
5
  Permission is hereby granted, free of charge, to any person obtaining
4
6
  a copy of this software and associated documentation files (the
data/README.markdown ADDED
@@ -0,0 +1,332 @@
1
+ # rack-rewrite
2
+
3
+ A rack middleware for defining and applying rewrite rules. In many cases you
4
+ can get away with rack-rewrite instead of writing Apache mod_rewrite rules.
5
+
6
+ ## Usage Examples
7
+
8
+ * [Rack::Rewrite for Site Maintenance and Downtime](http://blog.smartlogicsolutions.com/2009/11/16/rack-rewrite-for-site-maintenance-and-downtime/)
9
+ * [Rack::Rewrite + Google Analytics Makes Site Transitions Seamless](http://blog.smartlogicsolutions.com/2009/11/24/rack-rewrite-google-analytics-makes-site-transitions-seamless/)
10
+
11
+ ## Usage Details
12
+
13
+ ### Sample rackup file
14
+
15
+ ```ruby
16
+ gem 'rack-rewrite', '~> 1.2.1'
17
+ require 'rack/rewrite'
18
+ use Rack::Rewrite do
19
+ rewrite '/wiki/John_Trupiano', '/john'
20
+ r301 '/wiki/Yair_Flicker', '/yair'
21
+ r302 '/wiki/Greg_Jastrab', '/greg'
22
+ r301 %r{/wiki/(\w+)_\w+}, '/$1'
23
+ end
24
+ ```
25
+
26
+ ### Sample usage in a rails app
27
+
28
+ ```ruby
29
+ config.middleware.insert_before(Rack::Lock, Rack::Rewrite) do
30
+ rewrite '/wiki/John_Trupiano', '/john'
31
+ r301 '/wiki/Yair_Flicker', '/yair'
32
+ r302 '/wiki/Greg_Jastrab', '/greg'
33
+ r301 %r{/wiki/(\w+)_\w+}, '/$1'
34
+ end
35
+ ```
36
+
37
+ ## Redirection codes
38
+
39
+ All redirect status codes from the [HTTP spec](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html) are supported:
40
+
41
+ * 301 moved permanently
42
+ * 302 found
43
+ * 303 see other
44
+ * 307 temporary redirect
45
+
46
+ These translate to the following methods inside the Rack::Rewrite block:
47
+
48
+ ```ruby
49
+ r301 '/wiki/John_Trupiano', '/john'
50
+ moved_permanently '/wiki/John_Trupiano', '/john'
51
+ p '/wiki/John_Trupiano', '/john' # shortcut alias
52
+
53
+ r302 '/wiki/John_Trupiano', '/john'
54
+ found '/wiki/John_Trupiano', '/john'
55
+
56
+ r303 '/wiki/John_Trupiano', '/john'
57
+ see_other '/wiki/John_Trupiano', '/john'
58
+
59
+ r307 '/wiki/John_Trupiano', '/john'
60
+ temporary_redirect '/wiki/John_Trupiano', '/john'
61
+ t '/wiki/John_Trupiano', '/john' # shortcut alias
62
+ ```
63
+
64
+ The 303 and 307 codes were added to the HTTP spec to make unambiguously clear
65
+ what clients should do with the request method. 303 means that the new request
66
+ should always be made via GET. 307 means that the new request should use the
67
+ same method as the original request. Status code 302 was left as it is, since
68
+ it was already in use by the time these issues came to light. In practice it
69
+ behaves the same as 303.
70
+
71
+ ## Use Cases
72
+
73
+ ### Rebuild of existing site in a new technology
74
+
75
+ It's very common for sites built in older technologies to be rebuilt with the
76
+ latest and greatest. Let's consider a site that has already established quite
77
+ a bit of "google juice." When we launch the new site, we don't want to lose
78
+ that hard-earned reputation. By writing rewrite rules that issue 301's for
79
+ old URL's, we can "transfer" that google ranking to the new site. An example
80
+ rule might look like:
81
+
82
+ ```ruby
83
+ r301 '/contact-us.php', '/contact-us'
84
+ r301 '/wiki/John_Trupiano', '/john'
85
+ ```
86
+
87
+ ### Retiring old routes
88
+
89
+ As a web application evolves you will undoubtedly reach a point where you need
90
+ to change the name of something (a model, e.g.). This name change will
91
+ typically require a similar change to your routing. The danger here is that
92
+ any URL's previously generated (in a transactional email for instance) will
93
+ have the URL hard-coded. In order for your rails app to continue to serve
94
+ this URL, you'll need to add an extra entry to your routes file.
95
+ Alternatively, you could use rack-rewrite to redirect or pass through requests
96
+ to these routes and keep your routes.rb clean.
97
+
98
+ ```ruby
99
+ rewrite %r{/features(.*)}, '/facial_features$1'
100
+ ```
101
+
102
+ ### CNAME alternative
103
+
104
+ In the event that you do not control your DNS, you can leverage Rack::Rewrite
105
+ to redirect to a canonical domain. In the following rule we utilize the
106
+ $& substitution operator to capture the entire request URI.
107
+
108
+ ```ruby
109
+ r301 %r{.*}, 'http://mynewdomain.com$&', :if => Proc.new {|rack_env|
110
+ rack_env['SERVER_NAME'] != 'mynewdomain.com'
111
+ }
112
+ ```
113
+
114
+ ### Site Maintenance
115
+
116
+ Most capistrano users will be familiar with the following Apache rewrite rules:
117
+
118
+ ```
119
+ RewriteCond %{REQUEST_URI} !\.(css|jpg|png)$
120
+ RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f
121
+ RewriteCond %{SCRIPT_FILENAME} !maintenance.html
122
+ RewriteRule ^.*$ /system/maintenance.html [L]
123
+ ```
124
+
125
+ This rewrite rule says to render a maintenance page for all non-asset requests
126
+ if the maintenance file exists. In capistrano, you can quickly upload a
127
+ maintenance file using:
128
+
129
+ `cap deploy:web:disable REASON=upgrade UNTIL=12:30PM`
130
+
131
+ We can replace the mod_rewrite rules with the following Rack::Rewrite rule:
132
+
133
+ ```ruby
134
+ maintenance_file = File.join(RAILS_ROOT, 'public', 'system', 'maintenance.html')
135
+ send_file /.*/, maintenance_file, :if => Proc.new { |rack_env|
136
+ File.exists?(maintenance_file) && rack_env['PATH_INFO'] !~ /\.(css|jpg|png)/
137
+ }
138
+ ```
139
+
140
+ If you're running Ruby 1.9, this rule is simplified:
141
+
142
+ ```ruby
143
+ maintenance_file = File.join(RAILS_ROOT, 'public', 'system', 'maintenance.html')
144
+ send_file /(.*)$(?<!css|png|jpg)/, maintenance_file, :if => Proc.new { |rack_env|
145
+ File.exists?(maintenance_file)
146
+ }
147
+ ```
148
+
149
+ For those using the oniguruma gem with their ruby 1.8 installation, you can
150
+ get away with:
151
+
152
+ ```ruby
153
+ maintenance_file = File.join(RAILS_ROOT, 'public', 'system', 'maintenance.html')
154
+ send_file Oniguruma::ORegexp.new("(.*)$(?<!css|png|jpg)"), maintenance_file, :if => Proc.new { |rack_env|
155
+ File.exists?(maintenance_file)
156
+ }
157
+ ```
158
+
159
+ ## Rewrite Rules
160
+
161
+ ### :rewrite
162
+
163
+ Calls to #rewrite will simply update the PATH_INFO, QUERY_STRING and
164
+ REQUEST_URI HTTP header values and pass the request onto the next chain in
165
+ the Rack stack. The URL that a user's browser will show will not be changed.
166
+ See these examples:
167
+
168
+ ```ruby
169
+ rewrite '/wiki/John_Trupiano', '/john' # [1]
170
+ rewrite %r{/wiki/(\w+)_\w+}, '/$1' # [2]
171
+ ```
172
+
173
+ For [1], the user's browser will continue to display /wiki/John_Trupiano, but
174
+ the actual HTTP header values for PATH_INFO and REQUEST_URI in the request
175
+ will be changed to /john for subsequent nodes in the Rack stack. Rails
176
+ reads these headers to determine which routes will match.
177
+
178
+ Rule [2] showcases the use of regular expressions and substitutions. [2] is a
179
+ generalized version of [1] that will match any /wiki/FirstName_LastName URL's
180
+ and rewrite them as the first name only. This is an actual catch-all rule we
181
+ applied when we rebuilt our website in September 2009
182
+ ( http://www.smartlogicsolutions.com ).
183
+
184
+ ### :r301, :r302, :r303, :r307
185
+
186
+ Calls to #r301 and #r302 have the same signature as #rewrite. The difference,
187
+ however, is that these actually short-circuit the rack stack and send back
188
+ their respective status codes. See these examples:
189
+
190
+ ```ruby
191
+ r301 '/wiki/John_Trupiano', '/john' # [1]
192
+ r301 '/wiki/(.*)', 'http://www.google.com/?q=$1' # [2]
193
+ ```
194
+
195
+ Recall that rules are interpreted from top to bottom. So you can install
196
+ "default" rewrite rules if you like. [2] is a sample default rule that
197
+ will redirect all other requests to the wiki to a google search.
198
+
199
+ ### :send_file, :x_send_file
200
+
201
+ Calls to #send_file and #x_send_file also have the same signature as #rewrite.
202
+ If the rule matches, the 'to' parameter is interpreted as a path to a file
203
+ to be rendered instead of passing the application call up the rack stack.
204
+
205
+ ```ruby
206
+ send_file /*/, 'public/spammers.htm', :if => Proc.new { |rack_env|
207
+ rack_env['HTTP_REFERER'] =~ 'spammers.com'
208
+ }
209
+ x_send_file /^blog\/.*/, 'public/blog_offline.htm', :if => Proc.new { |rack_env|
210
+ File.exists?('public/blog_offline.htm')
211
+ }
212
+ ```
213
+
214
+ ## Options Parameter
215
+
216
+ Each rewrite rule takes an optional options parameter. The following options
217
+ are supported.
218
+
219
+ ### :host
220
+
221
+ Using the :host option you can match requests to a specific hostname.
222
+
223
+ ```ruby
224
+ r301 "/features", "/facial_features", :host => "facerecognizer.com"
225
+ ```
226
+ This rule will only match when the hostname is "facerecognizer.com"
227
+
228
+ ### :headers
229
+
230
+ Using the :headers option you can set custom response headers e.g. for HTTP
231
+ caching instructions.
232
+
233
+ ```ruby
234
+ r301 "/features", "/facial_features", :headers => {'Cache-Control' => 'no-cache'}
235
+ ```
236
+
237
+ Please be aware that the :headers value above is evaluated only once at app boot and shared amongst all matching requests.
238
+
239
+ Use a Proc as the :headers option if you wish to determine the additional headers at request-time. For example:
240
+
241
+ ```ruby
242
+ # We want the Expires value to always be 1 year in the future from now. If
243
+ # we didn't use a Proc here, then the Expires value would be set just once
244
+ # at app startup. The Proc will be evaluated for each matching request.
245
+ send_file /^.+\.(?:ico|jpg|jpeg|png|gif|)$/,
246
+ 'public/$&',
247
+ :headers => lambda { { 'Expires' => 1.year.from_now.httpdate } }
248
+ ```
249
+
250
+ ### :method
251
+
252
+ Using the :method option you can restrict the matching of a rule by the HTTP
253
+ method of a given request.
254
+
255
+ ```ruby
256
+ # redirect GET's one way
257
+ r301 "/players", "/current_players", :method => :get
258
+
259
+ # and redirect POST's another way
260
+ r302 "/players", "/no_longer_available.html?message=No&longer&supported", :method => :post
261
+ ```
262
+
263
+ ### :if
264
+
265
+ Using the :if option you can define arbitrary rule guards. Guards are any
266
+ object responding to #call that return true or false indicating whether the
267
+ rule matches. The following example demonstrates how the presence of a
268
+ maintenance page on the filesystem can be utilized to take your site(s) offline.
269
+
270
+ ```ruby
271
+ maintenance_file = File.join(RAILS_ROOT, 'public', 'system', 'maintenance.html')
272
+ x_send_file /.*/, maintenance_file, :if => Proc.new { |rack_env|
273
+ File.exists?(maintenance_file)
274
+ }
275
+ ```
276
+
277
+ ### :not
278
+
279
+ Using the :not option you can negatively match against the path. This can
280
+ be useful when writing a regular expression match is difficult.
281
+
282
+ ```ruby
283
+ rewrite %r{^\/features}, '/facial_features', :not => '/features'
284
+ ```
285
+
286
+ This will not match the relative URL /features but would match /features.xml.
287
+
288
+ ## Tips
289
+
290
+ ### Keeping your querystring
291
+
292
+ When rewriting a URL, you may want to keep your querystring in tact (for
293
+ example if you're tracking traffic sources). You will need to include a
294
+ capture group and substitution pattern in your rewrite rule to achieve this.
295
+
296
+ ```ruby
297
+ rewrite %r{/wiki/John_Trupiano(\?.*)?}, '/john$1'
298
+ ```
299
+
300
+ This rule will store the querystring in a capture group (via `(?.*)` ) and
301
+ will substitute the querystring back into the rewritten URL (via `$1`).
302
+
303
+ ### Arbitrary Rewriting
304
+
305
+ All rules support passing a Proc as the second argument allowing you to
306
+ perform arbitrary rewrites. The following rule will rewrite all requests
307
+ received between 12AM and 8AM to an unavailable page.
308
+
309
+ ```ruby
310
+ rewrite %r{(.*)}, lambda { |match, rack_env|
311
+ Time.now.hour < 8 ? "/unavailable.html" : match[1]
312
+ }
313
+ ```
314
+
315
+ ## Contribute
316
+
317
+ rack-rewrite is maintained by [@travisjeffery](http://github.com/travisjeffery).
318
+
319
+ Here's the most direct way to get your work merged into the project.
320
+
321
+ - Fork the project
322
+ - Clone down your fork
323
+ - Create a feature branch
324
+ - Hack away and add tests, not necessarily in that order
325
+ - Make sure everything still passes by running tests
326
+ - If necessary, rebase your commits into logical chunks without errors
327
+ - Push the branch up to your fork
328
+ - Send a pull request for your branch
329
+
330
+ ## Copyright
331
+
332
+ Copyright (c) 2012 — John Trupiano, Travis Jeffery. See LICENSE for details.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.3.0
1
+ 1.3.1
@@ -115,7 +115,14 @@ module Rack
115
115
  # (b) alter env as necessary and return true
116
116
  def apply!(env) #:nodoc:
117
117
  interpreted_to = self.interpret_to(env)
118
- additional_headers = @options[:headers] || {}
118
+ additional_headers = {}
119
+ if @options[:headers]
120
+ if @options[:headers].respond_to?(:call)
121
+ additional_headers = @options[:headers].call || {}
122
+ else
123
+ additional_headers = @options[:headers] || {}
124
+ end
125
+ end
119
126
  case self.rule_type
120
127
  when :r301
121
128
  [301, {'Location' => interpreted_to, 'Content-Type' => Rack::Mime.mime_type(::File.extname(interpreted_to))}.merge!(additional_headers), [redirect_message(interpreted_to)]]
data/rack-rewrite.gemspec CHANGED
@@ -10,12 +10,11 @@ Gem::Specification.new do |s|
10
10
  s.extra_rdoc_files = [
11
11
  "LICENSE",
12
12
  "History.rdoc",
13
- "README.rdoc"
14
13
  ]
15
14
  s.files = [
16
15
  "History.rdoc",
17
16
  "LICENSE",
18
- "README.rdoc",
17
+ "README.markdown",
19
18
  "Rakefile",
20
19
  "VERSION",
21
20
  "Gemfile",
data/test/rule_test.rb CHANGED
@@ -97,6 +97,28 @@ class RuleTest < Test::Unit::TestCase
97
97
  assert_equal 'no-cache', rule.apply!(env)[1]['Cache-Control']
98
98
  end
99
99
  end
100
+
101
+ should 'evaluate additional headers block once per redirect request' do
102
+ [:r301, :r302].each do |rule_type|
103
+ header_val = 'foo'
104
+ rule = Rack::Rewrite::Rule.new(rule_type, %r{/abc}, '/def.css', {:headers => lambda { {'X-Foobar' => header_val} } })
105
+ env = {'PATH_INFO' => '/abc'}
106
+ assert_equal 'foo', rule.apply!(env)[1]['X-Foobar']
107
+ header_val = 'bar'
108
+ assert_equal 'bar', rule.apply!(env)[1]['X-Foobar']
109
+ end
110
+ end
111
+
112
+ should 'evaluate additional headers block once per send file request' do
113
+ [:send_file, :x_send_file].each do |rule_type|
114
+ header_val = 'foo'
115
+ rule = Rack::Rewrite::Rule.new(rule_type, /.*/, File.join(TEST_ROOT, 'geminstaller.yml'), {:headers => lambda { {'X-Foobar' => header_val} } })
116
+ env = {'PATH_INFO' => '/abc'}
117
+ assert_equal 'foo', rule.apply!(env)[1]['X-Foobar']
118
+ header_val = 'bar'
119
+ assert_equal 'bar', rule.apply!(env)[1]['X-Foobar']
120
+ end
121
+ end
100
122
 
101
123
  context 'Given an :x_send_file rule that matches' do
102
124
  setup do
@@ -125,7 +147,7 @@ class RuleTest < Test::Unit::TestCase
125
147
  should 'return additional headers' do
126
148
  assert_equal 'no-cache', @response[1]['Cache-Control']
127
149
  end
128
-
150
+
129
151
  should 'return empty content' do
130
152
  assert_equal [], @response[2]
131
153
  end
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: 1.3.0
4
+ version: 1.3.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2012-10-13 00:00:00.000000000 Z
13
+ date: 2012-11-03 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: bundler
@@ -84,11 +84,10 @@ extensions: []
84
84
  extra_rdoc_files:
85
85
  - LICENSE
86
86
  - History.rdoc
87
- - README.rdoc
88
87
  files:
89
88
  - History.rdoc
90
89
  - LICENSE
91
- - README.rdoc
90
+ - README.markdown
92
91
  - Rakefile
93
92
  - VERSION
94
93
  - Gemfile
data/README.rdoc DELETED
@@ -1,266 +0,0 @@
1
- = rack-rewrite
2
-
3
- A rack middleware for defining and applying rewrite rules. In many cases you
4
- can get away with rack-rewrite instead of writing Apache mod_rewrite rules.
5
-
6
- == References
7
-
8
- * Source[http://github.com/jtrupiano/rack-rewrite]
9
- * {Rack::Rewrite for Site Maintenance and Downtime}[http://blog.smartlogicsolutions.com/2009/11/16/rack-rewrite-for-site-maintenance-and-downtime/]
10
- * {Rack::Rewrite + Google Analytics Makes Site Transitions Seamless}[http://blog.smartlogicsolutions.com/2009/11/24/rack-rewrite-google-analytics-makes-site-transitions-seamless/]
11
-
12
- == Usage
13
-
14
- === Sample rackup file
15
-
16
- gem 'rack-rewrite', '~> 1.2.1'
17
- require 'rack/rewrite'
18
- use Rack::Rewrite do
19
- rewrite '/wiki/John_Trupiano', '/john'
20
- r301 '/wiki/Yair_Flicker', '/yair'
21
- r302 '/wiki/Greg_Jastrab', '/greg'
22
- r301 %r{/wiki/(\w+)_\w+}, '/$1'
23
- end
24
-
25
- === Sample usage in a rails app
26
-
27
- config.middleware.insert_before(Rack::Lock, Rack::Rewrite) do
28
- rewrite '/wiki/John_Trupiano', '/john'
29
- r301 '/wiki/Yair_Flicker', '/yair'
30
- r302 '/wiki/Greg_Jastrab', '/greg'
31
- r301 %r{/wiki/(\w+)_\w+}, '/$1'
32
- end
33
-
34
- == Redirection codes
35
-
36
- All +redirect+ status codes from the HTTP spec[http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html] are supported:
37
-
38
- * 301 moved permanently
39
- * 302 found
40
- * 303 see other
41
- * 307 temporary redirect
42
-
43
- These translate to the following methods inside the Rack::Rewrite block:
44
-
45
- r301 '/wiki/John_Trupiano', '/john'
46
- moved_permanently '/wiki/John_Trupiano', '/john'
47
- p '/wiki/John_Trupiano', '/john' # shortcut alias
48
-
49
- r302 '/wiki/John_Trupiano', '/john'
50
- found '/wiki/John_Trupiano', '/john'
51
-
52
- r303 '/wiki/John_Trupiano', '/john'
53
- see_other '/wiki/John_Trupiano', '/john'
54
-
55
- r307 '/wiki/John_Trupiano', '/john'
56
- temporary_redirect '/wiki/John_Trupiano', '/john'
57
- t '/wiki/John_Trupiano', '/john' # shortcut alias
58
-
59
- The 303 and 307 codes were added to the HTTP spec to make unambiguously clear
60
- what clients should do with the request method. 303 means that the new request
61
- should always be made via GET. 307 means that the new request should use the
62
- same method as the original request. Status code 302 was left as it is, since
63
- it was already in use by the time these issues came to light. In practice it
64
- behaves the same as 303.
65
-
66
- == Use Cases
67
-
68
- === Rebuild of existing site in a new technology
69
-
70
- It's very common for sites built in older technologies to be rebuilt with the
71
- latest and greatest. Let's consider a site that has already established quite
72
- a bit of "google juice." When we launch the new site, we don't want to lose
73
- that hard-earned reputation. By writing rewrite rules that issue 301's for
74
- old URL's, we can "transfer" that google ranking to the new site. An example
75
- rule might look like:
76
-
77
- r301 '/contact-us.php', '/contact-us'
78
- r301 '/wiki/John_Trupiano', '/john'
79
-
80
- === Retiring old routes
81
-
82
- As a web application evolves you will undoubtedly reach a point where you need
83
- to change the name of something (a model, e.g.). This name change will
84
- typically require a similar change to your routing. The danger here is that
85
- any URL's previously generated (in a transactional email for instance) will
86
- have the URL hard-coded. In order for your rails app to continue to serve
87
- this URL, you'll need to add an extra entry to your routes file.
88
- Alternatively, you could use rack-rewrite to redirect or pass through requests
89
- to these routes and keep your routes.rb clean.
90
-
91
- rewrite %r{/features(.*)}, '/facial_features$1'
92
-
93
- === CNAME alternative
94
-
95
- In the event that you do not control your DNS, you can leverage Rack::Rewrite
96
- to redirect to a canonical domain. In the following rule we utilize the
97
- $& substitution operator to capture the entire request URI.
98
-
99
- r301 %r{.*}, 'http://mynewdomain.com$&', :if => Proc.new {|rack_env|
100
- rack_env['SERVER_NAME'] != 'mynewdomain.com'
101
- }
102
-
103
- === Site Maintenance
104
-
105
- Most capistrano users will be familiar with the following Apache rewrite rules:
106
-
107
- RewriteCond %{REQUEST_URI} !\.(css|jpg|png)$
108
- RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f
109
- RewriteCond %{SCRIPT_FILENAME} !maintenance.html
110
- RewriteRule ^.*$ /system/maintenance.html [L]
111
-
112
- This rewrite rule says to render a maintenance page for all non-asset requests
113
- if the maintenance file exists. In capistrano, you can quickly upload a
114
- maintenance file using:
115
-
116
- cap deploy:web:disable REASON=upgrade UNTIL=12:30PM
117
-
118
- We can replace the mod_rewrite rules with the following Rack::Rewrite rule:
119
-
120
- maintenance_file = File.join(RAILS_ROOT, 'public', 'system', 'maintenance.html')
121
- send_file /.*/, maintenance_file, :if => Proc.new { |rack_env|
122
- File.exists?(maintenance_file) && rack_env['PATH_INFO'] !~ /\.(css|jpg|png)/
123
- }
124
-
125
- If you're running Ruby 1.9, this rule is simplified:
126
-
127
- maintenance_file = File.join(RAILS_ROOT, 'public', 'system', 'maintenance.html')
128
- send_file /(.*)$(?<!css|png|jpg)/, maintenance_file, :if => Proc.new { |rack_env|
129
- File.exists?(maintenance_file)
130
- }
131
-
132
- For those using the oniguruma gem with their ruby 1.8 installation, you can
133
- get away with:
134
-
135
- maintenance_file = File.join(RAILS_ROOT, 'public', 'system', 'maintenance.html')
136
- send_file Oniguruma::ORegexp.new("(.*)$(?<!css|png|jpg)"), maintenance_file, :if => Proc.new { |rack_env|
137
- File.exists?(maintenance_file)
138
- }
139
-
140
- == Rewrite Rules
141
-
142
- === :rewrite
143
-
144
- Calls to #rewrite will simply update the PATH_INFO, QUERY_STRING and
145
- REQUEST_URI HTTP header values and pass the request onto the next chain in
146
- the Rack stack. The URL that a user's browser will show will not be changed.
147
- See these examples:
148
-
149
- rewrite '/wiki/John_Trupiano', '/john' # [1]
150
- rewrite %r{/wiki/(\w+)_\w+}, '/$1' # [2]
151
-
152
- For [1], the user's browser will continue to display /wiki/John_Trupiano, but
153
- the actual HTTP header values for PATH_INFO and REQUEST_URI in the request
154
- will be changed to /john for subsequent nodes in the Rack stack. Rails
155
- reads these headers to determine which routes will match.
156
-
157
- Rule [2] showcases the use of regular expressions and substitutions. [2] is a
158
- generalized version of [1] that will match any /wiki/FirstName_LastName URL's
159
- and rewrite them as the first name only. This is an actual catch-all rule we
160
- applied when we rebuilt our website in September 2009
161
- ( http://www.smartlogicsolutions.com ).
162
-
163
- === :r301, :r302, :r303, :r307
164
-
165
- Calls to #r301 and #r302 have the same signature as #rewrite. The difference,
166
- however, is that these actually short-circuit the rack stack and send back
167
- their respective status codes. See these examples:
168
-
169
- r301 '/wiki/John_Trupiano', '/john' # [1]
170
- r301 '/wiki/(.*)', 'http://www.google.com/?q=$1' # [2]
171
-
172
- Recall that rules are interpreted from top to bottom. So you can install
173
- "default" rewrite rules if you like. [2] is a sample default rule that
174
- will redirect all other requests to the wiki to a google search.
175
-
176
- === :send_file, :x_send_file
177
-
178
- Calls to #send_file and #x_send_file also have the same signature as #rewrite.
179
- If the rule matches, the 'to' parameter is interpreted as a path to a file
180
- to be rendered instead of passing the application call up the rack stack.
181
-
182
- send_file /*/, 'public/spammers.htm', :if => Proc.new { |rack_env|
183
- rack_env['HTTP_REFERER'] =~ 'spammers.com'
184
- }
185
- x_send_file /^blog\/.*/, 'public/blog_offline.htm', :if => Proc.new { |rack_env|
186
- File.exists?('public/blog_offline.htm')
187
- }
188
-
189
- == Options Parameter
190
-
191
- Each rewrite rule takes an optional options parameter. The following options
192
- are supported.
193
-
194
- === :host
195
-
196
- Using the :host option you can match requests to a specific hostname.
197
-
198
- r301 "/features", "/facial_features", :host => "facerecognizer.com"
199
-
200
- This rule will only match when the hostname is "facerecognizer.com"
201
-
202
- === :headers
203
-
204
- Using the :headers option you can set custom response headers e.g. for HTTP
205
- caching instructions.
206
-
207
- r301 "/features", "/facial_features", :headers => {'Cache-Control' => 'no-cache'}
208
-
209
- === :method
210
-
211
- Using the :method option you can restrict the matching of a rule by the HTTP
212
- method of a given request.
213
-
214
- # redirect GET's one way
215
- r301 "/players", "/current_players", :method => :get
216
-
217
- # and redirect POST's another way
218
- r302 "/players", "/no_longer_available.html?message=No&longer&supported", :method => :post
219
-
220
- === :if
221
-
222
- Using the :if option you can define arbitrary rule guards. Guards are any
223
- object responding to #call that return true or false indicating whether the
224
- rule matches. The following example demonstrates how the presence of a
225
- maintenance page on the filesystem can be utilized to take your site(s) offline.
226
-
227
- maintenance_file = File.join(RAILS_ROOT, 'public', 'system', 'maintenance.html')
228
- x_send_file /.*/, maintenance_file, :if => Proc.new { |rack_env|
229
- File.exists?(maintenance_file)
230
- }
231
-
232
- === :not
233
-
234
- Using the :not option you can negatively match against the path. This can
235
- be useful when writing a regular expression match is difficult.
236
-
237
- rewrite %r{^\/features}, '/facial_features', :not => '/features'
238
-
239
- This will not match the relative URL /features but would match /features.xml.
240
-
241
- == Tips
242
-
243
- === Keeping your querystring
244
-
245
- When rewriting a URL, you may want to keep your querystring in tact (for
246
- example if you're tracking traffic sources). You will need to include a
247
- capture group and substitution pattern in your rewrite rule to achieve this.
248
-
249
- rewrite %r{/wiki/John_Trupiano(\?.*)?}, '/john$1'
250
-
251
- This rule will store the querystring in a capture group (via '(?.*)' ) and
252
- will substitute the querystring back into the rewritten URL (via $1).
253
-
254
- === Arbitrary Rewriting
255
-
256
- All rules support passing a Proc as the second argument allowing you to
257
- perform arbitrary rewrites. The following rule will rewrite all requests
258
- received between 12AM and 8AM to an unavailable page.
259
-
260
- rewrite %r{(.*)}, lambda { |match, rack_env|
261
- Time.now.hour < 8 ? "/unavailable.html" : match[1]
262
- }
263
-
264
- == Copyright
265
-
266
- Copyright (c) 2009-2011 John Trupiano. See LICENSE for details.