mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00

This reverts commit eeef16e190
.
This also reverts the spec change.
Preventing the SystemStackError would be nice, but there is valid
code that the fix breaks, and it is probably more common than cases
that cause the SystemStackError.
Fixes [Bug #17182]
1128 lines
24 KiB
Ruby
1128 lines
24 KiB
Ruby
require_relative '../../spec_helper'
|
|
require_relative 'fixtures/refine'
|
|
|
|
describe "Module#refine" do
|
|
it "runs its block in an anonymous module" do
|
|
inner_self = nil
|
|
mod = Module.new do
|
|
refine String do
|
|
inner_self = self
|
|
end
|
|
end
|
|
|
|
mod.should_not == inner_self
|
|
inner_self.should be_kind_of(Module)
|
|
inner_self.name.should == nil
|
|
end
|
|
|
|
it "uses the same anonymous module for future refines of the same class" do
|
|
selves = []
|
|
mod = Module.new do
|
|
refine String do
|
|
selves << self
|
|
end
|
|
end
|
|
|
|
mod.module_eval do
|
|
refine String do
|
|
selves << self
|
|
end
|
|
end
|
|
|
|
selves[0].should == selves[1]
|
|
end
|
|
|
|
it "adds methods defined in its block to the anonymous module's public instance methods" do
|
|
inner_self = nil
|
|
mod = Module.new do
|
|
refine String do
|
|
def blah
|
|
"blah"
|
|
end
|
|
inner_self = self
|
|
end
|
|
end
|
|
|
|
inner_self.public_instance_methods.should include(:blah)
|
|
end
|
|
|
|
it "returns created anonymous module" do
|
|
inner_self = nil
|
|
result = nil
|
|
mod = Module.new do
|
|
result = refine String do
|
|
inner_self = self
|
|
end
|
|
end
|
|
|
|
result.should == inner_self
|
|
end
|
|
|
|
it "raises ArgumentError if not passed an argument" do
|
|
-> do
|
|
Module.new do
|
|
refine {}
|
|
end
|
|
end.should raise_error(ArgumentError)
|
|
end
|
|
|
|
it "raises TypeError if not passed a class" do
|
|
-> do
|
|
Module.new do
|
|
refine("foo") {}
|
|
end
|
|
end.should raise_error(TypeError)
|
|
end
|
|
|
|
it "accepts a module as argument" do
|
|
inner_self = nil
|
|
Module.new do
|
|
refine(Enumerable) do
|
|
def blah
|
|
end
|
|
inner_self = self
|
|
end
|
|
end
|
|
|
|
inner_self.public_instance_methods.should include(:blah)
|
|
end
|
|
|
|
it "applies refinements to the module" do
|
|
refinement = Module.new do
|
|
refine(Enumerable) do
|
|
def foo?
|
|
self.any? ? "yes" : "no"
|
|
end
|
|
end
|
|
end
|
|
|
|
foo = Class.new do
|
|
using refinement
|
|
|
|
def initialize(items)
|
|
@items = items
|
|
end
|
|
|
|
def result
|
|
@items.foo?
|
|
end
|
|
end
|
|
|
|
foo.new([]).result.should == "no"
|
|
foo.new([1]).result.should == "yes"
|
|
end
|
|
|
|
it "raises ArgumentError if not given a block" do
|
|
-> do
|
|
Module.new do
|
|
refine String
|
|
end
|
|
end.should raise_error(ArgumentError)
|
|
end
|
|
|
|
it "applies refinements to calls in the refine block" do
|
|
result = nil
|
|
Module.new do
|
|
refine(String) do
|
|
def foo; "foo"; end
|
|
result = "hello".foo
|
|
end
|
|
end
|
|
result.should == "foo"
|
|
end
|
|
|
|
it "doesn't apply refinements outside the refine block" do
|
|
Module.new do
|
|
refine(String) {def foo; "foo"; end}
|
|
-> {
|
|
"hello".foo
|
|
}.should raise_error(NoMethodError)
|
|
end
|
|
end
|
|
|
|
it "does not apply refinements to external scopes not using the module" do
|
|
Module.new do
|
|
refine(String) {def foo; 'foo'; end}
|
|
end
|
|
|
|
-> {"hello".foo}.should raise_error(NoMethodError)
|
|
end
|
|
|
|
# When defining multiple refinements in the same module,
|
|
# inside a refine block all refinements from the same
|
|
# module are active when a refined method is called
|
|
it "makes available all refinements from the same module" do
|
|
refinement = Module.new do
|
|
refine Integer do
|
|
def to_json_format
|
|
to_s
|
|
end
|
|
end
|
|
|
|
refine Array do
|
|
def to_json_format
|
|
"[" + map { |i| i.to_json_format }.join(", ") + "]"
|
|
end
|
|
end
|
|
|
|
refine Hash do
|
|
def to_json_format
|
|
"{" + map { |k, v| k.to_s.dump + ": " + v.to_json_format }.join(", ") + "}"
|
|
end
|
|
end
|
|
end
|
|
|
|
result = nil
|
|
|
|
Module.new do
|
|
using refinement
|
|
|
|
result = [{1 => 2}, {3 => 4}].to_json_format
|
|
end
|
|
|
|
result.should == '[{"1": 2}, {"3": 4}]'
|
|
end
|
|
|
|
it "does not make available methods from another refinement module" do
|
|
refinery_integer = Module.new do
|
|
refine Integer do
|
|
def to_json_format
|
|
to_s
|
|
end
|
|
end
|
|
end
|
|
|
|
refinery_array = Module.new do
|
|
refine Array do
|
|
def to_json_format
|
|
"[" + map { |i| i.to_json_format }.join(",") + "]"
|
|
end
|
|
end
|
|
end
|
|
|
|
result = nil
|
|
|
|
-> {
|
|
Module.new do
|
|
using refinery_integer
|
|
using refinery_array
|
|
|
|
[1, 2].to_json_format
|
|
end
|
|
}.should raise_error(NoMethodError)
|
|
end
|
|
|
|
# method lookup:
|
|
# * The prepended modules from the refinement for C
|
|
# * The refinement for C
|
|
# * The included modules from the refinement for C
|
|
# * The prepended modules of C
|
|
# * C
|
|
# * The included modules of C
|
|
describe "method lookup" do
|
|
it "looks in the object singleton class first" do
|
|
refined_class = ModuleSpecs.build_refined_class
|
|
|
|
refinement = Module.new do
|
|
refine refined_class do
|
|
def foo; "foo from refinement"; end
|
|
end
|
|
end
|
|
|
|
result = nil
|
|
Module.new do
|
|
using refinement
|
|
|
|
obj = refined_class.new
|
|
class << obj
|
|
def foo; "foo from singleton class"; end
|
|
end
|
|
result = obj.foo
|
|
end
|
|
|
|
result.should == "foo from singleton class"
|
|
end
|
|
|
|
it "looks in the included modules for builtin methods" do
|
|
result = ruby_exe(<<-RUBY)
|
|
a = Module.new do
|
|
def /(other) quo(other) end
|
|
end
|
|
|
|
refinement = Module.new do
|
|
refine Integer do
|
|
include a
|
|
end
|
|
end
|
|
|
|
result = nil
|
|
Module.new do
|
|
using refinement
|
|
result = 1 / 2
|
|
end
|
|
|
|
print result.class
|
|
RUBY
|
|
|
|
result.should == 'Rational'
|
|
end
|
|
|
|
it "looks in later included modules of the refined module first" do
|
|
a = Module.new do
|
|
def foo
|
|
"foo from A"
|
|
end
|
|
end
|
|
|
|
include_me_later = Module.new do
|
|
def foo
|
|
"foo from IncludeMeLater"
|
|
end
|
|
end
|
|
|
|
c = Class.new do
|
|
include a
|
|
end
|
|
|
|
refinement = Module.new do
|
|
refine c do; end
|
|
end
|
|
|
|
result = nil
|
|
Module.new do
|
|
using refinement
|
|
c.include include_me_later
|
|
result = c.new.foo
|
|
end
|
|
|
|
result.should == "foo from IncludeMeLater"
|
|
end
|
|
|
|
it "looks in prepended modules from the refinement first" do
|
|
refined_class = ModuleSpecs.build_refined_class
|
|
|
|
refinement = Module.new do
|
|
refine refined_class do
|
|
include ModuleSpecs::IncludedModule
|
|
prepend ModuleSpecs::PrependedModule
|
|
|
|
def foo; "foo from refinement"; end
|
|
end
|
|
end
|
|
|
|
result = nil
|
|
Module.new do
|
|
using refinement
|
|
result = refined_class.new.foo
|
|
end
|
|
|
|
result.should == "foo from prepended module"
|
|
end
|
|
|
|
it "looks in refinement then" do
|
|
refined_class = ModuleSpecs.build_refined_class
|
|
|
|
refinement = Module.new do
|
|
refine(refined_class) do
|
|
include ModuleSpecs::IncludedModule
|
|
|
|
def foo; "foo from refinement"; end
|
|
end
|
|
end
|
|
|
|
result = nil
|
|
Module.new do
|
|
using refinement
|
|
result = refined_class.new.foo
|
|
end
|
|
|
|
result.should == "foo from refinement"
|
|
end
|
|
|
|
it "looks in included modules from the refinement then" do
|
|
refined_class = ModuleSpecs.build_refined_class
|
|
|
|
refinement = Module.new do
|
|
refine refined_class do
|
|
include ModuleSpecs::IncludedModule
|
|
end
|
|
end
|
|
|
|
result = nil
|
|
Module.new do
|
|
using refinement
|
|
result = refined_class.new.foo
|
|
end
|
|
|
|
result.should == "foo from included module"
|
|
end
|
|
|
|
it "looks in the class then" do
|
|
refined_class = ModuleSpecs.build_refined_class
|
|
|
|
refinement = Module.new do
|
|
refine(refined_class) { }
|
|
end
|
|
|
|
result = nil
|
|
Module.new do
|
|
using refinement
|
|
result = refined_class.new.foo
|
|
end
|
|
|
|
result.should == "foo"
|
|
end
|
|
end
|
|
|
|
|
|
# methods in a subclass have priority over refinements in a superclass
|
|
it "does not override methods in subclasses" do
|
|
refined_class = ModuleSpecs.build_refined_class
|
|
|
|
subclass = Class.new(refined_class) do
|
|
def foo; "foo from subclass"; end
|
|
end
|
|
|
|
refinement = Module.new do
|
|
refine refined_class do
|
|
def foo; "foo from refinement"; end
|
|
end
|
|
end
|
|
|
|
result = nil
|
|
Module.new do
|
|
using refinement
|
|
result = subclass.new.foo
|
|
end
|
|
|
|
result.should == "foo from subclass"
|
|
end
|
|
|
|
context "for methods accessed indirectly" do
|
|
it "is honored by Kernel#send" do
|
|
refined_class = ModuleSpecs.build_refined_class
|
|
|
|
refinement = Module.new do
|
|
refine refined_class do
|
|
def foo; "foo from refinement"; end
|
|
end
|
|
end
|
|
|
|
result = nil
|
|
Module.new do
|
|
using refinement
|
|
result = refined_class.new.send :foo
|
|
end
|
|
|
|
result.should == "foo from refinement"
|
|
end
|
|
|
|
it "is honored by BasicObject#__send__" do
|
|
refined_class = ModuleSpecs.build_refined_class
|
|
|
|
refinement = Module.new do
|
|
refine refined_class do
|
|
def foo; "foo from refinement"; end
|
|
end
|
|
end
|
|
|
|
result = nil
|
|
Module.new do
|
|
using refinement
|
|
result = refined_class.new.__send__ :foo
|
|
end
|
|
|
|
result.should == "foo from refinement"
|
|
end
|
|
|
|
it "is honored by Symbol#to_proc" do
|
|
refinement = Module.new do
|
|
refine Integer do
|
|
def to_s
|
|
"(#{super})"
|
|
end
|
|
end
|
|
end
|
|
|
|
result = nil
|
|
Module.new do
|
|
using refinement
|
|
result = [1, 2, 3].map(&:to_s)
|
|
end
|
|
|
|
result.should == ["(1)", "(2)", "(3)"]
|
|
end
|
|
|
|
ruby_version_is "" ... "2.6" do
|
|
it "is not honored by Kernel#public_send" do
|
|
refined_class = ModuleSpecs.build_refined_class
|
|
|
|
refinement = Module.new do
|
|
refine refined_class do
|
|
def foo; "foo from refinement"; end
|
|
end
|
|
end
|
|
|
|
result = nil
|
|
Module.new do
|
|
using refinement
|
|
result = refined_class.new.public_send :foo
|
|
end
|
|
|
|
result.should == "foo"
|
|
end
|
|
end
|
|
|
|
ruby_version_is "2.6" do
|
|
it "is honored by Kernel#public_send" do
|
|
refined_class = ModuleSpecs.build_refined_class
|
|
|
|
refinement = Module.new do
|
|
refine refined_class do
|
|
def foo; "foo from refinement"; end
|
|
end
|
|
end
|
|
|
|
result = nil
|
|
Module.new do
|
|
using refinement
|
|
result = refined_class.new.public_send :foo
|
|
end
|
|
|
|
result.should == "foo from refinement"
|
|
end
|
|
end
|
|
|
|
it "is honored by string interpolation" do
|
|
refinement = Module.new do
|
|
refine Integer do
|
|
def to_s
|
|
"foo"
|
|
end
|
|
end
|
|
end
|
|
|
|
result = nil
|
|
Module.new do
|
|
using refinement
|
|
result = "#{1}"
|
|
end
|
|
|
|
result.should == "foo"
|
|
end
|
|
|
|
it "is honored by Kernel#binding" do
|
|
refinement = Module.new do
|
|
refine String do
|
|
def to_s
|
|
"hello from refinement"
|
|
end
|
|
end
|
|
end
|
|
|
|
klass = Class.new do
|
|
using refinement
|
|
|
|
def foo
|
|
"foo".to_s
|
|
end
|
|
|
|
def get_binding
|
|
binding
|
|
end
|
|
end
|
|
|
|
result = Kernel.eval("self.foo()", klass.new.get_binding)
|
|
result.should == "hello from refinement"
|
|
end
|
|
|
|
ruby_version_is "" ... "2.7" do
|
|
it "is not honored by Kernel#method" do
|
|
klass = Class.new
|
|
refinement = Module.new do
|
|
refine klass do
|
|
def foo; end
|
|
end
|
|
end
|
|
|
|
-> {
|
|
Module.new do
|
|
using refinement
|
|
klass.new.method(:foo)
|
|
end
|
|
}.should raise_error(NameError, /undefined method `foo'/)
|
|
end
|
|
end
|
|
|
|
ruby_version_is "2.7" do
|
|
it "is honored by Kernel#method" do
|
|
klass = Class.new
|
|
refinement = Module.new do
|
|
refine klass do
|
|
def foo; end
|
|
end
|
|
end
|
|
|
|
result = nil
|
|
Module.new do
|
|
using refinement
|
|
result = klass.new.method(:foo).class
|
|
end
|
|
|
|
result.should == Method
|
|
end
|
|
end
|
|
|
|
ruby_version_is "" ... "2.7" do
|
|
it "is not honored by Kernel#instance_method" do
|
|
klass = Class.new
|
|
refinement = Module.new do
|
|
refine klass do
|
|
def foo; end
|
|
end
|
|
end
|
|
|
|
-> {
|
|
Module.new do
|
|
using refinement
|
|
klass.instance_method(:foo)
|
|
end
|
|
}.should raise_error(NameError, /undefined method `foo'/)
|
|
end
|
|
end
|
|
|
|
ruby_version_is "2.7" do
|
|
it "is honored by Kernel#method" do
|
|
klass = Class.new
|
|
refinement = Module.new do
|
|
refine klass do
|
|
def foo; end
|
|
end
|
|
end
|
|
|
|
result = nil
|
|
Module.new do
|
|
using refinement
|
|
result = klass.instance_method(:foo).class
|
|
end
|
|
|
|
result.should == UnboundMethod
|
|
end
|
|
end
|
|
|
|
ruby_version_is "" ... "2.6" do
|
|
it "is not honored by Kernel#respond_to?" do
|
|
klass = Class.new
|
|
refinement = Module.new do
|
|
refine klass do
|
|
def foo; end
|
|
end
|
|
end
|
|
|
|
result = nil
|
|
Module.new do
|
|
using refinement
|
|
result = klass.new.respond_to?(:foo)
|
|
end
|
|
|
|
result.should == false
|
|
end
|
|
end
|
|
|
|
ruby_version_is "2.6" do
|
|
it "is honored by Kernel#respond_to?" do
|
|
klass = Class.new
|
|
refinement = Module.new do
|
|
refine klass do
|
|
def foo; end
|
|
end
|
|
end
|
|
|
|
result = nil
|
|
Module.new do
|
|
using refinement
|
|
result = klass.new.respond_to?(:foo)
|
|
end
|
|
|
|
result.should == true
|
|
end
|
|
end
|
|
|
|
ruby_version_is ""..."2.6" do
|
|
it "is not honored by &" do
|
|
refinement = Module.new do
|
|
refine String do
|
|
def to_proc(*args)
|
|
-> * { 'foo' }
|
|
end
|
|
end
|
|
end
|
|
|
|
-> do
|
|
Module.new do
|
|
using refinement
|
|
["hola"].map(&"upcase")
|
|
end
|
|
end.should raise_error(TypeError, /wrong argument type String \(expected Proc\)/)
|
|
end
|
|
end
|
|
|
|
ruby_version_is "2.6" do
|
|
it "is honored by &" do
|
|
refinement = Module.new do
|
|
refine String do
|
|
def to_proc(*args)
|
|
-> * { 'foo' }
|
|
end
|
|
end
|
|
end
|
|
|
|
result = nil
|
|
Module.new do
|
|
using refinement
|
|
result = ["hola"].map(&"upcase")
|
|
end
|
|
|
|
result.should == ['foo']
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when super is called in a refinement" do
|
|
it "looks in the included to refinery module" do
|
|
refined_class = ModuleSpecs.build_refined_class
|
|
|
|
refinement = Module.new do
|
|
refine refined_class do
|
|
include ModuleSpecs::IncludedModule
|
|
|
|
def foo
|
|
super
|
|
end
|
|
end
|
|
end
|
|
|
|
result = nil
|
|
Module.new do
|
|
using refinement
|
|
result = refined_class.new.foo
|
|
end
|
|
|
|
result.should == "foo from included module"
|
|
end
|
|
|
|
it "looks in the refined class" do
|
|
refined_class = ModuleSpecs.build_refined_class
|
|
|
|
refinement = Module.new do
|
|
refine refined_class do
|
|
def foo
|
|
super
|
|
end
|
|
end
|
|
end
|
|
|
|
result = nil
|
|
Module.new do
|
|
using refinement
|
|
result = refined_class.new.foo
|
|
end
|
|
|
|
result.should == "foo"
|
|
end
|
|
|
|
it "looks in the refined class from included module" do
|
|
refined_class = ModuleSpecs.build_refined_class(for_super: true)
|
|
|
|
a = Module.new do
|
|
def foo
|
|
[:A] + super
|
|
end
|
|
end
|
|
|
|
refinement = Module.new do
|
|
refine refined_class do
|
|
include a
|
|
end
|
|
end
|
|
|
|
result = nil
|
|
Module.new do
|
|
using refinement
|
|
|
|
result = refined_class.new.foo
|
|
end
|
|
|
|
result.should == [:A, :C]
|
|
end
|
|
|
|
it "looks in the refined ancestors from included module" do
|
|
refined_class = ModuleSpecs.build_refined_class(for_super: true)
|
|
subclass = Class.new(refined_class)
|
|
|
|
a = Module.new do
|
|
def foo
|
|
[:A] + super
|
|
end
|
|
end
|
|
|
|
refinement = Module.new do
|
|
refine refined_class do
|
|
include a
|
|
end
|
|
end
|
|
|
|
result = nil
|
|
Module.new do
|
|
using refinement
|
|
|
|
result = subclass.new.foo
|
|
end
|
|
|
|
result.should == [:A, :C]
|
|
end
|
|
|
|
# super in a method of a refinement invokes the method in the refined
|
|
# class even if there is another refinement which has been activated
|
|
# in the same context.
|
|
it "looks in the refined class first if called from refined method" do
|
|
refined_class = ModuleSpecs.build_refined_class(for_super: true)
|
|
|
|
refinement = Module.new do
|
|
refine refined_class do
|
|
def foo
|
|
[:R1]
|
|
end
|
|
end
|
|
end
|
|
|
|
refinement_with_super = Module.new do
|
|
refine refined_class do
|
|
def foo
|
|
[:R2] + super
|
|
end
|
|
end
|
|
end
|
|
|
|
result = nil
|
|
Module.new do
|
|
using refinement
|
|
using refinement_with_super
|
|
result = refined_class.new.foo
|
|
end
|
|
|
|
result.should == [:R2, :C]
|
|
end
|
|
|
|
it "looks only in the refined class even if there is another active refinement" do
|
|
refined_class = ModuleSpecs.build_refined_class(for_super: true)
|
|
|
|
refinement = Module.new do
|
|
refine refined_class do
|
|
def bar
|
|
"you cannot see me from super because I belong to another active R"
|
|
end
|
|
end
|
|
end
|
|
|
|
refinement_with_super = Module.new do
|
|
refine refined_class do
|
|
def bar
|
|
super
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
Module.new do
|
|
using refinement
|
|
using refinement_with_super
|
|
-> {
|
|
refined_class.new.bar
|
|
}.should raise_error(NoMethodError)
|
|
end
|
|
end
|
|
|
|
it "does't have access to active refinements for C from included module" do
|
|
refined_class = ModuleSpecs.build_refined_class
|
|
|
|
a = Module.new do
|
|
def foo
|
|
super + bar
|
|
end
|
|
end
|
|
|
|
refinement = Module.new do
|
|
refine refined_class do
|
|
include a
|
|
|
|
def bar
|
|
"bar is not seen from A methods"
|
|
end
|
|
end
|
|
end
|
|
|
|
Module.new do
|
|
using refinement
|
|
-> {
|
|
refined_class.new.foo
|
|
}.should raise_error(NameError) { |e| e.name.should == :bar }
|
|
end
|
|
end
|
|
|
|
it "does't have access to other active refinements from included module" do
|
|
refined_class = ModuleSpecs.build_refined_class
|
|
|
|
refinement_integer = Module.new do
|
|
refine Integer do
|
|
def bar
|
|
"bar is not seen from A methods"
|
|
end
|
|
end
|
|
end
|
|
|
|
a = Module.new do
|
|
def foo
|
|
super + 1.bar
|
|
end
|
|
end
|
|
|
|
refinement = Module.new do
|
|
refine refined_class do
|
|
include a
|
|
end
|
|
end
|
|
|
|
Module.new do
|
|
using refinement
|
|
using refinement_integer
|
|
-> {
|
|
refined_class.new.foo
|
|
}.should raise_error(NameError) { |e| e.name.should == :bar }
|
|
end
|
|
end
|
|
|
|
# https://bugs.ruby-lang.org/issues/16977
|
|
it "looks in the another active refinement if super called from included modules" do
|
|
refined_class = ModuleSpecs.build_refined_class(for_super: true)
|
|
|
|
a = Module.new do
|
|
def foo
|
|
[:A] + super
|
|
end
|
|
end
|
|
|
|
b = Module.new do
|
|
def foo
|
|
[:B] + super
|
|
end
|
|
end
|
|
|
|
refinement_a = Module.new do
|
|
refine refined_class do
|
|
include a
|
|
end
|
|
end
|
|
|
|
refinement_b = Module.new do
|
|
refine refined_class do
|
|
include b
|
|
end
|
|
end
|
|
|
|
result = nil
|
|
Module.new do
|
|
using refinement_a
|
|
using refinement_b
|
|
result = refined_class.new.foo
|
|
end
|
|
|
|
result.should == [:B, :A, :C]
|
|
end
|
|
|
|
it "looks in the current active refinement from included modules" do
|
|
refined_class = ModuleSpecs.build_refined_class(for_super: true)
|
|
|
|
a = Module.new do
|
|
def foo
|
|
[:A] + super
|
|
end
|
|
end
|
|
|
|
b = Module.new do
|
|
def foo
|
|
[:B] + super
|
|
end
|
|
end
|
|
|
|
refinement = Module.new do
|
|
refine refined_class do
|
|
def foo
|
|
[:LAST] + super
|
|
end
|
|
end
|
|
end
|
|
|
|
refinement_a_b = Module.new do
|
|
refine refined_class do
|
|
include a
|
|
include b
|
|
end
|
|
end
|
|
|
|
result = nil
|
|
Module.new do
|
|
using refinement
|
|
using refinement_a_b
|
|
result = refined_class.new.foo
|
|
end
|
|
|
|
result.should == [:B, :A, :LAST, :C]
|
|
end
|
|
|
|
it "looks in the lexical scope refinements before other active refinements" do
|
|
refined_class = ModuleSpecs.build_refined_class(for_super: true)
|
|
|
|
refinement_local = Module.new do
|
|
refine refined_class do
|
|
def foo
|
|
[:LOCAL] + super
|
|
end
|
|
end
|
|
end
|
|
|
|
a = Module.new do
|
|
using refinement_local
|
|
|
|
def foo
|
|
[:A] + super
|
|
end
|
|
end
|
|
|
|
refinement = Module.new do
|
|
refine refined_class do
|
|
include a
|
|
end
|
|
end
|
|
|
|
result = nil
|
|
Module.new do
|
|
using refinement
|
|
result = refined_class.new.foo
|
|
end
|
|
|
|
result.should == [:A, :LOCAL, :C]
|
|
end
|
|
end
|
|
|
|
it 'and alias aliases a method within a refinement module, but not outside it' do
|
|
Module.new do
|
|
using Module.new {
|
|
refine Array do
|
|
alias :orig_count :count
|
|
end
|
|
}
|
|
[1,2].orig_count.should == 2
|
|
end
|
|
-> { [1,2].orig_count }.should raise_error(NoMethodError)
|
|
end
|
|
|
|
it 'and alias_method aliases a method within a refinement module, but not outside it' do
|
|
Module.new do
|
|
using Module.new {
|
|
refine Array do
|
|
alias_method :orig_count, :count
|
|
end
|
|
}
|
|
[1,2].orig_count.should == 2
|
|
end
|
|
-> { [1,2].orig_count }.should raise_error(NoMethodError)
|
|
end
|
|
|
|
it "and instance_methods returns a list of methods including those of the refined module" do
|
|
methods = Array.instance_methods
|
|
methods_2 = []
|
|
Module.new do
|
|
refine Array do
|
|
methods_2 = instance_methods
|
|
end
|
|
end
|
|
methods.should == methods_2
|
|
end
|
|
|
|
# Refinements are inherited by module inclusion.
|
|
# That is, using activates all refinements in the ancestors of the specified module.
|
|
# Refinements in a descendant have priority over refinements in an ancestor.
|
|
context "module inclusion" do
|
|
it "activates all refinements from all ancestors" do
|
|
refinement_included = Module.new do
|
|
refine Integer do
|
|
def to_json_format
|
|
to_s
|
|
end
|
|
end
|
|
end
|
|
|
|
refinement = Module.new do
|
|
include refinement_included
|
|
|
|
refine Array do
|
|
def to_json_format
|
|
"[" + map { |i| i.to_s }.join(", ") + "]"
|
|
end
|
|
end
|
|
end
|
|
|
|
result = nil
|
|
Module.new do
|
|
using refinement
|
|
result = [5.to_json_format, [1, 2, 3].to_json_format]
|
|
end
|
|
|
|
result.should == ["5", "[1, 2, 3]"]
|
|
end
|
|
|
|
it "overrides methods of ancestors by methods in descendants" do
|
|
refinement_included = Module.new do
|
|
refine Integer do
|
|
def to_json_format
|
|
to_s
|
|
end
|
|
end
|
|
end
|
|
|
|
refinement = Module.new do
|
|
include refinement_included
|
|
|
|
refine Integer do
|
|
def to_json_format
|
|
"hello from refinement"
|
|
end
|
|
end
|
|
end
|
|
|
|
result = nil
|
|
Module.new do
|
|
using refinement
|
|
result = 5.to_json_format
|
|
end
|
|
|
|
result.should == "hello from refinement"
|
|
end
|
|
end
|
|
|
|
it 'does not list methods defined only in refinement' do
|
|
refine_object = Module.new do
|
|
refine Object do
|
|
def refinement_only_method
|
|
end
|
|
end
|
|
end
|
|
spec = self
|
|
klass = Class.new { instance_methods.should_not spec.send(:include, :refinement_only_method) }
|
|
instance = klass.new
|
|
instance.methods.should_not include :refinement_only_method
|
|
instance.respond_to?(:refinement_only_method).should == false
|
|
-> { instance.method :refinement_only_method }.should raise_error(NameError)
|
|
end
|
|
end
|