John Flow
John Flow

Reputation: 23

Add method to standard class inside a module

I want to add new method to class String, for example. But I don't want to make this change global (keeping classes clean is good, yes?).

So, instead of this code

class String
  def is_palindrome?
    self == self.reverse
  end
end

module MyModule
  class MyClass
    def filter_palindrome(str_arr)
      str_arr.select { |s| s.is_palindrome? }
    end
  end
end

I want to have something like this:

module MyModule
  class String
    def is_palindrome?
      self == self.reverse
    end
  end

  class MyClass
    def self.filter_palindrome(str_arr)
      str_arr.select { |s| s.is_palindrome? }
    end
  end
end

But, of course, it's not working (undefined method 'is_palindrome?' for :String). So, is there any point in what I want? And if there is, what is the best way to achieve it?

Upvotes: 1

Views: 475

Answers (1)

Arie Xiao
Arie Xiao

Reputation: 14082

If you are using Ruby 2.0, you can try refinements.

module MyModule
  module StringAlter
    refine String do
      def is_palindrome?
        self == self.reverse
      end
    end
  end
end

using MyModule::StringAlter
module MyModule
  class MyClass
    def self.filter_palindrome(str_arr)
      str_arr.select { |s| s.is_palindrome? }
    end
  end
end

If prior to Ruby 2.0, you cannot achieve this directly. Changes made to String will be global. However, instead of s.is_palindrome?, why not write a helper method and call it like is_palindrome?(s). Then you don't have to reopen String and you can restrict is_palindrome? to be available only in some given scope.

This is the way Python does (self), and so as to C# extension method.

Upvotes: 1

Related Questions