What's the point of an interactive Ruby subshell? - ruby

Starting up an interactive Ruby shell in the Terminal ('irb'), one can continue to open up irb subshells endlessly. What's the point of this?

So far I've seen three usefull things irb subsessions can do for you:
undefine local variables
change self of an irb session
irb is a part of a great set of tools
undefine local variables
The nested irb starts a new subsession in which all local variables (not classes, modules etc.) are not defined any more.
irb(main):001:0> a = 1
#=> 1
irb(main):002:0> irb
irb#1(main):001:0> a
NameError: undefined local variable or method `a' for main:Object from (irb#1):1
change self for an irb session
irb(main):001:0> self
#=> main
irb(main):002:0> irb "Hello World"
irb#1(Hello World):001:0> self
#=> "Hello World"
irb#1(Hello World):002:0> length
#=> 11
Note: This is also known as "change binding" of an irb session.
By the way: It's possible to change the binding without opening a subsession (cb, irb_change-binding both do that for you). But it's more convenient to get back to the old binding with subsession.
The best thing is, that irb is just one of a useful set of commands
irb: start a new subsession
jobs: list subsessions
fg: switch to a subsession
kill: kill a subsession
See this insteresting SO answer for details.

Related

Getting an undefined local variable error in Ruby when debugging

I have the following script in a file called foo.rb
def foo(msg)
msg
end
def bar
thing = 123
thing
end
debugger
p foo(:hai)
I run the program in debug mode, like so:
ruby --debug -r debug foo.rb
Notice I make sure the stdlib debug is loaded via -r and I also put the ruby environment into debug mode using --debug
So the first output I get is unexpected:
Debug.rb
Emacs support available.
/Users/M/.rbenv/versions/2.1.2/lib/ruby/2.1.0/rubygems/core_ext/kernel_require.rb:57: RUBYGEMS_ACTIVATION_MONITOR.enter
After that, if I press c to 'continue' the program ends with the following error:
Exception `NameError' at foo.rb:10 - undefined local variable or method `debugger' for main:Object foo.rb:10:in `<main>': undefined local variable or method `debugger' for main:Object (NameError)
Can anyone tell me what I'm doing wrong, and how to actually get debug mode to recognise the relevant debugger command (if that's even the correct command to be using; the docs aren't clear at all on this)
Note: I'm not interested in using 3rd party gems in this instance (e.g. pry or ruby-debug). I'm only interested in understanding how to use the stdlib debugger that comes with Ruby
Update
Since this question was answered, I've gone ahead and created a gist for future reference. For anyone stumbling across this thread you might find it useful: https://gist.github.com/Integralist/5658cb218bb50494a1fa
Don't use -r. The entire mechanism by which an interactive debugging sessions is loaded is by the literal line of code require 'debug'.
Your code should look like this:
def foo(msg)
msg
end
def bar
thing = 123
thing
end
require 'debug'
p foo(:hai)
And you should run the program by simply typing ruby <program_name>.rb.

irb access pre-load script local variables

