I’ve created an OpenStruct-like class called Ribbon:
class Ribbon < BasicObject
def __hash__
@hash ||= {}
end
def initialize(hash = {}, &block)
__hash__.merge! hash, &block
Ribbon.convert_all! self
end
def self.convert(object)
case object
when ::Hash then self.new object
when ::Array then object.map { |element| convert element }
else object
end
end
def self.convert_all!(ribbon)
ribbon.__hash__.each do |key, value|
ribbon[key] = case value
when Ribbon then convert_all! value
else convert value
end
end
ribbon
end
end
Not included: [key], [key] = value, and method_missing. Full code.
I’d like to provide a way to customize the string generated by to_s and inspect. I want to make it so that the custom format is also applied to nested Ribbons:
hash = { a: { b: { c: 'd' } }, e: 'f' }
ribbon = hash.to_ribbon # Ribbon.new(self)
ribbon.to_s { |k, v| "#{k} -> #{v}" }
=> { Ribbon a -> { Ribbon b -> { Ribbon c -> d } }, e -> f }
My first try consisted of the following:
def to_s(&b)
# __h__ contains the key => value mappings.
v = __h__.map { |k, v| b ? b.call(k, v) : "#{key}:#{value}" }
"{ Ribbon #{v.join ', '} }"
end
# Format is applied only for the receiver.
=> { Ribbon a -> { Ribbon b:{ Ribbon c:d } }, e -> f }
Since Ribbon objects are a special case, I thought I’d intercept and handle them specially:
def to_s(&b)
v = __h__.map do |k, v|
case v
when Ribbon then v.to_s &b
else b ? b.call(k, v) : "#{key}:#{value}"
end
end
"{ Ribbon #{v.join ', '} }"
end
# Now, the keys for all nested hashes but the deepest are hidden.
=> { Ribbon { Ribbon { Ribbon c -> d } }, e -> f }
I’m having trouble figuring this out. What is the best way to solve this problem?
Try this:
Testing: