Reputation: 1059
Short:
Is there a way to create extended Array
class named A
, that supports full method chain with map
, sort
, sort_by
and new method word
and does not harm Array
class?
Example:
[
A.new(a).word,
A.new(a).sort.word,
A.new(a).sort_by(&:-@).word,
A.new(a).map(&:chr).sort.map(&:ord).word
]
Long story:
I solved this kata and created code that extends Array
class with new word
method:
class Array
def word
[*self[0..1],*self[-2..-1]].map(&:chr).join
end
end
def sort_transform(a)
[
a.word,
a.sort.word,
a.sort_by(&:-@).word,
a.map(&:chr).sort.map(&:ord).word
].join ?-
end
Then I thought this is not a good idea to add method for such kind of base classes. And I tried to implement new class that inherited all behavior from an Array
.
class A < Array
def word
[*self[0..1],*self[-2..-1]].map(&:chr).join
end
end
This addition breaks my code, because map
, sort
, sort_by
returns an Array
instance: A.new([1,2,3]).sort.class # Array
. And Array does not understand a word
method. And instead of A.new(a).sort.word
I have to encapsulate part of chain into a A.new
constructor: A.new(a.sort).word
. That's definitely breaks pure method chain.
Is it possible to extend Array
such way to reach pure method chains like this; A.new(a).sort.word
?
When I tried to write line this: class A < Array
def word
[*self[0..1],*self[-2..-1]].map(&:chr).join
end
def sort
A.new(self.sort)
end
end
This brings me main.rb:8:in 'sort': stack level too deep (SystemStackError)
Finally already writing this lines I found a way to avoid deep stack: converting self to Array
and then again convert it to A
.
class A < Array
def word
[*self[0..1],*self[-2..-1]].map(&:chr).join
end
def sort
A.new(self.to_a.sort)
end
end
So is It an only way to implement such extension?
Upvotes: 1
Views: 246
Reputation: 198388
If you ever want to monkey-patch a core class but you think it's icky, you should remember that refinements are made just for this scenario.
module ArrayWithWord
refine Array do
def word
[*self[0..1],*self[-2..-1]].map(&:chr).join
end
end
end
class WordTest
using ArrayWithWord
# [].word works here
def self.sort_transform(a)
[
a.word,
a.sort.word,
a.sort_by(&:-@).word,
a.map(&:chr).sort.map(&:ord).word
].join ?-
end
end
WordTest.sort_transform(%w(foo bar baz quux))
# => "fbbq-bbfq-bbfq-bbfq"
# [].word doesn't work here
[].word
# => NoMethodError (undefined method `word' for []:Array)
Upvotes: 4