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.
@@ -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
@@ -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.3'
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.3'
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) && !%w(css jpg png).any? {|ext| maintenance_file =~ Regexp.new("\.#{ext}$")}
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 highly simplified:
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 this:
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)"), maintenace_file, :if => Proc.new { |rack_env|
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
@@ -57,5 +57,6 @@ Rake::RDocTask.new do |rdoc|
57
57
  rdoc.rdoc_dir = 'rdoc'
58
58
  rdoc.title = "rack-rewrite #{version}"
59
59
  rdoc.rdoc_files.include('README*')
60
+ rdoc.rdoc_files.include('History.rdoc')
60
61
  rdoc.rdoc_files.include('lib/**/*.rb')
61
62
  end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0
1
+ 0.2.1
@@ -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 QUERYSTRING headers of the Rack environment. The
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.from.is_a?(Regexp) || (Object.const_defined?(:Oniguruma) && self.from.is_a?(Oniguruma::ORegexp))
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.send(:interpret_to, env['REQUEST_URI'], env)
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['QUERYSTRING'] = interpreted_to[q_index+1..interpreted_to.size-1]
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['QUERYSTRING'] = ''
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
- private
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.from.is_a?(Regexp) && match(path)
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
@@ -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.0"
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{2009-11-14}
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",
@@ -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', 'QUERYSTRING' => ''}.merge(overrides)
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 QUERYSTRING to ''" do
96
- assert_equal '', @initial_args['QUERYSTRING']
125
+ should "set QUERY_STRING to ''" do
126
+ assert_equal '', @initial_args['QUERY_STRING']
97
127
  end
98
128
  end
99
129
  end
@@ -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 QUERYSTRING when a 301 rule matches a URL with a querystring' do
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', 'QUERYSTRING' => 'show_bio=1'}
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 QUERYSTRING when a rewrite rule that requires a querystring matches a URL with a querystring' do
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', 'QUERYSTRING' => 'show_bio=1'}
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['QUERYSTRING']
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 QUERYSTRING when a rewrite rule changes its value' do
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', 'QUERYSTRING' => 'show_bio=1'}
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['QUERYSTRING']
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) && !%w(css jpg png).any? {|ext| rack_env['REQUEST_URI'] =~ Regexp.new("\.#{ext}$")}
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.0
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: 2009-11-14 00:00:00 -05:00
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