Issue #16142 has been updated by ioquatix (Samuel Williams).


> Maybe we could change #source_location to return a `SourceLocation` and have `SourceLocation#to_a` so `file, line = proc.source_location` would still work?

I've spent some time thinking about this.

I wanted to understand why we have this proposal.

- Because `source_location` returns an array, so it's hard to extend with additional functionality.
- So, we introduce new method `code_range` which also returns an array. But this time, the array is different.

This proposal is repeating the same mistake with a new method name. The mistake was to use an Array rather than some rich object. Because it's an array, we cannot add meaningful operations like `#filename`, `#source` and other things which would be very useful.

So, given this, I'm against adding a new method, and I'd rather try to fix the existing one.

I thought about how to do this. I looked at some existing code which uses `source_location`.

I feel like we can do something like the following, to maintain backwards compatibility, while supporting the proposed use case, and allowing further extensions in the future.

```ruby
class SourceLocation
  def initialize(path, line_number, source_range: nil)
    @path = path
    @line_number = line_number
    @source_range = source_range
  end

  attr :path
  attr :line_number
  
  def to_ary
    [@path, @line_number]
  end
  
  def [] index
    case index
    when 0
      @path
    when 1
      @line_number
    end
  end
  
  def read
    File.open(@path) do |file|
      file.seek(@source_range.min)
      
      return file.read(@source_range.size)
    end
  end
end

module MethodSourceLocation
  def source_location
    SourceLocation.new(*super, source_range: 667...(667+30))
  end
end

Method.prepend(MethodSourceLocation)

class Test
  def foo
    return "bar"
  end
end

test = Test.new
method = test.method(:foo)

source = method.source_location.read

puts source
# def foo
#     return "bar"
#   end
```

----------------------------------------
Feature #16142: Implement code_range in Proc and Method
https://bugs.ruby-lang.org/issues/16142#change-82843

* Author: okuramasafumi (Masafumi OKURA)
* Status: Open
* Priority: Normal
* Assignee: 
* Target version: 
----------------------------------------
# Abstract

Add a new method `code_range` as an alternative to `source_location` to Proc and Method

# Background

I'd like to get a body from a Proc in TraceLocation gem (https://github.com/yhirano55/trace_location), in order to add what's executed to the output. There's no way to do that in current Ruby implementation, so as an alternative, I considered getting source code location of a Proc.

# Proposal

I propose that `Proc#code_range` and `Method#code_range`. Other names can work as well, for example `Proc#source_region`. It returns an array containing filename as a first argument and position information as a second array. For example:
`a_proc.position # => [(irb), [1, 5, 3, 25]]`

# Implementation

I've implemented a simpler version of this, see gist for more details.

https://gist.github.com/okuramasafumi/ac90bbf04a1c13b7d67954c9c5e62553

Notice I use `code_location` from iseq struct.

# Discussion

One might say that we can simply add columns and end position to Proc#source_location. However, this can easily brake existing apps such as Pry.
It's also possible that we add additional keyword argument to `Proc#source_location`, for instance:
`a_proc.source_location(including_range: true)`
This change can also break existing apps since in old Rubies this keyword argument cannot be accepted.
Therefore, adding a new method is better in terms of backward compatibility. It might be better at readability as well.

# Summary

I propose an API to get code position of Proc and Method so that we can get body of them (especially of a Proc).



-- 
https://bugs.ruby-lang.org/