Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

root/branches/1-2-stable/activesupport/lib/active_support/binding_of_caller.rb

Revision 784, 2.8 kB (checked in by david, 4 years ago)

Documentation stuff

Line 
1 begin
2   require 'simplecc'
3 rescue LoadError
4   class Continuation # :nodoc: # for RDoc
5   end
6   def Continuation.create(*args, &block) # :nodoc:
7     cc = nil; result = callcc {|c| cc = c; block.call(cc) if block and args.empty?}
8     result ||= args
9     return *[cc, *result]
10   end
11 end
12
13 class Binding; end # for RDoc
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 Binding.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
45
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
67       set_trace_func(nil)
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)
71     end
72   end
73
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
83   end
84 end
Note: See TracBrowser for help on using the browser.