From: Yehuda Katz Date: 2009-08-09T03:04:39+09:00 Subject: [ruby-core:24831] Re: [Feature #1906] Kernel#backtrace: Objectifying Kernel#caller Ha. I mentioned that this would be desirable in a different thread. Put me down for an enthusiastic +1. Have you looked at Rubinius' objectified backtrace? -- Yehuda Run Paint Run Run wrote: > Feature #1906: Kernel#backtrace: Objectifying Kernel#caller > http://redmine.ruby-lang.org/issues/show/1906 > > Author: Run Paint Run Run > Status: Open, Priority: Normal > Category: core > > Inspired by nobu's recent refactoring of Kernel#rand`, several conversations with Ruby implementors about Kernel#caller, and Rubinius' Backtrace class, I've put together a _rough_ demo of how we could "objectify" Kernel#caller. It's at http://tinyurl.com/m9fdrn [github.com], along with some initial specs. > > Rationale > ========= > > `caller` has two principle uses: > > * Allowing users to display the backtrace at a given point, e.g. `puts caller`. > * Introspection to determine the callpath that lead the current method. > > The first use is reasonably achievable with `caller`, as long as you don't want to do any formatting of the output. The second is hard because it requires parsing lines of the `caller` Array with regular expressions, and knowing what the various permutations of output imply. It would be easier if we could inspect the call stack with a Ruby-ish API. Further, this would allow alternative implementations to provide this functionality without having to reverse-engineer the output of `caller`. As a result, backtraces would become more useful and code using them more portable. > > Name > ==== > > The advantage of calling this feature #backtrace is that it's consistent with the usage of the term by Thread and Exception. This, however, could also be construed as a disadvantage because although identically named the output would be materially different. I'm not sure of the best approach in this regard. > > API > === > > A Kernel method named, for sake of argument, 'backtrace' which returns a Backtrace object. It can be treated like an Array, in the same way `caller` is, because it's an Enumerable. It also has shortcuts for accessing the most recent entry on the stack. Each line in the backtrace is represented by a Backtrace::Line object which has #file, #line, and #name accessors which correspond to the filename, the line number, and the method name, respectively. For example: > > backtrace.name # The name of the method which invoked the current one as a Symbol > backtrace.file(2) # The absolute filename of the 3rd entry in the backtrace > backtrace.each do |line| # Yields Line objects > puts line.method > end > backtrace.lines.select {|l| l.method == :foo} # #lines returns an Array of Line objects > > Simple stuff. > > Weaknesses > ========== > > Ideally, #name (not called #method because of the clash with Object#method) would return a Method object. One of the many advantages of this would be that we could combine backtraces with Method#parameters to display the signatures of each method. Unfortunately, I can't see a non-hackish way to create Method objects from the output of `caller`, because I don't know which object the method is bound to. but if this were possible it would be useful. > > I'm currently throwing away some of the output of `caller` because I don't completely understand it. We'll need to decide whether this would be useful to expose via the API, and if so how. > > I'd prefer to return a File object for #file, but the majority of Ruby APIs return pathnames instead, so I've went with convention. > > So is there any interest in this type of thing? Is it worth exploring further? > > > ---------------------------------------- > http://redmine.ruby-lang.org > >