rack-rewrite 0.1.3 → 0.2.0

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 ADDED
@@ -0,0 +1,23 @@
1
+ === 0.2.0 / 2009-11-14
2
+ * API
3
+ * Allow Proc's to be be passed as the 'to' argument to rule declarations
4
+ * Introduce rule guard support using :if => Proc.new option.
5
+ * :send_file and :x_send_file rules
6
+ * proxy rack_env to rule guards for arbitrary rule writing
7
+
8
+ * Documentation
9
+ * Add example of writing capistrano maintenance page rewrite rules
10
+ * Add examples of rule guards and arbitrary rewriting
11
+ * Add examples of :send_file and :x_send_file rules
12
+
13
+ === 0.1.3 / 2009-11-14
14
+ * Maintenance
15
+ * Ensure Content-Type header is set for 301's and 302's (thanks to Sebastian Röbke)
16
+ * Documentation
17
+ * Add HISTORY.rdoc
18
+
19
+ === 0.1.2 / 2009-10-13
20
+
21
+ * Initial Feature Set
22
+ * :r301, :r302 and :redirect are supported in the rewrite DSL
23
+ * Regex matching/substitution patterns supported in rules
data/README.rdoc CHANGED
@@ -3,6 +3,29 @@
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
+ == Usage
7
+
8
+ === Sample rackup file
9
+
10
+ gem 'rack-rewrite', '~> 0.1.3'
11
+ require 'rack-rewrite
12
+ use Rack::Rewrite do
13
+ rewrite '/wiki/John_Trupiano', '/john'
14
+ r301 '/wiki/Yair_Flicker', '/yair'
15
+ r302 '/wiki/Greg_Jastrab', '/greg'
16
+ r301 %r{/wiki/(\w+)_\w+}, '/$1'
17
+ end
18
+
19
+ === Sample usage in a rails app
20
+ config.gem 'rack-rewrite', '~> 0.1.3'
21
+ require 'rack-rewrite
22
+ config.middleware.insert_before(Rack::Lock, Rack::Rewrite) do
23
+ rewrite '/wiki/John_Trupiano', '/john'
24
+ r301 '/wiki/Yair_Flicker', '/yair'
25
+ r302 '/wiki/Greg_Jastrab', '/greg'
26
+ r301 %r{/wiki/(\w+)_\w+}, '/$1'
27
+ end
28
+
6
29
  == Use Cases
7
30
 
8
31
  === Rebuild of existing site in a new technology
@@ -30,27 +53,49 @@ to these routes and keep your routes.rb clean.
30
53
 
31
54
  rewrite %r{/features(.*)}, '/facial_features$1'
32
55
 
33
- == Sample rackup file
34
-
35
- gem 'rack-rewrite', '~> 0.1.2'
36
- require 'rack-rewrite
37
- use Rack::Rewrite do
38
- rewrite '/wiki/John_Trupiano', '/john'
39
- r301 '/wiki/Yair_Flicker', '/yair'
40
- r302 '/wiki/Greg_Jastrab', '/greg'
41
- r301 %r{/wiki/(\w+)_\w+}, '/$1'
42
- end
56
+ === Site Maintenance
43
57
 
44
- == Sample usage in a rails app
45
- config.gem 'rack-rewrite', '~> 0.1.2'
46
- require 'rack-rewrite
47
- config.middleware.insert_before(Rack::Lock, Rack::Rewrite) do
48
- rewrite '/wiki/John_Trupiano', '/john'
49
- r301 '/wiki/Yair_Flicker', '/yair'
50
- r302 '/wiki/Greg_Jastrab', '/greg'
51
- r301 %r{/wiki/(\w+)_\w+}, '/$1'
52
- end
58
+ Most capistrano users will be familiar with the following Apache rewrite rules:
59
+
60
+ RewriteCond %{REQUEST_URI} !\.(css|jpg|png)$
61
+ RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f
62
+ RewriteCond %{SCRIPT_FILENAME} !maintenance.html
63
+ RewriteRule ^.*$ /system/maintenance.html [L]
64
+
65
+ This rewrite rule says to render a maintenance page for all non-asset requests
66
+ if the maintenance file exists. In capistrano, you can quickly upload a
67
+ maintenance file using:
68
+
69
+ cap deploy:web:disable REASON=upgrade UNTIL=12:30PM
53
70
 
