diff options
Diffstat (limited to 'spec/ruby/core/module/define_method_spec.rb')
| -rw-r--r-- | spec/ruby/core/module/define_method_spec.rb | 294 |
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 |
