0

This is an example of what I am after:

def already_taken?
  # Magic goes here...
end

"Charlotte".already_taken?

Would it be possible to construct a method in a way where I can call it directly on a String object, without having to modify the String class itself?

2 Answers2

2

You could patch the String class with a custom module:

module MyStringPatch

  def already_taken?
    'yes'
  end

end

String.include MyStringPatch

"Charlotte".already_taken?
iGian
  • 11,023
  • 3
  • 21
  • 36
  • Why is this better than just adding the method to the string class directly? – Mark Jan 16 '19 at 14:32
  • `"Charlotte".extend(MyStringPatch).already_taken?` would add the methods to that instance only, leaving the `String` class unchanged. – Stefan Jan 16 '19 at 14:47
  • Sure, but why is including the module better than just adding the already_taken? method directly into the string class? Class String def already_taken? end end – Mark Jan 16 '19 at 15:19
  • @Mark, this is just my opinion: is cleaner, DRYer, useful for mixin and prevent to override default methods, where both opening the class or `String.prepend MyStringPatch` do. Here an interesting post: https://stackoverflow.com/questions/4470108/when-monkey-patching-a-method-can-you-call-the-overridden-method-from-the-new-i – iGian Jan 16 '19 at 18:20
  • One reason this is better is that if someone is looking at your code and wondering where this method comes from, they can do something like `'Charlotte'.method(:already_taken?).owner #=> MyStringPatch`, and it is very clear where the method comes from. Even better if the name of the module were more self-explanatory than just `MyStringPatch`. – Jörg W Mittag Jan 17 '19 at 09:04
1

If you want to add methods to any class (String in this case), without monkey-patching it, you should consider using Refinements.

module StringRefinements 
  refine String do
    def already_taken?
      puts "yes!"
    end
  end
end

# in another file...
using StringRefinements
"Charlotte".already_taken?

The already_taken? method will only be available in a scope that calls using StringRefinements and nowhere else.

DannyB
  • 12,810
  • 5
  • 55
  • 65
  • Thank you, Danny! This is exactly what I was after. Also, one more question: Is this considered a bad implementation compared to just modifying the String class? –  Jan 16 '19 at 15:37
  • @MihkelPajunen in general, you should avoid modifying Ruby's build-in classes. – Stefan Jan 16 '19 at 15:49
  • Both Refinements and monkey patching have their place. Refinements is a good practice especially when you know you want the method "locally". Monkey patching (i.e. opening the `String` class) is NOT considered bad practice if used with caution. For example, in your own projects, if you need a globally available method. But if it is something local, or if you develop a gem for public consumption - I would consider if monkey patching is needed. And I disagree with @Stefan's comment above - Ruby has open classes for this exact reason and many widely used projects monkey patch everything. – DannyB Jan 16 '19 at 17:15