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.
- data/CHANGELOG +45 -0
- data/Rakefile +5 -3
- data/lib/action_controller.rb +0 -0
- data/lib/action_controller/assertions/response_assertions.rb +6 -2
- data/lib/action_controller/assertions/routing_assertions.rb +5 -2
- data/lib/action_controller/base.rb +4 -3
- data/lib/action_controller/cgi_ext/cookie.rb +1 -1
- data/lib/action_controller/filters.rb +5 -3
- data/lib/action_controller/http_authentication.rb +2 -4
- data/lib/action_controller/integration.rb +3 -2
- data/lib/action_controller/layout.rb +14 -15
- data/lib/action_controller/mime_type.rb +4 -1
- data/lib/action_controller/polymorphic_routes.rb +90 -15
- data/lib/action_controller/record_identifier.rb +4 -4
- data/lib/action_controller/request.rb +29 -14
- data/lib/action_controller/request_forgery_protection.rb +41 -34
- data/lib/action_controller/request_profiler.rb +16 -6
- data/lib/action_controller/response.rb +0 -0
- data/lib/action_controller/routing.rb +3 -3
- data/lib/action_controller/session/active_record_store.rb +7 -8
- data/lib/action_controller/session/cookie_store.rb +2 -3
- data/lib/action_controller/templates/rescues/_trace.erb +5 -5
- data/lib/action_controller/test_case.rb +24 -4
- data/lib/action_controller/test_process.rb +3 -3
- data/lib/action_controller/url_rewriter.rb +29 -28
- data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +2 -2
- data/lib/action_controller/verification.rb +73 -57
- data/lib/action_pack/version.rb +1 -1
- data/lib/action_view/base.rb +24 -6
- data/lib/action_view/helpers/active_record_helper.rb +1 -1
- data/lib/action_view/helpers/asset_tag_helper.rb +12 -6
- data/lib/action_view/helpers/atom_feed_helper.rb +21 -17
- data/lib/action_view/helpers/date_helper.rb +6 -6
- data/lib/action_view/helpers/form_helper.rb +3 -2
- data/lib/action_view/helpers/form_options_helper.rb +12 -1
- data/lib/action_view/helpers/form_tag_helper.rb +18 -0
- data/lib/action_view/helpers/number_helper.rb +1 -1
- data/lib/action_view/helpers/prototype_helper.rb +1 -1
- data/lib/action_view/helpers/text_helper.rb +1 -1
- data/lib/action_view/helpers/url_helper.rb +5 -9
- data/lib/action_view/template_error.rb +11 -4
- data/test/activerecord/active_record_store_test.rb +1 -1
- data/test/activerecord/fixtures_test.rb +24 -0
- data/test/controller/action_pack_assertions_test.rb +37 -2
- data/test/controller/base_test.rb +4 -1
- data/test/controller/cgi_test.rb +4 -3
- data/test/controller/filters_test.rb +4 -7
- data/test/controller/html-scanner/sanitizer_test.rb +7 -1
- data/test/controller/integration_test.rb +0 -1
- data/test/controller/mime_type_test.rb +5 -0
- data/test/controller/new_render_test.rb +25 -2
- data/test/controller/polymorphic_routes_test.rb +106 -75
- data/test/controller/redirect_test.rb +11 -0
- data/test/controller/render_test.rb +19 -0
- data/test/controller/request_forgery_protection_test.rb +9 -0
- data/test/controller/request_test.rb +33 -5
- data/test/controller/routing_test.rb +4 -4
- data/test/controller/session/cookie_store_test.rb +0 -0
- data/test/controller/test_test.rb +43 -1
- data/test/controller/url_rewriter_test.rb +34 -4
- data/test/fixtures/layouts/block_with_layout.erb +3 -0
- data/test/fixtures/layouts/partial_with_layout.erb +3 -0
- data/test/template/asset_tag_helper_test.rb +17 -13
- data/test/template/atom_feed_helper_test.rb +52 -2
- data/test/template/compiled_templates_test.rb +5 -1
- data/test/template/date_helper_test.rb +1 -1
- data/test/template/deprecate_ivars_test.rb +51 -0
- data/test/template/form_options_helper_test.rb +29 -0
- data/test/template/form_tag_helper_test.rb +18 -0
- data/test/template/number_helper_test.rb +1 -0
- data/test/template/text_helper_test.rb +32 -14
- data/test/template/url_helper_test.rb +1 -1
- data/test/testing_sandbox.rb +8 -4
- 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
|
128
|
-
#
|
129
|
-
# delimited list in the case of multiple chained proxies; the
|
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
|
-
|
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(',')
|
141
|
-
|
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.
|
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
|
-
#
|
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(
|
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
|
-
|
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(
|
42
|
-
|
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 [
|
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{
|
310
|
-
path.gsub!(
|
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.
|
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
|
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
|
-
|
53
|
-
|
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
|
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
|
52
|
+
unless options.delete(:only_path)
|
53
53
|
url << (options.delete(:protocol) || 'http')
|
54
|
-
url << '://' unless url.match("://")
|
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
|
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
|
-
|
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
|
-
|
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))}@"
|