summaryrefslogtreecommitdiff
path: root/spec/ruby/core/module/define_method_spec.rb
diff options
context:
space:
mode:
Diffstat (limited to 'spec/ruby/core/module/define_method_spec.rb')
-rw-r--r--spec/ruby/core/module/define_method_spec.rb294
1 files changed, 242 insertions, 52 deletions
diff --git a/spec/ruby/core/module/define_method_spec.rb b/spec/ruby/core/module/define_method_spec.rb
index 3f35c051a1..c5dfc53764 100644
--- a/spec/ruby/core/module/define_method_spec.rb
+++ b/spec/ruby/core/module/define_method_spec.rb
@@ -12,7 +12,7 @@ describe "passed { |a, b = 1| } creates a method that" do
end
it "raises an ArgumentError when passed zero arguments" do
- lambda { @klass.new.m }.should raise_error(ArgumentError)
+ -> { @klass.new.m }.should raise_error(ArgumentError)
end
it "has a default value for b when passed one argument" do
@@ -24,7 +24,7 @@ describe "passed { |a, b = 1| } creates a method that" do
end
it "raises an ArgumentError when passed three arguments" do
- lambda { @klass.new.m(1, 2, 3) }.should raise_error(ArgumentError)
+ -> { @klass.new.m(1, 2, 3) }.should raise_error(ArgumentError)
end
end
@@ -83,11 +83,28 @@ describe "Module#define_method when given an UnboundMethod" do
define_method :piggy, instance_method(:ziggy)
end
- lambda { foo.new.ziggy }.should raise_error(NoMethodError)
+ -> { foo.new.ziggy }.should raise_error(NoMethodError)
foo.new.piggy.should == 'piggy'
end
end
+describe "Module#define_method" do
+ describe "when the default definee is not the same as the module" do
+ it "sets the visibility of the method to public" do
+ klass = Class.new
+ class << klass
+ private
+ define_method(:meta) do
+ define_method(:foo) { :foo }
+ end
+ end
+
+ klass.send :meta
+ klass.new.foo.should == :foo
+ end
+ end
+end
+
describe "Module#define_method when name is not a special private name" do
describe "given an UnboundMethod" do
describe "and called from the target module" do
@@ -116,6 +133,17 @@ describe "Module#define_method when name is not a special private name" do
klass.should have_public_instance_method(:baz)
end
end
+
+ it "sets the method owner for a dynamically added method with a different original owner" do
+ mixin_module = Module.new do
+ def bar; end
+ end
+
+ foo = Object.new
+ foo.singleton_class.define_method(:bar, mixin_module.instance_method(:bar))
+
+ foo.method(:bar).owner.should == foo.singleton_class
+ end
end
describe "passed a block" do
@@ -194,7 +222,7 @@ describe "Module#define_method" do
it "defines a new method with the given name and the given block as body in self" do
class DefineMethodSpecClass
define_method(:block_test1) { self }
- define_method(:block_test2, &lambda { self })
+ define_method(:block_test2, &-> { self })
end
o = DefineMethodSpecClass.new
@@ -202,22 +230,59 @@ describe "Module#define_method" do
o.block_test2.should == o
end
+ it "raises TypeError if name cannot converted to String" do
+ -> {
+ Class.new { define_method(1001, -> {}) }
+ }.should raise_error(TypeError, /is not a symbol nor a string/)
+
+ -> {
+ Class.new { define_method([], -> {}) }
+ }.should raise_error(TypeError, /is not a symbol nor a string/)
+ end
+
+ it "converts non-String name to String with #to_str" do
+ obj = Object.new
+ def obj.to_str() "foo" end
+
+ new_class = Class.new { define_method(obj, -> { :called }) }
+ new_class.new.foo.should == :called
+ end
+
+ it "raises TypeError when #to_str called on non-String name returns non-String value" do
+ obj = Object.new
+ def obj.to_str() [] end
+
+ -> {
+ Class.new { define_method(obj, -> {}) }
+ }.should raise_error(TypeError, /can't convert Object to String/)
+ end
+
it "raises a TypeError when the given method is no Method/Proc" do
- lambda {
+ -> {
Class.new { define_method(:test, "self") }
- }.should raise_error(TypeError)
+ }.should raise_error(TypeError, "wrong argument type String (expected Proc/Method/UnboundMethod)")
- lambda {
+ -> {
Class.new { define_method(:test, 1234) }
- }.should raise_error(TypeError)
+ }.should raise_error(TypeError, "wrong argument type Integer (expected Proc/Method/UnboundMethod)")
- lambda {
+ -> {
Class.new { define_method(:test, nil) }
- }.should raise_error(TypeError)
+ }.should raise_error(TypeError, "wrong argument type NilClass (expected Proc/Method/UnboundMethod)")
+ end
+
+ it "uses provided Method/Proc even if block is specified" do
+ new_class = Class.new do
+ define_method(:test, -> { :method_is_called }) do
+ :block_is_called
+ end
+ end
+
+ new_class.new.test.should == :method_is_called
end
it "raises an ArgumentError when no block is given" do
- lambda {
+ -> {
Class.new { define_method(:test) }
}.should raise_error(ArgumentError)
end
@@ -230,7 +295,7 @@ describe "Module#define_method" do
end
end
- lambda {
+ -> {
o.define(:foo) { raise "not used" }
}.should raise_error(ArgumentError)
end
@@ -242,13 +307,13 @@ describe "Module#define_method" do
end
obj = DefineMethodSpecClass.new
- lambda { obj.proc_style_test :arg }.should raise_error(ArgumentError)
+ -> { obj.proc_style_test :arg }.should raise_error(ArgumentError)
end
- it "raises a #{frozen_error_class} if frozen" do
- lambda {
+ it "raises a FrozenError if frozen" do
+ -> {
Class.new { freeze; define_method(:foo) {} }
- }.should raise_error(frozen_error_class)
+ }.should raise_error(FrozenError)
end
it "accepts a Method (still bound)" do
@@ -267,7 +332,7 @@ describe "Module#define_method" do
c = klass.new
c.data = :bar
c.other_inspect.should == "data is bar"
- lambda{o.other_inspect}.should raise_error(NoMethodError)
+ ->{o.other_inspect}.should raise_error(NoMethodError)
end
it "raises a TypeError when a Method from a singleton class is defined on another class" do
@@ -279,9 +344,9 @@ describe "Module#define_method" do
end
m = c.method(:foo)
- lambda {
+ -> {
Class.new { define_method :bar, m }
- }.should raise_error(TypeError)
+ }.should raise_error(TypeError, /can't bind singleton method to a different class/)
end
it "raises a TypeError when a Method from one class is defined on an unrelated class" do
@@ -291,7 +356,7 @@ describe "Module#define_method" do
end
m = c.new.method(:foo)
- lambda {
+ -> {
Class.new { define_method :bar, m }
}.should raise_error(TypeError)
end
@@ -305,7 +370,7 @@ describe "Module#define_method" do
o = DefineMethodSpecClass.new
DefineMethodSpecClass.send(:undef_method, :accessor_method)
- lambda { o.accessor_method }.should raise_error(NoMethodError)
+ -> { o.accessor_method }.should raise_error(NoMethodError)
DefineMethodSpecClass.send(:define_method, :accessor_method, m)
@@ -333,6 +398,14 @@ describe "Module#define_method" do
object2.other_cool_method.should == "data is foo"
end
+ it "accepts a proc from a Symbol" do
+ symbol_proc = :+.to_proc
+ klass = Class.new do
+ define_method :foo, &symbol_proc
+ end
+ klass.new.foo(1, 2).should == 3
+ end
+
it "maintains the Proc's scope" do
class DefineMethodByProcClass
in_scope = true
@@ -355,15 +428,8 @@ describe "Module#define_method" do
klass.new.string_test.should == "string_test result"
end
- ruby_version_is ''...'2.5' do
- it "is a private method" do
- Module.should have_private_instance_method(:define_method)
- end
- end
- ruby_version_is '2.5' do
- it "is a public method" do
- Module.should have_public_instance_method(:define_method)
- end
+ it "is a public method" do
+ Module.should have_public_instance_method(:define_method)
end
it "returns its symbol" do
@@ -396,20 +462,72 @@ describe "Module#define_method" do
klass.new.should respond_to(:bar)
end
+
+ it "allows an UnboundMethod of a Kernel method retrieved from Object to defined on a BasicObject subclass" do
+ klass = Class.new(BasicObject) do
+ define_method :instance_of?, ::Object.instance_method(:instance_of?)
+ end
+ klass.new.instance_of?(klass).should == true
+ end
+
it "raises a TypeError when an UnboundMethod from a child class is defined on a parent class" do
- lambda {
+ -> {
ParentClass = Class.new { define_method(:foo) { :bar } }
ChildClass = Class.new(ParentClass) { define_method(:foo) { :baz } }
ParentClass.send :define_method, :foo, ChildClass.instance_method(:foo)
- }.should raise_error(TypeError)
+ }.should raise_error(TypeError, /bind argument must be a subclass of ChildClass/)
+ ensure
+ Object.send(:remove_const, :ParentClass)
+ Object.send(:remove_const, :ChildClass)
end
it "raises a TypeError when an UnboundMethod from one class is defined on an unrelated class" do
- lambda {
+ -> {
DestinationClass = Class.new {
define_method :bar, ModuleSpecs::InstanceMeth.instance_method(:foo)
}
- }.should raise_error(TypeError)
+ }.should raise_error(TypeError, /bind argument must be a subclass of ModuleSpecs::InstanceMeth/)
+ end
+
+ it "raises a TypeError when an UnboundMethod from a singleton class is defined on another class" do
+ c = Class.new do
+ class << self
+ def foo
+ end
+ end
+ end
+ m = c.method(:foo).unbind
+
+ -> {
+ Class.new { define_method :bar, m }
+ }.should raise_error(TypeError, /can't bind singleton method to a different class/)
+ end
+
+ it "defines a new method with public visibility when a Method passed and the class/module of the context isn't equal to the receiver of #define_method" do
+ c = Class.new do
+ private def foo
+ "public"
+ end
+ end
+
+ object = c.new
+ object.singleton_class.define_method(:bar, object.method(:foo))
+
+ object.bar.should == "public"
+ end
+
+ it "defines the new method according to the scope visibility when a Method passed and the class/module of the context is equal to the receiver of #define_method" do
+ c = Class.new do
+ def foo; end
+ end
+
+ object = c.new
+ object.singleton_class.class_eval do
+ private
+ define_method(:bar, c.new.method(:foo))
+ end
+
+ -> { object.bar }.should raise_error(NoMethodError)
end
end
@@ -426,11 +544,11 @@ describe "Module#define_method" do
end
it "raises an ArgumentError when passed one argument" do
- lambda { @klass.new.m 1 }.should raise_error(ArgumentError)
+ -> { @klass.new.m 1 }.should raise_error(ArgumentError)
end
it "raises an ArgumentError when passed two arguments" do
- lambda { @klass.new.m 1, 2 }.should raise_error(ArgumentError)
+ -> { @klass.new.m 1, 2 }.should raise_error(ArgumentError)
end
end
@@ -446,11 +564,11 @@ describe "Module#define_method" do
end
it "raises an ArgumentError when passed one argument" do
- lambda { @klass.new.m 1 }.should raise_error(ArgumentError)
+ -> { @klass.new.m 1 }.should raise_error(ArgumentError)
end
it "raises an ArgumentError when passed two arguments" do
- lambda { @klass.new.m 1, 2 }.should raise_error(ArgumentError)
+ -> { @klass.new.m 1, 2 }.should raise_error(ArgumentError)
end
end
@@ -462,21 +580,50 @@ describe "Module#define_method" do
end
it "raises an ArgumentError when passed zero arguments" do
- lambda { @klass.new.m }.should raise_error(ArgumentError)
+ -> { @klass.new.m }.should raise_error(ArgumentError)
end
it "raises an ArgumentError when passed zero arguments and a block" do
- lambda { @klass.new.m { :computed } }.should raise_error(ArgumentError)
+ -> { @klass.new.m { :computed } }.should raise_error(ArgumentError)
end
it "raises an ArgumentError when passed two arguments" do
- lambda { @klass.new.m 1, 2 }.should raise_error(ArgumentError)
+ -> { @klass.new.m 1, 2 }.should raise_error(ArgumentError)
end
it "receives the value passed as the argument when passed one argument" do
@klass.new.m(1).should == 1
end
+ end
+ describe "passed { |a,| } creates a method that" do
+ before :each do
+ @klass = Class.new do
+ define_method(:m) { |a,| a }
+ end
+ end
+
+ it "raises an ArgumentError when passed zero arguments" do
+ -> { @klass.new.m }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when passed zero arguments and a block" do
+ -> { @klass.new.m { :computed } }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when passed two arguments" do
+ -> { @klass.new.m 1, 2 }.should raise_error(ArgumentError)
+ end
+
+ it "receives the value passed as the argument when passed one argument" do
+ @klass.new.m(1).should == 1
+ end
+
+ it "does not destructure the passed argument" do
+ @klass.new.m([1, 2]).should == [1, 2]
+ # for comparison:
+ proc { |a,| a }.call([1, 2]).should == 1
+ end
end
describe "passed { |*a| } creates a method that" do
@@ -507,7 +654,7 @@ describe "Module#define_method" do
end
it "raises an ArgumentError when passed zero arguments" do
- lambda { @klass.new.m }.should raise_error(ArgumentError)
+ -> { @klass.new.m }.should raise_error(ArgumentError)
end
it "returns the value computed by the block when passed one argument" do
@@ -535,19 +682,19 @@ describe "Module#define_method" do
end
it "raises an ArgumentError when passed zero arguments" do
- lambda { @klass.new.m }.should raise_error(ArgumentError)
+ -> { @klass.new.m }.should raise_error(ArgumentError)
end
it "raises an ArgumentError when passed one argument" do
- lambda { @klass.new.m 1 }.should raise_error(ArgumentError)
+ -> { @klass.new.m 1 }.should raise_error(ArgumentError)
end
it "raises an ArgumentError when passed one argument and a block" do
- lambda { @klass.new.m(1) { } }.should raise_error(ArgumentError)
+ -> { @klass.new.m(1) { } }.should raise_error(ArgumentError)
end
it "raises an ArgumentError when passed three arguments" do
- lambda { @klass.new.m 1, 2, 3 }.should raise_error(ArgumentError)
+ -> { @klass.new.m 1, 2, 3 }.should raise_error(ArgumentError)
end
end
@@ -559,15 +706,15 @@ describe "Module#define_method" do
end
it "raises an ArgumentError when passed zero arguments" do
- lambda { @klass.new.m }.should raise_error(ArgumentError)
+ -> { @klass.new.m }.should raise_error(ArgumentError)
end
it "raises an ArgumentError when passed one argument" do
- lambda { @klass.new.m 1 }.should raise_error(ArgumentError)
+ -> { @klass.new.m 1 }.should raise_error(ArgumentError)
end
it "raises an ArgumentError when passed one argument and a block" do
- lambda { @klass.new.m(1) { } }.should raise_error(ArgumentError)
+ -> { @klass.new.m(1) { } }.should raise_error(ArgumentError)
end
it "receives an empty array as the third argument when passed two arguments" do
@@ -580,7 +727,7 @@ describe "Module#define_method" do
end
end
-describe "Method#define_method when passed a Method object" do
+describe "Module#define_method when passed a Method object" do
before :each do
@klass = Class.new do
def m(a, b, *c)
@@ -605,7 +752,7 @@ describe "Method#define_method when passed a Method object" do
end
end
-describe "Method#define_method when passed an UnboundMethod object" do
+describe "Module#define_method when passed an UnboundMethod object" do
before :each do
@klass = Class.new do
def m(a, b, *c)
@@ -630,7 +777,7 @@ describe "Method#define_method when passed an UnboundMethod object" do
end
end
-describe "Method#define_method when passed a Proc object" do
+describe "Module#define_method when passed a Proc object" do
describe "and a method is defined inside" do
it "defines the nested method in the default definee where the Proc was created" do
prc = nil
@@ -654,3 +801,46 @@ describe "Method#define_method when passed a Proc object" do
end
end
end
+
+describe "Module#define_method when passed a block" do
+ describe "behaves exactly like a lambda" do
+ it "for return" do
+ Class.new do
+ define_method(:foo) do
+ return 42
+ end
+ end.new.foo.should == 42
+ end
+
+ it "for break" do
+ Class.new do
+ define_method(:foo) do
+ break 42
+ end
+ end.new.foo.should == 42
+ end
+
+ it "for next" do
+ Class.new do
+ define_method(:foo) do
+ next 42
+ end
+ end.new.foo.should == 42
+ end
+
+ it "for redo" do
+ Class.new do
+ result = []
+ define_method(:foo) do
+ if result.empty?
+ result << :first
+ redo
+ else
+ result << :second
+ result
+ end
+ end
+ end.new.foo.should == [:first, :second]
+ end
+ end
+end