actionpack 2.0.2 → 2.0.4

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of actionpack might be problematic. Click here for more details.

Files changed (74) hide show
  1. data/CHANGELOG +45 -0
  2. data/Rakefile +5 -3
  3. data/lib/action_controller.rb +0 -0
  4. data/lib/action_controller/assertions/response_assertions.rb +6 -2
  5. data/lib/action_controller/assertions/routing_assertions.rb +5 -2
  6. data/lib/action_controller/base.rb +4 -3
  7. data/lib/action_controller/cgi_ext/cookie.rb +1 -1
  8. data/lib/action_controller/filters.rb +5 -3
  9. data/lib/action_controller/http_authentication.rb +2 -4
  10. data/lib/action_controller/integration.rb +3 -2
  11. data/lib/action_controller/layout.rb +14 -15
  12. data/lib/action_controller/mime_type.rb +4 -1
  13. data/lib/action_controller/polymorphic_routes.rb +90 -15
  14. data/lib/action_controller/record_identifier.rb +4 -4
  15. data/lib/action_controller/request.rb +29 -14
  16. data/lib/action_controller/request_forgery_protection.rb +41 -34
  17. data/lib/action_controller/request_profiler.rb +16 -6
  18. data/lib/action_controller/response.rb +0 -0
  19. data/lib/action_controller/routing.rb +3 -3
  20. data/lib/action_controller/session/active_record_store.rb +7 -8
  21. data/lib/action_controller/session/cookie_store.rb +2 -3
  22. data/lib/action_controller/templates/rescues/_trace.erb +5 -5
  23. data/lib/action_controller/test_case.rb +24 -4
  24. data/lib/action_controller/test_process.rb +3 -3
  25. data/lib/action_controller/url_rewriter.rb +29 -28
  26. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +2 -2
  27. data/lib/action_controller/verification.rb +73 -57
  28. data/lib/action_pack/version.rb +1 -1
  29. data/lib/action_view/base.rb +24 -6
  30. data/lib/action_view/helpers/active_record_helper.rb +1 -1
  31. data/lib/action_view/helpers/asset_tag_helper.rb +12 -6
  32. data/lib/action_view/helpers/atom_feed_helper.rb +21 -17
  33. data/lib/action_view/helpers/date_helper.rb +6 -6
  34. data/lib/action_view/helpers/form_helper.rb +3 -2
  35. data/lib/action_view/helpers/form_options_helper.rb +12 -1
  36. data/lib/action_view/helpers/form_tag_helper.rb +18 -0
  37. data/lib/action_view/helpers/number_helper.rb +1 -1
  38. data/lib/action_view/helpers/prototype_helper.rb +1 -1
  39. data/lib/action_view/helpers/text_helper.rb +1 -1
  40. data/lib/action_view/helpers/url_helper.rb +5 -9
  41. data/lib/action_view/template_error.rb +11 -4
  42. data/test/activerecord/active_record_store_test.rb +1 -1
  43. data/test/activerecord/fixtures_test.rb +24 -0
  44. data/test/controller/action_pack_assertions_test.rb +37 -2
  45. data/test/controller/base_test.rb +4 -1
  46. data/test/controller/cgi_test.rb +4 -3
  47. data/test/controller/filters_test.rb +4 -7
  48. data/test/controller/html-scanner/sanitizer_test.rb +7 -1
  49. data/test/controller/integration_test.rb +0 -1
  50. data/test/controller/mime_type_test.rb +5 -0
  51. data/test/controller/new_render_test.rb +25 -2
  52. data/test/controller/polymorphic_routes_test.rb +106 -75
  53. data/test/controller/redirect_test.rb +11 -0
  54. data/test/controller/render_test.rb +19 -0
  55. data/test/controller/request_forgery_protection_test.rb +9 -0
  56. data/test/controller/request_test.rb +33 -5
  57. data/test/controller/routing_test.rb +4 -4
  58. data/test/controller/session/cookie_store_test.rb +0 -0
  59. data/test/controller/test_test.rb +43 -1
  60. data/test/controller/url_rewriter_test.rb +34 -4
  61. data/test/fixtures/layouts/block_with_layout.erb +3 -0
  62. data/test/fixtures/layouts/partial_with_layout.erb +3 -0
  63. data/test/template/asset_tag_helper_test.rb +17 -13
  64. data/test/template/atom_feed_helper_test.rb +52 -2
  65. data/test/template/compiled_templates_test.rb +5 -1
  66. data/test/template/date_helper_test.rb +1 -1
  67. data/test/template/deprecate_ivars_test.rb +51 -0
  68. data/test/template/form_options_helper_test.rb +29 -0
  69. data/test/template/form_tag_helper_test.rb +18 -0
  70. data/test/template/number_helper_test.rb +1 -0
  71. data/test/template/text_helper_test.rb +32 -14
  72. data/test/template/url_helper_test.rb +1 -1
  73. data/test/testing_sandbox.rb +8 -4
  74. metadata +9 -4
