1

If you look through the documentation for Pathname you won't find a Pathname#gsub instance method. But there is a Pathname#sub.

So you get a (NoMethodError) when you try to call it:

require 'pathname'

some_pathname = Pathname.new("path/with/two/path/occurances/")
p some_pathname.sub(/path/, 'foobar')
#=> #<Pathname:some/foobar/with/two/occurances/of/path>
p some_pathname.gsub(/path/, 'foobar')
# undefined method `gsub' (NoMethodError)

Why wouldn't Pathname have a gsub method? It seems like if you have a sub you might as well have a gsub.

Because ruby has open classes you can monkey patch the Pathname class and add a gsub instance method.

That's what I did below:

require 'pathname'

class Pathname
  def gsub(re, str)
    gsubbed_pn = self
    while gsubbed_pn.to_s =~ re
      gsubbed_pn = gsubbed_pn.sub(re, str)
    end
    gsubbed_pn
  end
end

monkey_pathname = Pathname.new("path/with/two/path/occurances")
p monkey_pathname.gsub(/path/, 'foobar')
#=> #<Pathname:foobar/with/two/foobar/occurances>
p monkey_pathname
#<Pathname:path/with/two/path/occurances>

But I've read that monkey patching should probably not be the first tool I reach for.

Is there a better alternative in this case? Is Pathname missing a gsub method by design?

Community
  • 1
  • 1
mbigras
  • 7,664
  • 11
  • 50
  • 111
  • Why not just `Pathname.new(...).to_s.gsub(...)`? Pathnames are not strings, but they do sort of pretend to be kind of like them sometimes. – tadman Jan 05 '17 at 00:06
  • @tadman because I was hoping to make my `gsub` part of a method chain that eventually returns another instance of `Pathname` `some_pn.sub(...).gsub(...).sub(...) #=> some_other_pn` – mbigras Jan 05 '17 at 00:11
  • 1
    If you do want to go about this, be careful. Patching core classes can be trouble, *especially* if this is included in some gem other people use. It's possible that a future version of Ruby could include `gsub` here, so normally it's best to test if `instance_methods` on that class already includes one, and if not, only then do you inject your own. I've had a few situations where my monkeypatched method was later replaced by one of the same name in core (Rails) that did the opposite of what I intended. – tadman Jan 05 '17 at 00:15
  • You can scope your patch using [refinements](https://ruby-doc.org/core-2.1.1/doc/syntax/refinements_rdoc.html), which is a lot less disruptive. – max pleaner Jan 05 '17 at 00:24
  • @tadman thanks for the tip will check that out. Also is it less worry-some if I'm adding the `gsub` method to `SomeDir < Pathname`? That's what I'm actually doing in [my project](https://gist.github.com/mbigras/ef6085af94489f59482a16fb36da5cec#file-some_dir-rb-L41-L49) but I wanted to keep the question specific. Maybe I should change the wording in my question? – mbigras Jan 05 '17 at 00:31
  • You can make something like MySpecialPathName which inherits from Pathname and has extra methods. That should be the safest way to do this. Patching the core class carries risks, but sometimes this is worthwhile if you stand to benefit in other ways. It's your call. – tadman Jan 05 '17 at 01:12

0 Answers0