why does pry history keep cloberring irb history? - ruby

When I'm using pry, the history of pry keeps overwriting my irb history and it is very annoying. If i'm using pry, i'd like to see pry history and if i'm using irb, i'd like to see irb history. Is there an obvious problem in my configuration?
~/.irbrc looks like
IRB.conf[:AUTO_INDENT] = true
IRB.conf[:USE_READLINE] = true
IRB.conf[:LOAD_MODULES] = [] unless IRB.conf.key?(:LOAD_MODULES)
unless IRB.conf[:LOAD_MODULES].include?('irb/completion')
IRB.conf[:LOAD_MODULES] << 'irb/completion'
end
IRB.conf[:SAVE_HISTORY] = 1000
my .pryrc file is empty, which based on docs indicates to me that pry should be using .pry_history, which appears to be happening.
/etc/irbrc looks like
# Some default enhancements/settings for IRB, based on
# http://wiki.rubygarden.org/Ruby/page/show/Irb/TipsAndTricks
unless defined? ETC_IRBRC_LOADED
# Require RubyGems by default.
require 'rubygems'
# Activate auto-completion.
require 'irb/completion'
# Use the simple prompt if possible.
IRB.conf[:PROMPT_MODE] = :SIMPLE if IRB.conf[:PROMPT_MODE] == :DEFAULT
# Setup permanent history.
HISTFILE = "~/.irb_history"
MAXHISTSIZE = 100
begin
histfile = File::expand_path(HISTFILE)
if File::exists?(histfile)
lines = IO::readlines(histfile).collect { |line| line.chomp }
puts "Read #{lines.nitems} saved history commands from '#{histfile}'." if $VERBOSE
Readline::HISTORY.push(*lines)
else
puts "History file '#{histfile}' was empty or non-existant." if $VERBOSE
end
Kernel::at_exit do
lines = Readline::HISTORY.to_a.reverse.uniq.reverse
lines = lines[-MAXHISTSIZE, MAXHISTSIZE] if lines.nitems > MAXHISTSIZE
puts "Saving #{lines.length} history lines to '#{histfile}'." if $VERBOSE
File::open(histfile, File::WRONLY|File::CREAT|File::TRUNC) { |io| io.puts lines.join("\n") }
end
rescue => e
puts "Error when configuring permanent history: #{e}" if $VERBOSE
end
ETC_IRBRC_LOADED=true
end

Why the problem happens
Both Pry and IRB write their histories to Readline::HISTORY. When you enter Pry from IRB (or rails console) you have all your IRB history already in Readline::HISTORY. Pry then loads its history on top of that. When you exit Pry Readline::HISTORY isn't changed so you end up back in IRB with all of Pry's history appending to IRB's history. Finally, when you exit IRB it writes the history including Pry's to IRB's history file thus clobbering it.
Current state of the issue
I did some searching and found this is a known issue with Pry. I was also really interested in the problem so I managed to work up a solution that is awaiting a pull request review.
Workaround until merged
If you'd like to try it out before it get's merged you can grab the version from the branch I made on my fork of Pry. You can find it at https://github.com/agrberg/pry under branch save_irb_history_and_replace_on_close. If you use bundler all you need to do is add or update your line for pry to:
gem 'pry', git: 'git#github.com:agrberg/pry.git', branch: 'save_irb_history_and_replace_on_close'

Related

Customising IRB console for gem

