diff options
Diffstat (limited to 'doc/syntax/refinements.rdoc')
| -rw-r--r-- | doc/syntax/refinements.rdoc | 104 |
1 files changed, 70 insertions, 34 deletions
diff --git a/doc/syntax/refinements.rdoc b/doc/syntax/refinements.rdoc index d57080686a..80595eb445 100644 --- a/doc/syntax/refinements.rdoc +++ b/doc/syntax/refinements.rdoc @@ -7,7 +7,7 @@ changes. This can cause unintended side-effects or breakage of programs. Refinements are designed to reduce the impact of monkey patching on other users of the monkey-patched class. Refinements provide a way to extend a -class locally. +class locally. Refinements can modify both classes and modules. Here is a basic refinement: @@ -26,8 +26,7 @@ Here is a basic refinement: end First, a class +C+ is defined. Next a refinement for +C+ is created using -Module#refine. Refinements only modify classes, not modules so the argument -must be a class. +Module#refine. Module#refine creates an anonymous module that contains the changes or refinements to the class (+C+ in the example). +self+ in the refine block is @@ -211,53 +210,90 @@ all refinements from the same module are active when a refined method == Method Lookup -When looking up a method for an instance of class +C+ Ruby checks: +Method lookup in Ruby is based on the ancestor chain. You can see the +ancestor chain for any object in Ruby by doing: -* If refinements are active for +C+, in the reverse order they were activated: - * 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+ + object.singleton_class.ancestors + # or, if the object does not support a singleton class: + object.class.ancestors -If no method was found at any point this repeats with the superclass of +C+. +The ancestor chain is constructed as follows: -Note that methods in a subclass have priority over refinements in a -superclass. For example, if the method <code>/</code> is defined in a -refinement for Numeric <code>1 / 2</code> invokes the original Integer#/ -because Integer is a subclass of Numeric and is searched before the refinements -for the superclass Numeric. Since the method <code>/</code> is also present -in child +Integer+, the method lookup does not move up to the superclass. +* Subclasses are before superclasses in the ancestor chain +* Prepended modules are before the class they prepend in the ancestor + chain, in reverse order in which they were prepended. +* Included modules are after the class they are included in in the + ancestor chain, in reverse order in which they were included. + +When looking up a method for an object, Ruby goes through each ancestor: + +* If the class/module has been refined, Ruby will consider the refinements + activated at the point the method was called, in reverse order of + activation. +* Otherwise, Ruby will check the methods of the class/module itself. + +If no method was found at either point this repeats with the next +ancestor. -However, if a method +foo+ is defined on Numeric in a refinement, <code>1.foo</code> +Note that methods in a earlier ancestor have priority over refinements in a +later ancestor. For example, if the method <code>/</code> is defined in a +refinement for Numeric <code>1 / 2</code> invokes the original Integer#/ +because Integer is a comes before Numeric in the ancestor chain. However, +if a method +foo+ is defined on Numeric in a refinement, <code>1.foo</code> invokes that method since +foo+ does not exist on Integer. == +super+ -When +super+ is invoked method lookup checks: +When +super+ is invoked, method lookup starts: + +* If the method is in a refinement, at the refined class or module +* Otherwise, at the next ancestor -* The included modules of the current class. Note that the current class may - be a refinement. -* If the current class is a refinement, the method lookup proceeds as in the - Method Lookup section above. -* If the current class has a direct superclass, the method proceeds as in the - Method Lookup section above using the superclass. +Method lookup then proceeds as described in the Method Lookup section +above. -Note that +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. +Refinements activated at the call site of a refinement method do not +affect +super+ inside that method. Only refinements activated at the +point +super+ was called affect method lookup for that +super+ call. +You cannot use refinements to insert into the middle of a method +lookup chain, only to insert at the start of a method lookup chain, +unless you control the +super+ call sites. -== Indirect Method Calls +Note that if you refine a module, the refinement method can call +super+ +to call the method in the module, but the method in the module cannot +call +super+ to continue the method lookup process to further ancestors. -When using indirect method access such as Kernel#send, Kernel#method or -Kernel#respond_to? refinements are not honored for the caller context during -method lookup. +== Methods Introspection + +When using introspection methods such as Kernel#method or Kernel#methods refinements are not honored. This behavior may be changed in the future. +== Refinement inheritance by Module#include + +When a module X is included into a module Y, Y inherits refinements from X. + +For example, C inherits refinements from A and B in the following code: + + module A + refine X do ... end + refine Y do ... end + end + module B + refine Z do ... end + end + module C + include A + include B + end + + using C + # Refinements in A and B are activated here. + +Refinements in descendants have higher precedence than those of ancestors. + == Further Reading -See https://bugs.ruby-lang.org/projects/ruby-trunk/wiki/RefinementsSpec for the +See https://github.com/ruby/ruby/wiki/Refinements-Spec for the current specification for implementing refinements. The specification also contains more details. |
