From: Brian Mitchell Date: 2006-10-13T00:31:52+09:00 Subject: Re: My .irbrc for console/irb On 10/12/06, Dr Nic wrote: > I recently discovered that I can create a .irbrc file to run setup for > my irb/console. I am in love. > > My current .irbrc is: > > require 'irb/completion' > require 'map_by_method' > require 'what_methods' > require 'pp' > IRB.conf[:AUTO_INDENT]=true > > Explanation of the different libraries: > http://drnicwilliams.com/2006/10/12/my-irbrc-for-consoleirb/ > > Does anyone any interesting things in their .irbrc file? > Mine is rather large and in flux right now but I will post a few nuggets from it bellow: require 'rubygems' # Very basic prelude of enhancements for Object module ObjectEnhancer def clone! self.clone rescue self end def tap yield self self end def _ yield if block_given? nil end end class Object include ObjectEnhancer end # Lazy loading prety print support class Object def pp(*a, &b) # pass the block just-in-case this changes. require 'pp' pp(*a, &b) end end # Tab completion require 'irb/completion' IRB.conf[:USE_READLINE] = true # Histories require 'irb/ext/save-history' IRB.conf[:SAVE_HISTORY] = 1000 IRB.conf[:EVAL_HISTORY] = 100 # Prompts IRB.conf[:PROMPT][:CUSTOM] = { :PROMPT_N => ">> ", :PROMPT_I => ">> ", :PROMPT_S => nil, :PROMPT_C => " > ", :RETURN => "=> %s\n" } # Set default prompt IRB.conf[:PROMPT_MODE] = :CUSTOM # Simple ri integration def ri(*names) system("ri #{names.map {|name| name.to_s}.join(" ")}") end # fresh irb. It uses an at_exit handler to yield it a block is given. def reset_irb at_exit {exec($0)} # first registered is last to run at_exit {yield if block_given?} # From finalizer code in irb/ext/save-history.rb.. very ugly way to do it :S.. who wants to rewrite irb? if num = IRB.conf[:SAVE_HISTORY] and (num = num.to_i) > 0 if hf = IRB.conf[:HISTORY_FILE] file = File.expand_path(hf) end file = IRB.rc_file("_history") unless file open(file, 'w') do |file| hist = IRB::HistorySavingAbility::HISTORY.to_a file.puts(hist[-num..-1] || hist) end end # Make irb give us a clean exit (up until our at_exit handler above) throw :IRB_EXIT, 0 end # clear the screen.. with some self destruction ;-) def clear eval "def clear; print #{`clear`.inspect} end" clear end private :clear # Simple webserver (Loazy loading) def serve_files(opts = {}) require 'webrick' opts[:host] ||= Socket.gethostname opts[:dir] ||= Dir.pwd opts[:port] ||= opts[:dir].hash % 1000 + 10000 opts[:log] ||= Nop.new # hidden and simple. server = WEBrick::HTTPServer.new( :Host => opts[:host], :Port => opts[:port], :DocumentRoot => opts[:dir], :Logger => opts[:log] ) trap("INT") {server.shutdown} puts "Serving \"#{opts[:dir]}\" at http://#{opts[:host]}:#{opts[:port]}/" server.start nil rescue puts "Failed to start server! See $webrick_error for the exception." $webrick_error = $! nil end private :serve_files # SSH support. Needs a lot of work still but it is nice to have. # This was just a 5 min hack. Thanks goes to Jamis for the # nice library. # Note that you must sleep to have the event loop run. def ssh_session(opts = {}) puts "Note: You must 'sleep' in order for the event loop to run in irb." if require 'net/ssh' dynamic_session_class = Class.new do @@default_opts = { :user => ENV['USER'] || ENV['USERNAME'], :port => 22 }.freeze def initialize(opts = {}, aux = {}) opts, opts[:host] = aux, opts unless Hash === opts opts = aux.merge opts opts = @@default_opts.merge opts @shutdown = false @queue = [] ready = false Thread.new { begin Net::SSH.start(opts[:host], :username => opts[:user], :password => opts[:password], :port => opts[:port] ) do |session| ready = true loop { break if self.shutdown? self.process(session) session.loop sleep 0.01 } end rescue puts "Failed while running ssh session! See $ssh_error for the exception." $ssh_error = $! ensure ready = true end } sleep 0 until ready end def shutdown? @shutdown end def shutdown @shutdown = true end def execute(&blk) raise "Session shutdown" if shutdown? @queue << blk nil end def process(session) while proc = @queue.pop proc.call(session) end end def forward_local(port, host, aux_port = port) execute {|session| session.forward.local('0.0.0.0', port, host, aux_port) } end alias outgoing forward_local def forward_remote(port, host, aux_port = port) execute {|session| session.forward.remote_to(port, host, aux_port) } end def shell require 'termios' puts "Note: You will need to interrupt 'sleep' when your shell is done (usually ^C)." execute {|session| stdin_buffer = lambda do |enable| attrs = Termios::getattr($stdin) if enable attrs.c_lflag |= Termios::ICANON | Termios::ECHO else attrs.c_lflag &= ~(Termios::ICANON | Termios::ECHO) end Termios::setattr($stdin, Termios::TCSANOW, attrs) end begin stdin_buffer[false] shell = session.shell.open(:pty => true) loop do break unless shell.open? if IO.select([$stdin],nil,nil,0.01) data = $stdin.sysread(1) shell.send_data data end $stdout.print shell.stdout while shell.stdout? $stdout.flush end ensure stdin_buffer[true] end } sleep end alias incoming forward_remote end Object.const_set('DynamicSSHSession', dynamic_session_class) unless Object.constants.include? 'DynamicSSHSession' dynamic_session_class.new(opts) rescue puts "Failed to create an ssh session! See $ssh_error for the exception." $ssh_error = $! end private :ssh_session # Like haskell's sequence. Really nice to have but recursive. # I should change this it an iterative solution sometime. # Recursion is usually not a problem for realistic inputs. class Array def sequence(i = 0, *a) return [a] if i == size self[i].map {|x| sequence(i+1, *(a + [x])) }.inject([]) {|m, x| m + x} end end class Symbol def to_proc lambda {|*args| args.shift.__send__(self, *args)} end end # Modulized blank slate. Only removes current not future # methods for simplicities sake. module Blank def self.append_features(base) base.module_eval { instance_methods.each {|m| undef_method m unless m =~ /^__/} } end end # This is mostly a toy but it has been useful in a few cases # where I needed to slowly build up a proc inside multiple # calls class It < Proc instance_methods.each {|m| undef_method m unless m =~ /^__/} def method_missing(*args, &blk) It.new {|x| Proc.instance_method(:call).bind(self).call(x).send(*args, &blk)} end end def it if block_given? It.new else It.new {|x| x} end end private :it That is about half of it. I've got more stuff like gray number stuf, unbound method extensions, and some new meta-programming stuff but it all needs a little more work first. I should also note that I wrap my irbrc file with a begin rescue end. The rescue just prints the presence of an error and then shoves $! into $irbrc_error. This is nice for occasions when you might be trying out new 1.9 builds or use rails (they don't correctly initialize IRB so it causes failed loads of irbrc -- keeps the output a little cleaner). I will probably do another clean-up before RubyConf so we can all share .irbrc's ;-). I'll probably end up removing more things then I add (i.e. I really don't use Symbol#to_proc that much). One last thing, I was wondering if anyone would be interested in a series of gems that act as automatic irb plugins? it might be fun to gemify some of these things. Brian.