# frozen_string_literal: false require 'test/unit' class TestRefinement < Test::Unit::TestCase module Sandbox BINDING = binding end class Foo def x return "Foo#x" end def y return "Foo#y" end def a return "Foo#a" end def call_x return x end end module FooExt refine Foo do def x return "FooExt#x" end def y return "FooExt#y " + super end def z return "FooExt#z" end def a return "FooExt#a" end end end module FooExt2 refine Foo do def x return "FooExt2#x" end def y return "FooExt2#y " + super end def z return "FooExt2#z" end end end class FooSub < Foo def x return "FooSub#x" end def y return "FooSub#y " + super end end class FooExtClient using TestRefinement::FooExt begin def self.map_x_on(foo) [foo].map(&:x)[0] end def self.invoke_x_on(foo) return foo.x end def self.invoke_y_on(foo) return foo.y end def self.invoke_z_on(foo) return foo.z end def self.send_z_on(foo) return foo.send(:z) end def self.method_z(foo) return foo.method(:z) end def self.invoke_call_x_on(foo) return foo.call_x end def self.return_proc(&block) block end end end class TestRefinement::FooExtClient2 using TestRefinement::FooExt using TestRefinement::FooExt2 begin def self.invoke_y_on(foo) return foo.y end def self.invoke_a_on(foo) return foo.a end end end def test_override foo = Foo.new assert_equal("Foo#x", foo.x) assert_equal("FooExt#x", FooExtClient.invoke_x_on(foo)) assert_equal("Foo#x", foo.x) end def test_super foo = Foo.new assert_equal("Foo#y", foo.y) assert_equal("FooExt#y Foo#y", FooExtClient.invoke_y_on(foo)) assert_equal("Foo#y", foo.y) end def test_super_not_chained foo = Foo.new assert_equal("Foo#y", foo.y) assert_equal("FooExt2#y Foo#y", FooExtClient2.invoke_y_on(foo)) assert_equal("Foo#y", foo.y) end def test_using_same_class_refinements foo = Foo.new assert_equal("Foo#a", foo.a) assert_equal("FooExt#a", FooExtClient2.invoke_a_on(foo)) assert_equal("Foo#a", foo.a) end def test_new_method foo = Foo.new assert_raise(NoMethodError) { foo.z } assert_equal("FooExt#z", FooExtClient.invoke_z_on(foo)) assert_raise(NoMethodError) { foo.z } end module RespondTo class Super def foo end end class Sub < Super end module M refine Sub do def foo end end end end def test_send_should_use_refinements foo = Foo.new assert_raise(NoMethodError) { foo.send(:z) } assert_equal("FooExt#z", FooExtClient.send_z_on(foo)) assert_raise(NoMethodError) { foo.send(:z) } assert_equal(true, RespondTo::Sub.new.respond_to?(:foo)) end def test_method_should_not_use_refinements foo = Foo.new assert_raise(NameError) { foo.method(:z) } assert_raise(NameError) { FooExtClient.method_z(foo) } assert_raise(NameError) { foo.method(:z) } end def test_no_local_rebinding foo = Foo.new assert_equal("Foo#x", foo.call_x) assert_equal("Foo#x", FooExtClient.invoke_call_x_on(foo)) assert_equal("Foo#x", foo.call_x) end def test_subclass_is_prior sub = FooSub.new assert_equal("FooSub#x", sub.x) assert_equal("FooSub#x", FooExtClient.invoke_x_on(sub)) assert_equal("FooSub#x", sub.x) end def test_super_in_subclass sub = FooSub.new assert_equal("FooSub#y Foo#y", sub.y) # not "FooSub#y FooExt#y Foo#y" assert_equal("FooSub#y Foo#y", FooExtClient.invoke_y_on(sub)) assert_equal("FooSub#y Foo#y", sub.y) end def test_new_method_on_subclass sub = FooSub.new assert_raise(NoMethodError) { sub.z } assert_equal("FooExt#z", FooExtClient.invoke_z_on(sub)) assert_raise(NoMethodError) { sub.z } end def test_module_eval foo = Foo.new assert_equal("Foo#x", foo.x) assert_equal("Foo#x", FooExt.module_eval { foo.x }) assert_equal("Foo#x", FooExt.module_eval("foo.x")) assert_equal("Foo#x", foo.x) end def test_instance_eval_without_refinement foo = Foo.new ext_client = FooExtClient.new assert_equal("Foo#x", foo.x) assert_equal("Foo#x", ext_client.instance_eval { foo.x }) assert_equal("Foo#x", foo.x) end module IntegerSlashExt refine Integer do def /(other) quo(other) end end end def test_override_builtin_method assert_equal(0, 1 / 2) assert_equal(Rational(1, 2), eval_using(IntegerSlashExt, "1 / 2")) assert_equal(0, 1 / 2) end module IntegerPlusExt refine Integer do def self.method_added(*args); end def +(other) "overridden" end end end def test_override_builtin_method_with_method_added assert_equal(3, 1 + 2) assert_equal("overridden", eval_using(IntegerPlusExt, "1 + 2")) assert_equal(3, 1 + 2) end def test_return_value_of_refine mod = nil result = nil Module.new { result = refine(Object) { mod = self } } assert_equal mod, result end module RefineSameClass REFINEMENT1 = refine(Integer) { def foo; return "foo" end } REFINEMENT2 = refine(Integer) { def bar; return "bar" end } REFINEMENT3 = refine(String) { def baz; return "baz" end } end def test_refine_same_class_twice assert_equal("foo", eval_using(RefineSameClass, "1.foo")) assert_equal("bar", eval_using(RefineSameClass, "1.bar")) assert_equal(RefineSameClass::REFINEMENT1, RefineSameClass::REFINEMENT2) assert_not_equal(RefineSameClass::REFINEMENT1, RefineSameClass::REFINEMENT3) end module IntegerFooExt refine Integer do def foo; "foo"; end end end def test_respond_to_should_not_use_refinements assert_equal(false, 1.respond_to?(:foo)) assert_equal(false, eval_using(IntegerFooExt, "1.respond_to?(:foo)")) end module StringCmpExt refine String do def <=>(other) return 0 end end end module ArrayEachExt refine Array do def each super do |i| yield 2 * i end end end end def test_builtin_method_no_local_rebinding assert_equal(false, eval_using(StringCmpExt, '"1" >= "2"')) assert_equal(1, eval_using(ArrayEachExt, "[1, 2, 3].min")) end module RefinePrependedClass module M1 def foo super << :m1 end end class C prepend M1 def foo [:c] end end module M2 refine C do def foo super << :m2 end end end end def test_refine_prepended_class x = eval_using(RefinePrependedClass::M2, "TestRefinement::RefinePrependedClass::C.new.foo") assert_equal([:c, :m1, :m2], x) end module RefineModule module M def foo "M#foo" end def bar "M#bar" end def baz "M#baz" end end class C include M def baz "#{super} C#baz" end end module M2 refine M do def foo "M@M2#foo" end def bar "#{super} M@M2#bar" end def baz "#{super} M@M2#baz" end end end using M2 def self.call_foo C.new.foo end def self.call_bar C.new.bar end def self.call_baz C.new.baz end end def test_refine_module assert_equal("M@M2#foo", RefineModule.call_foo) assert_equal("M#bar M@M2#bar", RefineModule.call_bar) assert_equal("M#baz C#baz", RefineModule.call_baz) end def test_refine_neither_class_nor_module assert_raise(TypeError) do Module.new { refine Object.new do end } end assert_raise(TypeError) do Module.new { refine 123 do end } end assert_raise(TypeError) do Module.new { refine "foo" do end } end end def test_refine_in_class assert_raise(NoMethodError) do Class.new { refine Integer do def foo "c" end end } end end def test_main_using assert_in_out_err([], <<-INPUT, %w(:C :M), []) class C def foo :C end end module M refine C do def foo :M end end end c = C.new p c.foo using M p c.foo INPUT end def test_main_using_is_private assert_raise(NoMethodError) do eval("self.using Module.new", Sandbox::BINDING) end end def test_no_kernel_using assert_raise(NoMethodError) do using Module.new end end class UsingClass end def test_module_using_class assert_raise(TypeError) do eval("using TestRefinement::UsingClass", Sandbox::BINDING) end end def test_refine_without_block c1 = Class.new assert_raise_with_message(ArgumentError, "no block given") { Module.new do refine c1 end } end module Inspect module M Integer = refine(Integer) {} end end def test_inspect assert_equal("#", Inspect::M::Integer.inspect) end def test_using_method_cache assert_in_out_err([], <<-INPUT, %w(:M1 :M2), []) class C def foo "original" end end module M1 refine C do def foo :M1 end end end module M2 refine C do def foo :M2 end end end c = C.new using M1 p c.foo using M2 p c.foo INPUT end module RedefineRefinedMethod class C def foo "original" end end module M refine C do def foo "refined" end end end class C EnvUtil.suppress_warning do def foo "redefined" end end end end def test_redefine_refined_method x = eval_using(RedefineRefinedMethod::M, "TestRefinement::RedefineRefinedMethod::C.new.foo") assert_equal("refined", x) end module StringExt refine String do def foo "foo" end end end module RefineScoping refine String do def foo "foo" end def RefineScoping.call_in_refine_block "".foo end end def self.call_outside_refine_block "".foo end end def test_refine_scoping assert_equal("foo", RefineScoping.call_in_refine_block) assert_raise(NoMethodError) do RefineScoping.call_outside_refine_block end end module StringRecursiveLength refine String do def recursive_length if empty? 0 else self[1..-1].recursive_length + 1 end end end end def test_refine_recursion x = eval_using(StringRecursiveLength, "'foo'.recursive_length") assert_equal(3, x) end module ToJSON refine Integer do def to_json; to_s; end end refine Array do def to_json; "[" + map { |i| i.to_json }.join(",") + "]" end end refine Hash do def to_json; "{" + map { |k, v| k.to_s.dump + ":" + v.to_json }.join(",") + "}" end end end def test_refine_mutual_recursion x = eval_using(ToJSON, "[{1=>2}, {3=>4}].to_json") assert_equal('[{"1":2},{"3":4}]', x) end def test_refine_with_proc assert_raise(ArgumentError) do Module.new { refine(String, &Proc.new {}) } end end def test_using_in_module assert_raise(RuntimeError) do eval(<<-EOF, Sandbox::BINDING) $main = self module M end module M2 $main.send(:using, M) end EOF end end def test_using_in_method assert_raise(RuntimeError) do eval(<<-EOF, Sandbox::BINDING) $main = self module M end class C def call_using_in_method $main.send(:using, M) end end C.new.call_using_in_method EOF end end module IncludeIntoRefinement class C def bar return "C#bar" end def baz return "C#baz" end end module Mixin def foo return "Mixin#foo" end def bar return super << " Mixin#bar" end def baz return super << " Mixin#baz" end end module M refine C do include Mixin def baz return super << " M#baz" end end end end eval <<-EOF, Sandbox::BINDING using TestRefinement::IncludeIntoRefinement::M module TestRefinement::IncludeIntoRefinement::User def self.invoke_foo_on(x) x.foo end def self.invoke_bar_on(x) x.bar end def self.invoke_baz_on(x) x.baz end end EOF def test_include_into_refinement x = IncludeIntoRefinement::C.new assert_equal("Mixin#foo", IncludeIntoRefinement::User.invoke_foo_on(x)) assert_equal("C#bar Mixin#bar", IncludeIntoRefinement::User.invoke_bar_on(x)) assert_equal("C#baz Mixin#baz M#baz", IncludeIntoRefinement::User.invoke_baz_on(x)) end module PrependIntoRefinement class C def bar return "C#bar" end def baz return "C#baz" end end module Mixin def foo return "Mixin#foo" end def bar return super << " Mixin#bar" end def baz return super << " Mixin#baz" end end module M refine C do prepend Mixin def baz return super << " M#baz" end end end end eval <<-EOF, Sandbox::BINDING using TestRefinement::PrependIntoRefinement::M module TestRefinement::PrependIntoRefinement::User def self.invoke_foo_on(x) x.foo end def self.invoke_bar_on(x) x.bar end def self.invoke_baz_on(x) x.baz end end EOF def test_prepend_into_refinement x = PrependIntoRefinement::C.new assert_equal("Mixin#foo", PrependIntoRefinement::User.invoke_foo_on(x)) assert_equal("C#bar Mixin#bar", PrependIntoRefinement::User.invoke_bar_on(x)) assert_equal("C#baz M#baz Mixin#baz", PrependIntoRefinement::User.invoke_baz_on(x)) end PrependAfterRefine_CODE = <<-EOC module PrependAfterRefine class C def foo "original" end end module M refine C do def foo "refined" end def bar "refined" end end end module Mixin def foo "mixin" end def bar "mixin" end end class C prepend Mixin end end EOC eval PrependAfterRefine_CODE def test_prepend_after_refine_wb_miss if /\A(arm|mips)/ =~ RUBY_PLATFORM skip "too slow cpu" end assert_normal_exit %Q{ GC.stress = true 10.times{ #{PrependAfterRefine_CODE} undef PrependAfterRefine } } end def test_prepend_after_refine x = eval_using(PrependAfterRefine::M, "TestRefinement::PrependAfterRefine::C.new.foo") assert_equal("refined", x) assert_equal("mixin", TestRefinement::PrependAfterRefine::C.new.foo) y = eval_using(PrependAfterRefine::M, "TestRefinement::PrependAfterRefine::C.new.bar") assert_equal("refined", y) assert_equal("mixin", TestRefinement::PrependAfterRefine::C.new.bar) end module SuperInBlock class C def foo(*args) [:foo, *args] end end module R refine C do def foo(*args) tap do return super(:ref, *args) end end end end end def test_super_in_block bug7925 = '[ruby-core:52750] [Bug #7925]' x = eval_using(SuperInBlock::R, "TestRefinement:: SuperInBlock::C.new.foo(#{bug7925.dump})") assert_equal([:foo, :ref, bug7925], x, bug7925) end module ModuleUsing using FooExt def self.invoke_x_on(foo) return foo.x end def self.invoke_y_on(foo) return foo.y end def self.invoke_z_on(foo) return foo.z end def self.send_z_on(foo) return foo.send(:z) end def self.method_z(foo) return foo.method(:z) end def self.invoke_call_x_on(foo) return foo.call_x end end def test_module_using foo = Foo.new assert_equal("Foo#x", foo.x) assert_equal("Foo#y", foo.y) assert_raise(NoMethodError) { foo.z } assert_equal("FooExt#x", ModuleUsing.invoke_x_on(foo)) assert_equal("FooExt#y Foo#y", ModuleUsing.invoke_y_on(foo)) assert_equal("FooExt#z", ModuleUsing.invoke_z_on(foo)) assert_equal("Foo#x", foo.x) assert_equal("Foo#y", foo.y) assert_raise(NoMethodError) { foo.z } end def test_module_using_in_method assert_raise(RuntimeError) do Module.new.send(:using, FooExt) end end def test_module_using_invalid_self assert_raise(RuntimeError) do eval <<-EOF, Sandbox::BINDING module TestRefinement::TestModuleUsingInvalidSelf Module.new.send(:using, TestRefinement::FooExt) end EOF end end class Bar end module BarExt refine Bar do def x return "BarExt#x" end end end module FooBarExt include FooExt include BarExt end module FooBarExtClient using FooBarExt def self.invoke_x_on(foo) return foo.x end end def test_module_inclusion foo = Foo.new assert_equal("FooExt#x", FooBarExtClient.invoke_x_on(foo)) bar = Bar.new assert_equal("BarExt#x", FooBarExtClient.invoke_x_on(bar)) end module FooFoo2Ext include FooExt include FooExt2 end module FooFoo2ExtClient using FooFoo2Ext def self.invoke_x_on(foo) return foo.x end def self.invoke_y_on(foo) return foo.y end end def test_module_inclusion2 foo = Foo.new assert_equal("FooExt2#x", FooFoo2ExtClient.invoke_x_on(foo)) assert_equal("FooExt2#y Foo#y", FooFoo2ExtClient.invoke_y_on(foo)) end def test_eval_scoping assert_in_out_err([], <<-INPUT, ["HELLO WORLD", "dlrow olleh", "HELLO WORLD"], []) module M refine String do def upcase reverse end end end puts "hello world".upcase puts eval(%{using M; "hello world".upcase}) puts "hello world".upcase INPUT end def test_eval_with_binding_scoping assert_in_out_err([], <<-INPUT, ["HELLO WORLD", "dlrow olleh", "dlrow olleh"], []) module M refine String do def upcase reverse end end end puts "hello world".upcase b = binding puts eval(%{using M; "hello world".upcase}, b) puts eval(%{"hello world".upcase}, b) INPUT end def test_case_dispatch_is_aware_of_refinements assert_in_out_err([], <<-RUBY, ["refinement used"], []) module RefineSymbol refine Symbol do def ===(other) true end end end using RefineSymbol case :a when :b puts "refinement used" else puts "refinement not used" end RUBY end def test_refine_after_using assert_separately([], <<-"end;") bug8880 = '[ruby-core:57079] [Bug #8880]' module Test refine(String) do end end using Test def t 'Refinements are broken!'.chop! end t module Test refine(String) do def chop! self.sub!(/broken/, 'fine') end end end assert_equal('Refinements are fine!', t, bug8880) end; end def test_instance_methods bug8881 = '[ruby-core:57080] [Bug #8881]' assert_not_include(Foo.instance_methods(false), :z, bug8881) assert_not_include(FooSub.instance_methods(true), :z, bug8881) end def test_method_defined assert_not_send([Foo, :method_defined?, :z]) assert_not_send([FooSub, :method_defined?, :z]) end def test_undef_refined_method bug8966 = '[ruby-core:57466] [Bug #8966]' assert_in_out_err([], <<-INPUT, ["NameError"], [], bug8966) module Foo refine Object do def foo puts "foo" end end end using Foo class Object begin undef foo rescue Exception => e p e.class end end INPUT assert_in_out_err([], <<-INPUT, ["NameError"], [], bug8966) module Foo refine Object do def foo puts "foo" end end end # without `using Foo' class Object begin undef foo rescue Exception => e p e.class end end INPUT end def test_refine_undefed_method_and_call assert_in_out_err([], <<-INPUT, ["NoMethodError"], []) class Foo def foo end undef foo end module FooExt refine Foo do def foo end end end begin Foo.new.foo rescue => e p e.class end INPUT end def test_refine_undefed_method_and_send assert_in_out_err([], <<-INPUT, ["NoMethodError"], []) class Foo def foo end undef foo end module FooExt refine Foo do def foo end end end begin Foo.new.send(:foo) rescue => e p e.class end INPUT end def test_adding_private_method bug9452 = '[ruby-core:60111] [Bug #9452]' assert_in_out_err([], <<-INPUT, ["Success!", "NoMethodError"], [], bug9452) module R refine Object do def m puts "Success!" end private(:m) end end using R m 42.m rescue p($!.class) INPUT end def test_making_private_method_public bug9452 = '[ruby-core:60111] [Bug #9452]' assert_in_out_err([], <<-INPUT, ["Success!", "Success!"], [], bug9452) class Object private def m end end module R refine Object do def m puts "Success!" end end end using R m 42.m INPUT end def test_refine_basic_object assert_separately([], <<-"end;") bug10106 = '[ruby-core:64166] [Bug #10106]' module RefinementBug refine BasicObject do def foo 1 end end end assert_raise(NoMethodError, bug10106) {Object.new.foo} end; assert_separately([], <<-"end;") bug10707 = '[ruby-core:67389] [Bug #10707]' module RefinementBug refine BasicObject do def foo end end end assert(methods, bug10707) assert_raise(NameError, bug10707) {method(:foo)} end; end def test_change_refined_new_method_visibility assert_separately([], <<-"end;") bug10706 = '[ruby-core:67387] [Bug #10706]' module RefinementBug refine Object do def foo end end end assert_raise(NameError, bug10706) {private(:foo)} end; end def test_alias_refined_method assert_separately([], <<-"end;") bug10731 = '[ruby-core:67523] [Bug #10731]' class C end module RefinementBug refine C do def foo end def bar end end end assert_raise(NameError, bug10731) do class C alias foo bar end end end; end def test_singleton_method_should_not_use_refinements assert_separately([], <<-"end;") bug10744 = '[ruby-core:67603] [Bug #10744]' class C end module RefinementBug refine C.singleton_class do def foo end end end assert_raise(NameError, bug10744) { C.singleton_method(:foo) } end; end def test_refined_method_defined assert_separately([], <<-"end;") bug10753 = '[ruby-core:67656] [Bug #10753]' c = Class.new do def refined_public; end def refined_protected; end def refined_private; end public :refined_public protected :refined_protected private :refined_private end m = Module.new do refine(c) do def refined_public; end def refined_protected; end def refined_private; end public :refined_public protected :refined_protected private :refined_private end end using m assert_equal(true, c.public_method_defined?(:refined_public), bug10753) assert_equal(false, c.public_method_defined?(:refined_protected), bug10753) assert_equal(false, c.public_method_defined?(:refined_private), bug10753) assert_equal(false, c.protected_method_defined?(:refined_public), bug10753) assert_equal(true, c.protected_method_defined?(:refined_protected), bug10753) assert_equal(false, c.protected_method_defined?(:refined_private), bug10753) assert_equal(false, c.private_method_defined?(:refined_public), bug10753) assert_equal(false, c.private_method_defined?(:refined_protected), bug10753) assert_equal(true, c.private_method_defined?(:refined_private), bug10753) end; end def test_undefined_refined_method_defined assert_separately([], <<-"end;") bug10753 = '[ruby-core:67656] [Bug #10753]' c = Class.new m = Module.new do refine(c) do def undefined_refined_public; end def undefined_refined_protected; end def undefined_refined_private; end public :undefined_refined_public protected :undefined_refined_protected private :undefined_refined_private end end using m assert_equal(false, c.public_method_defined?(:undefined_refined_public), bug10753) assert_equal(false, c.public_method_defined?(:undefined_refined_protected), bug10753) assert_equal(false, c.public_method_defined?(:undefined_refined_private), bug10753) assert_equal(false, c.protected_method_defined?(:undefined_refined_public), bug10753) assert_equal(false, c.protected_method_defined?(:undefined_refined_protected), bug10753) assert_equal(false, c.protected_method_defined?(:undefined_refined_private), bug10753) assert_equal(false, c.private_method_defined?(:undefined_refined_public), bug10753) assert_equal(false, c.private_method_defined?(:undefined_refined_protected), bug10753) assert_equal(false, c.private_method_defined?(:undefined_refined_private), bug10753) end; end def test_remove_refined_method assert_separately([], <<-"end;") bug10765 = '[ruby-core:67722] [Bug #10765]' class C def foo "C#foo" end end module RefinementBug refine C do def foo "RefinementBug#foo" end end end using RefinementBug class C remove_method :foo end assert_equal("RefinementBug#foo", C.new.foo, bug10765) end; end def test_remove_undefined_refined_method assert_separately([], <<-"end;") bug10765 = '[ruby-core:67722] [Bug #10765]' class C end module RefinementBug refine C do def foo end end end using RefinementBug assert_raise(NameError, bug10765) { class C remove_method :foo end } end; end module NotIncludeSuperclassMethod class X def foo end end class Y < X end module Bar refine Y do def foo end end end end def test_instance_methods_not_include_superclass_method bug10826 = '[ruby-dev:48854] [Bug #10826]' assert_not_include(NotIncludeSuperclassMethod::Y.instance_methods(false), :foo, bug10826) assert_include(NotIncludeSuperclassMethod::Y.instance_methods(true), :foo, bug10826) end def test_undef_original_method assert_in_out_err([], <<-INPUT, ["NoMethodError"], []) module NoPlus refine String do undef + end end using NoPlus "a" + "b" rescue p($!.class) INPUT end def test_undef_prepended_method bug13096 = '[ruby-core:78944] [Bug #13096]' klass = EnvUtil.labeled_class("X") do def foo; end end klass.prepend(Module.new) ext = EnvUtil.labeled_module("Ext") do refine klass do def foo end end end assert_nothing_raised(NameError, bug13096) do klass.class_eval do undef :foo end end end def test_call_refined_method_in_duplicate_module bug10885 = '[ruby-dev:48878]' assert_in_out_err([], <<-INPUT, [], [], bug10885) module M refine Object do def raise # do nothing end end class << self using M def m0 raise end end using M def M.m1 raise end end M.dup.m0 M.dup.m1 INPUT end def test_check_funcall_undefined bug11117 = '[ruby-core:69064] [Bug #11117]' x = Class.new Module.new do refine x do def to_regexp // end end end assert_nothing_raised(NoMethodError, bug11117) { assert_nil(Regexp.try_convert(x.new)) } end def test_funcall_inherited bug11117 = '[ruby-core:69064] [Bug #11117]' Module.new {refine(Dir) {def to_s; end}} x = Class.new(Dir).allocate assert_nothing_raised(NoMethodError, bug11117) { x.inspect } end def test_alias_refined_method2 bug11182 = '[ruby-core:69360]' assert_in_out_err([], <<-INPUT, ["C"], [], bug11182) class C def foo puts "C" end end module M refine C do def foo puts "Refined C" end end end class D < C alias bar foo end using M D.new.bar INPUT end def test_reopen_refinement_module assert_separately([], <<-"end;") $VERBOSE = nil class C end module R refine C do def m :foo end end end using R assert_equal(:foo, C.new.m) module R refine C do def m :bar end end end assert_equal(:bar, C.new.m, "[ruby-core:71423] [Bug #11672]") end; end module MixedUsing1 class C def foo :orig_foo end end module R1 refine C do def foo [:R1, super] end end end module_function def foo [:foo, C.new.foo] end using R1 def bar [:bar, C.new.foo] end end module MixedUsing2 class C def foo :orig_foo end end module R1 refine C do def foo [:R1_foo, super] end end end module R2 refine C do def bar [:R2_bar, C.new.foo] end using R1 def baz [:R2_baz, C.new.foo] end end end using R2 module_function def f1; C.new.bar; end def f2; C.new.baz; end end def test_mixed_using assert_equal([:foo, :orig_foo], MixedUsing1.foo) assert_equal([:bar, [:R1, :orig_foo]], MixedUsing1.bar) assert_equal([:R2_bar, :orig_foo], MixedUsing2.f1) assert_equal([:R2_baz, [:R1_foo, :orig_foo]], MixedUsing2.f2) end module MethodMissing class Foo end module Bar refine Foo do def method_missing(mid, *args) "method_missing refined" end end end using Bar def self.call_undefined_method Foo.new.foo end end def test_method_missing assert_raise(NoMethodError) do MethodMissing.call_undefined_method end end module VisibleRefinements module RefA refine Object do def in_ref_a end end end module RefB refine Object do def in_ref_b end end end module RefC using RefA refine Object do def in_ref_c end end end module Foo using RefB USED_MODS = Module.used_modules end module Bar using RefC USED_MODS = Module.used_modules end module Combined using RefA using RefB USED_MODS = Module.used_modules end end def test_used_modules ref = VisibleRefinements assert_equal [], Module.used_modules assert_equal [ref::RefB], ref::Foo::USED_MODS assert_equal [ref::RefC], ref::Bar::USED_MODS assert_equal [ref::RefB, ref::RefA], ref::Combined::USED_MODS end def test_warn_setconst_in_refinmenet bug10103 = '[ruby-core:64143] [Bug #10103]' warnings = [ "-:3: warning: not defined at the refinement, but at the outer class/module", "-:4: warning: not defined at the refinement, but at the outer class/module" ] assert_in_out_err([], <<-INPUT, [], warnings, bug10103) module M refine String do FOO = 123 @@foo = 456 end end INPUT end def test_symbol_proc assert_equal("FooExt#x", FooExtClient.map_x_on(Foo.new)) assert_equal("Foo#x", FooExtClient.return_proc(&:x).(Foo.new)) end def test_symbol_proc_with_block assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}") bug = '[ruby-core:80219] [Bug #13325]' begin; module M refine Class.new do end end class C def call(a, x, &b) b.call(a, &x) end end o = C.new r = nil x = ->(z){r = z} assert_equal(42, o.call(42, x, &:tap)) assert_equal(42, r) using M r = nil assert_equal(42, o.call(42, x, &:tap), bug) assert_equal(42, r, bug) end; end module AliasInSubclass class C def foo :original end end class D < C alias bar foo end module M refine D do def bar :refined end end end end def test_refine_alias_in_subclass assert_equal(:refined, eval_using(AliasInSubclass::M, "AliasInSubclass::D.new.bar")) end def test_refine_with_prepend assert_separately([], "#{<<-"begin;"}\n#{<<-"end;"}") begin; bug = '[ruby-core:78073] [Bug #12920]' Integer.prepend(Module.new) Module.new do refine Integer do define_method(:+) {} end end assert_kind_of(Time, Time.now, bug) end; end def test_public_in_refine assert_separately([], "#{<<-"begin;"}\n#{<<-"end;"}") begin; bug12729 = '[ruby-core:77161] [Bug #12729]' class Cow private def moo() "Moo"; end end module PublicCows refine(Cow) { public :moo } end using PublicCows assert_equal("Moo", Cow.new.moo, bug12729) end; end module SuperToModule class Parent end class Child < Parent end module FooBar refine Parent do def to_s "Parent" end end refine Child do def to_s super + " -> Child" end end end using FooBar def Child.test new.to_s end end def test_super_to_module bug = '[ruby-core:79588] [Bug #13227]' assert_equal("Parent -> Child", SuperToModule::Child.test, bug) end def test_include_refinement bug = '[ruby-core:79632] [Bug #13236] cannot include refinement module' r = nil m = Module.new do r = refine(String) {def test;:ok end} end assert_raise_with_message(ArgumentError, /refinement/, bug) do m.module_eval {include r} end assert_raise_with_message(ArgumentError, /refinement/, bug) do m.module_eval {prepend r} end end class ParentDefiningPrivateMethod private def some_inherited_method end end module MixinDefiningPrivateMethod private def some_included_method end end class SomeChildClassToRefine < ParentDefiningPrivateMethod include MixinDefiningPrivateMethod private def some_method end end def test_refine_inherited_method_with_visibility_changes Module.new do refine(SomeChildClassToRefine) do def some_inherited_method; end def some_included_method; end def some_method; end end end obj = SomeChildClassToRefine.new assert_raise_with_message(NoMethodError, /private/) do obj.some_inherited_method end assert_raise_with_message(NoMethodError, /private/) do obj.some_included_method end assert_raise_with_message(NoMethodError, /private/) do obj.some_method end end def test_refined_method_alias_warning c = Class.new do def t; :t end def f; :f end end Module.new do refine(c) do alias foo t end end assert_warning('', '[ruby-core:82385] [Bug #13817] refined method is not redefined') do c.class_eval do alias foo f end end end def test_using_wrong_argument bug = '[ruby-dev:50270] [Bug #13956]' pattern = /expected Module/ assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}") bug = ""#{bug.dump} pattern = /#{pattern}/ begin; assert_raise_with_message(TypeError, pattern, bug) { using(1) do end } end; assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}") bug = ""#{bug.dump} pattern = /#{pattern}/ begin; assert_raise_with_message(TypeError, pattern, bug) { Module.new {using(1) {}} } end; end class ToString c = self using Module.new {refine(c) {def to_s; "ok"; end}} def string "#{self}" end end def test_tostring assert_equal("ok", ToString.new.string) assert_predicate(ToString.new.taint.string, :tainted?) end class ToSymbol c = self using Module.new {refine(c) {def intern; "<#{upcase}>"; end}} def symbol :"#{@string}" end def initialize(string) @string = string end end def test_dsym_literal assert_equal(:foo, ToSymbol.new("foo").symbol) end module ToProc def self.call &block block.call end class ReturnProc c = self using Module.new { refine c do def to_proc proc { "to_proc" } end end } def call ToProc.call &self end end class ReturnNoProc c = self using Module.new { refine c do def to_proc true end end } def call ToProc.call &self end end class PrivateToProc c = self using Module.new { refine c do private def to_proc proc { "private_to_proc" } end end } def call ToProc.call &self end end class NonProc def call ToProc.call &self end end class MethodMissing def method_missing *args proc { "method_missing" } end def call ToProc.call &self end end class ToProcAndMethodMissing def method_missing *args proc { "method_missing" } end c = self using Module.new { refine c do def to_proc proc { "to_proc" } end end } def call ToProc.call &self end end class ToProcAndRefinements def to_proc proc { "to_proc" } end c = self using Module.new { refine c do def to_proc proc { "refinements_to_proc" } end end } def call ToProc.call &self end end end def test_to_proc assert_equal("to_proc", ToProc::ReturnProc.new.call) assert_equal("private_to_proc", ToProc::PrivateToProc.new.call) assert_raise(TypeError){ ToProc::ReturnNoProc.new.call } assert_raise(TypeError){ ToProc::NonProc.new.call } assert_equal("method_missing", ToProc::MethodMissing.new.call) assert_equal("to_proc", ToProc::ToProcAndMethodMissing.new.call) assert_equal("refinements_to_proc", ToProc::ToProcAndRefinements.new.call) end def test_unused_refinement_for_module bug14068 = '[ruby-core:83613] [Bug #14068]' assert_in_out_err([], <<-INPUT, ["M1#foo"], [], bug14068) module M1 def foo puts "M1#foo" end end module M2 end module UnusedRefinement refine(M2) do def foo puts "M2#foo" end end end include M1 include M2 foo() INPUT end def test_refining_module_repeatedly bug14070 = '[ruby-core:83617] [Bug #14070]' assert_in_out_err([], <<-INPUT, ["ok"], [], bug14070) 1000.times do Class.new do include Enumerable end Module.new do refine Enumerable do def foo end end end end puts "ok" INPUT end def test_super_from_refined_module a = EnvUtil.labeled_module("A") do def foo;"[A#{super}]";end end b = EnvUtil.labeled_class("B") do def foo;"[B]";end end c = EnvUtil.labeled_class("C", b) do include a def foo;"[C#{super}]";end end d = EnvUtil.labeled_module("D") do refine(a) do def foo;end end end assert_equal("[C[A[B]]]", c.new.foo, '[ruby-dev:50390] [Bug #14232]') end private def eval_using(mod, s) eval("using #{mod}; #{s}", Sandbox::BINDING) end end