Is it possible to see the ruby code in a proc? - ruby

p = Proc.new{ puts 'ok' }
Is is possible to see the ruby code in the proc?
inspect returns the memory location:
puts p.inspect
#<Proc:0x007f9e42980b88#(irb):2>
Ruby 1.9.3

Take a look at the sourcify gem:
proc { x + y }.to_source
# >> "proc { (x + y) }"

Do you mean the original source code or its bytecode representation ?
For the former you may use standard Proc's method source_location
p.source_location
=> ["test.rb", 21]
and read the appropriate lines of code.
For the latter it may come handy the RubyVM::InstructionSequence and its class method disassemble:
irb> RubyVM::InstructionSequence.disasm p
=> "== disasm: <RubyVM::InstructionSequence:block in irb_binding#(irb)>
=====\n== catch table\n| catch type: redo st: 0000 ed: 0011 sp: 0000
cont: 0000\n| catch type: next st: 0000 ed: 0011 sp: 0000 cont:
0011\n|------------------------------------------------------------------------\n
0000 trace 1
( 1)\n0002 putself \n0003 putstring \"ok\"\n0005
send :puts, 1, nil, 8, <ic:0>\n0011 leave \n"

No, there is no way to do that in Ruby.
Some Ruby implementations may or may not have implementation-specific ways of getting the source code.
You can also try to use Proc#source_location to find the file that the Proc was defined in, and then parse that file to find the source code. But that won't work if the Proc wasn't defined in a file (e.g. if it was defined dynamically with eval) or if the source file no longer exists, e.g. because you are running an AOT-compiled version of your program.
So, the short answer is: no, there is no way.
The long answer is: there are some ways that may or may not sometimes work depending on way too many factors to even begin to make this work reliably.
That's not even taking into account Procs which don't even have a Ruby source code because they were defined in native code.

If proc is defined into a file, U can get the file location of proc then serialize it, then after deserialize use the location to get back to the proc again
proc_location_array = proc.source_location
after deserialize:
file_name = proc_location_array[0]
line_number = proc_location_array[1]
proc_line_code = IO.readlines(file_name)[line_number - 1]
proc_hash_string = proc_line_code[proc_line_code.index("{")..proc_line_code.length]
proc = eval("lambda #{proc_hash_string}")

Although an old question, still, I wanted to share my thoughts.
You can use Pry gem and end up with something like this:
[11] pry> p = Proc.new{ puts 'ok' }
=> #<Proc:0x007febe00e6360#(pry):23>
[12] pry> show-source p
From: (pry)
Number of lines: 1
p = Proc.new{ puts 'ok' }
Also, if you would use it from Rails context, you can put:
::Kernel.binding.pry
in your controllers or models, and
- require 'pry'; binding.pry
in your views, where you want to start debugging.
And in the tests, I use a combination, first require 'pry' at the top, and then ::Kernel.binding.pry where needed.
References:
http://pryrepl.org
https://github.com/pry/pry
https://github.com/pry/pry/wiki/Source-browsing

Related

What happens when you use string interpolation in ruby?