71
+ We can replace the mod_rewrite rules with the following Rack::Rewrite rule:
72
+
73
+ maintenance_file = File.join(RAILS_ROOT, 'public', 'system', 'maintenance.html')
74
+ 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}$")}
76
+ }
77
+
78
+ If you're running Ruby 1.9, this rule is highly simplified:
79
+
80
+ maintenance_file = File.join(RAILS_ROOT, 'public', 'system', 'maintenance.html')
81
+ send_file /(.*)$(?<!css|png|jpg)/, maintenance_file, :if => Proc.new { |rack_env|
82
+ File.exists?(maintenance_file)
83
+ }
84
+
85
+ For those using the oniguruma gem with their ruby 1.8 installation, you can
86
+ get away with this:
87
+
88
+ 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|
90
+ File.exists?(maintenance_file)
91
+ }
92
+
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
+
54
99
  == Rewrite Rules
55
100
 
56
101
  === :rewrite
@@ -86,6 +131,19 @@ Recall that rules are interpreted from top to bottom. So you can install
86
131
  "default" rewrite rules if you like. [2] is a sample default rule that
87
132
  will redirect all other requests to the wiki to a google search.
88
133
 
134
+ === :send_file, :x_send_file
135
+
136
+ Calls to #send_file and #x_send_file also have the same signature as #rewrite.
137
+ If the rule matches, the 'to' parameter is interpreted as a path to a file
138
+ to be rendered instead of passing the application call up the rack stack.
139
+
140
+ send_file /*/, 'public/spammers.htm', :if => Proc.new { |rack_env|
141
+ rack_env['HTTP_REFERER'] =~ 'spammers.com'
142
+ }
143
+ x_send_file /^blog\/.*/, 'public/blog_offline.htm', :if => Proc.new { |rack_env|
144
+ File.exists?('public/blog_offline.htm')
145
+ }
146
+
89
147
  == Tips
90
148
 
91
149
  === Keeping your querystring
@@ -99,6 +157,39 @@ capture group and substitution pattern in your rewrite rule to achieve this.
99
157
  This rule will store the querystring in a capture group (via '(?.*)' ) and
100
158
  will substitute the querystring back into the rewritten URL (via $1).
101
159
 
160
+ === Rule Guards
161
+
162
+ All rules support passing guards as Procs/lambdas. Guards simply return
163
+ true or false indicating whether the rule declaration is a match. The
164
+ following example demonstrates how the presence of a maintenance page
165
+ on the filesystem can be utilized to take your site(s) offline.
166
+
167
+ maintenance_file = File.join(RAILS_ROOT, 'public', 'system', 'maintenance.html')
168
+ x_send_file /.*/, maintenance_file, :if => Proc.new { |rack_env|
169
+ File.exists?(maintenance_file)
170
+ }
171
+
172
+ === Arbitrary Rewriting
173
+
174
+ All rules support passing a Proc as the second argument allowing you to
175
+ perform arbitrary rewrites. The following rule will rewrite all requests
176
+ received between 12AM and 8AM to an unavailable page.
177
+
178
+ rewrite %r{(.*)}, lambda { |match, rack_env|
179
+ Time.now.hour < 8 ? "/unavailable.html" : match[1]
180
+ }
181
+
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
+
102
193
  == Copyright
103
194
 
104
195
  Copyright (c) 2009 John Trupiano. See LICENSE for details.
