require 'tins/xt'
require 'irb/completion'
require 'enumerator'
require 'pp'
require_maybe 'ap'
if Readline.respond_to?(:point) && Readline.respond_to?(:line_buffer)
require 'pry-editline'
end
require 'utils'
require 'drb'
$editor = Utils::Editor.new
$pager = ENV['PAGER'] || 'less -r'
if IRB.conf[:PROMPT]
IRB.conf[:PROMPT][:CUSTOM] = {
:PROMPT_I => ">> ",
:PROMPT_N => ">> ",
:PROMPT_S => "%l> ",
:PROMPT_C => "+> ",
:RETURN => " # => %s\n"
}
IRB.conf[:PROMPT_MODE] = :CUSTOM
end
module Utils
module IRB
module Shell
require 'fileutils'
include FileUtils
include Tins::Find
# Start _ri_ for +pattern+. If +pattern+ is not string like, call it with
# pattern.class.name as argument.
def ri(*patterns)
patterns.map! { |p| p.respond_to?(:to_str) ? p.to_str : p.class.name }
system "ri #{patterns.map { |p| "'#{p}'" } * ' '} | #{$pager}"
end
# Restart this irb.
def irb_restart
exec $0
end
# Start an irb server.
def irb_server(hostname = nil, port = nil)
Utils::IRB::Service.start(hostname, port) {}
end
# Conenct to an irb server.
def irb_connect(hostname = nil, port = nil)
Utils::IRB::Service.connect(hostname, port)
end
# Return all instance methods of obj's class.
def irb_all_class_instance_methods(obj)
methods = obj.class.instance_methods
irb_wrap_methods obj, methods
end
# Return instance methods of obj's class without the inherited/mixed in
# methods.
def irb_class_instance_methods(obj)
methods = obj.class.instance_methods(false)
irb_wrap_methods obj, methods
end
# Return all instance methods defined in module modul.
def irb_all_instance_methods(modul)
methods = modul.instance_methods
irb_wrap_methods modul, methods, true
end
# Return instance methods defined in module modul without the inherited/mixed
# in methods.
def irb_instance_methods(modul)
methods = modul.instance_methods(false)
irb_wrap_methods modul, methods, true
end
# Return all methods of obj (including obj's eigenmethods.)
def irb_all_methods(obj)
methods = obj.methods
irb_wrap_methods obj, methods
end
# Return instance methods of obj's class without the inherited/mixed in
# methods, but including obj's eigenmethods.
def irb_methods(obj)
methods = obj.class.ancestors[1..-1].inject(obj.methods) do |all, a|
all -= a.instance_methods
end
irb_wrap_methods obj, methods
end
# Return all eigen methods of obj.
def irb_eigen_methods(obj)
irb_wrap_methods obj, obj.methods(false)
end
def irb_wrap_methods(obj, methods, modul = false)
methods.map do |name|
MethodWrapper.new(obj, name, modul) rescue nil
end.compact.sort!
end
class WrapperBase
include Comparable
def initialize(name)
@name =
case
when name.respond_to?(:to_str)
name.to_str
when name.respond_to?(:to_sym)
name.to_sym.to_s
else
name.to_s
end
end
attr_reader :name
attr_reader :description
alias to_str description
alias inspect description
alias to_s description
def ==(name)
@name = name
end
alias eql? ==
def hash
@name.hash
end
def <=>(other)
@name <=> other.name
end
end
class MethodWrapper < WrapperBase
def initialize(obj, name, modul)
super(name)
@method = modul ? obj.instance_method(name) : obj.method(name)
@description = "#{owner}##@name(#{arity})"
end
attr_reader :method
def owner
method.respond_to?(:owner) ? method.owner : nil
end
def arity
method.arity
end
def source_location
method.source_location
end
def <=>(other)
@description <=> other.description
end
end
class ConstantWrapper < WrapperBase
def initialize(obj, name)
super(name)
@klass = obj.class
@description = "#@name:#@klass"
end
attr_reader :klass
end
# Return all the constants defined in +modul+.
def irb_constants(modul)
modul.constants.map { |c| ConstantWrapper.new(modul.const_get(c), c) }.sort
end
# Return all the subclasses of +klass+. TODO implement subclasses w/out rails
def irb_subclasses(klass)
klass.subclasses.map { |c| ConstantWrapper.new(eval(c), c) }.sort
end
unless Object.const_defined?(:Infinity)
Infinity = 1.0 / 0 # I like to define the infinite.
end
# Output all kinds of information about +obj+. If detailed is given output
# details about the methods (+ arity) in inheritance chain of +obj+ as well.
# * detailed as 0 output instance methods only of part 0 (the first) of the
# chain.
# * detailed as 1..2 output instance methods of +obj+ inherited from parts 1
# and 2 of the the chain.
def irb_info(obj, detailed = nil)
if Module === obj
modul = obj
klassp = Class === modul
if klassp
begin
allocated = modul.allocate
rescue TypeError
else
obj = allocated
end
end
else
modul = obj.class
end
inspected = obj.inspect
puts "obj = #{inspected.size > 40 ? inspected[0, 40] + '...' : inspected} is of class #{obj.class}."
am = irb_all_methods(obj).size
ms = irb_methods(obj).size
ems = irb_eigen_methods(obj).size
puts "obj: #{am} methods, #{ms} only local#{ems > 0 ? " (#{ems} eigenmethods),": ','} #{am - ms} inherited/mixed in."
acim = irb_all_class_instance_methods(obj).size
cim = irb_class_instance_methods(obj).size
puts "obj: #{acim} instance methods, #{cim} local, #{acim - cim} only inherited/mixed in."
if klassp
s = modul.superclass
puts "Superclass of #{modul}: #{s}"
end
a = []
ec = true
begin
a << (class << obj; self; end)
rescue TypeError
ec = false
end
a.concat modul.ancestors
if ec
puts "Ancestors of #{modul}: (#{a[0]},) #{a[1..-1].map { |k| "#{k}#{k == s ? '*' : ''}" } * ', '}"
else
puts "Ancestors of #{modul}: #{a[0..-1].map { |k| "#{k}#{k == s ? '*' : ''}" } * ', '}"
end
if Class === modul and detailed
if detailed.respond_to? :to_int
detailed = detailed..detailed
end
detailed.each do |i|
break if i >= a.size
k = a[i]
puts "#{k}:"
puts irb_wrap_methods(obj, k.instance_methods(false)).sort
end
end
nil
end
# Output *all* the irb_info about +obj+. You may need to buy a bigger screen for
# this or use:
# less { irb_fullinfo object }
def irb_fullinfo(obj)
irb_info obj, 0..Infinity
end
def capture_output(with_stderr = false)
return "missing block" unless block_given?
require 'tempfile'
begin
old_stdout, $stdout = $stdout, Tempfile.new('irb')
if with_stderr
old_stderr, $stderr = $stderr, $stdout
end
yield
ensure
$stdout, temp = old_stdout, $stdout
with_stderr and $stderr = old_stderr
end
temp.rewind
temp.read
end
# Use pager on the output of the commands given in the block.
def less(with_stderr = false, &block)
IO.popen($pager, 'w') do |f|
f.write capture_output(with_stderr, &block)
f.close_write
end
nil
end
def irb_time
s = Time.now
yield
d = Time.now - s
warn "Took %.3fs seconds." % d
d
end
def irb_time_tap
r = nil
irb_time { r = yield }
r
end
def irb_time_watch(duration = 1)
start = Time.now
pre = nil
loop do
cur = [ yield ].flatten
unless pre
pre = cur.map(&:to_f)
cur = [ yield ].flatten
end
expired = Time.now - start
diffs = cur.zip(pre).map { |c, p| c - p }
rates = diffs.map { |d| d / duration }
warn "#{expired} #{cur.zip(rates, diffs).map(&:inspect) * ' '} # / per sec."
pre = cur.map(&:to_f)
sleep duration
end
end
def irb_write(filename, text = nil)
File.secure_write filename, text, 'wb'
end
def irb_read(filename, chunk_size = 8_192)
if block_given?
File.open(filename) do |file|
until file.eof?
yield file.read(chunk_size)
end
end
else
IO.read filename
end
end
def irb_load!(*files)
files = files.map { |f| f.gsub(/(\.rb)?\Z/, '.rb') }
loaded = {}
for file in files
catch :found do
Find.find('.') do |f|
File.directory?(f) and next
md5_f = Utils::MD5.md5(f)
if f.end_with?(file) and !loaded[md5_f]
Kernel.load f
loaded[md5_f] = true
STDERR.puts "Loaded '#{f}'."
end
end
Find.find('.') do |f|
File.directory?(f) and next
md5_f = Utils::MD5.md5(f)
if f.end_with?(file) and !loaded[md5_f]
Kernel.load f
loaded[md5_f] = true
STDERR.puts "Loaded '#{f}'."
end
end
end
end
nil
end
def irb_edit(*files)
$editor.full?(:edit, *files)
end
# List contents of directory
def ls(*args)
puts `ls #{args.map { |x| "'#{x}'" } * ' '}`
end
if defined?(ActiveRecord::Base)
$logger = Logger.new(STDERR)
def irb_toggle_logging
require 'logger'
if ActiveRecord::Base.logger != $logger
$old_logger = ActiveRecord::Base.logger
ActiveRecord::Base.logger = $logger
true
else
ActiveRecord::Base.logger = $old_logger
false
end
end
end
end
module Module
# Start +ri+ for +module#pattern+, trying to find a method matching +pattern+
# for all modules in the ancestors chain of this module.
def ri(pattern = nil)
if pattern
pattern = pattern.to_sym.to_s if pattern.respond_to? :to_sym
ancestors.each do |a|
if method = a.instance_methods(false).find { |m| pattern === m }
a = Object if a == Kernel # ri seems to be confused
system "ri #{a}##{method} | #{$pager}"
end
end
else
system "ri #{self} | #{$pager}"
end
return
end
end
module Regexp
# Show the match of this Regexp on the +string+.
def show_match(string)
string =~ self ? "#{$`}<<#{$&}>>#{$'}" : "no match"
end
end
module String
# Pipe this string into +cmd+.
def |(cmd)
IO.popen(cmd, 'w+') do |f|
f.write self
f.close_write
return f.read
end
end
# Write this string into file +filename+.
def >>(filename)
File.secure_write(filename, self)
end
end
module Relation
def explain
connection.select_all("EXPLAIN #{to_sql}")
end
end
module Service
class << self
attr_accessor :hostname
attr_accessor :port
def start(hostname = nil, port = nil, &block)
hostname ||= self.hostname
port ||= self.port
block ||= proc {}
uri = "druby://#{hostname}:#{port}"
puts "Starting IRB server listening to #{uri.inspect}."
DRb.start_service(uri, eval('irb_current_working_binding', block.binding))
end
def connect(hostname = nil, port = nil)
hostname ||= self.hostname
port ||= self.port
uri = "druby://#{hostname}:#{port}"
irb = DRbObject.new_with_uri(uri)
Proxy.new(irb)
end
end
self.hostname = 'localhost'
self.port = 6642
class Proxy
def initialize(irb)
@irb = irb
end
def eval(code)
@irb.conf.workspace.evaluate nil, code
end
def load(filename)
unless filename.start_with?('/')
filename = File.expand_path filename
end
@irb.load filename
end
end
end
end
end
module IRB
class Context
def init_save_history
unless (class<<@io;self;end).include?(HistorySavingAbility)
@io.extend(HistorySavingAbility)
end
end
def save_history
IRB.conf[:SAVE_HISTORY]
end
def save_history=(val)
IRB.conf[:SAVE_HISTORY] = val
if val
main_context = IRB.conf[:MAIN_CONTEXT]
main_context = self unless main_context
main_context.init_save_history
end
end
def history_file
IRB.conf[:HISTORY_FILE]
end
def history_file=(hist)
IRB.conf[:HISTORY_FILE] = hist
end
end
module HistorySavingAbility
include Readline
def HistorySavingAbility.create_finalizer
at_exit do
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 |f|
hist = HISTORY.to_a
f.puts(hist[-num..-1] || hist)
end
end
end
end
def HistorySavingAbility.extended(obj)
HistorySavingAbility.create_finalizer
obj.load_history
obj
end
def load_history
hist = IRB.conf[:HISTORY_FILE]
hist = IRB.rc_file("_history") unless hist
if File.exist?(hist)
open(hist) do |f|
f.each {|l| HISTORY << l.chomp}
end
end
end
end
end
IRB.conf[:SAVE_HISTORY] = 1000
if defined?(ActiveRecord::Relation) && !ActiveRecord::Relation.method_defined?(:examine)
class ActiveRecord::Relation
include Utils::IRB::Relation
end
end
class String
include Utils::IRB::String
end
class Object
include Utils::IRB::Shell
end
class Module
include Utils::IRB::Module
end
class Regexp
include Utils::IRB::Regexp
end