summaryrefslogtreecommitdiff
path: root/spec/ruby/core/method
diff options
context:
space:
mode:
Diffstat (limited to 'spec/ruby/core/method')
-rw-r--r--spec/ruby/core/method/arity_spec.rb222
-rw-r--r--spec/ruby/core/method/call_spec.rb7
-rw-r--r--spec/ruby/core/method/case_compare_spec.rb7
-rw-r--r--spec/ruby/core/method/clone_spec.rb13
-rw-r--r--spec/ruby/core/method/compose_spec.rb99
-rw-r--r--spec/ruby/core/method/curry_spec.rb36
-rw-r--r--spec/ruby/core/method/dup_spec.rb15
-rw-r--r--spec/ruby/core/method/element_reference_spec.rb7
-rw-r--r--spec/ruby/core/method/eql_spec.rb6
-rw-r--r--spec/ruby/core/method/equal_value_spec.rb6
-rw-r--r--spec/ruby/core/method/fixtures/classes.rb246
-rw-r--r--spec/ruby/core/method/hash_spec.rb15
-rw-r--r--spec/ruby/core/method/inspect_spec.rb6
-rw-r--r--spec/ruby/core/method/name_spec.rb22
-rw-r--r--spec/ruby/core/method/original_name_spec.rb22
-rw-r--r--spec/ruby/core/method/owner_spec.rb30
-rw-r--r--spec/ruby/core/method/parameters_spec.rb304
-rw-r--r--spec/ruby/core/method/private_spec.rb9
-rw-r--r--spec/ruby/core/method/protected_spec.rb9
-rw-r--r--spec/ruby/core/method/public_spec.rb9
-rw-r--r--spec/ruby/core/method/receiver_spec.rb22
-rw-r--r--spec/ruby/core/method/shared/call.rb51
-rw-r--r--spec/ruby/core/method/shared/dup.rb32
-rw-r--r--spec/ruby/core/method/shared/eql.rb94
-rw-r--r--spec/ruby/core/method/shared/to_s.rb81
-rw-r--r--spec/ruby/core/method/source_location_spec.rb126
-rw-r--r--spec/ruby/core/method/super_method_spec.rb64
-rw-r--r--spec/ruby/core/method/to_proc_spec.rb104
-rw-r--r--spec/ruby/core/method/to_s_spec.rb6
-rw-r--r--spec/ruby/core/method/unbind_spec.rb46
30 files changed, 1716 insertions, 0 deletions
diff --git a/spec/ruby/core/method/arity_spec.rb b/spec/ruby/core/method/arity_spec.rb
new file mode 100644
index 0000000000..4bb821735a
--- /dev/null
+++ b/spec/ruby/core/method/arity_spec.rb
@@ -0,0 +1,222 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Method#arity" do
+ SpecEvaluate.desc = "for method definition"
+
+ context "returns zero" do
+ evaluate <<-ruby do
+ def m() end
+ ruby
+
+ method(:m).arity.should == 0
+ end
+
+ evaluate <<-ruby do
+ def n(&b) end
+ ruby
+
+ method(:n).arity.should == 0
+ end
+ end
+
+ context "returns positive values" do
+ evaluate <<-ruby do
+ def m(a) end
+ def n(a, b) end
+ def o(a, b, c) end
+ def p(a, b, c, d) end
+ ruby
+
+ method(:m).arity.should == 1
+ method(:n).arity.should == 2
+ method(:o).arity.should == 3
+ method(:p).arity.should == 4
+ end
+
+ evaluate <<-ruby do
+ def m(a:) end
+ def n(a:, b:) end
+ def o(a: 1, b:, c:, d: 2) end
+ ruby
+
+ method(:m).arity.should == 1
+ method(:n).arity.should == 1
+ method(:o).arity.should == 1
+ end
+
+ evaluate <<-ruby do
+ def m(a, b:) end
+ def n(a, b:, &l) end
+ ruby
+
+ method(:m).arity.should == 2
+ method(:n).arity.should == 2
+ end
+
+ evaluate <<-ruby do
+ def m(a, b, c:, d: 1) end
+ def n(a, b, c:, d: 1, **k, &l) end
+ ruby
+
+ method(:m).arity.should == 3
+ method(:n).arity.should == 3
+ end
+ end
+
+ context "returns negative values" do
+ evaluate <<-ruby do
+ def m(a=1) end
+ def n(a=1, b=2) end
+ ruby
+
+ method(:m).arity.should == -1
+ method(:n).arity.should == -1
+ end
+
+ evaluate <<-ruby do
+ def m(a, b=1) end
+ def n(a, b, c=1, d=2) end
+ ruby
+
+ method(:m).arity.should == -2
+ method(:n).arity.should == -3
+ end
+
+ evaluate <<-ruby do
+ def m(a=1, *b) end
+ def n(a=1, b=2, *c) end
+ ruby
+
+ method(:m).arity.should == -1
+ method(:n).arity.should == -1
+ end
+
+ evaluate <<-ruby do
+ def m(*) end
+ def n(*a) end
+ ruby
+
+ method(:m).arity.should == -1
+ method(:n).arity.should == -1
+ end
+
+ evaluate <<-ruby do
+ def m(a, *) end
+ def n(a, *b) end
+ def o(a, b, *c) end
+ def p(a, b, c, *d) end
+ ruby
+
+ method(:m).arity.should == -2
+ method(:n).arity.should == -2
+ method(:o).arity.should == -3
+ method(:p).arity.should == -4
+ end
+
+ evaluate <<-ruby do
+ def m(*a, b) end
+ def n(*a, b, c) end
+ def o(*a, b, c, d) end
+ ruby
+
+ method(:m).arity.should == -2
+ method(:n).arity.should == -3
+ method(:o).arity.should == -4
+ end
+
+ evaluate <<-ruby do
+ def m(a, *b, c) end
+ def n(a, b, *c, d, e) end
+ ruby
+
+ method(:m).arity.should == -3
+ method(:n).arity.should == -5
+ end
+
+ evaluate <<-ruby do
+ def m(a, b=1, c=2, *d, e, f) end
+ def n(a, b, c=1, *d, e, f, g) end
+ ruby
+
+ method(:m).arity.should == -4
+ method(:n).arity.should == -6
+ end
+
+ evaluate <<-ruby do
+ def m(a: 1) end
+ def n(a: 1, b: 2) end
+ ruby
+
+ method(:m).arity.should == -1
+ method(:n).arity.should == -1
+ end
+
+ evaluate <<-ruby do
+ def m(a=1, b: 2) end
+ def n(*a, b: 1) end
+ def o(a=1, b: 2) end
+ def p(a=1, *b, c: 2, &l) end
+ ruby
+
+ method(:m).arity.should == -1
+ method(:n).arity.should == -1
+ method(:o).arity.should == -1
+ method(:p).arity.should == -1
+ end
+
+ evaluate <<-ruby do
+ def m(**k, &l) end
+ def n(*a, **k) end
+ def o(a: 1, b: 2, **k) end
+ ruby
+
+ method(:m).arity.should == -1
+ method(:n).arity.should == -1
+ method(:o).arity.should == -1
+ end
+
+ evaluate <<-ruby do
+ def m(a=1, *b, c:, d: 2, **k, &l) end
+ ruby
+
+ method(:m).arity.should == -2
+ end
+
+ evaluate <<-ruby do
+ def m(a, b=1, *c, d, e:, f: 2, **k, &l) end
+ def n(a, b=1, *c, d:, e:, f: 2, **k, &l) end
+ def o(a=0, b=1, *c, d, e:, f: 2, **k, &l) end
+ def p(a=0, b=1, *c, d:, e:, f: 2, **k, &l) end
+ ruby
+
+ method(:m).arity.should == -4
+ method(:n).arity.should == -3
+ method(:o).arity.should == -3
+ method(:p).arity.should == -2
+ end
+ end
+
+ context "for a Method generated by respond_to_missing?" do
+ it "returns -1" do
+ obj = mock("method arity respond_to_missing")
+ obj.should_receive(:respond_to_missing?).and_return(true)
+
+ obj.method(:m).arity.should == -1
+ end
+ end
+
+ context "for a Method generated by attr_reader" do
+ it "return 0" do
+ obj = MethodSpecs::Methods.new
+ obj.method(:reader).arity.should == 0
+ end
+ end
+
+ context "for a Method generated by attr_writer" do
+ it "returns 1" do
+ obj = MethodSpecs::Methods.new
+ obj.method(:writer=).arity.should == 1
+ end
+ end
+end
diff --git a/spec/ruby/core/method/call_spec.rb b/spec/ruby/core/method/call_spec.rb
new file mode 100644
index 0000000000..6d997325fa
--- /dev/null
+++ b/spec/ruby/core/method/call_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/call'
+
+describe "Method#call" do
+ it_behaves_like :method_call, :call
+end
diff --git a/spec/ruby/core/method/case_compare_spec.rb b/spec/ruby/core/method/case_compare_spec.rb
new file mode 100644
index 0000000000..a78953e8ad
--- /dev/null
+++ b/spec/ruby/core/method/case_compare_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/call'
+
+describe "Method#===" do
+ it_behaves_like :method_call, :===
+end
diff --git a/spec/ruby/core/method/clone_spec.rb b/spec/ruby/core/method/clone_spec.rb
new file mode 100644
index 0000000000..b0eb5751a9
--- /dev/null
+++ b/spec/ruby/core/method/clone_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../spec_helper'
+require_relative 'shared/dup'
+
+describe "Method#clone" do
+ it_behaves_like :method_dup, :clone
+
+ it "preserves frozen status" do
+ method = Object.new.method(:method)
+ method.freeze
+ method.frozen?.should == true
+ method.clone.frozen?.should == true
+ end
+end
diff --git a/spec/ruby/core/method/compose_spec.rb b/spec/ruby/core/method/compose_spec.rb
new file mode 100644
index 0000000000..7506e33ea8
--- /dev/null
+++ b/spec/ruby/core/method/compose_spec.rb
@@ -0,0 +1,99 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative '../proc/shared/compose'
+
+describe "Method#<<" do
+ it "returns a Proc that is the composition of self and the passed Proc" do
+ succ = MethodSpecs::Composition.new.method(:succ)
+ upcase = proc { |s| s.upcase }
+
+ (succ << upcase).call('Ruby').should == "RUBZ"
+ end
+
+ it "calls passed Proc with arguments and then calls self with result" do
+ pow_2_proc = proc { |x| x * x }
+ double_proc = proc { |x| x + x }
+
+ pow_2_method = MethodSpecs::Composition.new.method(:pow_2)
+ double_method = MethodSpecs::Composition.new.method(:double)
+
+ (pow_2_method << double_proc).call(2).should == 16
+ (double_method << pow_2_proc).call(2).should == 8
+ end
+
+ it "accepts any callable object" do
+ inc = MethodSpecs::Composition.new.method(:inc)
+
+ double = Object.new
+ def double.call(n); n * 2; end
+
+ (inc << double).call(3).should == 7
+ end
+
+ it_behaves_like :proc_compose, :<<, -> { MethodSpecs::Composition.new.method(:upcase) }
+
+ describe "composition" do
+ it "is a lambda" do
+ pow_2 = MethodSpecs::Composition.new.method(:pow_2)
+ double = proc { |x| x + x }
+
+ (pow_2 << double).is_a?(Proc).should == true
+ (pow_2 << double).should_not.lambda?
+ end
+
+ it "may accept multiple arguments" do
+ inc = MethodSpecs::Composition.new.method(:inc)
+ mul = proc { |n, m| n * m }
+
+ (inc << mul).call(2, 3).should == 7
+ end
+ end
+end
+
+describe "Method#>>" do
+ it "returns a Proc that is the composition of self and the passed Proc" do
+ upcase = proc { |s| s.upcase }
+ succ = MethodSpecs::Composition.new.method(:succ)
+
+ (succ >> upcase).call('Ruby').should == "RUBZ"
+ end
+
+ it "calls passed Proc with arguments and then calls self with result" do
+ pow_2_proc = proc { |x| x * x }
+ double_proc = proc { |x| x + x }
+
+ pow_2_method = MethodSpecs::Composition.new.method(:pow_2)
+ double_method = MethodSpecs::Composition.new.method(:double)
+
+ (pow_2_method >> double_proc).call(2).should == 8
+ (double_method >> pow_2_proc).call(2).should == 16
+ end
+
+ it "accepts any callable object" do
+ inc = MethodSpecs::Composition.new.method(:inc)
+
+ double = Object.new
+ def double.call(n); n * 2; end
+
+ (inc >> double).call(3).should == 8
+ end
+
+ it_behaves_like :proc_compose, :>>, -> { MethodSpecs::Composition.new.method(:upcase) }
+
+ describe "composition" do
+ it "is a lambda" do
+ pow_2 = MethodSpecs::Composition.new.method(:pow_2)
+ double = proc { |x| x + x }
+
+ (pow_2 >> double).is_a?(Proc).should == true
+ (pow_2 >> double).should.lambda?
+ end
+
+ it "may accept multiple arguments" do
+ mul = MethodSpecs::Composition.new.method(:mul)
+ inc = proc { |n| n + 1 }
+
+ (mul >> inc).call(2, 3).should == 7
+ end
+ end
+end
diff --git a/spec/ruby/core/method/curry_spec.rb b/spec/ruby/core/method/curry_spec.rb
new file mode 100644
index 0000000000..219de0f6b5
--- /dev/null
+++ b/spec/ruby/core/method/curry_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Method#curry" do
+ it "returns a curried proc" do
+ x = Object.new
+ def x.foo(a,b,c); [a,b,c]; end
+
+ c = x.method(:foo).curry
+ c.should be_kind_of(Proc)
+ c.call(1).call(2, 3).should == [1,2,3]
+ end
+
+ describe "with optional arity argument" do
+ before(:each) do
+ @obj = MethodSpecs::Methods.new
+ end
+
+ it "returns a curried proc when given correct arity" do
+ @obj.method(:one_req).curry(1).should be_kind_of(Proc)
+ @obj.method(:zero_with_splat).curry(100).should be_kind_of(Proc)
+ @obj.method(:two_req_with_splat).curry(2).should be_kind_of(Proc)
+ end
+
+ it "raises ArgumentError when the method requires less arguments than the given arity" do
+ -> { @obj.method(:zero).curry(1) }.should raise_error(ArgumentError)
+ -> { @obj.method(:one_req_one_opt).curry(3) }.should raise_error(ArgumentError)
+ -> { @obj.method(:two_req_one_opt_with_block).curry(4) }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError when the method requires more arguments than the given arity" do
+ -> { @obj.method(:two_req_with_splat).curry(1) }.should raise_error(ArgumentError)
+ -> { @obj.method(:one_req).curry(0) }.should raise_error(ArgumentError)
+ end
+ end
+end
diff --git a/spec/ruby/core/method/dup_spec.rb b/spec/ruby/core/method/dup_spec.rb
new file mode 100644
index 0000000000..e3e29d8a68
--- /dev/null
+++ b/spec/ruby/core/method/dup_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+require_relative 'shared/dup'
+
+describe "Method#dup" do
+ ruby_version_is "3.4" do
+ it_behaves_like :method_dup, :dup
+
+ it "resets frozen status" do
+ method = Object.new.method(:method)
+ method.freeze
+ method.frozen?.should == true
+ method.dup.frozen?.should == false
+ end
+ end
+end
diff --git a/spec/ruby/core/method/element_reference_spec.rb b/spec/ruby/core/method/element_reference_spec.rb
new file mode 100644
index 0000000000..aa6c54d1cb
--- /dev/null
+++ b/spec/ruby/core/method/element_reference_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/call'
+
+describe "Method#[]" do
+ it_behaves_like :method_call, :[]
+end
diff --git a/spec/ruby/core/method/eql_spec.rb b/spec/ruby/core/method/eql_spec.rb
new file mode 100644
index 0000000000..b97c9e4db0
--- /dev/null
+++ b/spec/ruby/core/method/eql_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/eql'
+
+describe "Method#eql?" do
+ it_behaves_like :method_equal, :eql?
+end
diff --git a/spec/ruby/core/method/equal_value_spec.rb b/spec/ruby/core/method/equal_value_spec.rb
new file mode 100644
index 0000000000..0431d0c5f6
--- /dev/null
+++ b/spec/ruby/core/method/equal_value_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/eql'
+
+describe "Method#==" do
+ it_behaves_like :method_equal, :==
+end
diff --git a/spec/ruby/core/method/fixtures/classes.rb b/spec/ruby/core/method/fixtures/classes.rb
new file mode 100644
index 0000000000..464a519aea
--- /dev/null
+++ b/spec/ruby/core/method/fixtures/classes.rb
@@ -0,0 +1,246 @@
+module MethodSpecs
+
+
+ class SourceLocation
+ def self.location # This needs to be on this line
+ :location # for the spec to pass
+ end
+
+ def self.redefined
+ :first
+ end
+
+ def self.redefined
+ :last
+ end
+
+ def original
+ end
+
+ alias :aka :original
+ end
+
+ class Methods
+ def foo
+ true
+ end
+
+ alias bar foo
+ alias baz bar
+
+ def same_as_foo
+ true
+ end
+
+ def respond_to_missing? method, bool
+ [:handled_via_method_missing, :also_handled].include? method
+ end
+
+ def method_missing(method, *arguments)
+ if [:handled_via_method_missing, :also_handled].include? method
+ arguments
+ else
+ super
+ end
+ end
+
+ attr_accessor :attr
+
+ def zero; end
+ def one_req(a); end
+ def two_req(a, b); end
+
+ def one_req_named(a:); end
+
+ def zero_with_block(&blk); end
+ def one_req_with_block(a, &blk); end
+ def two_req_with_block(a, b, &blk); end
+
+ def one_opt(a=nil); end
+ def one_req_one_opt(a, b=nil); end
+ def one_req_two_opt(a, b=nil, c=nil); end
+ def two_req_one_opt(a, b, c=nil); end
+
+ def one_opt_named(a: nil); end
+
+ def one_opt_with_block(a=nil, &blk); end
+ def one_req_one_opt_with_block(a, b=nil, &blk); end
+ def one_req_two_opt_with_block(a, b=nil, c=nil, &blk); end
+ def two_req_one_opt_with_block(a, b, c=nil, &blk); end
+
+ def zero_with_splat(*a); end
+ def one_req_with_splat(a, *b); end
+ def two_req_with_splat(a, b, *c); end
+ def one_req_one_opt_with_splat(a, b=nil, *c); end
+ def two_req_one_opt_with_splat(a, b, c=nil, *d); end
+ def one_req_two_opt_with_splat(a, b=nil, c=nil, *d); end
+
+ def zero_with_double_splat(**a); end
+
+ def zero_with_splat_and_block(*a, &blk); end
+ def one_req_with_splat_and_block(a, *b, &blk); end
+ def two_req_with_splat_and_block(a, b, *c, &blk); end
+ def one_req_one_opt_with_splat_and_block(a, b=nil, *c, &blk); end
+ def two_req_one_opt_with_splat_and_block(a, b, c=nil, *d, &blk); end
+ def one_req_two_opt_with_splat_and_block(a, b=nil, c=nil, *d, &blk); end
+
+ def my_public_method; end
+ def my_protected_method; end
+ def my_private_method; end
+ protected :my_protected_method
+ private :my_private_method
+
+ define_method(:zero_defined_method, Proc.new {||})
+ define_method(:zero_with_splat_defined_method, Proc.new {|*x|})
+ define_method(:one_req_defined_method, Proc.new {|x|})
+ define_method(:two_req_defined_method, Proc.new {|x, y|})
+ define_method(:no_args_defined_method) {}
+ define_method(:two_grouped_defined_method) {|(_x1,_x2)|}
+
+ attr_reader :reader
+ attr_writer :writer
+ end
+
+ module MyMod
+ def bar; :bar; end
+ end
+
+ class MySuper
+ include MyMod
+ end
+
+ class MySub < MySuper; end
+
+ class A
+ def baz(a, b)
+ self.class
+ end
+ def overridden; end
+ end
+
+ class B < A
+ def overridden; end
+ end
+
+ module BetweenBAndC
+ def overridden; end
+ end
+
+ class C < B
+ include BetweenBAndC
+ def overridden; end
+ end
+
+ module OverrideAgain
+ def overridden; end
+ end
+
+ class D
+ def bar() 'done' end
+ end
+
+ class Eql
+
+ def same_body
+ 1 + 1
+ end
+
+ alias :same_body_alias :same_body
+
+ def same_body_with_args(arg)
+ 1 + 1
+ end
+
+ def different_body
+ 1 + 2
+ end
+
+ def same_body_two
+ 1 + 1
+ end
+
+ private
+ def same_body_private
+ 1 + 1
+ end
+ end
+
+ class Eql2
+
+ def same_body
+ 1 + 1
+ end
+
+ end
+
+ class ToProc
+ def method_called(a, b)
+ ScratchPad << [a, b]
+ end
+
+ def to_proc
+ method(:method_called).to_proc
+ end
+ end
+
+ class ToProcBeta
+ def method_called(a)
+ ScratchPad << a
+ a
+ end
+
+ def to_proc
+ method(:method_called).to_proc
+ end
+ end
+
+ class Composition
+ def upcase(s)
+ s.upcase
+ end
+
+ def succ(s)
+ s.succ
+ end
+
+ def pow_2(n)
+ n * n
+ end
+
+ def double(n)
+ n + n
+ end
+
+ def inc(n)
+ n + 1
+ end
+
+ def mul(n, m)
+ n * m
+ end
+ end
+
+ module InheritedMethods
+ module A
+ private
+ def derp(message)
+ 'A'
+ end
+ end
+
+ module B
+ private
+ def derp
+ 'B' + super('superclass')
+ end
+ end
+
+ class C
+ include A
+ include B
+
+ public :derp
+ alias_method :meow, :derp
+ end
+ end
+end
diff --git a/spec/ruby/core/method/hash_spec.rb b/spec/ruby/core/method/hash_spec.rb
new file mode 100644
index 0000000000..d6c8440acc
--- /dev/null
+++ b/spec/ruby/core/method/hash_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Method#hash" do
+ it "returns the same value for user methods that are eql?" do
+ obj = MethodSpecs::Methods.new
+ obj.method(:foo).hash.should == obj.method(:bar).hash
+ end
+
+ # See also redmine #6048
+ it "returns the same value for builtin methods that are eql?" do
+ obj = [42]
+ obj.method(:to_s).hash.should == obj.method(:inspect).hash
+ end
+end
diff --git a/spec/ruby/core/method/inspect_spec.rb b/spec/ruby/core/method/inspect_spec.rb
new file mode 100644
index 0000000000..e0fe1afdd0
--- /dev/null
+++ b/spec/ruby/core/method/inspect_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/to_s'
+
+describe "Method#inspect" do
+ it_behaves_like :method_to_s, :inspect
+end
diff --git a/spec/ruby/core/method/name_spec.rb b/spec/ruby/core/method/name_spec.rb
new file mode 100644
index 0000000000..de390c6f52
--- /dev/null
+++ b/spec/ruby/core/method/name_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Method#name" do
+ it "returns the name of the method" do
+ "abc".method(:upcase).name.should == :upcase
+ end
+
+ it "returns the name even when aliased" do
+ obj = MethodSpecs::Methods.new
+ obj.method(:foo).name.should == :foo
+ obj.method(:bar).name.should == :bar
+ obj.method(:bar).unbind.bind(obj).name.should == :bar
+ end
+
+ describe "for a Method generated by respond_to_missing?" do
+ it "returns the name passed to respond_to_missing?" do
+ @m = MethodSpecs::Methods.new
+ @m.method(:handled_via_method_missing).name.should == :handled_via_method_missing
+ end
+ end
+end
diff --git a/spec/ruby/core/method/original_name_spec.rb b/spec/ruby/core/method/original_name_spec.rb
new file mode 100644
index 0000000000..676fdaedb4
--- /dev/null
+++ b/spec/ruby/core/method/original_name_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Method#original_name" do
+ it "returns the name of the method" do
+ "abc".method(:upcase).original_name.should == :upcase
+ end
+
+ it "returns the original name when aliased" do
+ obj = MethodSpecs::Methods.new
+ obj.method(:foo).original_name.should == :foo
+ obj.method(:bar).original_name.should == :foo
+ obj.method(:bar).unbind.bind(obj).original_name.should == :foo
+ end
+
+ it "returns the original name even when aliased twice" do
+ obj = MethodSpecs::Methods.new
+ obj.method(:foo).original_name.should == :foo
+ obj.method(:baz).original_name.should == :foo
+ obj.method(:baz).unbind.bind(obj).original_name.should == :foo
+ end
+end
diff --git a/spec/ruby/core/method/owner_spec.rb b/spec/ruby/core/method/owner_spec.rb
new file mode 100644
index 0000000000..1cdc4edfa7
--- /dev/null
+++ b/spec/ruby/core/method/owner_spec.rb
@@ -0,0 +1,30 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Method#owner" do
+ it "returns the owner of the method" do
+ "abc".method(:upcase).owner.should == String
+ end
+
+ it "returns the same owner when aliased in the same classes" do
+ obj = MethodSpecs::Methods.new
+ obj.method(:foo).owner.should == MethodSpecs::Methods
+ obj.method(:bar).owner.should == MethodSpecs::Methods
+ end
+
+ it "returns the class/module it was defined in" do
+ MethodSpecs::C.new.method(:baz).owner.should == MethodSpecs::A
+ MethodSpecs::MySuper.new.method(:bar).owner.should == MethodSpecs::MyMod
+ end
+
+ describe "for a Method generated by respond_to_missing?" do
+ it "returns the owner of the method" do
+ @m = MethodSpecs::Methods.new
+ @m.method(:handled_via_method_missing).owner.should == MethodSpecs::Methods
+ end
+ end
+
+ it "returns the class on which public was called for a private method in ancestor" do
+ MethodSpecs::InheritedMethods::C.new.method(:derp).owner.should == MethodSpecs::InheritedMethods::C
+ end
+end
diff --git a/spec/ruby/core/method/parameters_spec.rb b/spec/ruby/core/method/parameters_spec.rb
new file mode 100644
index 0000000000..f1c2523cf0
--- /dev/null
+++ b/spec/ruby/core/method/parameters_spec.rb
@@ -0,0 +1,304 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Method#parameters" do
+ class MethodSpecs::Methods
+ def one_key(a: 1); end
+ def one_keyrest(**a); end
+
+ def one_keyreq(a:); end
+ def one_nokey(**nil); end
+
+ def one_splat_one_req(*a,b); end
+ def one_splat_two_req(*a,b,c); end
+ def one_splat_one_req_with_block(*a,b,&blk); end
+
+ def one_opt_with_stabby(a=-> b { true }); end
+
+ def one_unnamed_splat(*); end
+ def one_unnamed_keyrest(**); end
+
+ def one_splat_one_block(*args, &block)
+ local_is_not_parameter = {}
+ end
+
+ def forward_parameters(...) end
+
+ def underscore_parameters(_, _, _ = 1, *_, _:, _: 2, **_, &_); end
+
+ define_method(:one_optional_defined_method) {|x = 1|}
+ end
+
+ it "returns an empty Array when the method expects no arguments" do
+ MethodSpecs::Methods.instance_method(:zero).parameters.should == []
+ end
+
+ it "returns [[:req,:name]] for a method expecting one required argument called 'name'" do
+ MethodSpecs::Methods.instance_method(:one_req).parameters.should == [[:req,:a]]
+ end
+
+ it "returns [[:req,:a],[:req,:b]] for a method expecting two required arguments called 'a' and 'b''" do
+ m = MethodSpecs::Methods.instance_method(:two_req)
+ m.parameters.should == [[:req,:a], [:req,:b]]
+ end
+
+ it "returns [[:block,:blk]] for a method expecting one block argument called 'a'" do
+ m = MethodSpecs::Methods.instance_method(:zero_with_block)
+ m.parameters.should == [[:block,:blk]]
+ end
+
+ it "returns [[:req,:a],[:block,:b] for a method expecting a required argument ('a') and a block argument ('b')" do
+ m = MethodSpecs::Methods.instance_method(:one_req_with_block)
+ m.parameters.should == [[:req,:a], [:block,:blk]]
+ end
+
+ it "returns [[:req,:a],[:req,:b],[:block,:c] for a method expecting two required arguments ('a','b') and a block argument ('c')" do
+ m = MethodSpecs::Methods.instance_method(:two_req_with_block)
+ m.parameters.should == [[:req,:a], [:req,:b], [:block,:blk]]
+ end
+
+ it "returns [[:opt,:a]] for a method expecting one optional argument ('a')" do
+ m = MethodSpecs::Methods.instance_method(:one_opt)
+ m.parameters.should == [[:opt,:a]]
+ end
+
+ it "returns [[:req,:a],[:opt,:b]] for a method expecting one required argument ('a') and one optional argument ('b')" do
+ m = MethodSpecs::Methods.instance_method(:one_req_one_opt)
+ m.parameters.should == [[:req,:a],[:opt,:b]]
+ end
+
+ it "returns [[:req,:a],[:opt,:b]] for a method expecting one required argument ('a') and one optional argument ('b')" do
+ m = MethodSpecs::Methods.instance_method(:one_req_one_opt)
+ m.parameters.should == [[:req,:a],[:opt,:b]]
+ end
+
+ it "returns [[:req,:a],[:opt,:b],[:opt,:c]] for a method expecting one required argument ('a') and two optional arguments ('b','c')" do
+ m = MethodSpecs::Methods.instance_method(:one_req_two_opt)
+ m.parameters.should == [[:req,:a],[:opt,:b],[:opt,:c]]
+ end
+
+ it "returns [[:req,:a],[:req,:b],[:opt,:c]] for a method expecting two required arguments ('a','b') and one optional arguments ('c')" do
+ m = MethodSpecs::Methods.instance_method(:two_req_one_opt)
+ m.parameters.should == [[:req,:a],[:req,:b],[:opt,:c]]
+ end
+
+ it "returns [[:opt,:a],[:block,:b]] for a method expecting one required argument ('a') and one block argument ('b')" do
+ m = MethodSpecs::Methods.instance_method(:one_opt_with_block)
+ m.parameters.should == [[:opt,:a],[:block,:blk]]
+ end
+
+ it "returns [[:req,:a],[:opt,:b],[:block,:c]] for a method expecting one required argument ('a'), one optional argument ('b'), and a block ('c')" do
+ m = MethodSpecs::Methods.instance_method(:one_req_one_opt_with_block)
+ m.parameters.should == [[:req,:a],[:opt,:b],[:block,:blk]]
+ end
+
+ it "returns [[:req,:a],[:opt,:b],[:opt,:c],[:block,:d]] for a method expecting one required argument ('a'), two optional arguments ('b','c'), and a block ('d')" do
+ m = MethodSpecs::Methods.instance_method(:one_req_two_opt_with_block)
+ m.parameters.should == [[:req,:a],[:opt,:b],[:opt,:c],[:block,:blk]]
+ end
+
+ it "returns [[:rest,:a]] for a method expecting a single splat argument ('a')" do
+ m = MethodSpecs::Methods.instance_method(:zero_with_splat)
+ m.parameters.should == [[:rest,:a]]
+ end
+
+ it "returns [[:req,:a],[:rest,:b]] for a method expecting a splat argument ('a') and a required argument ('b')" do
+ m = MethodSpecs::Methods.instance_method(:one_req_with_splat)
+ m.parameters.should == [[:req,:a],[:rest,:b]]
+ end
+
+ it "returns [[:req,:a],[:req,:b],[:rest,:c]] for a method expecting two required arguments ('a','b') and a splat argument ('c')" do
+ m = MethodSpecs::Methods.instance_method(:two_req_with_splat)
+ m.parameters.should == [[:req,:a],[:req,:b],[:rest,:c]]
+ end
+
+ it "returns [[:req,:a],[:opt,:b],[:rest,:c]] for a method expecting a required argument ('a','b'), an optional argument ('b'), and a splat argument ('c')" do
+ m = MethodSpecs::Methods.instance_method(:one_req_one_opt_with_splat)
+ m.parameters.should == [[:req,:a],[:opt,:b],[:rest,:c]]
+ end
+
+ it "returns [[:req,:a],[:req,:b],[:opt,:b],[:rest,:d]] for a method expecting two required arguments ('a','b'), an optional argument ('c'), and a splat argument ('d')" do
+ m = MethodSpecs::Methods.instance_method(:two_req_one_opt_with_splat)
+ m.parameters.should == [[:req,:a],[:req,:b],[:opt,:c],[:rest,:d]]
+ end
+
+ it "returns [[:req,:a],[:opt,:b],[:opt,:c],[:rest,:d]] for a method expecting a required argument ('a'), two optional arguments ('b','c'), and a splat argument ('d')" do
+ m = MethodSpecs::Methods.instance_method(:one_req_two_opt_with_splat)
+ m.parameters.should == [[:req,:a],[:opt,:b],[:opt,:c],[:rest,:d]]
+ end
+
+ it "returns [[:rest,:a],[:block,:b]] for a method expecting a splat argument ('a') and a block argument ('b')" do
+ m = MethodSpecs::Methods.instance_method(:zero_with_splat_and_block)
+ m.parameters.should == [[:rest,:a],[:block,:blk]]
+ end
+
+ it "returns [[:req,:a],[:rest,:b],[:block,:c]] for a method expecting a required argument ('a'), a splat argument ('b'), and a block ('c')" do
+ m = MethodSpecs::Methods.instance_method(:one_req_with_splat_and_block)
+ m.parameters.should == [[:req,:a],[:rest,:b],[:block,:blk]]
+ end
+
+ it "returns [[:req,:a],[:req,:b],[:rest,:c],[:block,:d]] for a method expecting two required arguments ('a','b'), a splat argument ('c'), and a block ('d')" do
+ m = MethodSpecs::Methods.instance_method(:two_req_with_splat_and_block)
+ m.parameters.should == [[:req,:a],[:req,:b],[:rest,:c],[:block,:blk]]
+ end
+
+ it "returns [[:req,:a],[:opt,:b],[:rest,:c],[:block,:d]] for a method expecting a required argument ('a'), a splat argument ('c'), and a block ('d')" do
+ m = MethodSpecs::Methods.instance_method(:one_req_one_opt_with_splat_and_block)
+ m.parameters.should == [[:req,:a],[:opt,:b],[:rest,:c],[:block,:blk]]
+ end
+
+ it "returns [[:req,:a],[:req,:b],[:opt,:c],[:block,:d]] for a method expecting two required arguments ('a','b'), an optional argument ('c'), a splat argument ('d'), and a block ('e')" do
+ m = MethodSpecs::Methods.instance_method(:two_req_one_opt_with_splat_and_block)
+ m.parameters.should == [[:req,:a],[:req,:b],[:opt,:c],[:rest,:d],[:block,:blk]]
+ end
+
+ it "returns [[:rest,:a],[:req,:b]] for a method expecting a splat argument ('a') and a required argument ('b')" do
+ m = MethodSpecs::Methods.instance_method(:one_splat_one_req)
+ m.parameters.should == [[:rest,:a],[:req,:b]]
+ end
+
+ it "returns [[:rest,:a],[:req,:b],[:req,:c]] for a method expecting a splat argument ('a') and two required arguments ('b','c')" do
+ m = MethodSpecs::Methods.instance_method(:one_splat_two_req)
+ m.parameters.should == [[:rest,:a],[:req,:b],[:req,:c]]
+ end
+
+ it "returns [[:rest,:a],[:req,:b],[:block,:c]] for a method expecting a splat argument ('a'), a required argument ('b'), and a block ('c')" do
+ m = MethodSpecs::Methods.instance_method(:one_splat_one_req_with_block)
+ m.parameters.should == [[:rest,:a],[:req,:b],[:block,:blk]]
+ end
+
+ it "returns [[:key,:a]] for a method with a single optional keyword argument" do
+ m = MethodSpecs::Methods.instance_method(:one_key)
+ m.parameters.should == [[:key,:a]]
+ end
+
+ it "returns [[:keyrest,:a]] for a method with a keyword rest argument" do
+ m = MethodSpecs::Methods.instance_method(:one_keyrest)
+ m.parameters.should == [[:keyrest,:a]]
+ end
+
+ it "returns [[:keyreq,:a]] for a method with a single required keyword argument" do
+ m = MethodSpecs::Methods.instance_method(:one_keyreq)
+ m.parameters.should == [[:keyreq,:a]]
+ end
+
+ it "returns [[:nokey]] for a method with a single **nil parameter" do
+ m = MethodSpecs::Methods.instance_method(:one_nokey)
+ m.parameters.should == [[:nokey]]
+ end
+
+ it "works with ->(){} as the value of an optional argument" do
+ m = MethodSpecs::Methods.instance_method(:one_opt_with_stabby)
+ m.parameters.should == [[:opt,:a]]
+ end
+
+ # define_method variants
+ it "returns [] for a define_method method with explicit no-args || specification" do
+ m = MethodSpecs::Methods.instance_method(:zero_defined_method)
+ m.parameters.should == []
+ end
+
+ it "returns [[:rest, :x]] for a define_method method with rest arg 'x' only" do
+ m = MethodSpecs::Methods.instance_method(:zero_with_splat_defined_method)
+ m.parameters.should == [[:rest, :x]]
+ end
+
+ it "returns [[:req, :x]] for a define_method method expecting one required argument 'x'" do
+ m = MethodSpecs::Methods.instance_method(:one_req_defined_method)
+ m.parameters.should == [[:req, :x]]
+ end
+
+ it "returns [[:req, :x], [:req, :y]] for a define_method method expecting two required arguments 'x' and 'y'" do
+ m = MethodSpecs::Methods.instance_method(:two_req_defined_method)
+ m.parameters.should == [[:req, :x], [:req, :y]]
+ end
+
+ it "returns [] for a define_method method with no args specification" do
+ m = MethodSpecs::Methods.instance_method(:no_args_defined_method)
+ m.parameters.should == []
+ end
+
+ it "returns [[:req]] for a define_method method with a grouping as its only argument" do
+ m = MethodSpecs::Methods.instance_method(:two_grouped_defined_method)
+ m.parameters.should == [[:req]]
+ end
+
+ it "returns [[:opt, :x]] for a define_method method with an optional argument 'x'" do
+ m = MethodSpecs::Methods.instance_method(:one_optional_defined_method)
+ m.parameters.should == [[:opt, :x]]
+ end
+
+ it "returns [[:rest]] for a Method generated by respond_to_missing?" do
+ m = MethodSpecs::Methods.new
+ m.method(:handled_via_method_missing).parameters.should == [[:rest]]
+ end
+
+ it "adds rest arg with name * for \"star\" argument" do
+ m = MethodSpecs::Methods.new
+ m.method(:one_unnamed_splat).parameters.should == [[:rest, :*]]
+ end
+
+ it "adds keyrest arg with ** as a name for \"double star\" argument" do
+ m = MethodSpecs::Methods.new
+ m.method(:one_unnamed_keyrest).parameters.should == [[:keyrest, :**]]
+ end
+
+ it "adds block arg with name & for anonymous block argument" do
+ object = Object.new
+ def object.foo(&)
+ end
+
+ object.method(:foo).parameters.should == [[:block, :&]]
+ end
+
+ it "returns [:rest, :*], [:keyrest, :**], [:block, :&] for forward parameters operator" do
+ m = MethodSpecs::Methods.new
+ m.method(:forward_parameters).parameters.should == [[:rest, :*], [:keyrest, :**], [:block, :&]]
+ end
+
+ it "returns the args and block for a splat and block argument" do
+ m = MethodSpecs::Methods.new
+ m.method(:one_splat_one_block).parameters.should == [[:rest, :args], [:block, :block]]
+ end
+
+ it "returns [] for a Method generated by attr_reader" do
+ m = MethodSpecs::Methods.new
+ m.method(:reader).parameters.should == []
+ end
+
+ it "return [[:req]] for a Method generated by attr_writer" do
+ m = MethodSpecs::Methods.new
+ m.method(:writer=).parameters.should == [[:req]]
+ end
+
+ it "returns all parameters defined with the name _ as _" do
+ m = MethodSpecs::Methods.instance_method(:underscore_parameters)
+ m.parameters.should == [
+ [:req, :_],
+ [:req, :_],
+ [:opt, :_],
+ [:rest, :_],
+ [:keyreq, :_],
+ [:key, :_],
+ [:keyrest, :_],
+ [:block, :_]
+ ]
+ end
+
+ it "returns [[:rest]] for core methods with variable-length argument lists" do
+ # delete! takes rest args
+ "foo".method(:delete!).parameters.should == [[:rest]]
+ end
+
+ it "returns [[:rest]] or [[:opt]] for core methods with optional arguments" do
+ # pop takes 1 optional argument
+ [
+ [[:rest]],
+ [[:opt]]
+ ].should include([].method(:pop).parameters)
+ end
+
+ it "returns [[:req]] for each parameter for core methods with fixed-length argument lists" do
+ "foo".method(:+).parameters.should == [[:req]]
+ end
+end
diff --git a/spec/ruby/core/method/private_spec.rb b/spec/ruby/core/method/private_spec.rb
new file mode 100644
index 0000000000..e708542b2e
--- /dev/null
+++ b/spec/ruby/core/method/private_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Method#private?" do
+ it "has been removed" do
+ obj = MethodSpecs::Methods.new
+ obj.method(:my_private_method).should_not.respond_to?(:private?)
+ end
+end
diff --git a/spec/ruby/core/method/protected_spec.rb b/spec/ruby/core/method/protected_spec.rb
new file mode 100644
index 0000000000..f9e422ae3d
--- /dev/null
+++ b/spec/ruby/core/method/protected_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Method#protected?" do
+ it "has been removed" do
+ obj = MethodSpecs::Methods.new
+ obj.method(:my_protected_method).should_not.respond_to?(:protected?)
+ end
+end
diff --git a/spec/ruby/core/method/public_spec.rb b/spec/ruby/core/method/public_spec.rb
new file mode 100644
index 0000000000..4cb23f4cf1
--- /dev/null
+++ b/spec/ruby/core/method/public_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Method#public?" do
+ it "has been removed" do
+ obj = MethodSpecs::Methods.new
+ obj.method(:my_public_method).should_not.respond_to?(:public?)
+ end
+end
diff --git a/spec/ruby/core/method/receiver_spec.rb b/spec/ruby/core/method/receiver_spec.rb
new file mode 100644
index 0000000000..2b2e11dd2e
--- /dev/null
+++ b/spec/ruby/core/method/receiver_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Method#receiver" do
+ it "returns the receiver of the method" do
+ s = "abc"
+ s.method(:upcase).receiver.should equal(s)
+ end
+
+ it "returns the right receiver even when aliased" do
+ obj = MethodSpecs::Methods.new
+ obj.method(:foo).receiver.should equal(obj)
+ obj.method(:bar).receiver.should equal(obj)
+ end
+
+ describe "for a Method generated by respond_to_missing?" do
+ it "returns the receiver of the method" do
+ m = MethodSpecs::Methods.new
+ m.method(:handled_via_method_missing).receiver.should equal(m)
+ end
+ end
+end
diff --git a/spec/ruby/core/method/shared/call.rb b/spec/ruby/core/method/shared/call.rb
new file mode 100644
index 0000000000..f26e373695
--- /dev/null
+++ b/spec/ruby/core/method/shared/call.rb
@@ -0,0 +1,51 @@
+describe :method_call, shared: true do
+ it "invokes the method with the specified arguments, returning the method's return value" do
+ m = 12.method("+")
+ m.send(@method, 3).should == 15
+ m.send(@method, 20).should == 32
+
+ m = MethodSpecs::Methods.new.method(:attr=)
+ m.send(@method, 42).should == 42
+ end
+
+ it "raises an ArgumentError when given incorrect number of arguments" do
+ -> {
+ MethodSpecs::Methods.new.method(:two_req).send(@method, 1, 2, 3)
+ }.should raise_error(ArgumentError)
+ -> {
+ MethodSpecs::Methods.new.method(:two_req).send(@method, 1)
+ }.should raise_error(ArgumentError)
+ end
+
+ describe "for a Method generated by respond_to_missing?" do
+ it "invokes method_missing with the specified arguments and returns the result" do
+ @m = MethodSpecs::Methods.new
+ meth = @m.method(:handled_via_method_missing)
+ meth.send(@method, :argument).should == [:argument]
+ end
+
+ it "invokes method_missing with the method name and the specified arguments" do
+ @m = MethodSpecs::Methods.new
+ meth = @m.method(:handled_via_method_missing)
+
+ @m.should_receive(:method_missing).with(:handled_via_method_missing, :argument)
+ meth.send(@method, :argument)
+ end
+
+ it "invokes method_missing dynamically" do
+ @m = MethodSpecs::Methods.new
+ meth = @m.method(:handled_via_method_missing)
+
+ def @m.method_missing(*); :changed; end
+ meth.send(@method, :argument).should == :changed
+ end
+
+ it "does not call the original method name even if it now exists" do
+ @m = MethodSpecs::Methods.new
+ meth = @m.method(:handled_via_method_missing)
+
+ def @m.handled_via_method_missing(*); :not_called; end
+ meth.send(@method, :argument).should == [:argument]
+ end
+ end
+end
diff --git a/spec/ruby/core/method/shared/dup.rb b/spec/ruby/core/method/shared/dup.rb
new file mode 100644
index 0000000000..c74847083f
--- /dev/null
+++ b/spec/ruby/core/method/shared/dup.rb
@@ -0,0 +1,32 @@
+describe :method_dup, shared: true do
+ it "returns a copy of self" do
+ a = Object.new.method(:method)
+ b = a.send(@method)
+
+ a.should == b
+ a.should_not equal(b)
+ end
+
+ ruby_version_is "3.4" do
+ it "copies instance variables" do
+ method = Object.new.method(:method)
+ method.instance_variable_set(:@ivar, 1)
+ cl = method.send(@method)
+ cl.instance_variables.should == [:@ivar]
+ end
+
+ it "copies the finalizer" do
+ code = <<-'RUBY'
+ obj = Object.new.method(:method)
+
+ ObjectSpace.define_finalizer(obj, Proc.new { STDOUT.write "finalized\n" })
+
+ obj.clone
+
+ exit 0
+ RUBY
+
+ ruby_exe(code).lines.sort.should == ["finalized\n", "finalized\n"]
+ end
+ end
+end
diff --git a/spec/ruby/core/method/shared/eql.rb b/spec/ruby/core/method/shared/eql.rb
new file mode 100644
index 0000000000..5c720cbac1
--- /dev/null
+++ b/spec/ruby/core/method/shared/eql.rb
@@ -0,0 +1,94 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe :method_equal, shared: true do
+ before :each do
+ @m = MethodSpecs::Methods.new
+ @m_foo = @m.method(:foo)
+ @m2 = MethodSpecs::Methods.new
+ @a = MethodSpecs::A.new
+ end
+
+ it "returns true if methods are the same" do
+ m2 = @m.method(:foo)
+
+ @m_foo.send(@method, @m_foo).should be_true
+ @m_foo.send(@method, m2).should be_true
+ end
+
+ it "returns true on aliased methods" do
+ m_bar = @m.method(:bar)
+
+ m_bar.send(@method, @m_foo).should be_true
+ end
+
+ it "returns true if the two core methods are aliases" do
+ s = "hello"
+ a = s.method(:size)
+ b = s.method(:length)
+ a.send(@method, b).should be_true
+ end
+
+ it "returns false on a method which is neither aliased nor the same method" do
+ m2 = @m.method(:zero)
+
+ @m_foo.send(@method, m2).should be_false
+ end
+
+ it "returns false for a method which is not bound to the same object" do
+ m2_foo = @m2.method(:foo)
+ a_baz = @a.method(:baz)
+
+ @m_foo.send(@method, m2_foo).should be_false
+ @m_foo.send(@method, a_baz).should be_false
+ end
+
+ it "returns false if the two methods are bound to the same object but were defined independently" do
+ m2 = @m.method(:same_as_foo)
+ @m_foo.send(@method, m2).should be_false
+ end
+
+ it "returns true if a method was defined using the other one" do
+ MethodSpecs::Methods.send :define_method, :defined_foo, MethodSpecs::Methods.instance_method(:foo)
+ m2 = @m.method(:defined_foo)
+ @m_foo.send(@method, m2).should be_true
+ end
+
+ it "returns false if comparing a method defined via define_method and def" do
+ defn = @m.method(:zero)
+ defined = @m.method(:zero_defined_method)
+
+ defn.send(@method, defined).should be_false
+ defined.send(@method, defn).should be_false
+ end
+
+ describe 'missing methods' do
+ it "returns true for the same method missing" do
+ miss1 = @m.method(:handled_via_method_missing)
+ miss1bis = @m.method(:handled_via_method_missing)
+ miss2 = @m.method(:also_handled)
+
+ miss1.send(@method, miss1bis).should be_true
+ miss1.send(@method, miss2).should be_false
+ end
+
+ it 'calls respond_to_missing? with true to include private methods' do
+ @m.should_receive(:respond_to_missing?).with(:some_missing_method, true).and_return(true)
+ @m.method(:some_missing_method)
+ end
+ end
+
+ it "returns false if the two methods are bound to different objects, have the same names, and identical bodies" do
+ a = MethodSpecs::Eql.instance_method(:same_body)
+ b = MethodSpecs::Eql2.instance_method(:same_body)
+ a.send(@method, b).should be_false
+ end
+
+ it "returns false if the argument is not a Method object" do
+ String.instance_method(:size).send(@method, 7).should be_false
+ end
+
+ it "returns false if the argument is an unbound version of self" do
+ method(:load).send(@method, method(:load).unbind).should be_false
+ end
+end
diff --git a/spec/ruby/core/method/shared/to_s.rb b/spec/ruby/core/method/shared/to_s.rb
new file mode 100644
index 0000000000..b2d27d370f
--- /dev/null
+++ b/spec/ruby/core/method/shared/to_s.rb
@@ -0,0 +1,81 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe :method_to_s, shared: true do
+ before :each do
+ @m = MethodSpecs::MySub.new.method :bar
+ @string = @m.send(@method)
+ end
+
+ it "returns a String" do
+ @m.send(@method).should be_kind_of(String)
+ end
+
+ it "returns a String for methods defined with attr_accessor" do
+ m = MethodSpecs::Methods.new.method :attr
+ m.send(@method).should be_kind_of(String)
+ end
+
+ it "returns a String containing 'Method'" do
+ @string.should =~ /\bMethod\b/
+ end
+
+ it "returns a String containing the method name" do
+ @string.should =~ /\#bar/
+ end
+
+ it "returns a String containing method arguments" do
+ obj = MethodSpecs::Methods.new
+ obj.method(:zero).send(@method).should.include?("()")
+ obj.method(:one_req).send(@method).should.include?("(a)")
+ obj.method(:one_req_named).send(@method).should.include?("(a:)")
+ obj.method(:zero_with_block).send(@method).should.include?("(&blk)")
+ obj.method(:one_opt).send(@method).should.include?("(a=...)")
+ obj.method(:one_opt_named).send(@method).should.include?("(a: ...)")
+ obj.method(:zero_with_splat).send(@method).should.include?("(*a)")
+ obj.method(:zero_with_double_splat).send(@method).should.include?("(**a)")
+ obj.method(:one_req_one_opt_with_splat_and_block).send(@method).should.include?("(a, b=..., *c, &blk)")
+ end
+
+ it "returns a String containing the Module the method is defined in" do
+ @string.should =~ /MethodSpecs::MyMod/
+ end
+
+ it "returns a String containing the Module the method is referenced from" do
+ @string.should =~ /MethodSpecs::MySub/
+ end
+
+ it "returns a String including all details" do
+ @string.should.start_with? "#<Method: MethodSpecs::MySub(MethodSpecs::MyMod)#bar"
+ end
+
+ it "does not show the defining module if it is the same as the receiver class" do
+ MethodSpecs::A.new.method(:baz).send(@method).should.start_with? "#<Method: MethodSpecs::A#baz"
+ end
+
+ it "returns a String containing the Module containing the method if object has a singleton class but method is not defined in the singleton class" do
+ obj = MethodSpecs::MySub.new
+ obj.singleton_class
+ @m = obj.method(:bar)
+ @string = @m.send(@method)
+ @string.should.start_with? "#<Method: MethodSpecs::MySub(MethodSpecs::MyMod)#bar"
+
+ c = MethodSpecs::MySub.dup
+ m = Module.new{def bar; end}
+ c.extend(m)
+ @string = c.method(:bar).send(@method)
+ @string.should.start_with? "#<Method: #<Class:#{c.inspect}>(#{m.inspect})#bar"
+ end
+
+ it "returns a String containing the singleton class if method is defined in the singleton class" do
+ obj = MethodSpecs::MySub.new
+ def obj.bar; end
+ @m = obj.method(:bar)
+ @string = @m.send(@method).sub(/0x\h+/, '0xXXXXXX')
+ @string.should.start_with? "#<Method: #<MethodSpecs::MySub:0xXXXXXX>.bar"
+ end
+
+ it "shows the metaclass and the owner for a Module instance method retrieved from a class" do
+ String.method(:include).inspect.should.start_with?("#<Method: #<Class:String>(Module)#include")
+ end
+end
diff --git a/spec/ruby/core/method/source_location_spec.rb b/spec/ruby/core/method/source_location_spec.rb
new file mode 100644
index 0000000000..87413a2ab6
--- /dev/null
+++ b/spec/ruby/core/method/source_location_spec.rb
@@ -0,0 +1,126 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Method#source_location" do
+ before :each do
+ @method = MethodSpecs::SourceLocation.method(:location)
+ end
+
+ it "returns an Array" do
+ @method.source_location.should be_an_instance_of(Array)
+ end
+
+ it "sets the first value to the path of the file in which the method was defined" do
+ file = @method.source_location[0]
+ file.should be_an_instance_of(String)
+ file.should == File.realpath('fixtures/classes.rb', __dir__)
+ end
+
+ it "sets the last value to an Integer representing the line on which the method was defined" do
+ line = @method.source_location[1]
+ line.should be_an_instance_of(Integer)
+ line.should == 5
+ end
+
+ it "returns the last place the method was defined" do
+ MethodSpecs::SourceLocation.method(:redefined).source_location[1].should == 13
+ end
+
+ it "returns the location of the original method even if it was aliased" do
+ MethodSpecs::SourceLocation.new.method(:aka).source_location[1].should == 17
+ end
+
+ it "works for methods defined with a block" do
+ line = nil
+ klass = Class.new do
+ line = __LINE__ + 1
+ define_method(:f) { }
+ end
+
+ method = klass.new.method(:f)
+ method.source_location[0].should =~ /#{__FILE__}/
+ method.source_location[1].should == line
+ end
+
+ it "works for methods defined with a Method" do
+ line = nil
+ klass = Class.new do
+ line = __LINE__ + 1
+ def f
+ end
+ define_method :g, new.method(:f)
+ end
+
+ method = klass.new.method(:g)
+ method.source_location[0].should =~ /#{__FILE__}/
+ method.source_location[1].should == line
+ end
+
+ it "works for methods defined with an UnboundMethod" do
+ line = nil
+ klass = Class.new do
+ line = __LINE__ + 1
+ def f
+ end
+ define_method :g, instance_method(:f)
+ end
+
+ method = klass.new.method(:g)
+ method.source_location[0].should =~ /#{__FILE__}/
+ method.source_location[1].should == line
+ end
+
+ it "works for methods whose visibility has been overridden in a subclass" do
+ line = nil
+ superclass = Class.new do
+ line = __LINE__ + 1
+ def f
+ end
+ end
+ subclass = Class.new(superclass) do
+ private :f
+ end
+
+ method = subclass.new.method(:f)
+ method.source_location[0].should =~ /#{__FILE__}/
+ method.source_location[1].should == line
+ end
+
+ it "works for core methods where it returns nil or <internal:" do
+ loc = method(:__id__).source_location
+ if loc == nil
+ loc.should == nil
+ else
+ loc[0].should.start_with?('<internal:')
+ loc[1].should be_kind_of(Integer)
+ end
+
+ loc = method(:tap).source_location
+ if loc == nil
+ loc.should == nil
+ else
+ loc[0].should.start_with?('<internal:')
+ loc[1].should be_kind_of(Integer)
+ end
+ end
+
+ it "works for eval with a given line" do
+ c = Class.new do
+ eval('def self.m; end', nil, "foo", 100)
+ end
+ location = c.method(:m).source_location
+ ruby_version_is(""..."4.1") do
+ location.should == ["foo", 100]
+ end
+ ruby_version_is("4.1") do
+ location.should == ["foo", 100, 0, 100, 15]
+ end
+ end
+
+ describe "for a Method generated by respond_to_missing?" do
+ it "returns nil" do
+ m = MethodSpecs::Methods.new
+ m.method(:handled_via_method_missing).source_location.should be_nil
+ end
+ end
+end
diff --git a/spec/ruby/core/method/super_method_spec.rb b/spec/ruby/core/method/super_method_spec.rb
new file mode 100644
index 0000000000..c63a7aaa0f
--- /dev/null
+++ b/spec/ruby/core/method/super_method_spec.rb
@@ -0,0 +1,64 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Method#super_method" do
+ it "returns the method that would be called by super in the method" do
+ obj = MethodSpecs::C.new
+ obj.extend MethodSpecs::OverrideAgain
+ meth = obj.method(:overridden)
+
+ s_meth = meth.super_method
+ s_meth.owner.should == MethodSpecs::C
+ s_meth.receiver.should == obj
+ s_meth.name.should == :overridden
+
+ ss_meth = meth.super_method.super_method
+ ss_meth.owner.should == MethodSpecs::BetweenBAndC
+ ss_meth.receiver.should == obj
+ ss_meth.name.should == :overridden
+
+ sss_meth = meth.super_method.super_method.super_method
+ sss_meth.owner.should == MethodSpecs::B
+ sss_meth.receiver.should == obj
+ sss_meth.name.should == :overridden
+ end
+
+ it "returns nil when there's no super method in the parent" do
+ method = Object.new.method(:method)
+ method.super_method.should == nil
+ end
+
+ it "returns nil when the parent's method is removed" do
+ klass = Class.new do
+ def overridden; end
+ end
+ sub = Class.new(klass) do
+ def overridden; end
+ end
+ object = sub.new
+ method = object.method(:overridden)
+
+ klass.class_eval { undef :overridden }
+
+ method.super_method.should == nil
+ end
+
+ # https://github.com/jruby/jruby/issues/7240
+ context "after changing an inherited methods visibility" do
+ it "calls the proper super method" do
+ MethodSpecs::InheritedMethods::C.new.derp.should == 'BA'
+ end
+
+ it "returns the expected super_method" do
+ method = MethodSpecs::InheritedMethods::C.new.method(:derp)
+ method.super_method.owner.should == MethodSpecs::InheritedMethods::A
+ end
+ end
+
+ context "after aliasing an inherited method" do
+ it "returns the expected super_method" do
+ method = MethodSpecs::InheritedMethods::C.new.method(:meow)
+ method.super_method.owner.should == MethodSpecs::InheritedMethods::A
+ end
+ end
+end
diff --git a/spec/ruby/core/method/to_proc_spec.rb b/spec/ruby/core/method/to_proc_spec.rb
new file mode 100644
index 0000000000..4993cce239
--- /dev/null
+++ b/spec/ruby/core/method/to_proc_spec.rb
@@ -0,0 +1,104 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Method#to_proc" do
+ before :each do
+ ScratchPad.record []
+
+ @m = MethodSpecs::Methods.new
+ @meth = @m.method(:foo)
+ end
+
+ it "returns a Proc object corresponding to the method" do
+ @meth.to_proc.kind_of?(Proc).should == true
+ end
+
+ it "returns a Proc which does not depends on the value of self" do
+ 3.instance_exec(4, &5.method(:+)).should == 9
+ end
+
+
+ it "returns a Proc object with the correct arity" do
+ # This may seem redundant but this bug has cropped up in jruby, mri and yarv.
+ # http://jira.codehaus.org/browse/JRUBY-124
+ [ :zero, :one_req, :two_req,
+ :zero_with_block, :one_req_with_block, :two_req_with_block,
+ :one_opt, :one_req_one_opt, :one_req_two_opt, :two_req_one_opt,
+ :one_opt_with_block, :one_req_one_opt_with_block, :one_req_two_opt_with_block, :two_req_one_opt_with_block,
+ :zero_with_splat, :one_req_with_splat, :two_req_with_splat,
+ :one_req_one_opt_with_splat, :one_req_two_opt_with_splat, :two_req_one_opt_with_splat,
+ :zero_with_splat_and_block, :one_req_with_splat_and_block, :two_req_with_splat_and_block,
+ :one_req_one_opt_with_splat_and_block, :one_req_two_opt_with_splat_and_block, :two_req_one_opt_with_splat_and_block
+ ].each do |m|
+ @m.method(m).to_proc.arity.should == @m.method(m).arity
+ end
+ end
+
+ it "returns a proc that can be used by define_method" do
+ x = +'test'
+ to_s = class << x
+ define_method :foo, method(:to_s).to_proc
+ to_s
+ end
+
+ x.foo.should == to_s
+ end
+
+ it "returns a proc that can be yielded to" do
+ x = Object.new
+ def x.foo(*a); a; end
+ def x.bar; yield; end
+ def x.baz(*a); yield(*a); end
+
+ m = x.method :foo
+ x.bar(&m).should == []
+ x.baz(1,2,3,&m).should == [1,2,3]
+ end
+
+ it "returns a proc whose binding has the same receiver as the method" do
+ @meth.receiver.should == @meth.to_proc.binding.receiver
+ end
+
+ # #5926
+ it "returns a proc that can receive a block" do
+ x = Object.new
+ def x.foo; yield 'bar'; end
+
+ m = x.method :foo
+ result = nil
+ m.to_proc.call {|val| result = val}
+ result.should == 'bar'
+ end
+
+ it "can be called directly and not unwrap arguments like a block" do
+ obj = MethodSpecs::ToProcBeta.new
+ obj.to_proc.call([1]).should == [1]
+ end
+
+ it "should correct handle arguments (unwrap)" do
+ obj = MethodSpecs::ToProcBeta.new
+
+ array = [[1]]
+ array.each(&obj)
+ ScratchPad.recorded.should == [[1]]
+ end
+
+ it "executes method with whole array (one argument)" do
+ obj = MethodSpecs::ToProcBeta.new
+
+ array = [[1, 2]]
+ array.each(&obj)
+ ScratchPad.recorded.should == [[1, 2]]
+ end
+
+ it "returns a proc that properly invokes module methods with super" do
+ m1 = Module.new { def foo(ary); ary << :m1; end; }
+ m2 = Module.new { def foo(ary = []); super(ary); ary << :m2; end; }
+ c2 = Class.new do
+ include m1
+ include m2
+ end
+
+ c2.new.method(:foo).to_proc.call.should == %i[m1 m2]
+ end
+end
diff --git a/spec/ruby/core/method/to_s_spec.rb b/spec/ruby/core/method/to_s_spec.rb
new file mode 100644
index 0000000000..9f19011302
--- /dev/null
+++ b/spec/ruby/core/method/to_s_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/to_s'
+
+describe "Method#to_s" do
+ it_behaves_like :method_to_s, :to_s
+end
diff --git a/spec/ruby/core/method/unbind_spec.rb b/spec/ruby/core/method/unbind_spec.rb
new file mode 100644
index 0000000000..0b630e4d88
--- /dev/null
+++ b/spec/ruby/core/method/unbind_spec.rb
@@ -0,0 +1,46 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Method#unbind" do
+ before :each do
+ @normal = MethodSpecs::Methods.new
+ @normal_m = @normal.method :foo
+ @normal_um = @normal_m.unbind
+ @pop_um = MethodSpecs::MySub.new.method(:bar).unbind
+ @string = @pop_um.inspect.sub(/0x\w+/, '0xXXXXXX')
+ end
+
+ it "returns an UnboundMethod" do
+ @normal_um.should be_kind_of(UnboundMethod)
+ end
+
+ describe "#inspect" do
+ it "returns a String containing 'UnboundMethod'" do
+ @string.should =~ /\bUnboundMethod\b/
+ end
+
+ it "returns a String containing the method name" do
+ @string.should =~ /\#bar/
+ end
+
+ it "returns a String containing the Module the method is defined in" do
+ @string.should =~ /MethodSpecs::MyMod/
+ end
+
+ it "returns a String containing the Module the method is referenced from" do
+ @string.should =~ /MethodSpecs::MyMod/
+ end
+ end
+
+ it "keeps the origin singleton class if there is one" do
+ obj = Object.new
+ def obj.foo
+ end
+ obj.method(:foo).unbind.inspect.should.start_with?("#<UnboundMethod: #{obj.singleton_class}#foo")
+ end
+
+ specify "rebinding UnboundMethod to Method's obj produces exactly equivalent Methods" do
+ @normal_um.bind(@normal).should == @normal_m
+ @normal_m.should == @normal_um.bind(@normal)
+ end
+end