data/TODO CHANGED
@@ -1,4 +1,9 @@
1
- * Add :host support to restrict which URL's a rewrite rule matches [10/15/09]
2
- * Add :if => lambda support for arbitrary conditional rule application (this will allow us to do the capistrano maintenance page w/o apache's mod_rewrite)
3
- * Add support for specifying a config file instead of passing a block (e.g. config/rewrite.rb)
4
- * Better message than "Redirecting..." -- how about html that says where it's being redirected to?
1
+ * OUTSTANDING
2
+ * Add :host support to restrict which URL's a rewrite rule matches [10/15/09]
3
+ * Add support for specifying a config file instead of passing a block (e.g. config/rewrite.rb) [10/15/09]
4
+ * Better message than "Redirecting..." -- how about html that says where it's being redirected to? [10/16/09]
5
+ * Provide testing helpers (e.g. should_rewrite) to facilitate straightforward testing. [10/25/09]
6
+ * Allow rules to return arbitrary html (e.g. the contents of the maintenance page) [10/25/09]
7
+
8
+ * COMPLETED
9
+ * Add :if => lambda support for arbitrary conditional rule application (this will allow us to do the capistrano maintenance page w/o apache's mod_rewrite) [10/15/09]
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.3
1
+ 0.2.0
data/lib/rack-rewrite.rb CHANGED
@@ -23,7 +23,7 @@ module Rack
23
23
 
24
24
  private
25
25
  def find_first_matching_rule(env) #:nodoc:
26
- @rule_set.rules.detect { |rule| rule.matches?(env['REQUEST_URI']) }
26
+ @rule_set.rules.detect { |rule| rule.matches?(env) }
27
27
  end
28
28
  end
29
29
  end
@@ -1,3 +1,5 @@
1
+ require 'rack/mime'
2
+
1
3
  module Rack
2
4
  class Rewrite
3
5
  class RuleSet
@@ -15,40 +17,64 @@ module Rack
15
17
  # user's browser will continue to show the initially requested URL.
16
18
  #
17
19
  # rewrite '/wiki/John_Trupiano', '/john'
18
- # rewrite %r{/wiki/(\w+)_\w+}, '/$1'
19
- def rewrite(from, to)
20
- @rules << Rule.new(:rewrite, from, to)
20
+ # rewrite %r{/wiki/(\w+)_\w+}, '/$1'
21
+ # rewrite %r{(.*)}, '/maintenance.html', :if => lambda { File.exists?('maintenance.html') }
22
+ def rewrite(from, to, *args)
23
+ options = args.last.is_a?(Hash) ? args.last : {}
24
+ @rules << Rule.new(:rewrite, from, to, options[:if])
21
25
  end
22
26
 
23
27
  # Creates a redirect rule that will send a 301 when matching.
24
28
  #
25
29
  # r301 '/wiki/John_Trupiano', '/john'
26
30
  # r301 '/contact-us.php', '/contact-us'
27
- def r301(from, to)
28
- @rules << Rule.new(:r301, from, to)
31
+ def r301(from, to, *args)
32
+ options = args.last.is_a?(Hash) ? args.last : {}
33
+ @rules << Rule.new(:r301, from, to, options[:if])
29
34
  end
30
35
 
31
36
  # Creates a redirect rule that will send a 302 when matching.
32
37
  #
33
38
  # r302 '/wiki/John_Trupiano', '/john'
34
39
  # r302 '/wiki/(.*)', 'http://www.google.com/?q=$1'
35
- def r302(from, to)
36
- @rules << Rule.new(:r302, from, to)
40
+ def r302(from, to, *args)
41
+ options = args.last.is_a?(Hash) ? args.last : {}
42
+ @rules << Rule.new(:r302, from, to, options[:if])
37
43
  end
44
+
45
+ # Creates a rule that will render a file if matched.
46
+ #
47
+ # send_file /*/, 'public/system/maintenance.html',
48
+ # :if => { File.exists?('public/system/maintenance.html') }
49
+ def send_file(from, to, *args)
50
+ options = args.last.is_a?(Hash) ? args.last : {}
51
+ @rules << Rule.new(:send_file, from, to, options[:if])
52
+ end
53
+
54
+ # Creates a rule that will render a file using x-send-file
55
+ # if matched.
56
+ #
57
+ # x_send_file /*/, 'public/system/maintenance.html',
58
+ # :if => { File.exists?('public/system/maintenance.html') }
59
+ def x_send_file(from, to, *args)
60
+ options = args.last.is_a?(Hash) ? args.last : {}
61
+ @rules << Rule.new(:x_send_file, from, to, options[:if])
62
+ end
38
63
  end
39
64
 
40
65
  # TODO: Break rules into subclasses
41
66
  class Rule #:nodoc:
42
- attr_reader :rule_type, :from, :to
43
- def initialize(rule_type, from, to) #:nodoc:
44
- @rule_type, @from, @to = rule_type, from, to
67
+ attr_reader :rule_type, :from, :to, :guard
68
+ def initialize(rule_type, from, to, guard=nil) #:nodoc:
69
+ @rule_type, @from, @to, @guard = rule_type, from, to, guard
45
70
  end
46
71
 
47
- def matches?(path) #:nodoc:
48
- case self.from
49
- when Regexp
72
+ def matches?(rack_env) #:nodoc:
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))
50
76
  path =~ self.from
