๐ŸŽ“ A1Learner
๐Ÿ”
0 of 28 lessons completed

Ruby Refinements

A refinement adds a method to an existing class inside one small scope. The added method answers only where the refinement is switched on with using.

A refined method inside one class

The Shout refinement adds shout to String. Greeter switches it on with using, so shout answers inside Greeter and nowhere else.

module Shout
  refine String do
    def shout
      upcase + "!"
    end
  end
end

class Greeter
  using Shout
  def greet(word)
    word.shout
  end
end

puts Greeter.new.greet("hello")
puts "hello".respond_to?(:shout)

Two refinements stay separate

Loud and Soft each refine String with a different method. Speaker uses only Loud, so soft never switches on at all.

module Loud
  refine String do
    def loud
      upcase
    end
  end
end

module Soft
  refine String do
    def soft
      downcase
    end
  end
end

class Speaker
  using Loud
  def say(word)
    word.loud
  end
end

puts Speaker.new.say("hey")
puts "hey".respond_to?(:loud)
puts "hey".respond_to?(:soft)

Switching on for the rest of a file

using at the top level switches the refinement on for the rest of the file. Inside that scope, respond_to? agrees that the method exists.

module Wrap
  refine String do
    def wrapped
      "[#{self}]"
    end
  end
end

using Wrap
puts "ruby".wrapped
puts "ruby".respond_to?(:wrapped)

Example

module Prefix
  refine String do
    def tagged
      "tag: " + self
    end
  end
end

class Label
  using Prefix
  def print(word)
    word.tagged
  end
end

puts Label.new.print("sale")
puts "sale".respond_to?(:tagged)

Key points

  • refine names the class that gains a method inside a module.
  • A method defined under refine belongs to the refinement alone.
  • using switches a refinement on for one class body or the rest of a file.
  • Outside the using scope, respond_to? answers false for the refined method.
  • Two refinements of the same class stay separate until each one is used.

In this example, the Prefix refinement adds tagged to String. Label switches it on, so print builds the tagged string. Outside Label, respond_to? answers false for tagged.

Note

A refinement never changes the class for the whole program.

Try it Yourself

Edit the code, then press Run.

Running is simulated

Exercise

Inside a class that uses a refinement of String, word.shout answers. What does "word".respond_to?(:shout) answer outside that class?