@@ -111,7 +111,7 @@ module ActionController
111
111
  # end
112
112
  def format=(extension)
113
113
  parameters[:format] = extension.to_s
114
- format
114
+ @format = Mime::Type.lookup_by_extension(parameters[:format])
115
115
  end
116
116
 
117
117
  # Returns true if the request's "X-Requested-With" header contains
@@ -122,26 +122,41 @@ module ActionController
122
122
  end
123
123
  alias xhr? :xml_http_request?
124
124
 
125
+ # Which IP addresses are "trusted proxies" that can be stripped from
126
+ # the right-hand-side of X-Forwarded-For
127
+ TRUSTED_PROXIES = /^127\.0\.0\.1$|^(10|172\.(1[6-9]|2[0-9]|30|31)|192\.168)\./i
128
+
125
129
  # Determine originating IP address. REMOTE_ADDR is the standard
126
130
  # but will fail if the user is behind a proxy. HTTP_CLIENT_IP and/or
127
- # HTTP_X_FORWARDED_FOR are set by proxies so check for these before
128
- # falling back to REMOTE_ADDR. HTTP_X_FORWARDED_FOR may be a comma-
129
- # delimited list in the case of multiple chained proxies; the first is
130
- # the originating IP.
131
- #
132
- # Security note: do not use if IP spoofing is a concern for your
133
- # application. Since remote_ip checks HTTP headers for addresses forwarded
134
- # by proxies, the client may send any IP. remote_addr can't be spoofed but
135
- # also doesn't work behind a proxy, since it's always the proxy's IP.
131
+ # HTTP_X_FORWARDED_FOR are set by proxies so check for these if
132
+ # REMOTE_ADDR is a proxy. HTTP_X_FORWARDED_FOR may be a comma-
133
+ # delimited list in the case of multiple chained proxies; the last
134
+ # address which is not trusted is the originating IP.
135
+
136
136
  def remote_ip
137
- return @env['HTTP_CLIENT_IP'] if @env.include? 'HTTP_CLIENT_IP'
137
+ if TRUSTED_PROXIES !~ @env['REMOTE_ADDR']
138
+ return @env['REMOTE_ADDR']
139
+ end
140
+
141
+ if @env.include? 'HTTP_CLIENT_IP'
142
+ if @env.include? 'HTTP_X_FORWARDED_FOR'
143
+ # We don't know which came from the proxy, and which from the user
144
+ raise ActionControllerError.new(<<EOM)
145
+ IP spoofing attack?!
146
+ HTTP_CLIENT_IP=#{@env['HTTP_CLIENT_IP'].inspect}
147
+ HTTP_X_FORWARDED_FOR=#{@env['HTTP_X_FORWARDED_FOR'].inspect}
148
+ EOM
149
+ end
150
+ return @env['HTTP_CLIENT_IP']
151
+ end
138
152
 
139
153
  if @env.include? 'HTTP_X_FORWARDED_FOR' then
140
- remote_ips = @env['HTTP_X_FORWARDED_FOR'].split(',').reject do |ip|
141
- ip.strip =~ /^unknown$|^(10|172\.(1[6-9]|2[0-9]|30|31)|192\.168)\./i
154
+ remote_ips = @env['HTTP_X_FORWARDED_FOR'].split(',')
155
+ while remote_ips.size > 1 && TRUSTED_PROXIES =~ remote_ips.last.strip
156
+ remote_ips.pop
142
157
  end
143
158
 
144
- return remote_ips.first.strip unless remote_ips.empty?
159
+ return remote_ips.last.strip
145
160
  end
146
161
 