51
- when String
77
+ elsif self.from.is_a?(String)
52
78
  path == self.from
53
79
  else
54
80
  false
@@ -58,7 +84,7 @@ module Rack
58
84
  # Either (a) return a Rack response (short-circuiting the Rack stack), or
59
85
  # (b) alter env as necessary and return true
60
86
  def apply!(env) #:nodoc:
61
- interpreted_to = self.send(:interpret_to, env['REQUEST_URI'])
87
+ interpreted_to = self.send(:interpret_to, env['REQUEST_URI'], env)
62
88
  case self.rule_type
63
89
  when :r301
64
90
  [301, {'Location' => interpreted_to, 'Content-Type' => 'text/html'}, ['Redirecting...']]
@@ -75,25 +101,50 @@ module Rack
75
101
  env['QUERYSTRING'] = ''
76
102
  end
77
103
  true
104
+ when :send_file
105
+ [200, {
106
+ 'Content-Length' => ::File.size(interpreted_to).to_s,
107
+ 'Content-Type' => Rack::Mime.mime_type(::File.extname(interpreted_to))
108
+ }, ::File.read(interpreted_to)]
109
+ when :x_send_file
110
+ [200, {
111
+ 'X-Sendfile' => interpreted_to,
112
+ 'Content-Length' => ::File.size(interpreted_to).to_s,
113
+ 'Content-Type' => Rack::Mime.mime_type(::File.extname(interpreted_to))
114
+ }, []]
78
115
  else
79
116
  raise Exception.new("Unsupported rule: #{self.rule_type}")
80
117
  end
81
118
  end
82
119
 
83
120
  private
84
- # is there a better way to do this?
85
- def interpret_to(path) #:nodoc:
86
- if self.from.is_a?(Regexp)
87
- if from_match_data = self.from.match(path)
88
- computed_to = self.to.dup
89
- (from_match_data.size - 1).downto(1) do |num|
90
- computed_to.gsub!("$#{num}", from_match_data[num])
91
- end
92
- return computed_to
93
- end
94
- end
121
+ def interpret_to(path, env={}) #:nodoc:
122
+ return interpret_to_proc(path, env) if self.to.is_a?(Proc)
123
+ return computed_to(path) if compute_to?(path)
95
124
  self.to
96
125
  end
97
- end
126
+
127
+ def interpret_to_proc(path, env)
128
+ return self.to.call(match(path), env) if self.from.is_a?(Regexp)
129
+ self.to.call(self.from, env)
130
+ end
131
+
132
+ def compute_to?(path)
133
+ self.from.is_a?(Regexp) && match(path)
134
+ end
135
+
136
+ def match(path)
137
+ self.from.match(path)
138
+ end
139
+
140
+ def computed_to(path)
141
+ # is there a better way to do this?
142
+ computed_to = self.to.dup
143
+ (match(path).size - 1).downto(1) do |num|
144
+ computed_to.gsub!("$#{num}", match(path)[num])
145
+ end
146
+ return computed_to
147
+ end
148
+ end
98
149
  end