i'm using irb -I . -r script.rb to require a script before starting an interactive session. functions defined in the global scope are available but variables aren't unless they are declared with #
is there a way to access local variables defined in the global context of a script or a better way to do this
script.rb:
def func() "..." end
a = "str"
#b = 1
then after irb starts:
irb(main):001:0> a
NameError: undefined local variable or method `a' for main:Object
from (irb):1
from /usr/bin/irb:11:in `<main>'
irb(main):002:0> #b
=> 1
irb(main):003:0> func
=> "..."
i'm assuming that the script's contents are executed as if defined in a function (eg: main in C-type languages) and so the variables at the global context are local variables that are not accessible outside that scope
so do most people use # variables when writing their scripts?
the use-case is narrow in scope (script development) and the solution is trivial (search-replace any variable with #variable), but I'm learning the semantics of the language and I'm curious about this. Can't the execution context be exposed and merged into the current context somehow?
No. You can't access local variables from a different scope. That's the whole point of local variables: they are local to the scope they are defined in.
In this case, a is local to the script body of script.rb.
Maybe binding.irb is what you are looking for.
script.rb:
def func() "..." end
a = "str"
#b = 1
binding.irb
From the documentation:
Opens an IRB session where binding.irb is called which allows for
interactive debugging. You can call any methods or variables available
in the current scope, and mutate state if you need to.

Console not ready ruby sublime text file

I'm new to programming. Just about to start learning Ruby. I already took a console class, but I am stuck here.
I'm using a mac 10.6.8. I have done a quick 1+2 in the sublime text editor. I saved it. I went over to my console typed irb and then typed ruby example.rb. I have read elsewhere here that typing require './example' would help....it didn't. I am getting the following
NameError: undefined local variable or method `example' for main:Object
from (irb):2
from /usr/local/rvm/rubies/ruby-1.9.3-p392/bin/irb:16:in `<main>'
I don't understand what I am doing wrong. Thank you for your help. I really appreciate it.
-L
I would do as below:
kirti#kirti-Aspire-5733Z:~$ irb
2.0.0p0 :001 > require 'fileutils'
=> true
2.0.0p0 :002 > FileUtils.pwd
=> "/home/kirti"
2.0.0p0 :003 > FileUtils.cd "/home/kirti/ruby"
=> nil
2.0.0p0 :004 > load "./SO.rb"
3
=> true
2.0.0p0 :005 > require "./SO.rb"
3
=> true
My SO.rb file contains the below line :
puts 1+2
May be you wanna give a try.
Step 1: Navigate to your project/file folder by using command "cd folder_name/folder_location"
Step 2: load './example.rb'
For better solution you may wanna define some function inside example.rb
Like:
def sum
1 + 2
end
And to get the output enter sum in irb after loading the example.rb file.
irb is the interactive ruby shell. Within the shell, everything you type is interpreted as Ruby code, not bash commands. So, for example:
bash> puts 1 + 2
# command not found: puts
# this happens because you're not in a Ruby shell
bash> irb
# now you're in a Ruby shell
irb> puts 1 + 2
# 3
If you wrote some code in example.rb, you have two options:
From the bash shell, run ruby example.rb (from the same directory where your example.rb file is saved.
From the irb console, you can require 'example', which will load the contents of example.rb into your interpreter. In this case, it will immediately execute the Ruby code. If you wrapped the contents of example.rb in a class, it would load the class, but not execute code within it until you instantiated/called it.
Hopefully that helps!
My guess is that you are typing (into irb):
require example.rb
When you need to type:
require './example.rb'
The first tells ruby: "require what is in a variable called example". Because you did not define a variable called example, it results in the no variable or method error.
The second tells ruby: "require a string './example.rb'". Since the require method essentially knows how to find the file name passed as a string and evaluate the file, you'll get the right output
By the way, for this example, example.rb needs to be in the same directory. If example.rb is in another directory, you'll need to use the full path (I won't expand on it here) to source it.
You'll also notice that the output will look something like this:
3
=> true
This is because the file was evaluated (executing the code: puts 1+2) and the require method returns true to indicate it evaluated the file.
If you require the file again, you'll get false because the file is already loaded.

Ruby / IRB environment issue on MacOSX

I have a ruby script that is failing due to my environment, I think it is demonstrated by this strange behaviour in irb ( I am also using rvm but don't think that is the problem)
>> ruby -v
ruby 1.8.7 (2009-06-12 patchlevel 174) [i686-darwin9.8.0]
>> irb
>> FileUtils.mkdir_p('tmp')
NameError: uninitialized constant FileUtils
from (irb):1
>> help
=> nil
>> FileUtils.mkdir_p('tmp')
=> "tmp"
The FileUtils command initially fails but then after typing Help (which also fails) it seems to work.
I have tried require 'rubygems' and require 'FileUtils' - which does fix the problem - but would like to understand whats happening here.
I didn't know there was a "help" command, but apparently it has dependency on FileUtils, probably to load help files. "help" is loading its requirements into the IRB session.
>> before = ObjectSpace.each_object.map { |i| i.class }.uniq
=> [Regexp, String, Array, Class, Hash, Module, Proc, MatchData, File, Binding, NoMemoryError, Float, SystemStackError, fatal, Bignum, Object, IO, Thread, ThreadGroup, IRB::Locale, IRB::Notifier::LeveledNotifier, IRB::Notifier::CompositeNotifier, IRB::StdioOutputMethod, IRB::Notifier::NoMsgNotifier, Enumerable::Enumerator, RubyToken::TkNL, RubyToken::TkEND, RubyToken::TkBITOR, RubyToken::TkIDENTIFIER, RubyToken::TkDOT, RubyToken::TkRBRACE, RubyToken::TkSPACE, RubyToken::TkfLBRACE, RubyToken::TkCONSTANT, RubyToken::TkASSIGN, IRB::SLex::Node, IRB::SLex, RubyLex, IRB::ReadlineInputMethod, IRB::WorkSpace, IRB::Context, IRB::Irb]
>> help
=> nil
>> after = ObjectSpace.each_object.map { |i| i.class }.uniq
=> [Regexp, String, MatchData, Array, Class, RI::ClassEntry, RI::MethodEntry, Hash, Module, Dir, Proc, File, Binding, NoMemoryError, Float, SystemStackError, fatal, Bignum, Object, IO, Thread, ThreadGroup, IRB::Locale, Range, IRB::Notifier::LeveledNotifier, IRB::Notifier::CompositeNotifier, IRB::StdioOutputMethod, IRB::Notifier::NoMsgNotifier, YAML::Syck::Resolver, Gem::ConfigFile, RubyToken::TkNL, RubyToken::TkIDENTIFIER, IRB::SLex::Node, IRB::SLex, RubyLex, IRB::ReadlineInputMethod, IRB::WorkSpace, IRB::Context, IRB::Irb, RI::TopLevelEntry, RI::RiReader, GetoptLong, RI::RiCache, RI::Options, RiDriver, Rational, Date::Infinity, Enumerable::Enumerator, RubyToken::TkRBRACE, DefaultDisplay, RI::TextFormatter]
>> after == before
=> false
>> after - before
=> [RI::ClassEntry, RI::MethodEntry, Dir, Range, YAML::Syck::Resolver, Gem::ConfigFile, RI::TopLevelEntry, RI::RiReader, GetoptLong, RI::RiCache, RI::Options, RiDriver, Rational, Date::Infinity, DefaultDisplay, RI::TextFormatter]
It loads the classes in after - before. Where is FileUtils you say? I think its a module that is part of Dir, but I am not 100% on that.
You need to require 'fileutils':
require 'fileutils'
FileUtils.pwd # => "/"
It is not included by the interpreter by default, which is why IRB doesn't preload it. Because IRB is interactive, it has to do some things on-the-fly that the interpreter will not, such as load help files. That it does so in response to your request isn't anything unexpected to me, it's just what it was programmed to do. I'm sure if you looked at its code you'd be able to trace it easily enough.
So, basically, all you are seeing is IRB respond correctly to your syntax error, then do what it was told to do in response to your "help" command.
If you absolutely have to know what it's doing, you can figure it out by asking IRB to trace its processing:
echo help | irb -f --trace > irb.out
will generate a tracing of what IRB does when "help" is entered. Searching through the file shows:
#0:/Users/greg/.rvm/rubies/ruby-1.9.2-p136/lib/ruby/1.9.1/rdoc/ri/store.rb:2::-: require 'fileutils'
being required by store.rb as IRB loads 'ri'.
FileUtils is part of the Ruby standard library, so it is bundled with the interpreter, but not included automatically when the interpreter starts, like Dir and File. It is completely standalone, not a part of Dir.

How do I drop to the IRB prompt from a running script?

Can I drop to an IRB prompt from a running Ruby script?
I want to run a script, but then have it give me an IRB prompt at a point in the program with the current state of the program, but not just by running rdebug and having a breakpoint.
Pry (an IRB alternative) also lets you do this, in fact it was designed from the ground up for exactly this use case :)
It's as easy as putting binding.pry at the point you want to start the session:
require 'pry'
x = 10
binding.pry
And inside the session:
pry(main)> puts x
=> 10
Check out the website: http://pry.github.com
Pry let's you:
drop into a session at any point in your code
view method source code
view method documentation (not using RI so you dont have to pre-generate it)
pop in and out of different contexts
syntax highlighting
gist integration
view and replay history
open editors to edit methods using edit obj.my_method syntax
A tonne more great and original features
you can use ruby-debug to get access to irb
require 'rubygems'
require 'ruby-debug'
x = 23
puts "welcome"
debugger
puts "end"
when program reaches debugger you will get access to irb.
apparently it requires a chunk of code to drop into irb.
Here's the link (seems to work well).
http://jameskilton.com/2009/04/02/embedding-irb-into-your-ruby-application
require 'irb'
module IRB
def self.start_session(binding) # call this method to drop into irb
unless #__initialized
args = ARGV
ARGV.replace(ARGV.dup)
IRB.setup(nil)
ARGV.replace(args)
#__initialized = true
end
workspace = WorkSpace.new(binding)
irb = Irb.new(workspace)
#CONF[:IRB_RC].call(irb.context) if #CONF[:IRB_RC]
#CONF[:MAIN_CONTEXT] = irb.context
catch(:IRB_EXIT) do
irb.eval_input
end
end
end
This feature is available from Ruby 2.4. You can just use binding.irb
E.g.
require 'irb'
a = 10
binding.irb
puts a
If you run above code, you will get irb console, so that you can inspect values of local variables and anything else that is in scope.
Source: http://blog.redpanthers.co/new-binding-irb-introduced-ruby-2-4/
Ruby commit: https://github.com/ruby/ruby/commit/493e48897421d176a8faf0f0820323d79ecdf94a
Just add this line to where you want the breakpoint:
require 'ruby-debug';debugger
but i suggest use pry instead of irb, which is super handy, insert the following line instead:
require 'pry'; binding.pry
I'm quite late to the game but if you're loading a script from within irb/pry already, a simple raise also works to pop you back out to the irb/pry prompt. I use this quite often when writing one off scripts within the rails console.

Resources