Created
April 6, 2012 15:05
-
-
Save Mon-Ouie/2320633 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
# -*- coding: utf-8 -*- | |
require 'colorful_inspect' | |
require 'term/ansicolor' | |
class String | |
include Term::ANSIColor | |
end | |
Pry.print = proc do |output, value| | |
begin | |
output.puts "=> #{value.pretty_inspect.chomp}" | |
rescue Exception => e | |
output.puts "=> (unknown: #{e})" | |
end | |
end | |
prompt = Proc.new do |target_self, nest_level, pry| | |
if pry | |
"pry(#{Pry.view_clip(target_self)}):#{pry.input_array.size}> " | |
else | |
"pry(#{Pry.view_clip(target_self)})> " | |
end | |
end | |
Pry.prompt = [prompt, prompt] | |
Pry.exception_handler = proc do |output, ex| | |
output.puts "#{ex.class.name.red.bold}: #{ex.message.italic}" | |
output.puts " from #{ex.backtrace.first}" | |
end | |
Pry.pager = false | |
Pry.config.auto_indent = false | |
Pry.config.correct_indent = false | |
begin | |
require 'coolline' | |
Pry.config.input = Coolline.new do |cool| | |
cool.word_boundaries = [" ", "\t", ",", ";", ".", '"', "'", "`", "<", ">", | |
"=", ";", "|", "{", "}", "(", ")", "-"] | |
cool.history_file = Coolline::NullFile | |
cool.transform_proc = proc do | |
CodeRay.scan(cool.line, :ruby).term | |
end | |
end | |
rescue LoadError | |
end if ENV["TERM"] != "dumb" | |
# Custom commands | |
require 'slop' | |
module LsHelpers | |
def strip_colors_if_needed(str) | |
Pry.color ? str : strip_color(str) | |
end | |
def pretty(obj) | |
strip_colors_if_needed(obj.pretty_inspect.chomp) | |
end | |
def variables(scope, reg, verbose) | |
var_array = target.eval("#{scope}_variables").grep(reg) | |
if verbose | |
var_hash = {} | |
var_array.each do |name| | |
var_hash[name.to_sym] = target.eval(name.to_s) | |
end | |
var_hash | |
else | |
var_array | |
end | |
end | |
def constant_list(reg, verbose) | |
const_array = target.eval("constants").grep(reg) | |
if verbose | |
const_hash = {} | |
const_array.each do |name| | |
const_hash[name.to_sym] = target.eval("self").const_get(name) | |
end | |
const_hash | |
else | |
const_array | |
end | |
end | |
def method_info(method) | |
args = '' | |
if method.respond_to?(:parameters) && (arg_ary = method.parameters) | |
arg_ary.map!.each_with_index do |(type, name), index| | |
name ||= "arg#{index + 1}" | |
case type | |
when :req then "#{name}" | |
when :opt then "#{name} = ?" | |
when :rest then "*#{name}" | |
when :block then "&#{name}" | |
else name | |
end | |
end | |
args = '(' + arg_ary.join(', ') + ')' | |
elsif method.arity == 0 | |
args = "()" | |
elsif method.arity > 0 | |
n = method.arity | |
args = '(' + (1..n).map { |i| "arg#{i}" }.join(", ") + ')' | |
elsif method.arity < 0 | |
n = -method.arity | |
args = '(' + (1..n).map { |i| "arg#{i}" }.join(", ") + ')' | |
end | |
klass = if method.respond_to? :owner | |
method.owner.name | |
elsif method.inspect =~ /Method: (.*?)#/ | |
$1 | |
end | |
location = if method.respond_to? :source_location | |
file, line = method.source_location | |
"#{file}:#{line}" if file && line | |
end | |
[method.name.to_s, args, klass.to_s, location] | |
end | |
def print_method_list(output, methods, regexp, more, verbose, &block) | |
methods -= Object.instance_methods unless more | |
methods = methods.grep(regexp) | |
data = methods.sort.map do |name| | |
method_info(yield name) | |
end | |
max_name = data.map { |item| item[0].size }.max | |
max_args = data.map { |item| item[1].size }.max | |
max_klass = data.map { |item| item[2].size }.max | |
data.each do |(name, args, klass, location)| | |
str = " #{yellow(name.rjust(max_name))}" | |
str << args.ljust(max_args).blue | |
str << " #{gray(klass.ljust(max_klass))}" | |
str << " (#{location})" if verbose && location | |
output.puts str | |
end | |
end | |
def italic(string) | |
Pry.color ? "\033[#{3}m#{string}\033[0m" : string | |
end | |
def yellow(string) | |
Pry.color ? "\033[1;#{33}m#{string}\033[0m" : string | |
end | |
def gray(string) | |
Pry.color ? "\033[1;#{37}m#{string}\033[0m" : string | |
end | |
end | |
module YriHelpers | |
def wrap_text(text, columns = 80) | |
text = text.dup | |
res = [] | |
while text.length > columns | |
if text[0, columns] =~ /^(.+\s)(\S+)$/ | |
res << $1 | |
text = $2 + text[columns..-1] | |
else | |
res << text[0, columns] | |
text[0...columns] = '' | |
end | |
end | |
res << text | |
res | |
end | |
def signature_for(info) | |
sig = "#{info.name.to_s.cyan}(" + info.parameters.map { |(param, default)| | |
if default | |
"#{param} = #{default}" | |
else | |
param | |
end | |
}.join(", ") + ")" | |
if yield_tag = info.tag("yield") | |
args = yield_tag.types ? yield_tag.types.join(", ") : "" | |
args = "|#{args}| " unless args.empty? | |
sig << " { #{args}... }" | |
end | |
type = (tag = info.tag("return")) ? tag.type : "Object" | |
sig << " # => #{type.yellow}" | |
end | |
def format_parameter(param) | |
types = if param.types | |
param.types.map { |o| o.yellow }.join(', ') | |
else | |
"Object" | |
end | |
default = if param.respond_to? :defaults and param.defaults | |
" (default: #{param.defaults.join(", ")})" | |
end | |
text = (param.text || "").gsub("\n", "\n" + " " * 6) | |
" — (#{types}) #{param.name.bold}#{default} #{text}" | |
end | |
def document_info(info, output) | |
doc = info.docstring.split("\n") | |
doc.each do |line| | |
if line[0, 2] == " " * 2 | |
output.puts CodeRay.scan(line, :ruby).term | |
else | |
output.puts line | |
end | |
end | |
if deprecated = info.tag("deprecated") | |
output.puts | |
output.puts "#{'DEPRECATED:'.red.bold} #{deprecated.text}" | |
end | |
if note = info.tag("note") | |
output.puts | |
output.puts "#{'NOTE:'.red.bold} #{note.text}" | |
end | |
if abstract = info.tag("abstract") | |
output.puts | |
output.puts "#{'Abstract:'.bold} #{abstract.text}" | |
end | |
unless info.tags("param").empty? | |
output.puts | |
output.puts "Parameters: ".italic | |
info.tags("param").each do |param| | |
output.puts format_parameter(param) | |
end | |
end | |
unless info.tags("option").empty? | |
info.tags("option").group_by(&:name).each do |name, opts| | |
output.puts | |
output.puts "Options for #{name.bold}: ".italic | |
opts.each do |opt| | |
output.puts format_parameter(opt.pair) | |
end | |
end | |
end | |
if yield_tag = info.tag("yield") | |
output.puts | |
output.print "#{'Yields:'.bold.italic} " | |
output.puts yield_tag.text.to_s.gsub("\n", "\n ") | |
unless info.tags("yieldparam").empty? | |
output.puts | |
output.puts "Block arguments: ".bold.italic | |
info.tags("yieldparam").each do |param| | |
output.puts format_parameter(param) | |
end | |
end | |
if ret = info.tag("yieldreturn") | |
output.print "Block returns: ".bold.italic | |
output.print "(#{(ret.types || %w[Object]).join(', ')}) " | |
output.puts ret.text.gsub("\n", "\n ") | |
end | |
end | |
unless info.tags("raise").empty? | |
output.puts | |
output.puts "Exceptions: ".bold | |
info.tags("raise").each do |tag| | |
output.print " — #{(tag.types || %w[Object]).join(', ')}: ".italic | |
output.puts tag.text | |
end | |
end | |
if ret = info.tag("return") | |
output.print "Returns: ".bold.italic | |
output.print "(#{(ret.types || %w[Object]).join(', ')}) " | |
output.puts ret.text.to_s.gsub("\n", "\n ") | |
end | |
unless info.tags("example").empty? | |
info.tags("example").each do |ex| | |
output.puts | |
output.puts "Example: #{ex.name.bold}:".italic | |
code = " " + CodeRay.scan(ex.text, :ruby).term.gsub("\n", "\n ") | |
output.puts code | |
end | |
end | |
unless info.tags("see").empty? | |
output.puts | |
output.puts "See also: ".bold | |
info.tags("see").each do |tag| | |
output.puts " — #{tag.text}" | |
end | |
end | |
if author = info.tag("author") | |
output.puts | |
output.puts "#{'Author:'.bold} #{author.text}" | |
end | |
end | |
end | |
Commands = Pry::CommandSet.new Pry::Commands do | |
helpers do | |
include YriHelpers | |
include LsHelpers | |
end | |
# Dependency check broken? | |
# alias_command "gist", "gist-method" | |
# Confusing me | |
alias_command "exit", "exit-all" | |
command "req" do |*args| | |
args.each { |arg| require arg } | |
end | |
command "ls", "List a lot of stuff" do |*args| | |
show_help = args.empty? | |
opts = Slop.parse! args, :multiple_switches => true do | |
on :v, :verbose, "Show value of variables and constants and locations of methods" | |
on :L, :less, "Only show methods set by the receiver" | |
on :a, :more, "Show all of the methods, including those defined in Object" | |
on :f, :filter, "Regular expression to filter methods and variables", | |
:optional => false, :default => "" | |
on :l, :locals, "Show local variables" | |
on :g, :globals, "Show global variables" | |
on :i, 'instance-variables', "Show instance variables" | |
on :k, 'class-variables', "Show class variables" | |
on :c, :constants, "Show constants" | |
on :m, :methods, "Show methods" | |
on :M, 'instance-methods', "Show instance methods" | |
on :h, :help, 'Print this help message', :tail => true | |
end | |
if show_help || opts.help? | |
puts opts.help | |
next | |
end | |
opts = opts.to_hash(true) | |
regexp = Regexp.new(opts[:filter], 'i') | |
unless args.empty? | |
self.target = Pry.binding_for(target.eval(args.join(' '))) | |
end | |
if opts[:locals] | |
output.print italic("Local variables: ") | |
output.puts pretty(variables(:local, regexp, opts[:verbose])) | |
output.puts | |
end | |
if opts[:globals] | |
output.print italic("Global variables: ") | |
output.puts pretty(variables(:global, regexp, opts[:verbose])) | |
output.puts | |
end | |
if opts[:"instance-variables"] | |
output.print italic("Instance variables: ") | |
output.puts pretty(variables(:instance, regexp, opts[:verbose])) | |
output.puts | |
end | |
if opts[:"class-variables"] | |
output.print italic("Class variables: ") | |
if Module === target.eval('self') | |
output.puts pretty(variables(:class, regexp, opts[:verbose])) | |
else | |
output.puts "(not a module)" | |
end | |
output.puts | |
end | |
if opts[:constants] | |
output.print italic("Constant: ") | |
if Module === target.eval('self') | |
output.puts pretty(constant_list(regexp, opts[:verbose])) | |
else | |
output.puts "(not a module)" | |
end | |
output.puts | |
end | |
if opts[:"instance-methods"] | |
output.print italic("Instance methods: ") | |
if Module === target.eval('self') | |
obj = target.eval("self") | |
output.puts | |
print_method_list(output, obj.instance_methods(!opts[:less]), | |
regexp, opts[:more], opts[:verbose]) do |name| | |
obj.instance_method(name) | |
end | |
else | |
output.puts "(not a module)" | |
end | |
output.puts | |
end | |
if opts[:methods] | |
output.puts italic("Methods: ") | |
obj = target.eval("self") | |
print_method_list(output, obj.methods(!opts[:less]), | |
regexp, opts[:more], opts[:verbose]) do |name| | |
obj.method(name) | |
end | |
output.puts | |
end | |
end | |
command "yri", "Retrieve documentation from YARD" do |*args| | |
opts = Slop.parse! args do | |
on :m, "Use methods" | |
on :M, "Use instance methods" | |
on :s, :source, "Show source code" | |
end | |
method_name = args.shift | |
method = get_method_or_raise(method_name, target, opts.to_hash(true)) | |
unless method | |
output.puts "Could not find the method!" | |
next | |
end | |
info = Pry::MethodInfo.info_for(method) | |
unless info | |
output.puts "Could not find the documentation!" | |
next | |
end | |
output.puts "-" * 80 | |
if (overloads = info.tags("overload")).size > 0 | |
overloads.each do |overload| | |
output.puts wrap_text(signature_for(overload)) | |
end | |
else | |
output.puts wrap_text(signature_for(info)) | |
end | |
output.puts "-" * 80 | |
document_info(info, output) | |
info.tags("overload").each do |overload| | |
output.puts | |
output.puts "-" * 80 | |
output.puts "#{"Overload:".bold.italic} #{signature_for(overload)}" | |
output.puts "-" * 80 | |
document_info(overload, output) | |
end | |
if opts.source? | |
output.puts | |
output.puts "Source code: ".bold.italic | |
source = CodeRay.scan(info.source.to_s, info.source_type).term | |
output.puts " " + source.gsub("\n", "\n ") | |
end | |
end | |
alias_command "ri", "yri" | |
alias_command "?", "show-input" | |
end | |
Pry.commands = Commands | |
# Apeiros' benchmarking methods | |
# tiny bench method | |
def bench(n=100, runs=10) | |
n = n.to_i | |
t = [] | |
runs.times do | |
a = Time.now | |
for _ in 1..n | |
yield | |
end | |
t << (Time.now-a)*1000/n | |
end | |
mean = t.inject { |a,b| a+b }.quo(t.size) | |
stddev = t.map { |a| (a-mean)**2 }.inject { |a,b| a+b }.quo(t.size)**0.5 | |
[mean, stddev] | |
end | |
# tiny bench method with nice printing | |
def pbench(n=1, runs=5, &b) | |
m, s = *bench(n,runs,&b) | |
p = (100.0*s)/m | |
printf "ø %fms (%.1f%%)\n", m, p | |
end | |
# tiny bench method with nice printing, | |
# runs multiple tests | |
def mbench(n, runs, benches) | |
label_width = benches.keys.max_by(&:length).length+1 | |
measures = [] | |
benches.each do |label, b| | |
m, s = *bench(n,runs,&b) | |
p = (100.0*s)/m | |
measures << [label, m] | |
printf "%-*s ø %fms (%.1f%%)\n", label_width, "#{label}:", m, p | |
end | |
measures.sort_by! { |l,m| m } | |
rel = measures.first.last | |
puts measures.map { |l,m| sprintf "%s: %.1f", l, m/rel }.join(', ') | |
nil | |
end | |
# Require current project | |
$: << File.expand_path('.') | |
if File.directory? "lib" | |
$:.unshift File.expand_path("./lib") | |
$:.unshift File.expand_path("./ext") if File.directory? "ext" | |
begin | |
require File.basename(Dir.pwd) | |
rescue LoadError | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment