I have the following class:
class Alphabet
attr_reader :letter_freqs, :statistic_letter
def initialize(lang)
@lang = lang
case lang
when :en
@alphabet = ('A'..'Z').to_a
@letter_freqs = { ... }
when :ru
@alphabet = ('А'..'Я').to_a.insert(6, 'Ё')
@letter_freqs = { ... }
...
end
@statistic_letter = @letter_freqs.max_by { |k, v| v }[0]
end
end
foo = Alphabet.new(:en)
The central member here is @alphabet
.
I'd like to make it some sort of a container class to invoke Array methods directly like
foo[i]
foo.include?
instead of explicitly accessing @alphabet
:
foo.alphabet[i]
foo.alphabet.include?
I know I could define a lot of methods like
def [](i)
@alphabet[i]
end
but I'm looking for a proper way of "inheriting" them.
You can use Forwardable
(it is included in the Ruby standard library):
require 'forwardable'
class Alphabet
extend Forwardable
def_delegators :@alphabet, :[], :include?
def initialize
@alphabet = ('A'..'Z').to_a
end
end
foo = Alphabet.new
p foo[0] #=> "A"
p foo.include? 'ç' #=> false
If you wish to delegate all the methods not defined by your class you can use SimpleDelegator
(also in the standard library); it lets you delegate all the methods that are not responded by the instance to an object specified by __setobj__
:
require 'delegate'
class Alphabet < SimpleDelegator
def initialize
@alphabet = ('A'..'Z').to_a
__setobj__(@alphabet)
end
def index
'This is not @alphabet.index'
end
end
foo = Alphabet.new
p foo[0] #=> "A"
p foo.include? 'ç' #=> false
p foo.index #=> "This is not @alphabet.index"
When the delegate doesn't need to be dynamic you can arrange the master class to be a subclass of DelegateClass
, passing the name of the class to be delegated as argument and calling super
passing the object to be delegated in the #initialize
method of the master class:
class Alphabet < DelegateClass(Array)
def initialize
@alphabet = ('A'..'Z').to_a
super(@alphabet)
end
More info about the delegation design pattern in Ruby here
You could extend the Forwardable module:
class Alphabet
require 'forwardable'
extend Forwardable
attr_accessor :alphabet
def initialize
@alphabet = [1,2,3]
end
def_delegator :@alphabet, :[], :include?
end
Then you can do:
alpha = Alphabet.new
alpha[1]==hey.alphabet[1]
=> true
Warning:
Don't try to delegate all methods (don't know if that's even possible) since they probably share some of the same method names such as class
, which would probably make chaos.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With