How can I get source code of a method dynamically and also which file is this method locate in
Use source_location
:
class A
def foo
end
end
file, line = A.instance_method(:foo).source_location
# or
file, line = A.new.method(:foo).source_location
puts "Method foo is defined in #{file}, line #{line}"
# => "Method foo is defined in temp.rb, line 2"
Note that for builtin methods, source_location
returns nil
. If want to check out the C source code (have fun!), you'll have to look for the right C file (they're more or less organized by class) and find the rb_define_method
for the method (towards the end of the file).
In Ruby 1.8 this method does not exist, but you can use this gem.
Can I make ruby print the source code for a dynamic method?
If a class is extended at runtime, is its source safe from being investigated?
No, it is not safe. For example, ParseTree could be used to determine the actual runtime code for the method and reverse-engineer an equivalent implementation.
How could I extract the code of a method dynamically using reflection or something similar?
Reflection allows you to discover the class members like properties or methods, but it only gives information on their signature (parameter names and types and return type etc.) but no information about their implementation.
If you have access to the source code, you can use the C# compiler to do a syntax analysis for you.
See: Get started with syntax analysis (The .NET Compiler Platform SDK).
Especially the Traversing trees chapter explains how you can discover the structure of your code.
But maybe a better way to document your code is to use XML-comments (also called Documentation comments).
In the project options in the Build
page, check the XML documentation file
check box and then write comments like these:
/// <summary>
/// Base module for sandboxed operations.
/// </summary>
public abstract class SandboxModule
{
/// <summary>
/// Implementing modues must override this method.
/// </summary>
protected abstract void Execute();
}
/// <summary>
/// This is my super cool demo module.
/// </summary>
public class DemoModule : SandboxModule
{
/// <summary>
/// Does something super cool and new.
/// </summary>
/// <remarks>It does so by calling the private method <c>SomethingSuperCoolAndNew</c>.</remarks>
protected override void Execute()
{
int x = 3;
SomethingSuperCoolAndNew(x);
}
/// <summary>
/// Important things happen here.
/// </summary>
/// <param name="someArg">Magic parameter.</param>
private void SomethingSuperCoolAndNew(int someArg)
{
// Do something super cool and new.
}
}
In the output directory (bin\Debug) the compiler will create an XML file looking like this:
<?xml version="1.0"?>
<doc>
<assembly>
<name>StackOverflowTests3</name>
</assembly>
<members>
<member name="T:StackOverflowTests3.SandboxModule">
<summary>
Base module for sandboxed operations.
</summary>
</member>
<member name="M:StackOverflowTests3.SandboxModule.Execute">
<summary>
Implementing modues must override this method.
</summary>
</member>
<member name="T:StackOverflowTests3.DemoModule">
<summary>
This is my super cool demo module.
</summary>
</member>
<member name="M:StackOverflowTests3.DemoModule.Execute">
<summary>
Does something super cool and new.
</summary>
<remarks>It does so by calling the private method <c>SomethingSuperCoolAndNew</c>.</remarks>
</member>
<member name="M:StackOverflowTests3.DemoModule.SomethingSuperCoolAndNew(System.Int32)">
<summary>
Important things happen here.
</summary>
<param name="someArg">Magic parameter.</param>
</member>
</members>
</doc>
This Visual Studio Magazine article explains how you can convert these XML-files into help files: Doing Visual Studio and .NET Code Documentation Right.
The documentations comments are even visible within Visual Studio's IntelliSense tooltips.
how to get source code text in ruby?
You can't get the string representation of a part of the code, edit it and expect Ruby to reevaluate your changes. The only way to do something near to what you want is using ParseTree
to get s-expressions of the source, edit and use Ruby2Ruby
to generate a string of ruby code. Them add def ...
and end
to the string and call eval with it.
It's too hard and error-prone to be useful in a real-world situation. But I don't know any other way.
Note: ParseTree only works on Ruby 1.8.
Ruby / Rails -- from where was a method was included?
Whatever context you're in you can get the source location by using:
obj.method(:method).source_location
It won't give you exactly what you want, but the Rails core developers are good about properly namespacing things. The following example can be run from the rails console:
Time.method(:zone).source_location
["/Users/pete/.rvm/gems/ruby-1.9.2-p290@gemset/gems/activesupport-3.2.3/lib/active_support/core_ext/time/zones.rb", 9]
Then you can go to the Rails source and search for that file. Hint: type 't' on Github and start typing. It will bring you to that file and you can see that it is defined directly on the Time class.
Access Pry's show-source method from Ruby file
Ruby has the build-in method Method#source_location which can be used to find the location of the source. The method_source gem builds upon this by extracting the source based upon the source location. However this doesn't work for methods defined in the interactive console. Methods must be defined in a file.
Here is an example:
require 'set'
require 'method_source'
puts Set.method(:[]).source_location
# /home/user/.rvm/rubies/ruby-2.4.1/lib/ruby/2.4.0/set.rb
# 74
#=> nil
puts Set.method(:[]).source
# def self.[](*ary)
# new(ary)
# end
#=> nil
Keep in mind that all core Ruby methods are written in C and return nil
as source location. 1.method(:+).source_location #=> nil
The standard library is written in Ruby itself. Therefore the example above works for Set methods.
Source code for an eval'ed/dynamically generated function in Python
So I was able to partially figure this out by digging into the IPython source code. It makes use of the builtin module linecache, which contains functions for reading source code from files and caching the results. The inspect
and traceback
modules both use this module to get the source of a function.
The solution is to create the function the same way as in the question, but using compile
with a made-up and unique file name:
source = 'def foo(x, y):' + '\n\t' + 'return x / y'
filename = '<dynamic-123456>' # Angle brackets may be required?
code = compile(source, filename, 'exec')
g = {numpy: numpy, ...} # Modules and such required for function
l = {}
exec(src, g, l)
func = l['foo']
linecache
contains a variable cache
which is a dictionary mapping file names to (size, mtime, lines, fullname)
tuples. You can simply add an entry for the fake file name:
lines = [line + '\n' for line in source.splitlines()]
import linecache
linecache.cache[filename] = (len(source), None, lines, filename)
The function will then work with inspect.getsource()
, IPython's ?
/??
syntax, and in IPython tracebacks. However, it still doesn't seem to work in built-in tracebacks. This is mostly sufficient for me because I almost always work in IPython.
EDIT: see user2357112's comment below for how to get this working with traceback printing in the builtin interpreter.
Related Topics
Why Is It Bad Style to 'Rescue Exception =≫ E' in Ruby
Difference Between Attr_Accessor and Attr_Accessible
Ruby: What Does 'Require: False' in Gemfile Mean
Is There an Efficient Way to Perform Hundreds of Text Substitutions in Ruby
How to Avoid Tripping Over Utf-8 Bom When Reading Files
Block Definition - Difference Between Braces and Do-End
Difference Between Map and Collect in Ruby
Why Isn't the Eigenclass Equivalent to Self.Class, When It Looks So Similar
What Does ≪≪-Constant Do
Why Do Ruby Setters Need "Self." Qualification Within the Class
Sudo Gem Install' or 'Gem Install' and Gem Locations
Rails :Dependent =≫ :Destroy VS :Dependent =≫ :Delete_All
Using Sinatra For Larger Projects Via Multiple Files