I have two classes.
class Sky
attr_accessor :args
def initialize(args)
@args = args
puts 'Initializing sky'
end
end
class ShadowMask
attr_accessor :sky
def initialize(args)
args.each{|k, v| p "#{k}: #{v.to_s}"}
@sky = args.fetch(:sky, Sky.new({}))
end
end
ShadowMask
can be created either with a default Sky
:
sm_default = ShadowMask.new({})
# Initializing sky
# => #<ShadowMask:0x007fa215230eb0 @sky=#<Sky:0x007fa215230e60 @args={}>>
sm_default.sky
# => #<Sky:0x007fa215230e60 @args={}>
sm_default.sky.args
# => {}
or with a Sky
that has been previously created:
skyobj = Sky.new("Sky Object")
# Initializing sky
# => #<Sky:0x007fa21481a020 @args="Sky Object">
sm = ShadowMask.new(:sky => skyobj)
# "sky: #<Sky:0x007fa21481a020>"
# Initializing sky
# => #<ShadowMask:0x007fa21521ae80 @sky=#<Sky:0x007fa21481a020 @args="Sky Object">>
In this second case, the instance of Sky
already exists, and I do not want to see the output Initializing sky
from the Sky
initialization.
The problem with my actual code is that
puts 'Initializing sky'
is a call to a method that performs several calculations to complete the initialization and set several attributes. And this is repeated without need every time a ShadowMask
is created.
Interestingly, if I replace
@sky = args.fetch(:sky, Sky.new({}))
with something like
@sky = args.fetch(:sky, 'AnyString')
it works fine, but I would loose the possibility of creating a new Sky
if needed.
I am not sure if the problem is in the syntax or I am making a conceptual mistake.
If all you want is to provide a default Sky
object when key :sky
is omitted, then fetch
is a sub-optimal choice. This will work better:
@sky = args[:sky] || Sky.new({})
The "problem" with fetch
is that this will result in a nil sm.sky
:
sm = ShadowMask.new(sky: nil)
If this is desired behaviour for you, then use fetch
. If not, use ||
.