class Base def foo() :base end end class Derived < Base def foo() :derived end end class Interpreter attr_accessor :ret def do_a() @ret += "there, "; end def do_d() @ret += "Hello "; end def do_e() @ret += "!\n"; end def do_v() @ret += "Dave"; end Dispatcher = { "a" => instance_method(:do_a), "d" => instance_method(:do_d), "e" => instance_method(:do_e), "v" => instance_method(:do_v) } def interpret(string) @ret = "" string.split("").each {|b| Dispatcher[b].bind(self).call } end end assert 'demo' do interpreter = Interpreter.new interpreter.interpret('dave') assert_equal "Hello there, Dave!\n", interpreter.ret end assert 'Method#arity' do Class.new { attr_accessor :done def initialize; @done = false; end def m0() end def m1(a) end def m2(a, b) end def mo1(a = nil, &b) end def mo2(a, b = nil) end def mo3(*a) end def mo4(a, *b, &c) end def mo5(a, *b, c) end def mo6(a, *b, c, &d) end def mo7(a, b = nil, *c, d, &e) end def ma1((a), &b) nil && a end def run assert_equal(0, method(:m0).arity) assert_equal(1, method(:m1).arity) assert_equal(2, method(:m2).arity) assert_equal(-1, method(:mo1).arity) assert_equal(-2, method(:mo2).arity) assert_equal(-1, method(:mo3).arity) assert_equal(-2, method(:mo4).arity) assert_equal(-3, method(:mo5).arity) assert_equal(-3, method(:mo6).arity) assert_equal(-3, method(:mo7).arity) assert_equal(1, method(:ma1).arity) assert_equal(-1, method(:__send__).arity) assert_equal(-1, method(:nothing).arity) end def respond_to_missing?(m, b) m == :nothing end }.new.run end assert 'Method and UnboundMethod should not be have a `new` method' do assert_raise(NoMethodError){ Method.new } assert_raise(NoMethodError){ UnboundMethod.new } end assert 'instance' do assert_kind_of Method, 1.method(:+) assert_kind_of UnboundMethod, Integer.instance_method(:+) end assert 'Method#call' do assert_equal 3, 1.method(:+).call(2) assert_equal "ab", "a".method(:+)["b"] klass = Class.new { def foo; 42; end } klass2 = Class.new(klass) { def foo; super; end } assert_equal 42, klass2.new.method(:foo).call i = Class.new { def bar yield 3 end }.new assert_raise(LocalJumpError) { i.method(:bar).call } assert_equal 3, i.method(:bar).call { |i| i } end assert 'Method#call for regression' do obj = BasicObject.new assert_equal String, Kernel.instance_method(:inspect).bind(obj).call().class, "https://github.com/ksss/mruby-method/issues/4" end assert 'Method#call with undefined method' do c = Class.new { attr_accessor :m, :argv def respond_to_missing?(m, b) m == :foo end def method_missing(m, *argv) @m = m @argv = argv super end } cc = c.new assert_raise(NameError) { cc.method(:nothing) } assert_kind_of Method, cc.method(:foo) assert_raise(NoMethodError) { cc.method(:foo).call(:arg1, :arg2) } assert_equal :foo, cc.m assert_equal [:arg1, :arg2], cc.argv cc = c.new m = cc.method(:foo) c.class_eval do def foo :ng end end assert_raise(NoMethodError) { m.call(:arg1, :arg2) } end assert 'Method#source_location' do skip if proc{}.source_location.nil? filename = __FILE__ klass = Class.new lineno = __LINE__ + 1 klass.define_method(:find_me_if_you_can) {} assert_equal [filename, lineno], klass.new.method(:find_me_if_you_can).source_location lineno = __LINE__ + 1 class <<klass; define_method(:s_find_me_if_you_can) {}; end assert_equal [filename, lineno], klass.method(:s_find_me_if_you_can).source_location klass = Class.new { def respond_to_missing?(m, b); m == :nothing; end } assert_nil klass.new.method(:nothing).source_location end assert 'UnboundMethod#source_location' do skip if proc{}.source_location.nil? filename = __FILE__ klass = Class.new { def respond_to_missing?(m, b) m == :nothing end } lineno = __LINE__ + 1 klass.define_method(:find_me_if_you_can) {} assert_equal [filename, lineno], klass.instance_method(:find_me_if_you_can).source_location assert_nil klass.new.method(:nothing).unbind.source_location end assert 'Method#parameters' do klass = Class.new { def foo(a, b=nil, *c) end def respond_to_missing?(m, b) m == :missing end } assert_equal [[:req, :a], [:opt, :b], [:rest, :c]], klass.new.method(:foo).parameters assert_equal [[:rest]], klass.new.method(:missing).parameters end assert 'UnboundMethod#parameters' do klass = Class.new { def foo(a, b=nil, *c) end def respond_to_missing?(m, b) m == :nothing end } assert_equal [[:req, :a], [:opt, :b], [:rest, :c]], klass.instance_method(:foo).parameters assert_equal [[:rest]], klass.new.method(:nothing).unbind.parameters end assert 'Method#to_proc' do m = 3.method(:+) assert_kind_of Proc, m.to_proc assert_equal 7, m.call(4) o = Object.new def o.foo(a, b=nil, *c) [a, b, c] end assert_equal [:bar, nil, []], o.method(:foo).to_proc.call(:bar) # We can fix this issue but leave until the problem # assert_equal o.method(:foo).arity, o.method(:foo).to_proc.arity def o.bar yield 39 end assert_equal 42, o.bar(&3.method(:+)) end assert 'to_s' do o = Object.new def o.foo; end m = o.method(:foo) assert_equal("#<UnboundMethod: #{ class << o; self; end.inspect }#foo>", m.unbind.inspect) c = Class.new c.class_eval { def foo; end; } m = c.new.method(:foo) assert_equal("#<Method: #{ c.inspect }#foo>", m.inspect) m = c.instance_method(:foo) assert_equal("#<UnboundMethod: #{ c.inspect }#foo>", m.inspect) end assert 'owner' do c = Class.new do def foo; end def self.bar; end end m = Module.new do def baz; end end c.include(m) c2 = Class.new(c) assert_equal(c, c.instance_method(:foo).owner) assert_equal(c, c2.instance_method(:foo).owner) assert_equal(c, c.new.method(:foo).owner) assert_equal(c, c2.new.method(:foo).owner) assert_equal((class <<c; self; end), c2.method(:bar).owner) end assert 'owner missing' do c = Class.new do def respond_to_missing?(name, bool) name == :foo end end c2 = Class.new(c) assert_equal(c, c.new.method(:foo).owner) assert_equal(c2, c2.new.method(:foo).owner) end assert 'receiver name owner' do o = Object.new def o.foo; end m = o.method(:foo) assert_equal(o, m.receiver) assert_equal(:foo, m.name) assert_equal(class << o; self; end, m.owner) assert_equal(:foo, m.unbind.name) assert_equal(class << o; self; end, m.unbind.owner) end assert 'Method#unbind' do assert_equal(:derived, Derived.new.foo) um = Derived.new.method(:foo).unbind assert_kind_of(UnboundMethod, um) Derived.class_eval do def foo() :changed end end assert_equal(:changed, Derived.new.foo) assert_equal(:changed, Derived.new.foo{}) assert_equal(:derived, um.bind(Derived.new).call) assert_raise(TypeError) do um.bind(Base.new) end # TODO: # Block passed method not handled correctly with workaround. # See comment near `mrb_funcall_with_block` for detail. # assert_equal(:derived, um.bind(Derived.new).call{}) end assert 'Kernel#method' do c1 = Class.new { def foo; :foo; end } o = c1.new assert_kind_of Method, o.method(:foo) assert_kind_of Method, o.method('foo') assert_raise(TypeError) { o.method(nil) } assert_raise(NameError) { o.method('bar') } assert_raise(NameError) { o.method(:bar) } end assert "Module#instance_method" do assert_kind_of UnboundMethod, Object.instance_method(:object_id) assert_raise(NameError) { Object.instance_method(:nothing) } c = Class.new { def respond_to_missing?(m, b) false end } assert_raise(NameError) { c.instance_method(:nothing) } end assert 'Kernel#singleton_method' do c1 = Class.new { def foo; :foo; end } o = c1.new def o.bar; :bar; end assert_kind_of Method, o.method(:foo) assert_raise(NameError) { o.singleton_method(:foo) } assert_kind_of Method, o.singleton_method(:bar) assert_raise(TypeError) { o.singleton_method(nil) } m = assert_nothing_raised(NameError) { break o.singleton_method(:bar) } assert_equal(:bar, m.call) end assert 'Method#super_method' do o = Derived.new m = o.method(:foo).super_method assert_equal(Base, m.owner) assert_true(o.equal? m.receiver) assert_equal(:foo, m.name) assert_nil(m.super_method) c = Class.new { def foo; end } o = c.new o.extend Module.new { def foo; end } assert_equal c, o.method(:foo).super_method.owner assert_equal :foo, o.method(:foo).super_method.name assert_equal o, o.method(:foo).super_method.receiver end assert 'Method#==' do o = Object.new class << o def foo; end end assert_not_equal(o.method(:foo), nil) m = o.method(:foo) def m.foo; end # TODO: assert_not_equal(o.method(:foo), m) assert_equal(o.method(:foo), o.method(:foo)) # TODO: assert_false(o.method(:foo).eql? m) assert_true(o.method(:foo).eql? o.method(:foo)) assert_false(0.method(:+) == 1.method(:+)) assert_false(0.method(:+) == 0.method(:-)) a = 0.method(:+) assert_true(a.method(:==) == a.method(:eql?)) end assert "Method#initialize_copy" do c = Class.new { def foo end }.new m1 = c.method(:foo) m2 = m1.clone assert_equal(m1, m2) end assert "Method#<< and Method#>>" do obj = Object.new class << obj def mul2(n); n * 2; end def add3(n); n + 3; end end f = obj.method(:mul2) g = obj.method(:add3) m1 = f << g assert_kind_of Proc, m1 assert_equal 16, m1.call(5) m2 = f >> g assert_kind_of Proc, m2 assert_equal 13, m2.call(5) end assert 'UnboundMethod#arity' do c = Class.new { def foo(a, b) end def respond_to_missing?(m, b) m == :nothing end } assert_equal 2, c.instance_method(:foo).arity assert_equal(-1, c.new.method(:nothing).unbind.arity) end assert 'UnboundMethod#==' do assert_false(Integer.instance_method(:+) == Integer.instance_method(:-)) assert_true(Integer.instance_method(:+) == Integer.instance_method(:+)) assert_false(Integer.instance_method(:+) == Float.instance_method(:+)) assert_true(UnboundMethod.instance_method(:==) == UnboundMethod.instance_method(:eql?)) end assert 'UnboundMethod#super_method' do m = Derived.instance_method(:foo) m = m.super_method assert_equal(Base.instance_method(:foo), m) assert_nil(m.super_method) m = Object.instance_method(:object_id) assert_nil(m.super_method) end assert 'UnboundMethod#bind' do m = Module.new{ def meth() :meth end }.instance_method(:meth) assert_raise(ArgumentError) { m.bind } assert_kind_of Method, m.bind(1) assert_kind_of Method, m.bind(:sym) assert_kind_of Method, m.bind(Object.new) assert_equal(:meth, m.bind(1).call) assert_equal(:meth, m.bind(:sym).call) assert_equal(:meth, m.bind(Object.new).call) sc = nil Class.new { sc = class << self def foo end self end } assert_raise(TypeError) { sc.instance_method(:foo).bind([]) } assert_raise(TypeError) { Array.instance_method(:each).bind(1) } assert_kind_of Method, Object.instance_method(:object_id).bind(Object.new) end assert 'UnboundMethod#bind_call' do m = Array.instance_method(:size) assert_equal(:size, m.name) assert_equal(0, m.bind_call([])) assert_equal(1, m.bind_call([1])) assert_equal(2, m.bind_call([1,2])) end