'Chain methods on String without core extensions
I am looking for a way (or perhaps a best practice) to chain various methods on a string object without actually opening up the String class. This is because the transformations I want to apply to the String object are fairly project specific, and I don't see a reason to pollute the global space with it.
Is there a way to achieve this? Maybe a wrapper of some sort? I experimented with gsub! on an instance variable but gsub!, unless the gsub, throws nil on failure to match, so it halts the chaining I want to achieve.
Basically, I need to be able to do:
"my_string".transformation1.transformation2.transformation3
and get to keep all those transformations namespaced to my application. Any ideas?
Solution 1:[1]
You can add a method to an instance, but you're going to need to jump through a hoop to chain the methods:
str = 'now is the time'
def str.foo(target, replacement)
self.gsub(target, replacement)
end
def str.bar
self.reverse
end
str.singleton_methods # => [:foo, :bar]
str.foo('ow', 'ever') # => "never is the time"
str.bar # => "emit eht si won"
This won't work:
str.foo('ow', 'ever').bar
# ~> NoMethodError
# ~> undefined method `bar' for "never is the time":String
You can use "bang" type methods, that mutate the original object:
str = 'now is the time'
def str.foo(target, replacement)
self.gsub!(target, replacement)
self
end
def str.bar
self.reverse!
self
end
str.singleton_methods # => [:foo, :bar]
str.foo('ow', 'ever').bar # => "emit eht si reven"
Be careful though. This won't work on every type of object, only ones that allow mutating.
And, the fact that the added methods are only available to that particular variable is really limiting; It's messier reusing that same functionality. You can clone the variable and assign that clone to a new variable, but replacing the value with something different becomes messy:
str2 = str.clone
str.object_id # => 70259449706680
str2.object_id # => 70259449706520
str.singleton_methods # => [:foo]
str2.singleton_methods # => [:foo]
str2 = 'all good men'
str2.foo('ll', '') # =>
# ~> NoMethodError
# ~> undefined method `foo' for "all good men":String
Personally, I'd do it through a subclass:
class MyString < String
def foo(s, t)
self.gsub(s, t)
end
end
str = MyString.new('now is the time')
str.foo('ow', 'ever') # => "never is the time"
str2 = 'all good men'
str2.foo('ll', '') # =>
# ~> NoMethodError
# ~> undefined method `foo' for "all good men":String
If you're on Ruby v2+, look at @mdesantis's answer using refinements. They were introduced to solve this sort of problem. If you're < v2.0, I'd go the sub-class route or accept having to modify String.
Solution 2:[2]
If you are using Ruby >= 2.0 you can use refinements:
module MyRefinements
refine String do
def transformation1
# ...
self
end
def transformation2
# ...
self
end
def transformation3
# ...
self
end
end
end
# Either in global scope (active until the end-of-file)
# or in lexical scope (active until the lexical scope end)
# More on this in the refinements docs scope section
# global scope
using MyRefinements
"test".transformation1.transformation2.transformation3 #=> "test"
# lexical scope
begin
using MyRefinements
"test".transformation1.transformation2.transformation3 #=> "test"
end
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 |