99
- end
150
+ end
data/rack-rewrite.gemspec CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{rack-rewrite}
8
- s.version = "0.1.3"
8
+ s.version = "0.2.0"
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"]
@@ -19,7 +19,7 @@ Gem::Specification.new do |s|
19
19
  s.files = [
20
20
  ".document",
21
21
  ".gitignore",
22
- "HISTORY.rdoc",
22
+ "History.rdoc",
23
23
  "LICENSE",
24
24
  "README.rdoc",
25
25
  "Rakefile",
@@ -28,6 +28,7 @@ Gem::Specification.new do |s|
28
28
  "lib/rack-rewrite.rb",
29
29
  "lib/rack-rewrite/rule.rb",
30
30
  "rack-rewrite.gemspec",
31
+ "test/geminstaller.yml",
31
32
  "test/rack-rewrite_test.rb",
32
33
  "test/rule_test.rb",
33
34
  "test/test_helper.rb"
@@ -0,0 +1,9 @@
1
+ gems:
2
+ - name: jeweler
3
+ version: '~> 1.3.0'
4
+ - name: shoulda
5
+ version: '~> 2.10.2'
6
+ - name: mocha
7
+ version: '~> 0.9.7'
8
+ - name: oniguruma
9
+ version: '~> 1.1.0'
data/test/rule_test.rb CHANGED
@@ -1,7 +1,36 @@
1
- require 'test_helper'
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
2
 
3
3
  class RuleTest < Test::Unit::TestCase
4
+
5
+ def self.should_pass_maintenance_tests
6
+ context 'and the maintenance file does in fact exist' do
7
+ setup { File.stubs(:exists?).returns(true) }
4
8
 
9
+ should('match for the root') { assert @rule.matches?({'REQUEST_URI' => '/'}) }
10
+ should('match for a regular rails route') { assert @rule.matches?({'REQUEST_URI' => '/users/1'}) }
11
+ should('match for an html page') { assert @rule.matches?({'REQUEST_URI' => '/index.html'}) }
12
+ should('not match for a css file') { assert !@rule.matches?({'REQUEST_URI' => '/stylesheets/style.css'}) }
13
+ should('not match for a jpg file') { assert !@rule.matches?({'REQUEST_URI' => '/images/sls.jpg'}) }
14
+ should('not match for a png file') { assert !@rule.matches?({'REQUEST_URI' => '/images/sls.png'}) }
15
+ end
16
+ end
17
+
18
+ def self.negative_lookahead_supported?
19
+ begin
20
+ require 'oniguruma'
21
+ rescue LoadError; end
22
+ RUBY_VERSION =~ /^1\.9/ || Object.const_defined?(:Oniguruma)
23
+ end
24
+
25
+ def negative_lookahead_regexp
26
+ if RUBY_VERSION =~ /^1\.9/
27
+ # have to use the constructor instead of the literal syntax b/c load errors occur in Ruby 1.8
28
+ Regexp.new("(.*)$(?<!css|png|jpg)")
29
+ else
30
+ Oniguruma::ORegexp.new("(.*)$(?<!css|png|jpg)")
31
+ end
32
+ end
33
+
5
34
  context '#Rule#apply' do
6
35
  should 'set Location header to result of #interpret_to for a 301' do
7
36
  rule = Rack::Rewrite::Rule.new(:r301, %r{/abc}, '/def')
@@ -40,6 +69,64 @@ class RuleTest < Test::Unit::TestCase
40
69
  assert_equal 'text/html', rule.apply!(env)[1]['Content-Type']
41
70
  end
42
71
  end
72
+
73
+ context 'Given an :x_send_file rule that matches' do
74
+ setup do
75
+ @file = File.join(TEST_ROOT, 'geminstaller.yml')
76
+ @rule = Rack::Rewrite::Rule.new(:x_send_file, /.*/, @file)
77
+ env = {'PATH_INFO' => '/abc'}
78
+ @response = @rule.apply!(env)
79
+ end
80
+
81
+ should 'return 200' do
82
+ assert_equal 200, @response[0]
83
+ end
84
+
85
+ should 'return an X-Sendfile header' do
86
+ assert @response[1].has_key?('X-Sendfile')
87
+ end
88
+
89
+ should 'return a Content-Type of text/yaml' do
90
+ assert_equal 'text/yaml', @response[1]['Content-Type']
91
+ end
92
+
93
+ should 'return the proper Content-Length' do
94
+ assert_equal File.size(@file).to_s, @response[1]['Content-Length']
95
+ end
96
+
97
+ should 'return empty content' do
98
+ assert_equal [], @response[2]
99
+ end
100
+ end
101
+
102
+ context 'Given a :send_file rule that matches' do
103
+ setup do
104
+ @file = File.join(TEST_ROOT, 'geminstaller.yml')
105
+ @rule = Rack::Rewrite::Rule.new(:send_file, /.*/, @file)
106
+ env = {'PATH_INFO' => '/abc'}
107
+ @response = @rule.apply!(env)
108
+ end
109
+
110
+ should 'return 200' do
111
+ assert_equal 200, @response[0]
112
+ end
113
+
114
+ should 'not return an X-Sendfile header' do
115
+ assert !@response[1].has_key?('X-Sendfile')
116
+ end
117
+
118
+ should 'return a Content-Type of text/yaml' do
119
+ assert_equal 'text/yaml', @response[1]['Content-Type']
120
+ end
121
+
122
+ should 'return the proper Content-Length' do
123
+ assert_equal File.size(@file).to_s, @response[1]['Content-Length']
124
+ end
125
+
126
+ should 'return the contents of geminstaller.yml' do
127
+ assert_equal File.read(@file), @response[2]
128
+ end
129
+ end
43
130
  end
44
131
 
45
132
  context 'Rule#matches' do
@@ -49,15 +136,15 @@ class RuleTest < Test::Unit::TestCase
49
136
  end
50
137
 
51
138
  should 'match PATH_INFO of /features' do
52
- assert @rule.matches?("/features")
139
+ assert @rule.matches?({'REQUEST_URI' => "/features"})
53
140
  end
54
141
 
55
142
  should 'not match PATH_INFO of /features.xml' do
56
- assert !@rule.matches?("/features.xml")
143
+ assert !@rule.matches?({'REQUEST_URI' => "/features.xml"})
57
144
  end
58
145
 
59
146
  should 'not match PATH_INFO of /my_features' do
60
- assert !@rule.matches?("/my_features")
147
+ assert !@rule.matches?({'REQUEST_URI' => "/my_features"})
61
148
  end
62
149
  end
63
150
 
@@ -67,23 +154,72 @@ class RuleTest < Test::Unit::TestCase
67
154
  end
68
155
 
69
156
  should 'match PATH_INFO of /features' do
70
- assert @rule.matches?("/features")
157
+ assert @rule.matches?({'REQUEST_URI' => "/features"})
71
158
  end
72
159
 
73
160
  should 'match PATH_INFO of /features.xml' do
74
- assert @rule.matches?('/features.xml')
161
+ assert @rule.matches?({'REQUEST_URI' => '/features.xml'})
75
162
  end
76
163
 
77
164
  should 'match PATH_INFO of /features/1' do
78
- assert @rule.matches?('/features/1')
165
+ assert @rule.matches?({'REQUEST_URI' => '/features/1'})
79
166
  end
80
167
 
81
168
  should 'match PATH_INFO of /features?filter_by=name' do
82
- assert @rule.matches?('/features?filter_by_name=name')
169
+ assert @rule.matches?({'REQUEST_URI' => '/features?filter_by_name=name'})
83
170
  end
84
171
 
85
172
  should 'match PATH_INFO of /features/1?hide_bio=1' do
86
- assert @rule.matches?('/features/1?hide_bio=1')
173
+ assert @rule.matches?({'REQUEST_URI' => '/features/1?hide_bio=1'})
174
+ end
175
+ end
176
+
177
+ context 'Given a rule with a guard that checks for the presence of a file' do
178
+ setup do
179
+ @rule = Rack::Rewrite::Rule.new(:rewrite, %r{(.)*}, '/maintenance.html', lambda { |rack_env|
180
+ File.exists?('maintenance.html')
181
+ })
182
+ end
183
+
184
+ context 'when the file exists' do
185
+ setup do
186
+ File.stubs(:exists?).returns(true)
187
+ end
188
+
189
+ should 'match' do
190
+ assert @rule.matches?({'REQUEST_URI' => '/anything/should/match'})
191
+ end
192
+ end
193
+
194
+ context 'when the file does not exist' do
195
+ setup do
196
+ File.stubs(:exists?).returns(false)
197
+ end
198
+
199
+ should 'not match' do
200
+ assert !@rule.matches?({'REQUEST_URI' => '/nothing/should/match'})
201
+ end
202
+ end
203
+ end
204
+
205
+ context 'Given the capistrano maintenance.html rewrite rule given in our README' do
206
+ setup do
207
+ @rule = Rack::Rewrite::Rule.new(:rewrite, /.*/, '/system/maintenance.html', lambda { |rack_env|
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}$")}
210
+ })
211
+ end
212
+ should_pass_maintenance_tests
213
+ end
214
+
215
+ if negative_lookahead_supported?
216
+ context 'Given the negative lookahead regular expression version of the capistrano maintenance.html rewrite rule given in our README' do
217
+ setup do
218
+ @rule = Rack::Rewrite::Rule.new(:rewrite, negative_lookahead_regexp, '/system/maintenance.html', lambda { |rack_env|
219
+ File.exists?(File.join('public', 'system', 'maintenance.html'))
220
+ })
221
+ end
222
+ should_pass_maintenance_tests
87
223
  end