I thought that ruby just call method to_s but I can't explain how this works:
class Fake
def to_s
self
end
end
"#{Fake.new}"
By the logic this should raise stack level too deep because of infinity recursion. But it works fine and seems to call #to_s from an Object.
=> "#<Fake:0x137029f8>"
But why?
ADDED:
class Fake
def to_s
Fake2.new
end
end
class Fake2
def to_s
"Fake2#to_s"
end
end
This code works differently in two cases:
puts "#{Fake.new}" => "#<Fake:0x137d5ac4>"
But:
puts Fake.new.to_s => "Fake2#to_s"
I think it's abnormal. Can somebody suggest when in ruby interpreter it happens internally?
Short version
Ruby does call to_s, but it checks that to_s returns a string. If it doesn't, ruby calls the default implementation of to_s instead. Calling to_s recursively wouldn't be a good idea (no guarantee of termination) - you could crash the VM and ruby code shouldn't be able to crash the whole VM.
You get different output from Fake.new.to_s because irb calls inspect to display the result to you, and inspect calls to_s a second time
Long version
To answer "what happens when ruby does x", a good place to start is to look at what instructions get generated for the VM (this is all MRI specific). For your example:
puts RubyVM::InstructionSequence.compile('"#{Foo.new}"').disasm
outputs
0000 trace 1 ( 1)
0002 getinlinecache 9, <is:0>
0005 getconstant :Foo
0007 setinlinecache <is:0>
0009 opt_send_simple <callinfo!mid:new, argc:0, ARGS_SKIP>
0011 tostring
0012 concatstrings 1
0014 leave
There's some messing around with the cache, and you'll always get trace, leave but in a nutshell this says.
get the constant Foo
call its new method
execute the tostring instruction
execute the concatstrings instruction with the result of the tostring instruction (the last value on the stack (if you do this with multiple #{} sequences you can see it building up all the individual strings and then calling concatstrings once on all consuming all of those strings)
The instructions in this dump are defined in insns.def: this maps these instructions to their implementation. You can see that tostring just calls rb_obj_as_string.
If you search for rb_obj_as_string through the ruby codebase (I find http://rxr.whitequark.org useful for this) you can see it's defined here as
VALUE
rb_obj_as_string(VALUE obj)
{
VALUE str;
if (RB_TYPE_P(obj, T_STRING)) {
return obj;
}
str = rb_funcall(obj, id_to_s, 0);
if (!RB_TYPE_P(str, T_STRING))
return rb_any_to_s(obj);
if (OBJ_TAINTED(obj)) OBJ_TAINT(str);
return str;
}
In brief, if we already have a string then return that. If not, call the object's to_s method. Then, (and this is what is crucial for your question), it checks the type of the result. If it's not a string it returns rb_any_to_s instead, which is the function that implements the default to_s

How can I trace string creation in Ruby?

In this answer to a separate question, I said that if you do
name = "Rohit " "Sharma"
you don't create two String objects with the contents "Rohit " and "Sharma" that combine to create a new String object with the contents "Rohit Sharma", but that you only create a single String object to start off with.
But it's only a book telling me that, rather than manually verifying it.
How would you be able to log the creation of a String?
I tried using
set_trace_func proc { |event, file, line, id, binding, classname|
printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, classname
}
string = "Insert content here"
But only got
c-return -:3 set_trace_func Kernel
line -:5
And "Programming Ruby 1.9" (the Pickaxe) says that modifying Class#new wouldn't work for strings, as they're constructed without using new.
You can't log this, since this concatenation doesn't happen at runtime. It happens at parse time.
In other words: regardless of whatever code you write to log this, the concatenation will have happened long before your code ever gets run.
In yet other words: you trust that there's only one string object created the same way that you trust that 42 only creates one Fixnum object with the value 42, not 42 Fixnum objects with the value 1: the ISO Ruby Language Specification says so, and the source code of every single existing Ruby implementation says so, and every Ruby book ever written says so.
This doesn't trace string creation as such, but the following suggests that the concatenation occurs before any string objects are created:
The following (I read about this in the Pickaxe, in the section "Behind the Curtain: The Ruby VM") works in YARV - I don't think it'd work in any other implementations of ruby:
irb(main):003:0> uncompiled_code = 'name = "Rohit " "Sharma"'
=> "name = \"Rohit \" \"Sharma\""
irb(main):004:0> code = RubyVM::InstructionSequence.compile(uncompiled_code)
=> <RubyVM::InstructionSequence:<compiled>#<compiled>>
irb(main):005:0> puts code.disassemble
== disasm: <RubyVM::InstructionSequence:<compiled>#<compiled>>==========
local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1] s1)
[ 2] name
0000 trace 1 ( 1)
0002 putstring "Rohit Sharma"
0004 dup
0005 setlocal name
0007 leave
=> nil

Why don't numbers support .dup?

>> a = 5
=> 5
>> b = "hello, world!"
=> "hello, world!"
>> b.dup
=> "hello, world!"
>> a.dup
TypeError: can't dup Fixnum
from (irb):4:in `dup'
from (irb):4
I understand that Ruby will make a copy every time you assign an integer to a new variable, but why does Numeric#dup raise an error?
Wouldn't this break abstraction, since all objects should be expected to respond to .dup properly?
Rewriting the dup method will fix the problem, as far as I can tell:
>> class Numeric
>> def dup()
>> self
>> end
>> end
Does this have a downside I'm not seeing? Why isn't this built into Ruby?
Most objects in Ruby are passed by reference and can be dupped. Eg:
s = "Hello"
t = s # s & t reference to the same string
t.upcase! # modifying either one will affect the other
s # ==> "HELLO"
A few objects in Ruby are immediate, though. They are passed by value, there can only be one of this value and it therefore cannot be duped. These are any (small) integers, true, false, symbols and nil. Many floats are also immediates in Ruby 2.0 on 64 bit systems.
In this (preposterous) example, any "42" will hold the same instance variable.
class Fixnum
attr_accessor :name
alias_method :original_to_s, :to_s
def to_s
name || original_to_s
end
end
42.name = "The Answer"
puts *41..43 # => 41, The Answer, 43
Since you would normally expect something.dup.name = "new name" to not affect any other object than the copy obtained with dup, Ruby chooses not to define dup on immediates.
Your question is more complex than it appears. There was some discussion on ruby-core as to how this can be made easier. Also, other types of Numeric objects (floats, bignums, rationals and complex numbers) can not be duped although they are not immediates either.
Note that ActiveSupport (part of rails) provide the method duplicable? on all objects
The problem with the dup() function that you defined is that it doesn't return a copy of the object, but rather returns the object itself. This is not what a duplicate procedure is supposed to do.
I don't know Ruby, but a possible reason I can think of for dup not being defined for numbers is that a number is a basic type and thus, doing something like:
>> a = 5
>> b = a
would automatically assign the value 5 into the variable b, as opposed to making b and a point to the same value in memory.

Catching line numbers in ruby exceptions

Consider the following ruby code
test.rb:
begin
puts
thisFunctionDoesNotExist
x = 1+1
rescue Exception => e
p e
end
For debugging purposes, I would like the rescue block to know that the error occurred in line 4 of this file. Is there a clean way of doing that?
p e.backtrace
I ran it on an IRB session which has no source and it still gave relevant info.
=> ["(irb):11:in `foo'",
"(irb):17:in `irb_binding'",
"/usr/lib64/ruby/1.8/irb/workspace.rb:52:in `irb_binding'",
"/usr/lib64/ruby/1.8/irb/workspace.rb:52"]
If you want a nicely parsed backtrace, the following regex might be handy:
p x.backtrace.map{ |x|
x.match(/^(.+?):(\d+)(|:in `(.+)')$/);
[$1,$2,$4]
}
[
["(irb)", "11", "foo"],
["(irb)", "48", "irb_binding"],
["/usr/lib64/ruby/1.8/irb/workspace.rb", "52", "irb_binding"],
["/usr/lib64/ruby/1.8/irb/workspace.rb", "52", nil]
]
( Regex /should/ be safe against weird characters in function names or directories/filenames )
( If you're wondering where foo camefrom, i made a def to grab the exception out :
>>def foo
>> thisFunctionDoesNotExist
>> rescue Exception => e
>> return e
>>end
>>x = foo
>>x.backtrace
You can access the backtrace from an Exception object. To see the entire backtrace:
p e.backtrace
It will contain an array of files and line numbers for the call stack. For a simple script like the one in your question, it would just contain one line.
["/Users/dan/Desktop/x.rb:4"]
If you want the line number, you can examine the first line of the backtrace, and extract the value after the colon.
p e.backtrace[0].split(":").last
Usually the backtrace contains a lot of lines from external gems
It's much more convenient to see only lines related to the project itself
My suggestion is to filter the backtrace by the project folder name
puts e.backtrace.select { |x| x.match(/HERE-IS-YOUR-PROJECT-FOLDER-NAME/) }
And then you can parse filtered lines to extract line numbers as suggested in other answers.
Throwing my $0.02 in on this old thread-- here's a simple solution that maintains all the original data:
print e.backtrace.join("\n")
It is possible that in Ruby 1.9.3 you will be able to get access to not only this information in a more structured, reliable, and simpler way without using regular expressions to cut strings.
The basic idea is to introduce a call frame object which gives access to information about the call stack.
See http://wiki.github.com/rocky/rb-threadframe/, which alas, requires patching Ruby 1.9. In RubyKaigi 2010 (late August 2010) a meeting is scheduled to discuss introducing a frame object into Ruby.
Given this, the earliest this could happen is in Ruby 1.9.3.

What does 'yield called out of block' mean in Ruby?

I'm new to Ruby, and I'm trying the following:
mySet = numOfCuts.times.map{ rand(seqLength) }
but I get the 'yield called out of block' error. I'm not sure what his means. BTW, this question is part of a more general question I asked here.
The problem is that the times method expects to get a block that it will yield control to. However you haven't passed a block to it. There are two ways to solve this. The first is to not use times:
mySet = (1..numOfCuts).map{ rand(seqLength) }
or else pass a block to it:
mySet = []
numOfCuts.times {mySet.push( rand(seqLength) )}
if "numOfCuts" is an integer,
5.times.foo
is invalid
"times" expects a block.
5.times{ code here }
You're combining functions that don't seem to make sense -- if numOfCuts is an integer, then just using times and a block will run the block that many times (though it only returns the original integer:
irb(main):089:0> 2.times {|x| puts x}
0
1
2
map is a function that works on ranges and arrays and returns an array:
irb(main):092:0> (1..3).map { |x| puts x; x+1 }
1
2
3
[2, 3, 4]
I'm not sure what you're trying to achieve with the code - what are you trying to do? (as opposed to asking specifically about what appears to be invalid syntax)
Bingo, I just found out what this is. Its a JRuby bug.
Under MRI
>> 3.times.map
=> [0, 1, 2]
>>
Under JRuby
irb(main):001:0> 3.times.map
LocalJumpError: yield called out of block
from (irb):2:in `times'
from (irb):2:in `signal_status'
irb(main):002:0>
Now, I don't know if MRI (the standard Ruby implementation) is doing the right thing here. It probably should complain that this does not make sense, but when n.times is called in MRI it returns an Enumerator, whereas Jruby complains that it needs a block.
Integer.times expects a block. The error message means the yield statement inside the times method can not be called because you did not give it a block.
As for your code, I think what you are looking for is a range:
(1..5).map{ do something }
Here is thy rubydoc for the Integer.times and Range.

Resources