147
162
  @env['REMOTE_ADDR']
@@ -13,33 +13,46 @@ module ActionController #:nodoc:
13
13
  base.extend(ClassMethods)
14
14
  end
15
15
 
16
+ # Protecting controller actions from CSRF attacks by ensuring that all forms are coming from the current web application, not a
17
+ # forged link from another site, is done by embedding a token based on the session (which an attacker wouldn't know) in all
18
+ # forms and Ajax requests generated by Rails and then verifying the authenticity of that token in the controller. Only
19
+ # HTML/JavaScript requests are checked, so this will not protect your XML API (presumably you'll have a different authentication
20
+ # scheme there anyway). Also, GET requests are not protected as these should be indempotent anyway.
21
+ #
22
+ # This is turned on with the <tt>protect_from_forgery</tt> method, which will check the token and raise an
23
+ # ActionController::InvalidAuthenticityToken if it doesn't match what was expected. You can customize the error message in
24
+ # production by editing public/422.html. A call to this method in ApplicationController is generated by default in post-Rails 2.0
25
+ # applications.
26
+ #
27
+ # The token parameter is named <tt>authenticity_token</tt> by default. If you are generating an HTML form manually (without the
28
+ # use of Rails' <tt>form_for</tt>, <tt>form_tag</tt> or other helpers), you have to include a hidden field named like that and
29
+ # set its value to what is returned by <tt>form_authenticity_token</tt>. Same applies to manually constructed Ajax requests. To
30
+ # make the token available through a global variable to scripts on a certain page, you could add something like this to a view:
31
+ #
32
+ # <%= javascript_tag "window._token = '#{form_authenticity_token}'" %>
33
+ #
34
+ # Request forgery protection is disabled by default in test environment. If you are upgrading from Rails 1.x, add this to
35
+ # config/environments/test.rb:
36
+ #
37
+ # # Disable request forgery protection in test environment
38
+ # config.action_controller.allow_forgery_protection = false
39
+ #
40
+ # == Learn more about CSRF (Cross-Site Request Forgery) attacks
41
+ #
42
+ # Here are some resources:
43
+ # * http://isc.sans.org/diary.html?storyid=1750
44
+ # * http://en.wikipedia.org/wiki/Cross-site_request_forgery
45
+ #
46
+ # Keep in mind, this is NOT a silver-bullet, plug 'n' play, warm security blanket for your rails application.
47
+ # There are a few guidelines you should follow:
48
+ #
49
+ # * Keep your GET requests safe and idempotent. More reading material:
50
+ # * http://www.xml.com/pub/a/2002/04/24/deviant.html
51
+ # * http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.1
52
+ # * Make sure the session cookies that Rails creates are non-persistent. Check in Firefox and look for "Expires: at end of session"
53
+ #
16
54
  module ClassMethods
17
- # Protect a controller's actions from CSRF attacks by ensuring that all forms are coming from the current web application, not
18
- # a forged link from another site. This is done by embedding a token based on the session (which an attacker wouldn't know) in
19
- # all forms and Ajax requests generated by Rails and then verifying the authenticity of that token in the controller. Only
20
- # HTML/JavaScript requests are checked, so this will not protect your XML API (presumably you'll have a different authentication
21
- # scheme there anyway). Also, GET requests are not protected as these should be indempotent anyway.
22
- #
23
- # You turn this on with the #protect_from_forgery method, which will perform the check and raise
24
- # an ActionController::InvalidAuthenticityToken if the token doesn't match what was expected. And it will add
25
- # a _authenticity_token parameter to all forms that are automatically generated by Rails. You can customize the error message
26
- # given through public/422.html.
27
- #
28
- # Learn more about CSRF (Cross-Site Request Forgery) attacks:
29
- #
30
- # * http://isc.sans.org/diary.html?storyid=1750
31
- # * http://en.wikipedia.org/wiki/Cross-site_request_forgery
32
- #
33
- # Keep in mind, this is NOT a silver-bullet, plug 'n' play, warm security blanket for your rails application.
34
- # There are a few guidelines you should follow:
35
- #
36
- # * Keep your GET requests safe and idempotent. More reading material:
37
- # * http://www.xml.com/pub/a/2002/04/24/deviant.html
38
- # * http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.1
39
- # * Make sure the session cookies that Rails creates are non-persistent. Check in Firefox and look for "Expires: at end of session"
40
- #
41
- # If you need to construct a request yourself, but still want to take advantage of forgery protection, you can grab the
42
- # authenticity_token using the form_authenticity_token helper method and make it part of the parameters yourself.
55
+ # Turn on request forgery protection. Bear in mind that only non-GET, HTML/JavaScript requests are checked.
43
56
  #
