defined? vs nil? vs empty?

В Chef-рецептах часто требуется проверить, был ли передан какой-то параметр. В Ruby для этого есть несколько разных, логичных из названия, но не совсем очевидных по действию, методов: defined?, nil? и empty?. В частности их использование упоминалось раньше в статье про копировение hash, (defined?), дефолтные значения в Chef (nil?) и custom JSON в Chef (defined? и nil?).

Однако использование defined, nil или комплекта defined+nil, как выше - не самый удобный, очевидный и правильный способ. Использование empty? имеет уже другие особенности из-за того, что в случае, когда неизвестно, будет ли передаваться строка, массив или хэш, можно попасть на ошибку NoMethodError.

Чтобы не усложнять, не путаться и не получать ошибок - лучше использовать универсальную конструкцию "to_s.empty?", т.е. сначала преобразовать "неизвестно что" в строку (в случае Nil это дасть пустую, но именно строку!) и уже к String применить точно имеющийся у неё метод empty?.

Код:

default_aws_log = JSON.parse(JSON.generate(node['awslogs_conf_default']))

if default_aws_log['log_stream_name'].to_s.empty?
    default_aws_log['log_stream_name'] = instance_hostname
end

if node['awslogs_conf'].to_s.empty?
    Chef::Log.info("*** node['awslogs_conf'] is empty - set awslogs_conf_data to default ***")
    awslogs_conf_data = { 'default_aws_log': default_aws_log}
else
    Chef::Log.info("*** node['awslogs_conf'] defined and is '#{node['awslogs_conf']}' ***")
    awslogs_conf_data = JSON.parse(JSON.generate(node['awslogs_conf']))
    
    Chef::Log.info("*** check awslogs_conf_data = '#{awslogs_conf_data}' ***")
    awslogs_conf_data.each do |log_conf_name, cur_log|
        default_aws_log.each do |key, value|
            if not defined?(awslogs_conf_data[log_conf_name][key])
                Chef::Log.info("*** #{log_conf_name}[#{key}] is not defined, set to '#{value}' ***")
                awslogs_conf_data[log_conf_name][key] = value
            elsif awslogs_conf_data[log_conf_name][key].nil?
                Chef::Log.info("*** #{log_conf_name}[#{key}] is nil, set to '#{value}' ***")
                awslogs_conf_data[log_conf_name][key] = value
            end    
        end
    end
end
Chef::Log.info("*** awslogs_conf_data = '#{awslogs_conf_data}' ***")

Также ещё стоит упомянуть про метод blank?, являющийся подвидом empty? и который даст true даже если строка не пустая, но содержит "пустые" символы (пробелы).

Tags: 

Если вам помогла или просто понравилась статья - плюсаните/поделитесь, пожалуйста.

Добавить комментарий