2015-09-04 4 views
1

У меня есть два класса.Ruby Hash инициализировать путаницу

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 может быть создан либо по умолчанию Sky:

sm_default = ShadowMask.new({}) 
# Initializing sky 
# => #<ShadowMask:0x007fa215230eb0 @sky=#<Sky:0x007fa215230e60 @args={}>> 
sm_default.sky 
# => #<Sky:0x007fa215230e60 @args={}> 
sm_default.sky.args 
# => {} 

или с Sky, который был ранее создан:

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">> 

В этом втором случае, экземпляр Sky уже существует, и я не хочу видеть вывод Initializing sky от Sky инициализация.

Проблемы с моим фактическим кодом является то, что

puts 'Initializing sky' 

является вызовом метода, который выполняет несколько вычислений для завершения инициализации и задать несколько атрибутов. И это повторяется без необходимости каждый раз, когда создается ShadowMask.

Интересно, если я заменю

@sky = args.fetch(:sky, Sky.new({})) 

с чем-то вроде

@sky = args.fetch(:sky, 'AnyString') 

это работает хорошо, но я бы потерять возможность создания нового Sky при необходимости.

Я не уверен, что проблема в синтаксисе или я делаю концептуальную ошибку.

ответ

2

Если вы хотите предоставить объект по умолчанию Sky, когда ключ :sky опущен, то fetch является субоптимальным выбором. Это будет работать лучше:

@sky = args[:sky] || Sky.new({}) 

«Проблема» с fetch является то, что это приведет к нулевой sm.sky:

sm = ShadowMask.new(sky: nil) 

Если это желательно поведение для вас, а затем использовать fetch. Если нет, используйте ||.

+1

Итак, с '||' 'args [: sky]' оценивается сначала, и только если это 'false' или' nil', тогда будет оцениваться 'Sky.new ({})'. Правильно? – Rojj

+2

@Rojj: Да, это называется «оценка короткого замыкания», –

4

Я думаю, что вам нужно передать блок fetch для того, чтобы не видеть Initializing sky:

@sky = args.fetch(:sky) {Sky.new({})} 

идея в том, что при вызове любого метода, первоначально его PARAMS (в данном случае Sky.new({})) будет вызывается. Когда вы передаете блок - он будет вызываться после внутреннего метода fetch, а не раньше.

+1

Да, аргументы метода оцениваются до вызова метода. Это означает создание здесь «Небо». –

+0

Итак, если я правильно понимаю: ': sky' и' Sky.new ({}) 'оцениваются до вызова fetch? – Rojj

+1

Rojj: Да, точно. –