88
224
  end
89
225
  end
@@ -109,5 +245,16 @@ class RuleTest < Test::Unit::TestCase
109
245
  rule = Rack::Rewrite::Rule.new(:rewrite, %r{(\w)(\w)(\w)(\w)(\w)(\w)(\w)(\w)(\w)(\w)}, '$10$9$8$7$6$5$4$3$2$1')
110
246
  assert_equal 'jihgfedcba', rule.send(:interpret_to, "abcdefghij")
111
247
  end
248
+
249
+ should 'call to with from when it is a lambda' do
250
+ rule = Rack::Rewrite::Rule.new(:rewrite, 'a', lambda { |from, env| from * 2 })
251
+ assert_equal 'aa', rule.send(:interpret_to, 'a')
252
+ end
253
+
254
+ should 'call to with from match data' do
255
+ rule = Rack::Rewrite::Rule.new(:rewrite, %r{/person_(\d+)(.*)}, lambda {|match, env| "people-#{match[1].to_i * 3}#{match[2]}"})
256
+ assert_equal 'people-3?show_bio=1', rule.send(:interpret_to, '/person_1?show_bio=1')
257
+ end
112
258
  end
259
+
113
260
  end
data/test/test_helper.rb CHANGED
@@ -11,3 +11,5 @@ require 'rack-rewrite'
11
11
 
