aboutsummaryrefslogtreecommitdiffstats
path: root/spec/ruby/core/module/using_spec.rb
diff options
context:
space:
mode:
Diffstat (limited to 'spec/ruby/core/module/using_spec.rb')
-rw-r--r--spec/ruby/core/module/using_spec.rb276
1 files changed, 276 insertions, 0 deletions
diff --git a/spec/ruby/core/module/using_spec.rb b/spec/ruby/core/module/using_spec.rb
new file mode 100644
index 0000000000..32e8f8f96b
--- /dev/null
+++ b/spec/ruby/core/module/using_spec.rb
@@ -0,0 +1,276 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../fixtures/using', __FILE__)
+
+describe "Module#using" do
+ it "imports class refinements from module into the current class/module" do
+ refinement = Module.new do
+ refine Integer do
+ def foo; "foo"; end
+ end
+ end
+
+ result = nil
+ Module.new do
+ using refinement
+ result = 1.foo
+ end
+
+ result.should == "foo"
+ end
+
+ it "accepts module as argument" do
+ refinement = Module.new do
+ refine Integer do
+ def foo; "foo"; end
+ end
+ end
+
+ -> () {
+ Module.new do
+ using refinement
+ end
+ }.should_not raise_error
+ end
+
+ it "accepts module without refinements" do
+ mod = Module.new
+
+ -> () {
+ Module.new do
+ using mod
+ end
+ }.should_not raise_error
+ end
+
+ it "does not accept class" do
+ klass = Class.new
+
+ -> () {
+ Module.new do
+ using klass
+ end
+ }.should raise_error(TypeError)
+ end
+
+ it "raises TypeError if passed something other than module" do
+ -> () {
+ Module.new do
+ using "foo"
+ end
+ }.should raise_error(TypeError)
+ end
+
+ it "returns self" do
+ refinement = Module.new
+
+ result = nil
+ mod = Module.new do
+ result = using refinement
+ end
+
+ result.should equal(mod)
+ end
+
+ it "works in classes too" do
+ refinement = Module.new do
+ refine Integer do
+ def foo; "foo"; end
+ end
+ end
+
+ result = nil
+ Class.new do
+ using refinement
+ result = 1.foo
+ end
+
+ result.should == "foo"
+ end
+
+ it "raises error in method scope" do
+ mod = Module.new do
+ def self.foo
+ using ModuleSpecs::EmptyRefinement
+ end
+ end
+
+ -> () {
+ mod.foo
+ }.should raise_error(RuntimeError, /Module#using is not permitted in methods/)
+ end
+
+ it "activates refinement even for existed objects" do
+ result = nil
+
+ Module.new do
+ klass = Class.new do
+ def foo; "foo"; end
+ end
+
+ refinement = Module.new do
+ refine klass do
+ def foo; "foo from refinement"; end
+ end
+ end
+
+ obj = klass.new
+ using refinement
+ result = obj.foo
+ end
+
+ result.should == "foo from refinement"
+ end
+
+ it "activates updates when refinement reopens later" do
+ result = nil
+
+ Module.new do
+ klass = Class.new do
+ def foo; "foo"; end
+ end
+
+ refinement = Module.new do
+ refine klass do
+ def foo; "foo from refinement"; end
+ end
+ end
+
+ using refinement
+
+ refinement.class_eval do
+ refine klass do
+ def foo; "foo from reopened refinement"; end
+ end
+ end
+
+ obj = klass.new
+ result = obj.foo
+ end
+
+ result.should == "foo from reopened refinement"
+ end
+
+ describe "scope of refinement" do
+ it "is active until the end of current class/module" do
+ ScratchPad.record []
+
+ Module.new do
+ Class.new do
+ using ModuleSpecs::RefinementForStringToS
+ ScratchPad << "1".to_s
+ end
+
+ ScratchPad << "1".to_s
+ end
+
+ ScratchPad.recorded.should == ["hello from refinement", "1"]
+ end
+
+ # Refinements are lexical in scope.
+ # Refinements are only active within a scope after the call to using.
+ # Any code before the using statement will not have the refinement activated.
+ it "is not active before the `using` call" do
+ ScratchPad.record []
+
+ Module.new do
+ Class.new do
+ ScratchPad << "1".to_s
+ using ModuleSpecs::RefinementForStringToS
+ ScratchPad << "1".to_s
+ end
+ end
+
+ ScratchPad.recorded.should == ["1", "hello from refinement"]
+ end
+
+ # If you call a method that is defined outside the current scope
+ # the refinement will be deactivated
+ it "is not active for code defined outside the current scope" do
+ result = nil
+
+ Module.new do
+ klass = Class.new do
+ def foo; "foo"; end
+ end
+
+ refinement = Module.new do
+ refine klass do
+ def foo; "foo from refinement"; end
+ end
+ end
+
+ def self.call_foo(c)
+ c.foo
+ end
+
+ using refinement
+
+ result = call_foo(klass.new)
+ end
+
+ result.should == "foo"
+ end
+
+ # If a method is defined in a scope where a refinement is active
+ # the refinement will be active when the method is called.
+ it "is active for method defined in a scope wherever it's called" do
+ klass = Class.new do
+ def foo; "foo"; end
+ end
+
+ mod = Module.new do
+ refinement = Module.new do
+ refine klass do
+ def foo; "foo from refinement"; end
+ end
+ end
+
+ using refinement
+
+ def self.call_foo(c)
+ c.foo
+ end
+ end
+
+ c = klass.new
+ mod.call_foo(c).should == "foo from refinement"
+ end
+
+ it "is not active if `using` call is not evaluated" do
+ result = nil
+
+ Module.new do
+ if false
+ using ModuleSpecs::RefinementForStringToS
+ end
+ result = "1".to_s
+ end
+
+ result.should == "1"
+ end
+
+ # The refinements in module are not activated automatically
+ # if the class is reopened later
+ it "is not active when class/module reopens" do
+ refinement = Module.new do
+ refine String do
+ def to_s
+ "hello from refinement"
+ end
+ end
+ end
+
+ result = []
+ klass = Class.new do
+ using refinement
+ result << "1".to_s
+ end
+
+ klass.class_eval do
+ result << "1".to_s
+ end
+
+ result.should == ["hello from refinement", "1"]
+ end
+ end
+end