44
57
  # Example:
45
58
  #
@@ -54,16 +67,10 @@ module ActionController #:nodoc:
54
67
  # skip_before_filter :verify_authenticity_token
55
68
  # end
56
69
  #
57
- # If you are upgrading from Rails 1.x, disable forgery protection to
58
- # simplify your tests. Add this to config/environments/test.rb:
59
- #
60
- # # Disable request forgery protection in test environment
61
- # config.action_controller.allow_forgery_protection = false
62
- #
63
70
  # Valid Options:
64
71
  #
65
- # * <tt>:only/:except</tt> - passed to the before_filter call. Set which actions are verified.
66
- # * <tt>:secret</tt> - Custom salt used to generate the form_authenticity_token.
72
+ # * <tt>:only/:except</tt> - passed to the <tt>before_filter</tt> call. Set which actions are verified.
73
+ # * <tt>:secret</tt> - Custom salt used to generate the <tt>form_authenticity_token</tt>.
67
74
  # Leave this off if you are using the cookie session store.
68
75
  # * <tt>:digest</tt> - Message digest used for hashing. Defaults to 'SHA1'
69
76
  def protect_from_forgery(options = {})
@@ -13,20 +13,21 @@ module ActionController
13
13
 
14
14
  def initialize(script_path)
15
15
  @quiet = false
16
- define_run_method(File.read(script_path))
16
+ define_run_method(script_path)
17
17
  reset!
18
18
  end
19
19
 
20
20
  def benchmark(n)
21
21
  @quiet = true
22
22
  print ' '
23
+
23
24
  result = Benchmark.realtime do
24
25
  n.times do |i|
25
26
  run
26
- print i % 10 == 0 ? 'x' : '.'
27
- $stdout.flush
27
+ print_progress(i)
28
28
  end
29
29
  end
30
+
30
31
  puts
31
32
  result
32
33
  ensure
@@ -38,8 +39,17 @@ module ActionController
38
39
  end
39
40
 
40
41
  private
41
- def define_run_method(script)
42
- instance_eval "def run; #{script}; end", __FILE__, __LINE__
42
+ def define_run_method(script_path)
43
+ script = File.read(script_path)
44
+ source = "def run\n#{script}\nreset!\nend"
45
+ instance_eval source, script_path, 1
46
+ end
47
+
48
+ def print_progress(i)
49
+ print "\n " if i % 60 == 0
50
+ print ' ' if i % 10 == 0
51
+ print '.'
52
+ $stdout.flush
43
53
  end
44
54
  end
45
55
 
@@ -98,7 +108,7 @@ module ActionController
98
108
  OptionParser.new do |opt|
99
109
  opt.banner = "USAGE: #{$0} [options] [session script path]"
100
110
 
101
- opt.on('-n', '--times [0000]', 'How many requests to process. Defaults to 100.') { |v| options[:n] = v.to_i }
111
+ opt.on('-n', '--times [100]', 'How many requests to process. Defaults to 100.') { |v| options[:n] = v.to_i if v }
102
112
  opt.on('-b', '--benchmark', 'Benchmark instead of profiling') { |v| options[:benchmark] = v }
103
113
  opt.on('--open [CMD]', 'Command to open profile results. Defaults to "open %s &"') { |v| options[:open] = v }
104
114
  opt.on('-h', '--help', 'Show this help') { puts opt; exit }
File without changes
@@ -306,8 +306,8 @@ module ActionController
306
306
  gsub(%r{(.)[\\/]$}, '\1') # drop final / or \ if path ends with it
307
307
 
308
308
  # eliminate .. paths where possible
309
- re = %r{\w+[/\\]\.\.[/\\]}
310
- path.gsub!(%r{\w+[/\\]\.\.[/\\]}, "") while path.match(re)
309
+ re = %r{[^/\\]+[/\\]\.\.[/\\]}
310
+ path.gsub!(re, "") while path.match(re)
311
311
  path
