Reputation: 2435
I'd like to use delegate to pass map from a string on to chars, then join back to a string result.
require 'forwardable'
module Map
module String
extend Forwardable
def self.included(base)
base.send :extend, Forwardable
end
# Map for String
delegate map: :chars
end
end
class String
include Map::String
end
As it's originally a string I'd like to perform join after the delegated method has performed its duties. How do I modify the delegate line to include the additional change? The closest thing I've seen online is SimpleDelegator
with __setobj__
. But that's not well documented and I'm not able to ascertain how to use it for this.
I'm strictly looking for an answer in regards to delegate or SimpleDelegate
The equivalent behavior I'm looking for is this:
module Map
module String
def map(*args, &block)
if (!args.compact.empty? || !block.nil?)
self.chars.map(*args,&block).join
else
self.chars.map(*args,&block)
end
end
end
end
class String
include Map::String
end
I'm looking to understand how to do this with delegate.
Upvotes: 0
Views: 107
Reputation: 48599
The Fowardable docs are hilarious--as if that first example will run without a hundred errors. Your pseudo code tells ruby to forward the method call String#map
, which doesn't exist, to String#chars
, and you want to join() the result of that? Skip all the method calls and just write puts "some_string"
. So your question doesn't seem to make a lot of sense. In any case, Forwardable#delegate()
does not allow you to map one name to another name.
With regards to SimpleDelegat**or**, you can do this:
module Map
require 'delegate'
class MyStringDecorator < SimpleDelegator
def map
chars.shuffle.join('|')
end
end
end
d = Map::MyStringDecorator.new 'hello'
puts d.map
--output:--
h|o|l|l|e
Response to edit: The equivalent behavior I'm looking for..
The problem is ruby won't let you do this:
class String < SomeClass
end
which is what include
does, and you need to be able to do that in order to use delegate to forward all the method calls sent to one class to another class. This is the best you can do:
require 'delegate'
class MyString < DelegateClass(String)
def map(*args, &block)
if (!args.compact.empty? || !block.nil?)
self.chars.map(*args,&block).join
else
self.chars.map(*args,&block)
end
end
end
s = MyString.new 'hello'
puts s.upcase
puts s.map {|letter| letter.succ }
--output:--
HELLO
ifmmp
Or:
require 'forwardable'
class MyString
extend Forwardable
def initialize(str)
@str = str
end
def_delegators :@str, :upcase, :capitalize, :[], :chars #etc., etc., etc.
#Or: delegate({[:upcase, :capitalize, :[], :chars] => :@str})
#Or: instance_delegate({[:upcase, :capitalize, :[], :chars] => :@str})
def map(*args, &block)
if (!args.compact.empty? || !block.nil?)
self.chars.map(*args,&block).join
else
self.chars.map(*args,&block)
end
end
end
s = MyString.new('hello')
puts s.upcase
puts s.map {|letter| letter.succ }
--output:--
HELLO
ifmmp
Of course, you could always override String#method_missing()
to do what you want. What is it that you read about delegate that made you think it could replace include?
Upvotes: 1