From 30a9931bbc96a3ccda672a2cfb0ed0cd883d7c18 Mon Sep 17 00:00:00 2001 From: drbrain Date: Thu, 24 Jan 2013 00:40:49 +0000 Subject: * doc/syntax/refinements.rdoc: Added refinements document based on the specification from the wiki. * doc/syntax.rdoc: Added link to refinements document. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@38913 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- doc/syntax.rdoc | 3 + doc/syntax/refinements.rdoc | 240 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 243 insertions(+) create mode 100644 doc/syntax/refinements.rdoc (limited to 'doc') diff --git a/doc/syntax.rdoc b/doc/syntax.rdoc index d840d936ed..fe0f98ce4c 100644 --- a/doc/syntax.rdoc +++ b/doc/syntax.rdoc @@ -26,6 +26,9 @@ Exceptions[rdoc-ref:syntax/exceptions.rdoc] :: Precedence[rdoc-ref:syntax/precedence.rdoc] :: Precedence of ruby operators +Refinements[rdoc-ref:syntax/refinements.rdoc] :: + Use and behavior of the experimental refinements feature + Miscellaneous[rdoc-ref:syntax/miscellaneous.rdoc] :: +alias+, +undef+, +BEGIN+, +END+ diff --git a/doc/syntax/refinements.rdoc b/doc/syntax/refinements.rdoc new file mode 100644 index 0000000000..c8581768fd --- /dev/null +++ b/doc/syntax/refinements.rdoc @@ -0,0 +1,240 @@ += Refinements + +Due to Ruby's open classes you can redefine or add functionality to existing +classes. This is called a "monkey patch". Unfortunately the scope of such +changes is global. All users of the monkey-patched class see the same +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. + +Refinements are an experimental feature in Ruby 2.0. At the time of writing, +refinements are expected to exist in future versions of Ruby but the +specification of refinements may change. You will receive a warning the first +time you define or activate a refinement. + +Here is a basic refinement: + + class C + def foo + puts "C#foo + end + end + + module M + refine C do + def foo + puts "C#foo in M" + end + end + 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 creates an anonymous module that contains the changes or +refinements to the class (+C+ in the example). +self+ in the refine block is +this anonymous module similar to Module#module_eval. + +Activate the refinement with #using: + + using M + + x = C.new + + c.foo # prints "C#foo in M" + +== Scope + +You may only activate refinements at top-level to the end of the file or in a +string passed to Kernel#eval, Kernel#instance_eval or Kernel#module_eval until +the end of the string. + +Refinements are lexical in scope. When control is transferred outside the +scope the refinement is deactivated. This means that if you require or load a +file or call a method that is defined outside the current scope the refinement +will be deactivated: + + class C + end + + module M + refine C do + def foo + puts "C#foo in M" + end + end + end + + def call_foo(x) + x.foo + end + + using M + + x = C.new + x.foo # prints "C#foo in M" + call_foo(x) #=> raises NoMethodError + +If a method is defined in a scope where a refinement is active the refinement +will be active when the method is called. This example spans multiple files: + +c.rb: + + class C + end + +m.rb: + + require "c" + + module M + refine C do + def foo + puts "C#foo in M" + end + end + end + +m_user.rb: + + require "m" + + using M + + class MUser + def call_foo(x) + x.foo + end + end + +main.rb: + + require "m_user" + + x = C.new + m_user = MUser.new + m_user.call_foo(x) # prints "C#foo in M" + x.foo #=> raises NoMethodError + +Since the refinement +M+ is active in m_user.rb where +MUser#call_foo is defined it is also active when +main.rb calls +call_foo+. + +Since #using is a method, refinements are only active when it is called. Here +are examples of where a refinement +M+ is and is not active. + +In a file: + + # not activated here + using M + # activated here + class Foo + # activated here + def foo + # activated here + end + # activated here + end + # activated here + +In eval: + + # not activated here + eval <2}, {3=>4}].to_json # prints "[{\"1\":2},{\"3\":4}]" + +== Method Lookup + +When looking up a method for a class +C+ Ruby checks: + +* 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+ + +If no method was found at any point this repeats with the superclass of +C+. + +Note that methods in a subclass have priority over refinements in a +superclass. For example, if the method / is defined in a +refinement for Integer 1 / 2 invokes the original Fixnum#/ +because Fixnum is a subclass of Integer and is searched before the refinements +for the superclass Integer. + +If a method +foo+ is defined on Integer in a refinement, 1.foo +invokes that method since +foo+ does not exist on Fixnum. + +== +super+ + +When +super+ is invoked method lookup checks: + +* 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. + +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. + +== Indirect Method Calls + +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. + +This behavior may be changed in the future. + +== Further Reading + +See http://bugs.ruby-lang.org/projects/ruby-trunk/wiki/RefinementsSpec for the +current specification for implementing refinements. The specification also +contains more details. + -- cgit v1.2.3