U:RDoc::TopLevel[ i I"syntax/refinements.rdoc:EFcRDoc::Parser::Simpleo:RDoc::Markup::Document:@parts[^S:RDoc::Markup::Heading:
leveli: textI"Refinements;To:RDoc::Markup::BlankLine o:RDoc::Markup::Paragraph;[ I"RDue to Ruby's open classes you can redefine or add functionality to existing ;TI"Qclasses. This is called a "monkey patch". Unfortunately the scope of such ;TI"Lchanges is global. All users of the monkey-patched class see the same ;TI"Nchanges. This can cause unintended side-effects or breakage of programs.;T@
o;
;[I"ORefinements are designed to reduce the impact of monkey patching on other ;TI"Ousers of the monkey-patched class. Refinements provide a way to extend a ;TI"class locally.;T@
o;
;[I" Here is a basic refinement:;T@
o:RDoc::Markup::Verbatim;[I"
class C
;TI" def foo
;TI" puts "C#foo"
;TI" end
;TI" end
;TI"
;TI"module M
;TI" refine C do
;TI" def foo
;TI" puts "C#foo in M"
;TI"
end
;TI" end
;TI" end
;T:@format0o;
;[I"PFirst, a class +C+ is defined. Next a refinement for +C+ is created using ;TI"RModule#refine. Refinements only modify classes, not modules so the argument ;TI"must be a class.;T@
o;
;[I"LModule#refine creates an anonymous module that contains the changes or ;TI"Srefinements to the class (+C+ in the example). +self+ in the refine block is ;TI"9this anonymous module similar to Module#module_eval.;T@
o;
;[I")Activate the refinement with #using:;T@
o;;[
I"
using M
;TI"
;TI"c = C.new
;TI"
;TI"!c.foo # prints "C#foo in M"
;T;0S; ;
i;I"
Scope;T@
o;
;[ I"PYou may activate refinements at top-level, and inside classes and modules. ;TI"RYou may not activate refinements in method scope. Refinements are activated ;TI"Runtil the end of the current class or module definition, or until the end of ;TI"/the current file if used at the top-level.;T@
o;
;[I"QYou may activate refinements in a string passed to Kernel#eval. Refinements ;TI"+are active the end of the eval string.;T@
o;
;[I"SRefinements are lexical in scope. Refinements are only active within a scope ;TI"Tafter the call to using. Any code before the using statement will not have the ;TI"refinement activated.;T@
o;
;[I"RWhen control is transferred outside the scope the refinement is deactivated. ;TI"TThis means that if you require or load a file or call a method that is defined ;TI"Boutside the current scope the refinement will be deactivated:;T@
o;;[I"
class C
;TI" end
;TI"
;TI"module M
;TI" refine C do
;TI" def foo
;TI" puts "C#foo in M"
;TI"
end
;TI" end
;TI" end
;TI"
;TI"def call_foo(x)
;TI"
x.foo
;TI" end
;TI"
;TI"
using M
;TI"
;TI"x = C.new
;TI"'x.foo # prints "C#foo in M"
;TI"*call_foo(x) #=> raises NoMethodError
;T;0o;
;[I"SIf a method is defined in a scope where a refinement is active the refinement ;TI"Rwill be active when the method is called. This example spans multiple files:;T@
o;
;[I"
c.rb:;T@
o;;[I"
class C
;TI" end
;T;0o;
;[I"
m.rb:;T@
o;;[I"require "c"
;TI"
;TI"module M
;TI" refine C do
;TI" def foo
;TI" puts "C#foo in M"
;TI"
end
;TI" end
;TI" end
;T;0o;
;[I"m_user.rb:;T@
o;;[I"require "m"
;TI"
;TI"
using M
;TI"
;TI"class MUser
;TI" def call_foo(x)
;TI" x.foo
;TI" end
;TI" end
;T;0o;
;[I"
main.rb:;T@
o;;[I"require "m_user"
;TI"
;TI"x = C.new
;TI"m_user = MUser.new
;TI".m_user.call_foo(x) # prints "C#foo in M"
;TI"1x.foo #=> raises NoMethodError
;T;0o;
;[I"HSince the refinement +M+ is active in m_user.rb
where ;TI"CMUser#call_foo
is defined it is also active when ;TI"+main.rb
calls +call_foo+.;T@
o;
;[I"TSince #using is a method, refinements are only active when it is called. Here ;TI"Aare examples of where a refinement +M+ is and is not active.;T@
o;
;[I"In a file:;T@
o;;[I"# not activated here
;TI"
using M
;TI"# activated here
;TI"class Foo
;TI" # activated here
;TI" def foo
;TI" # activated here
;TI" end
;TI" # activated here
;TI" end
;TI"# activated here
;T;0o;
;[I"In a class:;T@
o;;[I"# not activated here
;TI"class Foo
;TI" # not activated here
;TI" def foo
;TI" # not activated here
;TI" end
;TI" using M
;TI" # activated here
;TI" def bar
;TI" # activated here
;TI" end
;TI" # activated here
;TI" end
;TI"# not activated here
;T;0o;
;[I"QNote that the refinements in M are not activated automatically if the class ;TI"Foo is reopened later.;T@
o;
;[I"
In eval:;T@
o;;[I"# not activated here
;TI"eval <2}, {3=>4}].to_json # prints "[{\"1\":2},{\"3\":4}]"
;T;0S; ;
i;I"Method Lookup;T@
o;
;[I"GWhen looking up a method for an instance of class +C+ Ruby checks:;T@
o:RDoc::Markup::List:
@type:BULLET:@items[ o:RDoc::Markup::ListItem:@label0;[o;
;[I"QIf refinements are active for +C+, in the reverse order they were activated:;To;;;;[o;;0;[o;
;[I"6The prepended modules from the refinement for +C+;To;;0;[o;
;[I"The refinement for +C+;To;;0;[o;
;[I"5The included modules from the refinement for +C+;To;;0;[o;
;[I"!The prepended modules of +C+;To;;0;[o;
;[I"+C+;To;;0;[o;
;[I" The included modules of +C+;T@
o;
;[I"QIf no method was found at any point this repeats with the superclass of +C+.;T@
o;
;[
I"INote that methods in a subclass have priority over refinements in a ;TI"Lsuperclass. For example, if the method /
is defined in a ;TI"Mrefinement for Integer 1 / 2
invokes the original Fixnum#/ ;TI"Tbecause Fixnum is a subclass of Integer and is searched before the refinements ;TI" for the superclass Integer.;T@
o;
;[I"QIf a method +foo+ is defined on Integer in a refinement, 1.foo
;TI">invokes that method since +foo+ does not exist on Fixnum.;T@
S; ;
i;I"+super+;T@
o;
;[I"2When +super+ is invoked method lookup checks:;T@
o;;;;[o;;0;[o;
;[I"QThe included modules of the current class. Note that the current class may ;TI"be a refinement.;To;;0;[o;
;[I"PIf the current class is a refinement, the method lookup proceeds as in the ;TI"!Method Lookup section above.;To;;0;[o;
;[I"QIf the current class has a direct superclass, the method proceeds as in the ;TI"6Method Lookup section above using the superclass.;T@
o;
;[I"MNote that +super+ in a method of a refinement invokes the method in the ;TI"Srefined class even if there is another refinement which has been activated in ;TI"the same context.;T@
S; ;
i;I"Indirect Method Calls;T@
o;
;[I"MWhen using indirect method access such as Kernel#send, Kernel#method or ;TI"RKernel#respond_to? refinements are not honored for the caller context during ;TI"method lookup.;T@
o;
;[I"0This behavior may be changed in the future.;T@
S; ;
i;I"Further Reading;T@
o;
;[I"USee https://bugs.ruby-lang.org/projects/ruby-trunk/wiki/RefinementsSpec for the ;TI"Qcurrent specification for implementing refinements. The specification also ;TI"contains more details.;T:
@file@:0@omit_headings_from_table_of_contents_below0