1= Refinements
2
3Due to Ruby's open classes you can redefine or add functionality to existing
4classes.  This is called a "monkey patch".  Unfortunately the scope of such
5changes is global.  All users of the monkey-patched class see the same
6changes.  This can cause unintended side-effects or breakage of programs.
7
8Refinements are designed to reduce the impact of monkey patching on other
9users of the monkey-patched class.  Refinements provide a way to extend a
10class locally.
11
12Refinements are an experimental feature in Ruby 2.0.  At the time of writing,
13refinements are expected to exist in future versions of Ruby but the
14specification of refinements may change.  You will receive a warning the first
15time you define or activate a refinement.
16
17Here is a basic refinement:
18
19  class C
20    def foo
21      puts "C#foo"
22    end
23  end
24
25  module M
26    refine C do
27      def foo
28        puts "C#foo in M"
29      end
30    end
31  end
32
33First, a class +C+ is defined.  Next a refinement for +C+ is created using
34Module#refine.  Refinements only modify classes, not modules so the argument
35must be a class.
36
37Module#refine creates an anonymous module that contains the changes or
38refinements to the class (+C+ in the example).  +self+ in the refine block is
39this anonymous module similar to Module#module_eval.
40
41Activate the refinement with #using:
42
43  using M
44
45  x = C.new
46
47  c.foo # prints "C#foo in M"
48
49== Scope
50
51You may only activate refinements at top-level to the end of the file or in a
52string passed to Kernel#eval, Kernel#instance_eval or Kernel#module_eval until
53the end of the string.
54
55Refinements are lexical in scope.  When control is transferred outside the
56scope the refinement is deactivated.  This means that if you require or load a
57file or call a method that is defined outside the current scope the refinement
58will be deactivated:
59
60  class C
61  end
62
63  module M
64    refine C do
65      def foo
66        puts "C#foo in M"
67      end
68    end
69  end
70
71  def call_foo(x)
72    x.foo
73  end
74
75  using M
76
77  x = C.new
78  x.foo       # prints "C#foo in M"
79  call_foo(x) #=> raises NoMethodError
80
81If a method is defined in a scope where a refinement is active the refinement
82will be active when the method is called.  This example spans multiple files:
83
84c.rb:
85
86  class C
87  end
88
89m.rb:
90
91  require "c"
92
93  module M
94    refine C do
95      def foo
96        puts "C#foo in M"
97      end
98    end
99  end
100
101m_user.rb:
102
103  require "m"
104
105  using M
106
107  class MUser
108    def call_foo(x)
109      x.foo
110    end
111  end
112
113main.rb:
114
115  require "m_user"
116
117  x = C.new
118  m_user = MUser.new
119  m_user.call_foo(x) # prints "C#foo in M"
120  x.foo              #=> raises NoMethodError
121
122Since the refinement +M+ is active in <code>m_user.rb</code> where
123<code>MUser#call_foo</code> is defined it is also active when
124<code>main.rb</code> calls +call_foo+.
125
126Since #using is a method, refinements are only active when it is called.  Here
127are examples of where a refinement +M+ is and is not active.
128
129In a file:
130
131  # not activated here
132  using M
133  # activated here
134  class Foo
135    # activated here
136    def foo
137      # activated here
138    end
139    # activated here
140  end
141  # activated here
142
143In eval:
144
145  # not activated here
146  eval <<EOF
147    # not activated here
148    using M
149    # activated here
150  EOF
151  # not activated here
152
153When not evaluated:
154
155  # not activated here
156  if false
157    using M
158  end
159  # not activated here
160
161When defining multiple refinements in the same module, inside a refine block
162all refinements from the same module are active when a refined method is
163called:
164
165  module ToJSON
166    refine Integer do
167      def to_json
168        to_s
169      end
170    end
171
172    refine Array do
173      def to_json
174        "[" + map { |i| i.to_json }.join(",") + "]"
175      end
176    end
177
178    refine Hash do
179      def to_json
180        "{" + map { |k, v| k.to_s.dump + ":" + v.to_json }.join(",") + "}"
181      end
182    end
183  end
184
185  using ToJSON
186
187  p [{1=>2}, {3=>4}].to_json # prints "[{\"1\":2},{\"3\":4}]"
188
189== Method Lookup
190
191When looking up a method for an instance of class +C+ Ruby checks:
192
193* If refinements are active for +C+, in the reverse order they were activated:
194  * The prepended modules from the refinement for +C+
195  * The refinement for +C+
196  * The included modules from the refinement for +C+
197* The prepended modules of +C+
198* +C+
199* The included modules of +C+
200
201If no method was found at any point this repeats with the superclass of +C+.
202
203Note that methods in a subclass have priority over refinements in a
204superclass.  For example, if the method <code>/</code> is defined in a
205refinement for Integer <code>1 / 2</code> invokes the original Fixnum#/
206because Fixnum is a subclass of Integer and is searched before the refinements
207for the superclass Integer.
208
209If a method +foo+ is defined on Integer in a refinement, <code>1.foo</code>
210invokes that method since +foo+ does not exist on Fixnum.
211
212== +super+
213
214When +super+ is invoked method lookup checks:
215
216* The included modules of the current class.  Note that the current class may
217  be a refinement.
218* If the current class is a refinement, the method lookup proceeds as in the
219  Method Lookup section above.
220* If the current class has a direct superclass, the method proceeds as in the
221  Method Lookup section above using the superclass.
222
223Note that +super+ in a method of a refinement invokes the method in the
224refined class even if there is another refinement which has been activated in
225the same context.
226
227== Indirect Method Calls
228
229When using indirect method access such as Kernel#send, Kernel#method or
230Kernel#respond_to? refinements are not honored for the caller context during
231method lookup.
232
233This behavior may be changed in the future.
234
235== Further Reading
236
237See http://bugs.ruby-lang.org/projects/ruby-trunk/wiki/RefinementsSpec for the
238current specification for implementing refinements.  The specification also
239contains more details.
240
241