actionpack 1.2.0 → 1.3.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 +78 -0
- data/README +1 -1
- data/install.rb +4 -2
- data/lib/action_controller.rb +3 -0
- data/lib/action_controller/base.rb +16 -8
- data/lib/action_controller/caching.rb +401 -0
- data/lib/action_controller/cgi_process.rb +2 -1
- data/lib/action_controller/cookies.rb +3 -3
- data/lib/action_controller/filters.rb +94 -24
- data/lib/action_controller/layout.rb +63 -21
- data/lib/action_controller/session/mem_cache_store.rb +1 -1
- data/lib/action_controller/support/binding_of_caller.rb +72 -68
- data/lib/action_controller/support/breakpoint.rb +526 -524
- data/lib/action_controller/support/class_inheritable_attributes.rb +105 -29
- data/lib/action_controller/support/core_ext.rb +1 -0
- data/lib/action_controller/support/core_ext/hash.rb +5 -0
- data/lib/action_controller/support/core_ext/hash/keys.rb +35 -0
- data/lib/action_controller/support/core_ext/numeric.rb +7 -0
- data/lib/action_controller/support/core_ext/numeric/bytes.rb +33 -0
- data/lib/action_controller/support/core_ext/numeric/time.rb +59 -0
- data/lib/action_controller/support/core_ext/string.rb +5 -0
- data/lib/action_controller/support/core_ext/string/inflections.rb +41 -0
- data/lib/action_controller/support/dependencies.rb +1 -14
- data/lib/action_controller/support/inflector.rb +6 -6
- data/lib/action_controller/support/misc.rb +0 -24
- data/lib/action_controller/templates/rescues/_request_and_response.rhtml +7 -4
- data/lib/action_controller/test_process.rb +9 -0
- data/lib/action_controller/url_rewriter.rb +13 -5
- data/lib/action_view/helpers/active_record_helper.rb +10 -3
- data/lib/action_view/helpers/cache_helper.rb +10 -0
- data/lib/action_view/helpers/form_helper.rb +25 -2
- data/lib/action_view/helpers/form_options_helper.rb +1 -2
- data/lib/action_view/helpers/url_helper.rb +8 -5
- data/lib/action_view/partials.rb +2 -2
- data/rakefile +6 -3
- data/test/controller/filters_test.rb +118 -3
- data/test/controller/render_test.rb +5 -0
- data/test/controller/send_file_test.rb +24 -0
- data/test/controller/url_test.rb +47 -2
- metadata +16 -2
@@ -2,6 +2,7 @@ require 'action_controller/cgi_ext/cgi_ext'
|
|
2
2
|
require 'action_controller/cgi_ext/cookie_performance_fix'
|
3
3
|
require 'action_controller/session/drb_store'
|
4
4
|
require 'action_controller/session/active_record_store'
|
5
|
+
require 'action_controller/session/mem_cache_store'
|
5
6
|
|
6
7
|
module ActionController #:nodoc:
|
7
8
|
class Base
|
@@ -61,7 +62,7 @@ module ActionController #:nodoc:
|
|
61
62
|
end
|
62
63
|
|
63
64
|
def host
|
64
|
-
env["HTTP_X_FORWARDED_HOST"] || @cgi.host.split(":").first
|
65
|
+
env["HTTP_X_FORWARDED_HOST"] || @cgi.host.to_s.split(":").first
|
65
66
|
end
|
66
67
|
|
67
68
|
def session
|
@@ -51,16 +51,16 @@ module ActionController #:nodoc:
|
|
51
51
|
options = { "name" => name, "value" => options }
|
52
52
|
end
|
53
53
|
|
54
|
-
set_cookie(
|
54
|
+
set_cookie(options)
|
55
55
|
end
|
56
56
|
|
57
57
|
# Removes the cookie on the client machine by setting the value to an empty string.
|
58
58
|
def delete(name)
|
59
|
-
set_cookie(
|
59
|
+
set_cookie("name" => name.to_s, "value" => "")
|
60
60
|
end
|
61
61
|
|
62
62
|
private
|
63
|
-
def set_cookie(
|
63
|
+
def set_cookie(options) #:doc:
|
64
64
|
options["path"] = "/" unless options["path"]
|
65
65
|
cookie = CGI::Cookie.new(options)
|
66
66
|
@controller.logger.info "Cookie set: #{cookie}" unless @controller.logger.nil?
|
@@ -3,7 +3,7 @@ module ActionController #:nodoc:
|
|
3
3
|
def self.append_features(base)
|
4
4
|
super
|
5
5
|
base.extend(ClassMethods)
|
6
|
-
base.
|
6
|
+
base.send(:include, ActionController::Filters::InstanceMethods)
|
7
7
|
end
|
8
8
|
|
9
9
|
# Filters enable controllers to run shared pre and post processing code for its actions. These filters can be used to do
|
@@ -126,19 +126,47 @@ module ActionController #:nodoc:
|
|
126
126
|
# report_result
|
127
127
|
# end
|
128
128
|
# end
|
129
|
+
#
|
130
|
+
# == Filter conditions
|
131
|
+
#
|
132
|
+
# Filters can be limited to run for only specific actions. This can be expressed either by listing the actions to
|
133
|
+
# exclude or the actions to include when executing the filter. Available conditions are +:only+ or +:except+, both
|
134
|
+
# of which accept an arbitrary number of method references. For example:
|
135
|
+
#
|
136
|
+
# class Journal < ActionController::Base
|
137
|
+
# # only require authentication if the current action is edit or delete
|
138
|
+
# before_filter :authorize, :only => [ :edit, :delete ]
|
139
|
+
#
|
140
|
+
# private
|
141
|
+
# def authorize
|
142
|
+
# # redirect to login unless authenticated
|
143
|
+
# end
|
144
|
+
# end
|
145
|
+
#
|
146
|
+
# When setting conditions on inline method (proc) filters the condition must come first and be placed in parenthesis.
|
147
|
+
#
|
148
|
+
# class UserPreferences < ActionController::Base
|
149
|
+
# before_filter(:except => :new) { # some proc ... }
|
150
|
+
# # ...
|
151
|
+
# end
|
152
|
+
#
|
129
153
|
module ClassMethods
|
130
154
|
# The passed <tt>filters</tt> will be appended to the array of filters that's run _before_ actions
|
131
155
|
# on this controller are performed.
|
132
156
|
def append_before_filter(*filters, &block)
|
133
|
-
|
134
|
-
|
157
|
+
conditions = extract_conditions!(filters)
|
158
|
+
filters << block if block_given?
|
159
|
+
add_action_conditions(filters, conditions)
|
160
|
+
append_filter_to_chain("before", filters)
|
135
161
|
end
|
136
162
|
|
137
163
|
# The passed <tt>filters</tt> will be prepended to the array of filters that's run _before_ actions
|
138
164
|
# on this controller are performed.
|
139
165
|
def prepend_before_filter(*filters, &block)
|
140
|
-
|
141
|
-
|
166
|
+
conditions = extract_conditions!(filters)
|
167
|
+
filters << block if block_given?
|
168
|
+
add_action_conditions(filters, conditions)
|
169
|
+
prepend_filter_to_chain("before", filters)
|
142
170
|
end
|
143
171
|
|
144
172
|
# Short-hand for append_before_filter since that's the most common of the two.
|
@@ -147,15 +175,19 @@ module ActionController #:nodoc:
|
|
147
175
|
# The passed <tt>filters</tt> will be appended to the array of filters that's run _after_ actions
|
148
176
|
# on this controller are performed.
|
149
177
|
def append_after_filter(*filters, &block)
|
150
|
-
|
151
|
-
|
178
|
+
conditions = extract_conditions!(filters)
|
179
|
+
filters << block if block_given?
|
180
|
+
add_action_conditions(filters, conditions)
|
181
|
+
append_filter_to_chain("after", filters)
|
152
182
|
end
|
153
183
|
|
154
184
|
# The passed <tt>filters</tt> will be prepended to the array of filters that's run _after_ actions
|
155
185
|
# on this controller are performed.
|
156
186
|
def prepend_after_filter(*filters, &block)
|
157
|
-
|
158
|
-
|
187
|
+
conditions = extract_conditions!(filters)
|
188
|
+
filters << block if block_given?
|
189
|
+
add_action_conditions(filters, conditions)
|
190
|
+
prepend_filter_to_chain("after", filters)
|
159
191
|
end
|
160
192
|
|
161
193
|
# Short-hand for append_after_filter since that's the most common of the two.
|
@@ -169,8 +201,8 @@ module ActionController #:nodoc:
|
|
169
201
|
# A#before
|
170
202
|
# A#after
|
171
203
|
# B#after
|
172
|
-
def append_around_filter(filters)
|
173
|
-
for filter in
|
204
|
+
def append_around_filter(*filters)
|
205
|
+
for filter in filters.flatten
|
174
206
|
ensure_filter_responds_to_before_and_after(filter)
|
175
207
|
append_before_filter { |c| filter.before(c) }
|
176
208
|
prepend_after_filter { |c| filter.after(c) }
|
@@ -185,8 +217,8 @@ module ActionController #:nodoc:
|
|
185
217
|
# B#before
|
186
218
|
# B#after
|
187
219
|
# A#after
|
188
|
-
def prepend_around_filter(filters)
|
189
|
-
for filter in
|
220
|
+
def prepend_around_filter(*filters)
|
221
|
+
for filter in filters.flatten
|
190
222
|
ensure_filter_responds_to_before_and_after(filter)
|
191
223
|
prepend_before_filter { |c| filter.before(c) }
|
192
224
|
append_after_filter { |c| filter.after(c) }
|
@@ -206,6 +238,16 @@ module ActionController #:nodoc:
|
|
206
238
|
read_inheritable_attribute("after_filters")
|
207
239
|
end
|
208
240
|
|
241
|
+
# Returns a mapping between filters and the actions that may run them.
|
242
|
+
def included_actions #:nodoc:
|
243
|
+
read_inheritable_attribute("included_actions") || {}
|
244
|
+
end
|
245
|
+
|
246
|
+
# Returns a mapping between filters and actions that may not run them.
|
247
|
+
def excluded_actions #:nodoc:
|
248
|
+
read_inheritable_attribute("excluded_actions") || {}
|
249
|
+
end
|
250
|
+
|
209
251
|
private
|
210
252
|
def append_filter_to_chain(condition, filters)
|
211
253
|
write_inheritable_array("#{condition}_filters", filters)
|
@@ -220,6 +262,22 @@ module ActionController #:nodoc:
|
|
220
262
|
raise ActionControllerError, "Filter object must respond to both before and after"
|
221
263
|
end
|
222
264
|
end
|
265
|
+
|
266
|
+
def extract_conditions!(filters)
|
267
|
+
return nil unless filters.last.is_a? Hash
|
268
|
+
filters.pop
|
269
|
+
end
|
270
|
+
|
271
|
+
def add_action_conditions(filters, conditions)
|
272
|
+
return unless conditions
|
273
|
+
included, excluded = conditions[:only], conditions[:except]
|
274
|
+
write_inheritable_hash("included_actions", condition_hash(filters, included)) && return if included
|
275
|
+
write_inheritable_hash("excluded_actions", condition_hash(filters, excluded)) if excluded
|
276
|
+
end
|
277
|
+
|
278
|
+
def condition_hash(filters, *actions)
|
279
|
+
filters.inject({}) {|hash, filter| hash.merge(filter => actions.flatten.map {|action| action.to_s})}
|
280
|
+
end
|
223
281
|
end
|
224
282
|
|
225
283
|
module InstanceMethods # :nodoc:
|
@@ -252,18 +310,21 @@ module ActionController #:nodoc:
|
|
252
310
|
private
|
253
311
|
def call_filters(filters)
|
254
312
|
filters.each do |filter|
|
255
|
-
if
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
313
|
+
next if action_exempted?(filter)
|
314
|
+
filter_result = case
|
315
|
+
when filter.is_a?(Symbol)
|
316
|
+
self.send(filter)
|
317
|
+
when filter_block?(filter)
|
318
|
+
filter.call(self)
|
319
|
+
when filter_class?(filter)
|
320
|
+
filter.filter(self)
|
321
|
+
else
|
322
|
+
raise(
|
323
|
+
ActionControllerError,
|
324
|
+
"Filters need to be either a symbol, proc/method, or class implementing a static filter method"
|
325
|
+
)
|
266
326
|
end
|
327
|
+
return false if filter_result == false
|
267
328
|
end
|
268
329
|
end
|
269
330
|
|
@@ -274,6 +335,15 @@ module ActionController #:nodoc:
|
|
274
335
|
def filter_class?(filter)
|
275
336
|
filter.respond_to?("filter")
|
276
337
|
end
|
338
|
+
|
339
|
+
def action_exempted?(filter)
|
340
|
+
case
|
341
|
+
when self.class.included_actions[filter]
|
342
|
+
!self.class.included_actions[filter].include?(action_name)
|
343
|
+
when self.class.excluded_actions[filter]
|
344
|
+
self.class.excluded_actions[filter].include?(action_name)
|
345
|
+
end
|
346
|
+
end
|
277
347
|
end
|
278
348
|
end
|
279
349
|
end
|
@@ -58,12 +58,21 @@ module ActionController #:nodoc:
|
|
58
58
|
# <h1>Welcome</h1>
|
59
59
|
# Off-world colonies offers you a chance to start a new life
|
60
60
|
#
|
61
|
+
# == Automatic layout assignment
|
62
|
+
#
|
63
|
+
# If there is a template in <tt>app/views/layouts/</tt> with the same name as the current controller then it will be automatically
|
64
|
+
# set as that controller's layout unless explicitly told otherwise. Say you have a WeblogController, for example. If a template named
|
65
|
+
# <tt>app/views/layouts/weblog.rhtml</tt> or <tt>app/views/layouts/weblog.rxml</tt> exists then it will be automatically set as
|
66
|
+
# the layout for your WeblogController. You can create a layout with the name <tt>application.rhtml</tt> or <tt>application.rxml</tt>
|
67
|
+
# and this will be set as the default controller if there is no layout with the same name as the current controller and there is
|
68
|
+
# no layout explicitly assigned with the +layout+ method. Setting a layout explicity will always override the automatic behaviour.
|
69
|
+
#
|
61
70
|
# == Inheritance for layouts
|
62
71
|
#
|
63
72
|
# Layouts are shared downwards in the inheritance hierarchy, but not upwards. Examples:
|
64
73
|
#
|
65
74
|
# class BankController < ActionController::Base
|
66
|
-
# layout "
|
75
|
+
# layout "bank_standard"
|
67
76
|
#
|
68
77
|
# class InformationController < BankController
|
69
78
|
#
|
@@ -73,7 +82,7 @@ module ActionController #:nodoc:
|
|
73
82
|
# class EmployeeController < BankController
|
74
83
|
# layout nil
|
75
84
|
#
|
76
|
-
# The InformationController uses "
|
85
|
+
# The InformationController uses "bank_standard" inherited from the BankController, the VaultController overwrites
|
77
86
|
# and picks the layout dynamically, and the EmployeeController doesn't want to use a layout at all.
|
78
87
|
#
|
79
88
|
# == Types of layouts
|
@@ -104,16 +113,31 @@ module ActionController #:nodoc:
|
|
104
113
|
# class WeblogController < ActionController::Base
|
105
114
|
# layout proc{ |controller| controller.logged_in? ? "writer_layout" : "reader_layout" }
|
106
115
|
#
|
107
|
-
# Of course, the most common way of specifying a layout is still just as a plain template
|
116
|
+
# Of course, the most common way of specifying a layout is still just as a plain template name:
|
108
117
|
#
|
109
118
|
# class WeblogController < ActionController::Base
|
110
|
-
# layout "
|
119
|
+
# layout "weblog_standard"
|
111
120
|
#
|
112
|
-
#
|
121
|
+
# If no directory is specified for the template name, the template will by default by looked for in +app/views/layouts/+.
|
113
122
|
#
|
114
|
-
#
|
115
|
-
#
|
116
|
-
#
|
123
|
+
# == Conditional layouts
|
124
|
+
#
|
125
|
+
# If you have a layout that by default is applied to all the actions of a controller, you still have the option of rendering
|
126
|
+
# a given action or set of actions without a layout, or restricting a layout to only a single action or a set of actions. The
|
127
|
+
# +:only+ and +:except+ options can be passed to the layout call. For example:
|
128
|
+
#
|
129
|
+
# class WeblogController < ActionController::Base
|
130
|
+
# layout "weblog_standard", :except => :rss
|
131
|
+
#
|
132
|
+
# # ...
|
133
|
+
#
|
134
|
+
# end
|
135
|
+
#
|
136
|
+
# This will assign "weblog_standard" as the WeblogController's layout except for the +rss+ action, which will not wrap a layout
|
137
|
+
# around the rendered view.
|
138
|
+
#
|
139
|
+
# Both the +:only+ and +:except+ condition can accept an aribtrary number of method references, so +:except => [ :rss, :text_only ]+
|
140
|
+
# is valid, as is # +:except => :rss+.
|
117
141
|
#
|
118
142
|
# == Using a different layout in the action render call
|
119
143
|
#
|
@@ -130,32 +154,36 @@ module ActionController #:nodoc:
|
|
130
154
|
#
|
131
155
|
# As you can see, you pass the template as the first parameter, the status code as the second ("200" is OK), and the layout
|
132
156
|
# as the third.
|
133
|
-
#
|
134
|
-
# == Automatic layout assignment
|
135
|
-
#
|
136
|
-
# If there is a template in <tt>app/views/layouts/</tt> with the same name as the current controller then it will be automatically
|
137
|
-
# set as that controller's layout unless explicitly told otherwise. Say you have a WeblogController, for example. If a template named
|
138
|
-
# <tt>app/views/layouts/weblog.rhtml</tt> or <tt>app/views/layouts/weblog.rxml</tt> exists then it will be automatically set as
|
139
|
-
# the layout for your WeblogController. You can create a layout with the name <tt>application.rhtml</tt> or <tt>application.rxml</tt>
|
140
|
-
# and this will be set as the default controller if there is no layout with the same name as the current controller and there is
|
141
|
-
# no layout explicitly assigned with the +layout+ method. Setting a layout explicity will always override the automatic behaviour.
|
142
157
|
module ClassMethods
|
143
158
|
# If a layout is specified, all actions rendered through render and render_action will have their result assigned
|
144
159
|
# to <tt>@content_for_layout</tt>, which can then be used by the layout to insert their contents with
|
145
160
|
# <tt><%= @content_for_layout %></tt>. This layout can itself depend on instance variables assigned during action
|
146
161
|
# performance and have access to them as any normal template would.
|
147
|
-
def layout(template_name)
|
162
|
+
def layout(template_name, conditions = {})
|
163
|
+
add_layout_conditions(conditions)
|
148
164
|
write_inheritable_attribute "layout", template_name
|
149
165
|
end
|
150
166
|
|
167
|
+
def layout_conditions #:nodoc:
|
168
|
+
read_inheritable_attribute("layout_conditions")
|
169
|
+
end
|
170
|
+
|
151
171
|
private
|
152
172
|
def inherited(child)
|
153
173
|
inherited_without_layout(child)
|
154
|
-
child.layout(child.controller_name) unless layout_list.grep(/^#{child.controller_name}\.r(?:
|
174
|
+
child.layout(child.controller_name) unless layout_list.grep(/^#{child.controller_name}\.r(?:x|ht)ml$/).empty?
|
155
175
|
end
|
156
176
|
|
157
177
|
def layout_list
|
158
|
-
Dir.glob("#{template_root}/layouts/*.r{
|
178
|
+
Dir.glob("#{template_root}/layouts/*.r{x,ht}ml").map { |layout| File.basename(layout) }
|
179
|
+
end
|
180
|
+
|
181
|
+
def add_layout_conditions(conditions)
|
182
|
+
write_inheritable_hash "layout_conditions", normalize_conditions(conditions)
|
183
|
+
end
|
184
|
+
|
185
|
+
def normalize_conditions(conditions)
|
186
|
+
conditions.inject({}) {|hash, (key, value)| hash.merge(key => [value].flatten.map {|action| action.to_s})}
|
159
187
|
end
|
160
188
|
end
|
161
189
|
|
@@ -174,7 +202,7 @@ module ActionController #:nodoc:
|
|
174
202
|
end
|
175
203
|
|
176
204
|
def render_with_layout(template_name = default_template_name, status = nil, layout = nil) #:nodoc:
|
177
|
-
if layout ||= active_layout
|
205
|
+
if layout ||= active_layout and action_has_layout?
|
178
206
|
add_variables_to_assigns
|
179
207
|
logger.info("Rendering #{template_name} within #{layout}") unless logger.nil?
|
180
208
|
@content_for_layout = @template.render_file(template_name, true)
|
@@ -184,5 +212,19 @@ module ActionController #:nodoc:
|
|
184
212
|
end
|
185
213
|
end
|
186
214
|
|
215
|
+
private
|
216
|
+
|
217
|
+
def action_has_layout?
|
218
|
+
conditions = self.class.layout_conditions
|
219
|
+
case
|
220
|
+
when conditions[:only]
|
221
|
+
conditions[:only].include?(action_name)
|
222
|
+
when conditions[:except]
|
223
|
+
!conditions[:except].include?(action_name)
|
224
|
+
else
|
225
|
+
true
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
187
229
|
end
|
188
230
|
end
|
@@ -1,81 +1,85 @@
|
|
1
1
|
begin
|
2
2
|
require 'simplecc'
|
3
3
|
rescue LoadError
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
class Continuation #:nodoc:
|
5
|
+
def create(*args, &block)
|
6
|
+
cc = nil; result = callcc {|c| cc = c; block.call(cc) if block and args.empty?}
|
7
|
+
result ||= args
|
8
|
+
return *[cc, *result]
|
9
|
+
end
|
8
10
|
end
|
9
11
|
end
|
10
12
|
|
11
|
-
|
12
|
-
# method
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
# #
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
13
|
+
class Binding #:nodoc:
|
14
|
+
# This method returns the binding of the method that called your
|
15
|
+
# method. It will raise an Exception when you're not inside a method.
|
16
|
+
#
|
17
|
+
# It's used like this:
|
18
|
+
# def inc_counter(amount = 1)
|
19
|
+
# Binding.of_caller do |binding|
|
20
|
+
# # Create a lambda that will increase the variable 'counter'
|
21
|
+
# # in the caller of this method when called.
|
22
|
+
# inc = eval("lambda { |arg| counter += arg }", binding)
|
23
|
+
# # We can refer to amount from inside this block safely.
|
24
|
+
# inc.call(amount)
|
25
|
+
# end
|
26
|
+
# # No other statements can go here. Put them inside the block.
|
27
|
+
# end
|
28
|
+
# counter = 0
|
29
|
+
# 2.times { inc_counter }
|
30
|
+
# counter # => 2
|
31
|
+
#
|
32
|
+
# Binding.of_caller must be the last statement in the method.
|
33
|
+
# This means that you will have to put everything you want to
|
34
|
+
# do after the call to Binding.of_caller into the block of it.
|
35
|
+
# This should be no problem however, because Ruby has closures.
|
36
|
+
# If you don't do this an Exception will be raised. Because of
|
37
|
+
# the way that Binding.of_caller is implemented it has to be
|
38
|
+
# done this way.
|
39
|
+
def of_caller(&block)
|
40
|
+
old_critical = Thread.critical
|
41
|
+
Thread.critical = true
|
42
|
+
count = 0
|
43
|
+
cc, result, error, extra_data = Continuation.create(nil, nil)
|
44
|
+
error.call if error
|
42
45
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
46
|
+
tracer = lambda do |*args|
|
47
|
+
type, context, extra_data = args[0], args[4], args
|
48
|
+
if type == "return"
|
49
|
+
count += 1
|
50
|
+
# First this method and then calling one will return --
|
51
|
+
# the trace event of the second event gets the context
|
52
|
+
# of the method which called the method that called this
|
53
|
+
# method.
|
54
|
+
if count == 2
|
55
|
+
# It would be nice if we could restore the trace_func
|
56
|
+
# that was set before we swapped in our own one, but
|
57
|
+
# this is impossible without overloading set_trace_func
|
58
|
+
# in current Ruby.
|
59
|
+
set_trace_func(nil)
|
60
|
+
cc.call(eval("binding", context), nil, extra_data)
|
61
|
+
end
|
62
|
+
elsif type == "line" then
|
63
|
+
nil
|
64
|
+
elsif type == "c-return" and extra_data[3] == :set_trace_func then
|
65
|
+
nil
|
66
|
+
else
|
56
67
|
set_trace_func(nil)
|
57
|
-
|
68
|
+
error_msg = "Binding.of_caller used in non-method context or " +
|
69
|
+
"trailing statements of method using it aren't in the block."
|
70
|
+
cc.call(nil, lambda { raise(ArgumentError, error_msg) }, nil)
|
58
71
|
end
|
59
|
-
elsif type == "line" then
|
60
|
-
nil
|
61
|
-
elsif type == "c-return" and extra_data[3] == :set_trace_func then
|
62
|
-
nil
|
63
|
-
else
|
64
|
-
set_trace_func(nil)
|
65
|
-
error_msg = "Binding.of_caller used in non-method context or " +
|
66
|
-
"trailing statements of method using it aren't in the block."
|
67
|
-
cc.call(nil, lambda { raise(ArgumentError, error_msg) }, nil)
|
68
72
|
end
|
69
|
-
end
|
70
73
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
74
|
+
unless result
|
75
|
+
set_trace_func(tracer)
|
76
|
+
return nil
|
77
|
+
else
|
78
|
+
Thread.critical = old_critical
|
79
|
+
case block.arity
|
80
|
+
when 1 then yield(result)
|
81
|
+
else yield(result, extra_data)
|
82
|
+
end
|
79
83
|
end
|
80
84
|
end
|
81
|
-
end
|
85
|
+
end
|