12
12
  class Test::Unit::TestCase
13
13
  end
14
+
15
+ TEST_ROOT = File.dirname(__FILE__)
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.1.3
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Trupiano
@@ -34,7 +34,7 @@ extra_rdoc_files:
34
34
  files:
35
35
  - .document
36
36
  - .gitignore
37
- - HISTORY.rdoc
37
+ - History.rdoc
38
38
  - LICENSE
39
39
  - README.rdoc
40
40
  - Rakefile
@@ -43,6 +43,7 @@ files:
43
43
  - lib/rack-rewrite.rb
44
44
  - lib/rack-rewrite/rule.rb
45
45
  - rack-rewrite.gemspec
46
+ - test/geminstaller.yml
46
47
  - test/rack-rewrite_test.rb
47
48
  - test/rule_test.rb
48
49
  - test/test_helper.rb
data/HISTORY.rdoc DELETED
@@ -1,10 +0,0 @@
1
- === 0.1.3 / 2009-11-14
2
- * Maintenance
3
- * Ensure Content-Type header is set for 301's and 302's (thanks to Sebastian Röbke)
4
- * Documentation
5
- * Add HISTORY.rdoc
6
-
7
- === 0.1.2 / 2009-10-15
8
- * API
9
- * rewrite, r301 and r302 rules
10
- * regular expression matching and substitution rules