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