1
0
Fork 0
mirror of https://github.com/awesome-print/awesome_print synced 2023-03-27 23:22:34 -04:00

Display object.methods in human readable format

This commit is contained in:
Mike Dvorkin 2010-10-31 15:02:12 -07:00
parent 14e4fd5e7d
commit 5f78c06a05
5 changed files with 156 additions and 11 deletions

View file

@ -4,6 +4,8 @@
# See LICENSE file or http://www.opensource.org/licenses/mit-license.php
#------------------------------------------------------------------------------
require File.dirname(__FILE__) + "/ap/core_ext/string"
require File.dirname(__FILE__) + "/ap/core_ext/object"
require File.dirname(__FILE__) + "/ap/core_ext/class"
require File.dirname(__FILE__) + "/ap/core_ext/kernel"
require File.dirname(__FILE__) + "/ap/awesome_print"

View file

@ -7,7 +7,7 @@ require "shellwords"
class AwesomePrint
AP = :__awesome_print__
CORE = [ :array, :hash, :class, :file, :dir, :bigdecimal, :rational, :struct, :method ]
CORE = [ :array, :hash, :class, :file, :dir, :bigdecimal, :rational, :struct, :method, :unboundmethod ]
def initialize(options = {})
@options = {
@ -50,7 +50,9 @@ class AwesomePrint
def awesome_array(a)
return "[]" if a == []
if @options[:multiline]
if a.instance_variable_defined?('@__awesome_methods__')
methods_array(a)
elsif @options[:multiline]
width = (a.size - 1).to_s.size
data = a.inject([]) do |arr, item|
index = if @options[:index]
@ -143,8 +145,9 @@ class AwesomePrint
#------------------------------------------------------------------------------
def awesome_method(m)
name, args, owner = method_tuple(m)
"#{colorize(owner, :class)}##{colorize(name, :method)}(#{colorize(args, :args)})"
"#{colorize(owner, :class)}##{colorize(name, :method)}#{colorize(args, :args)}"
end
alias :awesome_unboundmethod :awesome_method
# Catch all method to format an arbitrary object.
#------------------------------------------------------------------------------
@ -167,6 +170,38 @@ class AwesomePrint
end
end
# Format object.methods array.
#------------------------------------------------------------------------------
def methods_array(a)
object = a.instance_variable_get('@__awesome_methods__')
tuples = a.map do |name|
if object.respond_to?(name, true) # Regular method?
method_tuple(object.method(name))
elsif object.respond_to?(:instance_method) # Unbound method?
method_tuple(object.instance_method(name))
else # WTF method.
[ name.to_s, '(?)', '' ]
end
end
width = (tuples.size - 1).to_s.size
name_width = tuples.map { |item| item[0].size }.max || 0
args_width = tuples.map { |item| item[1].size }.max || 0
data = tuples.inject([]) do |arr, item|
index = if @options[:index]
"#{indent}[#{arr.size.to_s.rjust(width)}]"
else
indent
end
indented do
arr << "#{index} #{colorize(item[0].rjust(name_width), :method)}#{colorize(item[1].ljust(args_width), :args)} #{colorize(item[2], :class)}"
end
end
"[\n" << data.join("\n") << "\n#{outdent}]"
end
# Format nested data, for example:
# arr = [1, 2]; arr << arr
# => [1,2, [...]]
@ -208,14 +243,16 @@ class AwesomePrint
end
end
# Return [ name, argument, owner ] tuple for a given method.
# Return [ name, arguments, owner ] tuple for a given method.
#------------------------------------------------------------------------------
def method_tuple(method)
args = method.arity.abs.times.map { |i| "arg#{i+1}" }.join(', ')
args << ', ...' if method.arity < 0
owner = $1.sub('(', ' (') if method.to_s =~ /Method: (.*?)#/
if method.to_s =~ /(Unbound)*Method: (.*?)[#\.]/
owner = "#{$2}#{$1 ? '(unbound)' : ''}".gsub('(', ' (')
end
[ method.name, args, owner || 'self' ]
[ method.name, "(#{args})", owner.to_s ]
end
# Format hash keys as plain string regardless of underlying data type.

15
lib/ap/core_ext/class.rb Normal file
View file

@ -0,0 +1,15 @@
# Copyright (c) 2010 Michael Dvorkin
#
# Awesome Print is freely distributable under the terms of MIT license.
# See LICENSE file or http://www.opensource.org/licenses/mit-license.php
#------------------------------------------------------------------------------
class Class
methods.grep(/instance_methods$/) do |name|
define_method name do |*args|
methods = super(*args)
# And now the evil part :-)
methods.instance_variable_set('@__awesome_methods__', self)
methods.sort!
end
end
end

16
lib/ap/core_ext/object.rb Normal file
View file

@ -0,0 +1,16 @@
# Copyright (c) 2010 Michael Dvorkin
#
# Awesome Print is freely distributable under the terms of MIT license.
# See LICENSE file or http://www.opensource.org/licenses/mit-license.php
#------------------------------------------------------------------------------
class Object
methods.grep(/methods$/) do |name|
next if name.to_s.include? 'instance' # Instance methods are trapped in Class.
define_method name do |*args|
methods = super(*args)
# And now the evil part :-)
methods.instance_variable_set('@__awesome_methods__', self)
methods.sort!
end
end
end

View file

@ -8,7 +8,7 @@ describe "Single method" do
it "color: should handle a method with no arguments" do
method = ''.method(:upcase)
method.ai.should == "\e[1;33mString\e[0m#\e[1;35mupcase\e[0m(\e[0;37m\e[0m)"
method.ai.should == "\e[1;33mString\e[0m#\e[1;35mupcase\e[0m\e[0;37m()\e[0m"
end
it "plain: should handle a method with one argument" do
@ -18,7 +18,7 @@ describe "Single method" do
it "color: should handle a method with one argument" do
method = ''.method(:match)
method.ai.should == "\e[1;33mString\e[0m#\e[1;35mmatch\e[0m(\e[0;37marg1\e[0m)"
method.ai.should == "\e[1;33mString\e[0m#\e[1;35mmatch\e[0m\e[0;37m(arg1)\e[0m"
end
it "plain: should handle a method with two arguments" do
@ -28,7 +28,7 @@ describe "Single method" do
it "color: should handle a method with two arguments" do
method = ''.method(:tr)
method.ai.should == "\e[1;33mString\e[0m#\e[1;35mtr\e[0m(\e[0;37marg1, arg2\e[0m)"
method.ai.should == "\e[1;33mString\e[0m#\e[1;35mtr\e[0m\e[0;37m(arg1, arg2)\e[0m"
end
it "plain: should handle a method with multiple arguments" do
@ -38,7 +38,7 @@ describe "Single method" do
it "color: should handle a method with multiple arguments" do
method = ''.method(:split)
method.ai.should == "\e[1;33mString\e[0m#\e[1;35msplit\e[0m(\e[0;37marg1, ...\e[0m)"
method.ai.should == "\e[1;33mString\e[0m#\e[1;35msplit\e[0m\e[0;37m(arg1, ...)\e[0m"
end
it "plain: should handle a method defined in mixin" do
@ -48,6 +48,81 @@ describe "Single method" do
it "color: should handle a method defined in mixin" do
method = ''.method(:grep)
method.ai.should == "\e[1;33mString (Enumerable)\e[0m#\e[1;35mgrep\e[0m(\e[0;37marg1\e[0m)"
method.ai.should == "\e[1;33mString (Enumerable)\e[0m#\e[1;35mgrep\e[0m\e[0;37m(arg1)\e[0m"
end
it "plain: should handle an unbound method" do
class Hello
def world; end
end
method = Hello.instance_method(:world)
method.ai(:plain => true).should == 'Hello (unbound)#world()'
Object.instance_eval{ remove_const :Hello }
end
it "color: should handle an unbound method" do
class Hello
def world(a,b); end
end
method = Hello.instance_method(:world)
method.ai.should == "\e[1;33mHello (unbound)\e[0m#\e[1;35mworld\e[0m\e[0;37m(arg1, arg2)\e[0m"
end
end
describe "object.methods" do
it "index: should handle object.methods" do
out = nil.methods.ai(:plain => true).grep(/is_a\?/).first
out.should =~ /^\s+\[\s*\d+\]\s+is_a\?\(arg1\)\s+NilClass \(Kernel\)\n$/
end
it "no index: should handle object.methods" do
out = nil.methods.ai(:plain => true, :index => false).grep(/is_a\?/).first
out.should =~ /^\s+is_a\?\(arg1\)\s+NilClass \(Kernel\)\n$/
end
end
describe "object.public_methods" do
it "index: should handle object.public_methods" do
out = nil.public_methods.ai(:plain => true).grep(/is_a\?/).first
out.should =~ /^\s+\[\s*\d+\]\s+is_a\?\(arg1\)\s+NilClass \(Kernel\)\n$/
end
it "no index: should handle object.public_methods" do
out = nil.public_methods.ai(:plain => true, :index => false).grep(/is_a\?/).first
out.should =~ /^\s+is_a\?\(arg1\)\s+NilClass \(Kernel\)\n$/
end
end
describe "object.private_methods" do
it "index: should handle object.private_methods" do
out = nil.private_methods.ai(:plain => true).grep(/sleep/).first
out.should =~ /^\s+\[\s*\d+\]\s+sleep\(arg1,\s\.{3}\)\s+NilClass \(Kernel\)\n$/
end
it "no index: should handle object.private_methods" do
out = nil.private_methods.ai(:plain => true, :index => false).grep(/sleep/).first
out.should =~ /^\s+sleep\(arg1,\s\.{3}\)\s+NilClass \(Kernel\)\n$/
end
end
describe "object.protected_methods" do
it "index: should handle object.protected_methods" do
class Hello
protected
def one; end
def two; end
end
Hello.new.protected_methods.ai(:plain => true).should == "[\n [0] one() Hello\n [1] two() Hello\n]"
Object.instance_eval{ remove_const :Hello }
end
it "index: should handle object.protected_methods" do
class Hello
protected
def world(a,b); end
end
Hello.new.protected_methods.ai(:plain => true, :index => false).should == "[\n world(arg1, arg2) Hello\n]"
Object.instance_eval{ remove_const :Hello }
end
end