actionpack 0.9.5 → 1.0.0
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 +177 -0
- data/README +0 -1
- data/install.rb +1 -0
- data/lib/action_controller.rb +6 -1
- data/lib/action_controller/assertions/active_record_assertions.rb +2 -2
- data/lib/action_controller/base.rb +53 -41
- data/lib/action_controller/benchmarking.rb +1 -1
- data/lib/action_controller/cgi_ext/cgi_methods.rb +14 -16
- data/lib/action_controller/cgi_process.rb +16 -6
- data/lib/action_controller/cookies.rb +70 -0
- data/lib/action_controller/dependencies.rb +106 -0
- data/lib/action_controller/helpers.rb +14 -3
- data/lib/action_controller/layout.rb +16 -2
- data/lib/action_controller/request.rb +17 -7
- data/lib/action_controller/rescue.rb +33 -3
- data/lib/action_controller/support/class_inheritable_attributes.rb +4 -0
- data/lib/action_controller/support/inflector.rb +14 -12
- data/lib/action_controller/support/misc.rb +6 -0
- data/lib/action_controller/templates/rescues/_request_and_response.rhtml +2 -2
- data/lib/action_controller/templates/rescues/diagnostics.rhtml +4 -6
- data/lib/action_controller/templates/rescues/template_error.rhtml +1 -9
- data/lib/action_controller/templates/scaffolds/edit.rhtml +2 -1
- data/lib/action_controller/templates/scaffolds/layout.rhtml +36 -0
- data/lib/action_controller/templates/scaffolds/new.rhtml +1 -0
- data/lib/action_controller/test_process.rb +27 -8
- data/lib/action_controller/url_rewriter.rb +15 -4
- data/lib/action_view/base.rb +4 -3
- data/lib/action_view/helpers/active_record_helper.rb +29 -15
- data/lib/action_view/helpers/date_helper.rb +6 -5
- data/lib/action_view/helpers/form_helper.rb +31 -4
- data/lib/action_view/helpers/form_options_helper.rb +13 -3
- data/lib/action_view/helpers/tag_helper.rb +14 -16
- data/lib/action_view/helpers/url_helper.rb +46 -1
- data/lib/action_view/partials.rb +8 -1
- data/lib/action_view/template_error.rb +10 -3
- data/lib/action_view/vendor/builder/blankslate.rb +33 -1
- data/lib/action_view/vendor/builder/xmlevents.rb +1 -1
- data/lib/action_view/vendor/builder/xmlmarkup.rb +1 -1
- data/rakefile +4 -13
- data/test/controller/action_pack_assertions_test.rb +39 -1
- data/test/controller/active_record_assertions_test.rb +6 -5
- data/test/controller/cgi_test.rb +33 -3
- data/test/controller/cookie_test.rb +43 -2
- data/test/controller/helper_test.rb +1 -1
- data/test/controller/render_test.rb +9 -0
- data/test/controller/url_test.rb +16 -0
- data/test/fixtures/scope/test/modgreet.rhtml +1 -0
- data/test/template/active_record_helper_test.rb +34 -8
- data/test/template/date_helper_test.rb +164 -20
- data/test/template/form_helper_test.rb +12 -0
- data/test/template/form_options_helper_test.rb +7 -16
- data/test/template/url_helper_test.rb +12 -0
- metadata +8 -2
@@ -15,7 +15,7 @@ module ActionController #:nodoc:
|
|
15
15
|
}
|
16
16
|
end
|
17
17
|
|
18
|
-
def render_with_benchmark(template_name =
|
18
|
+
def render_with_benchmark(template_name = default_template_name, status = "200 OK")
|
19
19
|
if logger.nil?
|
20
20
|
render_without_benchmark(template_name, status)
|
21
21
|
else
|
@@ -63,29 +63,27 @@ class CGIMethods #:nodoc:
|
|
63
63
|
end
|
64
64
|
end
|
65
65
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
66
|
+
PARAMS_HASH_RE = /^([^\[]+)(\[.*\])?(.)?.*$/
|
67
|
+
def CGIMethods.get_levels(key)
|
68
|
+
all, main, bracketed, trailing = PARAMS_HASH_RE.match(key).to_a
|
69
|
+
if main.nil?
|
70
|
+
[]
|
71
|
+
elsif trailing
|
72
|
+
[key]
|
73
|
+
elsif bracketed
|
74
|
+
[main] + bracketed.slice(1...-1).split('][')
|
75
|
+
else
|
76
|
+
[main]
|
77
77
|
end
|
78
|
-
|
79
|
-
levels
|
80
78
|
end
|
81
|
-
|
79
|
+
|
82
80
|
def CGIMethods.build_deep_hash(value, hash, levels)
|
83
81
|
if levels.length == 0
|
84
|
-
value
|
82
|
+
value
|
85
83
|
elsif hash.nil?
|
86
84
|
{ levels.first => CGIMethods.build_deep_hash(value, nil, levels[1..-1]) }
|
87
85
|
else
|
88
86
|
hash.update({ levels.first => CGIMethods.build_deep_hash(value, hash[levels.first], levels[1..-1]) })
|
89
87
|
end
|
90
88
|
end
|
91
|
-
end
|
89
|
+
end
|
@@ -36,7 +36,7 @@ module ActionController #:nodoc:
|
|
36
36
|
attr_accessor :cgi
|
37
37
|
|
38
38
|
DEFAULT_SESSION_OPTIONS =
|
39
|
-
{
|
39
|
+
{ :database_manager => CGI::Session::PStore, :prefix => "ruby_sess.", :session_path => "/" }
|
40
40
|
|
41
41
|
def initialize(cgi, session_options = {})
|
42
42
|
@cgi = cgi
|
@@ -67,7 +67,7 @@ module ActionController #:nodoc:
|
|
67
67
|
def session
|
68
68
|
return @session unless @session.nil?
|
69
69
|
begin
|
70
|
-
@session = (@session_options == false ? {} : CGI::Session.new(@cgi,
|
70
|
+
@session = (@session_options == false ? {} : CGI::Session.new(@cgi, session_options_with_string_keys))
|
71
71
|
@session["__valid_session"]
|
72
72
|
return @session
|
73
73
|
rescue ArgumentError => e
|
@@ -94,6 +94,10 @@ module ActionController #:nodoc:
|
|
94
94
|
def new_session
|
95
95
|
CGI::Session.new(@cgi, DEFAULT_SESSION_OPTIONS.merge(@session_options).merge("new_session" => true))
|
96
96
|
end
|
97
|
+
|
98
|
+
def session_options_with_string_keys
|
99
|
+
DEFAULT_SESSION_OPTIONS.merge(@session_options).inject({}) { |options, pair| options[pair.first.to_s] = pair.last; options }
|
100
|
+
end
|
97
101
|
end
|
98
102
|
|
99
103
|
class CgiResponse < AbstractResponse #:nodoc:
|
@@ -105,8 +109,12 @@ module ActionController #:nodoc:
|
|
105
109
|
def out
|
106
110
|
convert_content_type!(@headers)
|
107
111
|
$stdout.binmode if $stdout.respond_to?(:binmode)
|
112
|
+
$stdout.sync = false
|
108
113
|
print @cgi.header(@headers)
|
109
|
-
|
114
|
+
|
115
|
+
if @cgi.send(:env_table)['REQUEST_METHOD'] == 'HEAD'
|
116
|
+
return
|
117
|
+
elsif @body.respond_to?(:call)
|
110
118
|
@body.call(self)
|
111
119
|
else
|
112
120
|
print @body
|
@@ -115,9 +123,11 @@ module ActionController #:nodoc:
|
|
115
123
|
|
116
124
|
private
|
117
125
|
def convert_content_type!(headers)
|
118
|
-
|
119
|
-
|
120
|
-
|
126
|
+
%w( Content-Type Content-type content-type ).each do |ct|
|
127
|
+
if headers[ct]
|
128
|
+
headers["type"] = headers[ct]
|
129
|
+
headers.delete(ct)
|
130
|
+
end
|
121
131
|
end
|
122
132
|
end
|
123
133
|
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module ActionController #:nodoc:
|
2
|
+
# Cookies are read and written through ActionController#cookies. The cookies being read is what was received along with the request,
|
3
|
+
# the cookies being written is what will be sent out will the response. Cookies are read by value (so you won't get the cookie object
|
4
|
+
# itself back -- just the value it holds). Examples for writting:
|
5
|
+
#
|
6
|
+
# cookies["user_name"] = "david" # => Will set a simple session cookie
|
7
|
+
# cookies["login"] = { :value => "XJ-122", :expires => Time.now + 360} # => Will set a cookie that expires in 1 hour
|
8
|
+
#
|
9
|
+
# Examples for reading:
|
10
|
+
#
|
11
|
+
# cookies["user_name"] # => "david"
|
12
|
+
# cookies.size # => 2
|
13
|
+
#
|
14
|
+
# Example for deleting:
|
15
|
+
#
|
16
|
+
# cookies.delete "user_name"
|
17
|
+
#
|
18
|
+
# All the option symbols for setting cookies are:
|
19
|
+
#
|
20
|
+
# * <tt>value</tt> - the cookie's value or list of values (as an array).
|
21
|
+
# * <tt>path</tt> - the path for which this cookie applies. Defaults to the root of the application.
|
22
|
+
# * <tt>domain</tt> - the domain for which this cookie applies.
|
23
|
+
# * <tt>expires</tt> - the time at which this cookie expires, as a +Time+ object.
|
24
|
+
# * <tt>secure</tt> - whether this cookie is a secure cookie or not (default to false).
|
25
|
+
# Secure cookies are only transmitted to HTTPS servers.
|
26
|
+
module Cookies
|
27
|
+
# Returns the cookie container, which operates as described above.
|
28
|
+
def cookies
|
29
|
+
CookieJar.new(self)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class CookieJar < Hash #:nodoc:
|
34
|
+
def initialize(controller)
|
35
|
+
@controller, @cookies = controller, controller.instance_variable_get("@cookies")
|
36
|
+
super()
|
37
|
+
update(@cookies)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Returns the value of the cookie by +name+ -- or nil if no such cookie exist. You set new cookies using either the cookie method
|
41
|
+
# or cookies[]= (for simple name/value cookies without options).
|
42
|
+
def [](name)
|
43
|
+
@cookies[name.to_s].value.first if @cookies[name.to_s] && @cookies[name.to_s].respond_to?(:value)
|
44
|
+
end
|
45
|
+
|
46
|
+
def []=(name, options)
|
47
|
+
if options.is_a?(Hash)
|
48
|
+
options = options.inject({}) { |options, pair| options[pair.first.to_s] = pair.last; options }
|
49
|
+
options["name"] = name.to_s
|
50
|
+
else
|
51
|
+
options = { "name" => name, "value" => options }
|
52
|
+
end
|
53
|
+
|
54
|
+
set_cookie(name, options)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Removes the cookie on the client machine by setting the value to an empty string.
|
58
|
+
def delete(name)
|
59
|
+
set_cookie(name, "name" => name, "value" => "")
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
def set_cookie(name, options) #:doc:
|
64
|
+
options["path"] = "/" unless options["path"]
|
65
|
+
cookie = CGI::Cookie.new(options)
|
66
|
+
@controller.logger.info "Cookie set: #{cookie}" unless @controller.logger.nil?
|
67
|
+
@controller.response.headers["cookie"] << cookie
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
unless Object.respond_to?(:require_dependency)
|
2
|
+
Object.send(:define_method, :require_dependency) { |file_name| ActionController::Base.require_dependency(file_name) }
|
3
|
+
end
|
4
|
+
|
5
|
+
module ActionController #:nodoc:
|
6
|
+
module Dependencies #:nodoc:
|
7
|
+
def self.append_features(base)
|
8
|
+
super
|
9
|
+
|
10
|
+
base.class_eval do
|
11
|
+
# When turned on (which is default), all dependencies are included using "load". This mean that any change is instant in cached
|
12
|
+
# environments like mod_ruby or FastCGI. When set to false, "require" is used, which is faster but requires server restart to
|
13
|
+
# be effective.
|
14
|
+
@@reload_dependencies = true
|
15
|
+
cattr_accessor :reload_dependencies
|
16
|
+
end
|
17
|
+
|
18
|
+
base.class_eval { class << self; alias_method :inherited_without_model, :inherited; end }
|
19
|
+
|
20
|
+
base.extend(ClassMethods)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Dependencies control what classes are needed for the controller to run its course. This is an alternative to doing explicit
|
24
|
+
# +require+ statements that bring a number of benefits. It's more succinct, communicates what type of dependency we're talking about,
|
25
|
+
# can trigger special behavior (as in the case of +observer+), and enables Rails to be clever about reloading in cached environments
|
26
|
+
# like FCGI. Example:
|
27
|
+
#
|
28
|
+
# class ApplicationController < ActionController::Base
|
29
|
+
# model :account, :company, :person, :project, :category
|
30
|
+
# helper :access_control
|
31
|
+
# service :notifications, :billings
|
32
|
+
# observer :project_change_observer
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# Please note that a controller like ApplicationController will automatically attempt to require_dependency on a model of its name and a helper
|
36
|
+
# of its name. If nothing is found, no error is raised. This is especially useful for concrete controllers like PostController:
|
37
|
+
#
|
38
|
+
# class PostController < ApplicationController
|
39
|
+
# # model :post (already required)
|
40
|
+
# # helper :post (already required)
|
41
|
+
# end
|
42
|
+
module ClassMethods
|
43
|
+
# Loads the <tt>file_name</tt> if reload_dependencies is true or requires if it's false.
|
44
|
+
def require_dependency(file_name)
|
45
|
+
reload_dependencies ? silence_warnings { load("#{file_name}.rb") } : require(file_name)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Specifies a variable number of models that this controller depends on. Models are normally Active Record classes or a similar
|
49
|
+
# backend for modelling entity classes.
|
50
|
+
def model(*models)
|
51
|
+
require_dependencies(:model, models)
|
52
|
+
depend_on(:model, models)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Specifies a variable number of services that this controller depends on. Services are normally singletons or factories, like
|
56
|
+
# Action Mailer service or a Payment Gateway service.
|
57
|
+
def service(*services)
|
58
|
+
require_dependencies(:service, services)
|
59
|
+
depend_on(:service, services)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Specifies a variable number of observers that are to govern when this controller is handling actions. The observers will
|
63
|
+
# automatically have .instance called on them to make them active on assignment.
|
64
|
+
def observer(*observers)
|
65
|
+
require_dependencies(:observer, observers)
|
66
|
+
depend_on(:observer, observers)
|
67
|
+
instantiate_observers(observers)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Returns an array of symbols that specify the dependencies on a given layer. For the example at the top, calling
|
71
|
+
# <tt>ApplicationController.dependencies_on(:model)</tt> would return <tt>[:account, :company, :person, :project, :category]</tt>
|
72
|
+
def dependencies_on(layer)
|
73
|
+
read_inheritable_attribute("#{layer}_dependencies")
|
74
|
+
end
|
75
|
+
|
76
|
+
def depend_on(layer, dependencies) #:nodoc:
|
77
|
+
write_inheritable_array("#{layer}_dependencies", dependencies)
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
def instantiate_observers(observers)
|
82
|
+
observers.flatten.each { |observer| Object.const_get(Inflector.classify(observer.to_s)).instance }
|
83
|
+
end
|
84
|
+
|
85
|
+
def require_dependencies(layer, dependencies)
|
86
|
+
dependencies.flatten.each do |dependency|
|
87
|
+
begin
|
88
|
+
require_dependency(dependency.to_s)
|
89
|
+
rescue LoadError
|
90
|
+
raise LoadError, "Missing #{layer} #{dependency}.rb"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def inherited(child)
|
96
|
+
inherited_without_model(child)
|
97
|
+
begin
|
98
|
+
child.model(child.controller_name)
|
99
|
+
child.model(Inflector.singularize(child.controller_name))
|
100
|
+
rescue LoadError
|
101
|
+
# No neither singular or plural model available for this controller
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -2,6 +2,7 @@ module ActionController #:nodoc:
|
|
2
2
|
module Helpers #:nodoc:
|
3
3
|
def self.append_features(base)
|
4
4
|
super
|
5
|
+
base.class_eval { class << self; alias_method :inherited_without_helper, :inherited; end }
|
5
6
|
base.extend(ClassMethods)
|
6
7
|
end
|
7
8
|
|
@@ -30,7 +31,7 @@ module ActionController #:nodoc:
|
|
30
31
|
# Makes all the (instance) methods in the helper module available to templates rendered through this controller.
|
31
32
|
# See ActionView::Helpers (link:classes/ActionView/Helpers.html) for more about making your own helper modules
|
32
33
|
# available to the templates.
|
33
|
-
def add_template_helper(helper_module)
|
34
|
+
def add_template_helper(helper_module) #:nodoc:
|
34
35
|
template_class.class_eval "include #{helper_module}"
|
35
36
|
end
|
36
37
|
|
@@ -53,9 +54,9 @@ module ActionController #:nodoc:
|
|
53
54
|
file_name = Inflector.underscore(arg.to_s.downcase) + '_helper'
|
54
55
|
class_name = Inflector.camelize(file_name)
|
55
56
|
begin
|
56
|
-
|
57
|
+
require_dependency(file_name)
|
57
58
|
rescue LoadError
|
58
|
-
raise
|
59
|
+
raise LoadError, "Missing helper file helpers/#{file_name}.rb"
|
59
60
|
end
|
60
61
|
raise ArgumentError, "Missing #{class_name} module in helpers/#{file_name}.rb" unless Object.const_defined?(class_name)
|
61
62
|
add_template_helper(Object.const_get(class_name))
|
@@ -84,6 +85,16 @@ module ActionController #:nodoc:
|
|
84
85
|
def helper_attr(*attrs)
|
85
86
|
attrs.flatten.each { |attr| helper_method(attr, "#{attr}=") }
|
86
87
|
end
|
88
|
+
|
89
|
+
private
|
90
|
+
def inherited(child)
|
91
|
+
inherited_without_helper(child)
|
92
|
+
begin
|
93
|
+
child.helper(child.controller_name)
|
94
|
+
rescue LoadError
|
95
|
+
# No default helper available for this controller
|
96
|
+
end
|
97
|
+
end
|
87
98
|
end
|
88
99
|
end
|
89
100
|
end
|
@@ -2,11 +2,14 @@ module ActionController #:nodoc:
|
|
2
2
|
module Layout #:nodoc:
|
3
3
|
def self.append_features(base)
|
4
4
|
super
|
5
|
-
base.extend(ClassMethods)
|
6
5
|
base.class_eval do
|
7
6
|
alias_method :render_without_layout, :render
|
8
7
|
alias_method :render, :render_with_layout
|
8
|
+
class << self
|
9
|
+
alias_method :inherited_without_layout, :inherited
|
10
|
+
end
|
9
11
|
end
|
12
|
+
base.extend(ClassMethods)
|
10
13
|
end
|
11
14
|
|
12
15
|
# Layouts reverse the common pattern of including shared headers and footers in many templates to isolate changes in
|
@@ -119,6 +122,16 @@ module ActionController #:nodoc:
|
|
119
122
|
def layout(template_name)
|
120
123
|
write_inheritable_attribute "layout", template_name
|
121
124
|
end
|
125
|
+
|
126
|
+
private
|
127
|
+
def inherited(child)
|
128
|
+
inherited_without_layout(child)
|
129
|
+
child.layout(child.controller_name) unless layout_list.grep(/^#{child.controller_name}\.r(?:xml|html)$/).empty?
|
130
|
+
end
|
131
|
+
|
132
|
+
def layout_list
|
133
|
+
Dir.glob("#{template_root}/layouts/*.r{xml,html}").map { |layout| File.basename(layout) }
|
134
|
+
end
|
122
135
|
end
|
123
136
|
|
124
137
|
# Returns the name of the active layout. If the layout was specified as a method reference (through a symbol), this method
|
@@ -135,7 +148,7 @@ module ActionController #:nodoc:
|
|
135
148
|
active_layout.include?("/") ? active_layout : "layouts/#{active_layout}" if active_layout
|
136
149
|
end
|
137
150
|
|
138
|
-
def render_with_layout(template_name =
|
151
|
+
def render_with_layout(template_name = default_template_name, status = nil, layout = nil) #:nodoc:
|
139
152
|
if layout ||= active_layout
|
140
153
|
add_variables_to_assigns
|
141
154
|
logger.info("Rendering #{template_name} within #{layout}") unless logger.nil?
|
@@ -145,5 +158,6 @@ module ActionController #:nodoc:
|
|
145
158
|
render_without_layout(template_name, status)
|
146
159
|
end
|
147
160
|
end
|
161
|
+
|
148
162
|
end
|
149
163
|
end
|
@@ -26,6 +26,10 @@ module ActionController
|
|
26
26
|
method == :delete
|
27
27
|
end
|
28
28
|
|
29
|
+
def head?
|
30
|
+
method == :head
|
31
|
+
end
|
32
|
+
|
29
33
|
# Determine originating IP address. REMOTE_ADDR is the standard
|
30
34
|
# but will fail if the user is behind a proxy. HTTP_CLIENT_IP and/or
|
31
35
|
# HTTP_X_FORWARDED_FOR are set by proxies so check for these before
|
@@ -33,15 +37,17 @@ module ActionController
|
|
33
37
|
# delimited list in the case of multiple chained proxies; the first is
|
34
38
|
# the originating IP.
|
35
39
|
def remote_ip
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
env['HTTP_X_FORWARDED_FOR'].split(',').reject
|
40
|
+
return env['HTTP_CLIENT_IP'] if env.include? 'HTTP_CLIENT_IP'
|
41
|
+
|
42
|
+
if env.include? 'HTTP_X_FORWARDED_FOR' then
|
43
|
+
remote_ips = env['HTTP_X_FORWARDED_FOR'].split(',').reject do |ip|
|
40
44
|
ip =~ /^unknown$|^(10|172\.16|192\.168)\./i
|
41
|
-
|
42
|
-
|
43
|
-
|
45
|
+
end
|
46
|
+
|
47
|
+
return remote_ips.first.strip unless remote_ips.empty?
|
44
48
|
end
|
49
|
+
|
50
|
+
return env['REMOTE_ADDR']
|
45
51
|
end
|
46
52
|
|
47
53
|
def request_uri
|
@@ -52,6 +58,10 @@ module ActionController
|
|
52
58
|
port == 443 ? "https://" : "http://"
|
53
59
|
end
|
54
60
|
|
61
|
+
def ssl?
|
62
|
+
protocol == "https://"
|
63
|
+
end
|
64
|
+
|
55
65
|
def path
|
56
66
|
request_uri ? request_uri.split("?").first : ""
|
57
67
|
end
|
@@ -8,12 +8,19 @@ module ActionController #:nodoc:
|
|
8
8
|
module Rescue
|
9
9
|
def self.append_features(base) #:nodoc:
|
10
10
|
super
|
11
|
+
base.extend(ClassMethods)
|
11
12
|
base.class_eval do
|
12
13
|
alias_method :perform_action_without_rescue, :perform_action
|
13
14
|
alias_method :perform_action, :perform_action_with_rescue
|
14
15
|
end
|
15
16
|
end
|
16
17
|
|
18
|
+
module ClassMethods #:nodoc:
|
19
|
+
def process_with_exception(request, response, exception)
|
20
|
+
new.process(request, response, :rescue_action, exception)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
17
24
|
protected
|
18
25
|
# Exception handler called when the performance of an action raises an exception.
|
19
26
|
def rescue_action(exception)
|
@@ -66,7 +73,31 @@ module ActionController #:nodoc:
|
|
66
73
|
def perform_action_with_rescue #:nodoc:
|
67
74
|
begin
|
68
75
|
perform_action_without_rescue
|
69
|
-
rescue
|
76
|
+
rescue => exception
|
77
|
+
if defined?(Breakpoint) and @params["BP-RETRY"] then
|
78
|
+
msg = exception.backtrace.first
|
79
|
+
if md = /^(.+?):(\d+)(?::in `(.+)')?$/.match(msg) then
|
80
|
+
origin_file, origin_line = md[1], md[2].to_i
|
81
|
+
|
82
|
+
set_trace_func(lambda do |type, file, line, method, context, klass|
|
83
|
+
if file == origin_file and line == origin_line then
|
84
|
+
set_trace_func(nil)
|
85
|
+
@params["BP-RETRY"] = false
|
86
|
+
|
87
|
+
callstack = caller
|
88
|
+
callstack.slice!(0) if callstack.first["rescue.rb"]
|
89
|
+
file, line, method = *callstack.first.match(/^(.+?):(\d+)(?::in `(.*?)')?/).captures
|
90
|
+
|
91
|
+
message = "Exception at #{file}:#{line}#{" in `#{method}'" if method}."
|
92
|
+
|
93
|
+
Breakpoint.handle_breakpoint(context, message, file, line)
|
94
|
+
end
|
95
|
+
end)
|
96
|
+
|
97
|
+
retry
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
70
101
|
rescue_action(exception)
|
71
102
|
end
|
72
103
|
end
|
@@ -87,8 +118,7 @@ module ActionController #:nodoc:
|
|
87
118
|
end
|
88
119
|
|
89
120
|
def clean_backtrace(exception)
|
90
|
-
|
91
|
-
exception.backtrace.collect { |line| line.gsub(base_dir, "").gsub("/public/../config/environments/../../", "").gsub("/public/../", "") }
|
121
|
+
exception.backtrace.collect { |line| Object.const_defined?(:RAILS_ROOT) ? line.gsub(RAILS_ROOT, "") : line }
|
92
122
|
end
|
93
123
|
end
|
94
124
|
end
|