I'd like to extend the default console application that is built as standard with bundle gem by applying some of the IRB config options.
Looking at the documentation, I can see that it should be possible for instance to change the prompt, and this works fine on an interactive session. For example I can play with the displayed prompt like this:
2.1.4 :001 > conf.prompt_mode=:SIMPLE
=> :SIMPLE
>>
?> conf.prompt_mode=:DEFAULT
=> :DEFAULT
irb(main):004:0>
However, I cannot find how to translate this into syntax for use in the console app. For example this script:
require 'irb'
IRB.conf[:PROMPT_MODE] = :SIMPLE
IRB.start
Just starts with the generic configured prompt:
2.1.4 :001 >
I have spent some time trying to find an example use of IRB for a custom repl without loading global defaults, but not found anything I can copy from.
I can see that the undocumented method IRB.setup has something to do with this, it is setting all the config somehow. Is my only option to write my own version of IRB.start that applies my desired config after calling IRB.setup, or is there support for what I want to do built-in but not documented in standard location?
E.g. the following works, but I feel it's a bit heavy handed extending IRB module this way (and also prone to failing if IRB internals change).
require 'irb'
def IRB.custom_start custom_conf = {}
STDOUT.sync = true
IRB.setup(nil)
custom_conf.each do |k,v|
IRB.conf[k] = v
end
if #CONF[:SCRIPT]
irb = IRB::Irb.new(nil, #CONF[:SCRIPT])
else
irb = IRB::Irb.new
end
#CONF[:IRB_RC].call(irb.context) if #CONF[:IRB_RC]
#CONF[:MAIN_CONTEXT] = irb.context
trap("SIGINT") do
irb.signal_handle
end
begin
catch(:IRB_EXIT) do
irb.eval_input
end
ensure
irb_at_exit
end
end
IRB.custom_start :PROMPT_MODE => :SIMPLE
You can apply custom configurations in two ways.
The first one is to use irbrc file. It may be tricky in building console application (calling IRB.start from the ruby file instead of irb from the console).
The second one is the approach that you have described in the post. You can write your own IRB::start method based on the original one. There are exactly the same potential issues as in using undocumented API - it can break in the future with newer versions of irb.
You should think if you really need to build a console application on the top of irb. For example you can solve this problem using Pry. It allows to define configuration before starting interactive session.
require 'irb'
IRB.conf[:PROMPT_MODE] = :SIMPLE
IRB.start
The approach above doesn't work because conf[:PROMPT_MODE] gets over-riden in a method called IRB.init_config here
When IRB.start is called, it calls IRB.setup which in turn calls the method IRB.init_config -- which over-rides conf[:PROMPT_MODE] setting.
Here is one approach which solves the problem (relies on internal knowledge of the implementation).
require 'irb'
module IRB
singleton_class.send(:alias_method, :old_setup, :setup)
def IRB.setup(ap_path)
IRB.old_setup(ap_path)
conf[:PROMPT_MODE] = :SIMPLE
end
end
IRB.start

locating "irbrc" file on a Mac

I see a lot of cool stuff I can add to my Ruby console. For example, a good list is
"My .irbrc for console/irb".
I googled, but all I found is weblogs saying what gems people add to their .irbrc. No one is saying where to find it.
I cannot find "irbrc".
I opened my home folder and, if I type IRB, it goes to the Ruby console, but I can't find this file.
Can someone help me locate it?
It's a irbrc dotfile so you will need to ls -a in your home directory to find it. If it isn't in there, simply create a .irbrc file.
Mine's pretty simple but this is what I have in it:
require 'rubygems'
require 'ap'
require 'irb/completion'
ARGV.concat [ "--readline", "--prompt-mode", "simple" ]
module Readline
module History
LOG = "#{ENV['HOME']}/.irb-history"
def self.write_log(line)
File.open(LOG, 'ab') {|f| f << "#{line}\n"}
end
def self.start_session_log
write_log("\n# session start: #{Time.now}\n\n")
at_exit { write_log("\n# session stop: #{Time.now}\n") }
end
end
alias :old_readline :readline
def readline(*args)
ln = old_readline(*args)
begin
History.write_log(ln)
rescue
end
ln
end
end
IRB::Irb.class_eval do
def output_value
ap #context.last_value
end
end
Readline::History.start_session_log
require 'irb/ext/save-history'
IRB.conf[:SAVE_HISTORY] = 100
IRB.conf[:HISTORY_FILE] = "#{ENV['HOME']}/.irb-save-history"
IRB.conf[:PROMPT_MODE] = :SIMPLE
require 'irb/completion'
If you are unable to find the file.irbrc in your home directory, simply create it in your home directory and fill it with some lines such as:
require "irb/completion"
Then your irb will automatically load completion module when you launch irb.
PS: it also works for UNIX/Linux system.

Reload rubygems in irb?

I've this script right now.
def r(this)
require this
puts "#{this} is now loaded."
rescue LoadError
puts "The gem '#{this}' is missing."
puts "Should I install it? [y/n]"
data = gets
if data =~ /yes|y/i
puts "Installing #{this}, hold on."
if `gem install #{this}` =~ /Successfully/i
load this
end
else
puts "Okey, goodbye."
end
end
That makes it possible to require libs on the fly.
Like this: r "haml".
The problem is that I can't load the gem after it has been installed.
Using load this or load File.expand_path("~/.irbrc") does not work.
Here is an example.
>> r "absolutize"
The gem 'absolutize' is missing.
Should I install it? [y/n]
y
Installing absolutize, hold on
LoadError: no such file to load -- absolutize
>> require "absolutize"
LoadError: no such file to load -- absolutize
>> exit
$ irb
>> require "absolutize"
=> true
Is there a way to reload rubygems or irb on the fly?
I did not try, but I think you might be looking for Gem.clear_paths
Reset the dir and path values. The next time dir or path is requested, the values will be calculated from scratch. This is mainly used by the unit tests to provide test isolation.
You can reset irb by calling exec('irb')
Just remove the file from ´$"´:
require 'erb' # Loaded.
require 'erb' # Does nothing.
$".delete_if {|e| e =~ /erb\.(?:rb|so)/} # Remove "erb" from loaded libraies.
require 'erb' # Reloaded (with warnings if the first require was successful).
See http://www.zenspider.com/Languages/Ruby/QuickRef.html#19

irb history not working

in ~/.irbrc i have these lines:
require 'irb/ext/save-history'
#History configuration
IRB.conf[:SAVE_HISTORY] = 100
IRB.conf[:HISTORY_FILE] = "#{ENV['HOME']}/.irb-save-history"
and yet when i run irb and hit the up arrow nothing happens. also the irb history file specified is not getting created and nothing is logged to it.
irb history works in Debian Linux out of the box. There's no etc/irbrc, nor do I have a ~/.irbrc. So, hmmmm.
This person put a bit more in his irbrc than you did. Do you suppose the ARGV.concat could be the missing piece?
require 'irb/completion'
require 'irb/ext/save-history'
ARGV.concat [ "--readline", "--prompt-mode", "simple" ]
IRB.conf[:SAVE_HISTORY] = 100
IRB.conf[:HISTORY_FILE] = "#{ENV['HOME']}/.irb-save-history"
I don't have an answer for you why the above doesn't work, but I did find a file, /etc/irbrc on my system (OS X - Snow Leopard, Ruby 1.8.7) that does provide a working, persistent history for me. So two pieces of advice: i) check your /etc/irbrc (or equivalent) to make sure that there isn't anything in there that might interfere with your settings, and ii) try out the settings below to see if you can get history working that way.
# Some default enhancements/settings for IRB, based on
# http://wiki.rubygarden.org/Ruby/page/show/Irb/TipsAndTricks
unless defined? ETC_IRBRC_LOADED
# Require RubyGems by default.
require 'rubygems'
# Activate auto-completion.
require 'irb/completion'
# Use the simple prompt if possible.
IRB.conf[:PROMPT_MODE] = :SIMPLE if IRB.conf[:PROMPT_MODE] == :DEFAULT
# Setup permanent history.
HISTFILE = "~/.irb_history"
MAXHISTSIZE = 100
begin
histfile = File::expand_path(HISTFILE)
if File::exists?(histfile)
lines = IO::readlines(histfile).collect { |line| line.chomp }
puts "Read #{lines.nitems} saved history commands from '#{histfile}'." if $VERBOSE
Readline::HISTORY.push(*lines)
else
puts "History file '#{histfile}' was empty or non-existant." if $VERBOSE
end
Kernel::at_exit do
lines = Readline::HISTORY.to_a.reverse.uniq.reverse
lines = lines[-MAXHISTSIZE, MAXHISTSIZE] if lines.nitems > MAXHISTSIZE
puts "Saving #{lines.length} history lines to '#{histfile}'." if $VERBOSE
File::open(histfile, File::WRONLY|File::CREAT|File::TRUNC) { |io| io.puts lines.join("\n") }
end
rescue => e
puts "Error when configuring permanent history: #{e}" if $VERBOSE
end
ETC_IRBRC_LOADED=true
end
This is a known bug with a patch available. Easiest solution is to overwrite save-history.rb:
/usr/lib/ruby/1.8/irb/ext/save-history.rb
with a fixed version:
http://pastie.org/513500
or to do it in one go:
wget -O /usr/lib/ruby/1.8/irb/ext/save-history.rb http://pastie.org/pastes/513500/download
Check to make sure you built ruby with libreadline as irb history seems to not work without it.
This may also happen if you have extra irb config file, e.g. ~/.irbrc. If this is the case, copy the content from liwp's answer to the extra config and it should work.
I had the same problem on ubuntu 20.04 and fixed it by running:
gem install irb

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