#
# MethodFinder2 ( permutatoin arguments version )
#
# Author : David Tran
# Date : 2006-05-20
# Last updated : 2006-05-22
#
# Resources :
# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/32844
# http://www.nobugs.org/developer/ruby/method_finder.html
# http://redhanded.hobix.com/inspect/stickItInYourIrbrcMethodfinder.html
# http://atrustheotaku.livejournal.com/339449.html
#
class Object
def what?(*args, &block)
MethodFinder.new(self, *args, &block)
end
def _clone_
self.clone
rescue
self
end
end
class MethodFinder
@@black_list = %w[ what? _clone_ exec exit exit! fork sleep syscall system ]
def initialize(obj, *args, &block)
@obj = obj
@args = args
@block = block
end
def self.write(*args)
end
def self.find(obj, result, *args, &block)
methods = simple_find(false, obj, result, *args, &block)
(methods.empty? && args.size > 1) ? permutation_find(false, obj, result, *args, &block) : methods
end
def self.show(obj, result, *args, &block)
methods = simple_find(true, obj, result, *args, &block)
(methods.empty? && args.size > 1) ? permutation_find(true, obj, result, *args, &block) : methods
end
def ==(result)
# MethodFinder.find(@obj, result, *@args, &@block)
MethodFinder.show(@obj, result, *@args, &@block)
end
def self.simple_find(show, obj, result, *args, &block)
stdout, stderr = $stdout, $stderr
$stdout = $stderr = self
methods = obj.methods.select do |name|
arity = obj.method(name).arity
!@@black_list.include?(name) and
(arity == args.size) || (arity < 0 && (arity+1).abs <= args.size) and
begin obj._clone_.send(name, *args, &block) == result; rescue Object; end
end
$stdout, $stderr = stdout, stderr
if show
args_string = args.empty? ? '' : "(" + args.map {|a| a.inspect}.join(", ") + ")"
block_string = block.nil? ? '' : " {...}"
methods.each { |name| puts "#{obj.inspect}.#{name}#{args_string}#{block_string} == #{result.inspect}" }
end
methods
end
def self.permutation_find(show, obj, result, *args, &block)
(permutation(args).uniq - [args]).inject([]) do |methods, perm_args|
methods + simple_find(show, obj, result, *perm_args, &block)
end.uniq
end
def self.permutation(array)
return [array] if array.size < 2
perm = []
array.each_with_index do |e, i|
permutation(array[0...i] + array[i+1..-1]).each { |p| perm << (p << e) }
end
perm
end
private_class_method :simple_find, :permutation_find, :permutation
end
__END__
Some examples:
'ABC'.what?('x', 1) == 'AxBC'
"ABC".insert(1, "x") == "AxBC"
=> ["insert"]
[1,3,3].what?(0,1,2) == [3,3,1]
[1, 3, 3].indices(2, 1, 0) == [3, 3, 1]
[1, 3, 3].indexes(2, 1, 0) == [3, 3, 1]
[1, 3, 3].values_at(2, 1, 0) == [3, 3, 1]
[1, 3, 3].indices(1, 2, 0) == [3, 3, 1]
[1, 3, 3].indexes(1, 2, 0) == [3, 3, 1]
[1, 3, 3].values_at(1, 2, 0) == [3, 3, 1]
=> ["indices", "indexes", "values_at"]
-1.what? == 1
-1.-@ == 1
-1.abs == 1
-1.denominator == 1
=> ["-@", "abs", "denominator"]
'abc'.what? == 3
"abc".length == 3
"abc".size == 3
=> ["length", "size"]
[1,2].what? {|s,e| s+e} == 3
[1, 2].instance_eval {...} == 3
[1, 2].inject {...} == 3
=> ["instance_eval", "inject"]
[1,2].what?(0) {|s,e| s+e} == 3
[1, 2].inject(0) {...} == 3
=> ["inject"]
MethodFinder.show(%w[xxx z yy], %w[z yy xxx]) {|e| e.size}
["xxx", "z", "yy"].sort_by {...} == ["z", "yy", "xxx"]
=> ["sort_by"]
Note:
I modified Steven Graday's permutations method,
because there is a bug on his permutations method,
try permutations on [1,1] or [1,2,2] will return incorrect result.
#####
updated: 2006-05-22
bug: arity checking
arities = [args.size, -args.size - 1, -1]
arities.include?(obj.method(name).arity)
is not good.
Example:
def m(a, b=1);end
it has arity == -2, so if args.size == 2, [2, -2 - 1, -1].inlcude? check will fail.
==> new checking rule:
((arity >= 0 && arity == args.size) || (arity < 0 && (arity+1).abs <= args.size))
this can simplify to:
(arity == args.size) || (arity < 0 && (arity+1).abs <= args.size)