312
312
  end
313
313
 
@@ -1216,7 +1216,7 @@ module ActionController
1216
1216
  opts = if args.empty? || Hash === args.first
1217
1217
  args.first || {}
1218
1218
  else
1219
- options = args.last.is_a?(Hash) ? args.pop : {}
1219
+ options = args.extract_options!
1220
1220
  args = args.zip(#{route.segment_keys.inspect}).inject({}) do |h, (v, k)|
1221
1221
  h[k] = v
1222
1222
  h
@@ -1,7 +1,6 @@
1
1
  require 'cgi'
2
2
  require 'cgi/session'
3
3
  require 'digest/md5'
4
- require 'base64'
5
4
 
6
5
  class CGI
7
6
  class Session
@@ -80,8 +79,8 @@ class CGI
80
79
  find_by_session_id(session_id)
81
80
  end
82
81
 
83
- def marshal(data) Base64.encode64(Marshal.dump(data)) if data end
84
- def unmarshal(data) Marshal.load(Base64.decode64(data)) if data end
82
+ def marshal(data) ActiveSupport::Base64.encode64(Marshal.dump(data)) if data end
83
+ def unmarshal(data) Marshal.load(ActiveSupport::Base64.decode64(data)) if data end
85
84
 
86
85
  def create_table!
87
86
  connection.execute <<-end_sql
@@ -155,8 +154,8 @@ class CGI
155
154
  # The database connection, table name, and session id and data columns
156
155
  # are configurable class attributes. Marshaling and unmarshaling
157
156
  # are implemented as class methods that you may override. By default,
158
- # marshaling data is +Base64.encode64(Marshal.dump(data))+ and
159
- # unmarshaling data is +Marshal.load(Base64.decode64(data))+.
157
+ # marshaling data is +ActiveSupport::Base64.encode64(Marshal.dump(data))+ and
158
+ # unmarshaling data is +Marshal.load(ActiveSupport::Base64.decode64(data))+.
160
159
  #
161
160
  # This marshaling behavior is intended to store the widest range of
162
161
  # binary session data in a +text+ column. For higher performance,
@@ -190,8 +189,8 @@ class CGI
190
189
  end
191
190
  end
192
191
 
193
- def marshal(data) Base64.encode64(Marshal.dump(data)) if data end
194
- def unmarshal(data) Marshal.load(Base64.decode64(data)) if data end
192
+ def marshal(data) ActiveSupport::Base64.encode64(Marshal.dump(data)) if data end
193
+ def unmarshal(data) Marshal.load(ActiveSupport::Base64.decode64(data)) if data end
195
194
 
196
195
  def create_table!
197
196
  @@connection.execute <<-end_sql
@@ -333,4 +332,4 @@ class CGI
333
332
  end
334
333
  end
335
334
  end
336
- end
335
+ end
@@ -1,6 +1,5 @@
1
1
  require 'cgi'
2
2
  require 'cgi/session'
3
- require 'base64' # to convert Marshal.dump to ASCII
4
3
  require 'openssl' # to generate the HMAC message digest
5
4
 
6
5
  # This cookie-based session store is the Rails default. Sessions typically
@@ -130,7 +129,7 @@ class CGI::Session::CookieStore
130
129
  private
131
130
  # Marshal a session hash into safe cookie data. Include an integrity hash.
132
131
  def marshal(session)
133
- data = Base64.encode64(Marshal.dump(session)).chop
132
+ data = ActiveSupport::Base64.encode64(Marshal.dump(session)).chop
134
133
  CGI.escape "#{data}--#{generate_digest(data)}"
135
134
  end
136
135
 
@@ -142,7 +141,7 @@ class CGI::Session::CookieStore
142
141
  delete
143
142
  raise TamperedWithCookie
144
143
  end
145
- Marshal.load(Base64.decode64(data))
144
+ Marshal.load(ActiveSupport::Base64.decode64(data))
146
145
  end
147
146
  end
148
147
 
@@ -10,17 +10,17 @@
10
10
  <p><code>RAILS_ROOT: <%= defined?(RAILS_ROOT) ? RAILS_ROOT : "unset" %></code></p>
11
11
 
12
12
  <div id="traces">
13
- <% names.each do |name| -%>
13
+ <% names.each do |name| %>
14
14
  <%
15
15
  show = "document.getElementById('#{name.gsub /\s/, '-'}').style.display='block';"
16
16
  hide = (names - [name]).collect {|hide_name| "document.getElementById('#{hide_name.gsub /\s/, '-'}').style.display='none';"}
17
17
  %>
18
18
  <a href="#" onclick="<%= hide %><%= show %>; return false;"><%= name %></a> <%= '|' unless names.last == name %>
19
- <% end -%>
19
+ <% end %>
20
20
 
21
- <% traces.each do |name, trace| -%>
21
+ <% traces.each do |name, trace| %>
22
22
  <div id="<%= name.gsub /\s/, '-' %>" style="display: <%= name == "Application Trace" ? 'block' : 'none' %>;">
23
23
  <pre><code><%= trace.join "\n" %></code></pre>
24
24
  </div>
25
- <% end -%>
26
- </div>
25
+ <% end %>
26
+ </div>
@@ -3,9 +3,15 @@ require 'active_support/test_case'
3
3
  module ActionController
4
4
  class NonInferrableControllerError < ActionControllerError
5
5
  def initialize(name)
6
+ @name = name
6
7
  super "Unable to determine the controller to test from #{name}. " +
7
8
  "You'll need to specify it using 'tests YourController' in your " +
8
- "test case definition"
9
+ "test case definition. This could mean that #{inferred_controller_name} does not exist " +
10
+ "or it contains syntax errors"
11
+ end
12
+
13
+ def inferred_controller_name
14
+ @name.sub(/Test$/, '')
9
15
  end
10
16
  end
11
17
 
@@ -44,10 +50,24 @@ module ActionController
44
50
  end
45
51
  end
46
52
 
47
- def setup
53
+ def setup_with_controller
48
54
  @controller = self.class.controller_class.new
49
55
  @request = TestRequest.new
50
56
  @response = TestResponse.new
51
57
  end
52
- end
53
- end
58
+ alias_method :setup, :setup_with_controller
59
+
60
+ def self.method_added(method)
61
+ if method.to_s == 'setup'
62
+ unless method_defined?(:setup_without_controller)
63
+ alias_method :setup_without_controller, :setup
64
+ define_method(:setup) do
65
+ setup_with_fixtures if respond_to?(:setup_with_fixtures)
66
+ setup_with_controller
67
+ setup_without_controller
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -286,7 +286,7 @@ module ActionController #:nodoc:
286
286
 
287
287
  def initialize(attributes = nil)
288
288
  @session_id = ''
289
- @attributes = attributes
289
+ @attributes = attributes.nil? ? nil : attributes.stringify_keys
290
290
  @saved_attributes = nil
291
291
  end
292
292
 
@@ -295,11 +295,11 @@ module ActionController #:nodoc:
295
295
  end
296
296
 
297
297
  def [](key)
298
- data[key]
298
+ data[key.to_s]
299
299
  end
300
300
 
301
301
  def []=(key, value)
302
- data[key] = value
302
+ data[key.to_s] = value
303
303
  end
304
304
 
305
305
  def update
@@ -1,17 +1,17 @@
1
- module ActionController
1
+ module ActionController
2
2
  # Write URLs from arbitrary places in your codebase, such as your mailers.
3
- #
3
+ #
4
4
  # Example:
5
- #
5
+ #
6
6
  # class MyMailer
7
7
  # include ActionController::UrlWriter
8
8
  # default_url_options[:host] = 'www.basecamphq.com'
9
- #
9
+ #
10
10
  # def signup_url(token)
11
11
  # url_for(:controller => 'signup', action => 'index', :token => token)
12
12
  # end
13
13
  # end
14
- #
14
+ #
15
15
  # In addition to providing +url_for+, named routes are also accessible after
16
16
  # including UrlWriter.
17
17
  module UrlWriter
@@ -19,64 +19,65 @@ module ActionController
19
19
  # is provided.
20
20
  mattr_accessor :default_url_options
21
21
  self.default_url_options = {}
22
-
22
+
23
23
  def self.included(base) #:nodoc:
24
- ActionController::Routing::Routes.install_helpers base
24
+ ActionController::Routing::Routes.install_helpers(base)
25
25
  base.mattr_accessor :default_url_options
26
26
  base.default_url_options ||= default_url_options
27
27
  end
28
-
29
- # Generate a url based on the options provided, default_url_options and the
28
+
29
+ # Generate a url based on the options provided, default_url_options and the
30
30
  # routes defined in routes.rb. The following options are supported:
31
- #
31
+ #
32
32
  # * <tt>:only_path</tt> If true, the relative url is returned. Defaults to false.
33
33
  # * <tt>:protocol</tt> The protocol to connect to. Defaults to 'http'.
34
- # * <tt>:host</tt> Specifies the host the link should be targetted at. If <tt>:only_path</tt> is false, this option must be
35
- # provided either explicitly, or via default_url_options.
34
+ # * <tt>:host</tt> Specifies the host the link should be targetted at. If <tt>:only_path</tt> is false, this option must be
35
+ # provided either explicitly, or via default_url_options.
36
36
  # * <tt>:port</tt> Optionally specify the port to connect to.
37
37
  # * <tt>:anchor</tt> An anchor name to be appended to the path.
38
- #
38
+ # * <tt>:skip_relative_url_root</tt> If true, the url is not constructed using the relative_url_root set in <tt>ActionController::AbstractRequest.relative_url_root</tt>.
39
+ #
39
40
  # Any other key(:controller, :action, etc...) given to <tt>url_for</tt> is forwarded to the Routes module.
40
- #
41
+ #
41
42
  # Examples:
42
- #
43
+ #
43
44
  # url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :port=>'8080' # => 'http://somehost.org:8080/tasks/testing'
44
45
  # url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :anchor => 'ok', :only_path => true # => '/tasks/testing#ok'
45
46
  # url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :number => '33' # => 'http://somehost.org/tasks/testing?number=33'
46
- #
47
47
  def url_for(options)
48
48
  options = self.class.default_url_options.merge(options)
49
-
49
+
50
50
  url = ''
51
51
 
52
- unless options.delete :only_path
52
+ unless options.delete(:only_path)
53
53
  url << (options.delete(:protocol) || 'http')
54
- url << '://' unless url.match("://") #dont add separator if its already been specified in :protocol
55
-
54
+ url << '://' unless url.match("://")
55
+
56
56
  raise "Missing host to link to! Please provide :host parameter or set default_url_options[:host]" unless options[:host]
57
57
 
58
58
  url << options.delete(:host)
59
59
  url << ":#{options.delete(:port)}" if options.key?(:port)
60
60
  else
61
- # Delete the unused options to prevent their appearance in the query string
62
- [:protocol, :host, :port].each { |k| options.delete k }
61
+ # Delete the unused options to prevent their appearance in the query string.
62
+ [:protocol, :host, :port, :skip_relative_url_root].each { |k| options.delete(k) }
63
63
  end
64
64
 
65
- anchor = "##{CGI.escape options.delete(:anchor).to_param.to_s}" if options.key?(:anchor)
65
+ url << ActionController::AbstractRequest.relative_url_root.to_s unless options[:skip_relative_url_root]
66
+ anchor = "##{CGI.escape options.delete(:anchor).to_param.to_s}" if options[:anchor]
66
67
  url << Routing::Routes.generate(options, {})
67
68
  url << anchor if anchor
68
69
 
69
- return url
70
- end
70
+ url
71
+ end
71
72
  end
72
-
73
+
73
74
  # Rewrites URLs for Base.redirect_to and Base.url_for in the controller.
74
75
  class UrlRewriter #:nodoc:
75
76
  RESERVED_OPTIONS = [:anchor, :params, :only_path, :host, :protocol, :port, :trailing_slash, :skip_relative_url_root]
76
77
  def initialize(request, parameters)
77
78
  @request, @parameters = request, parameters
78
79
  end
79
-
80
+
80
81
  def rewrite(options = {})
81
82
  rewrite_url(options)
82
83
  end
@@ -123,7 +124,7 @@ module ActionController
123
124
  # Generates the query string, too
124
125
  Routing::Routes.generate(options, @request.symbolized_path_parameters)
125
126
  end
126
-
127
+
127
128
  def rewrite_authentication(options)
128
129
  if options[:user] && options[:password]
129
130
  "#{CGI.escape(options.delete(:user))}:#{CGI.escape(options.delete(:password))}@"