Как написать охранную оговорку с несколькими условиями в Ruby?

После запуска Rubocop против этого кода я получаю

Use a guard clause instead of wrapping the code inside a conditional expression.

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

Мой вопрос, однако, как бы я использовать охранное заявление с несколькими условиями

def auth_creds
  if %w(test1 qa demo ci).include? ENV['ENV']
    { key: 'key1', secret: 'secret1' }
  elsif ENV['ENV'] == 'live'
    { key: 'key2', secret: 'secret2' }
  else
    fail 'Unable to set key/secret'
  end
end

Спасибо

3 ответа

Ваш фрагмент не является допустимым примером для "охранных статей". Там нет ничего, чтобы защититься от. Это просто выбор данных. Это выглядело бы лучше как case/when но if цепь тоже в порядке.

def auth_creds
  case ENV['ENV']
  when 'test1', 'qa', 'demo', 'ci'
    { key: 'key1', secret: 'secret1' }
  when 'live'
    { key: 'key2', secret: 'secret2' }
  else
    fail 'Unable to set key/secret'
  end
end

Охранное предложение (или, как я их называю, ранние возвраты) используется, когда все тело метода заключено в условное выражение.

def process_project
  if project
    # do stuff
  end
end

Метод ничего не сделает, если нет project, Так что это делает код более читабельным, если мы сократим вложение здесь.

def process_project
  return unless project

  # do stuff with project
end

Опять не каждый if в вашем коде можно / нужно преобразовать в эту форму. Только там, где это уместно.

Все зависит от вашего фактического кода, но с данным фрагментом вы можете использовать пункт охраны, чтобы обеспечить действительный ENV['ENV'] значение:

VALID_ENVS = %w(test1 qa demo ci live)

def auth_creds
  fail 'invalid environment' unless VALID_ENVS.include? ENV['ENV']

  if ENV['ENV'] == 'live'
    { key: 'key2', secret: 'secret2' }
  else
    { key: 'key1', secret: 'secret1' }
  end
end

Как отметил Серхио Туленцев, ваши учетные данные хранятся в ENV (вместо имени env), вероятно, будет лучше:

def auth_creds
  { key: ENV.fetch('KEY'), secret: ENV.fetch('SECRET') }
end

fetch поднимет KeyError если данный ключ не найден в ENV,

Пункты охраны обычно идут так:

def do_something
  return 'x' if some_condition?
  # other code
end

Таким образом, ваш код может быть переписан как

def auth_creds
  return { key: 'key1', secret: 'secret1' } if %w(test1 qa demo ci).include? ENV['ENV']
  return { key: 'key2', secret: 'secret2' } if ENV['ENV'] == 'live'

  fail 'Unable to set key/secret'
end

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

def auth_creds
  return { key: 'key1', secret: 'secret1' } if test_env?
  return { key: 'key2', secret: 'secret2' } if live_env?

  fail 'Unable to set key/secret'
end

private # omit if `auth_creds` is also private

def test_env?
  %w(test1 qa demo ci).include? ENV['ENV']
end

def live_env?
  ENV['ENV'] == 'live'
end

Бонусные очки: извлечение %w(test1 qa demo ci) в постоянную!

Двойные бонусные баллы: (спасибо @Sergio Tulentsev) Извлеките из своего кода свои специфические для среды (и, вероятно, чувствительные!!!) учетные данные! Если вы используете Rails, поместите его в secrets.ymlиначе используйте для этого один из множества замечательных камней:


Несколько слов о Рубокопе: примите его совет с зерном соли. Например, ваш код на самом деле не подходит для защитного предложения, он просто возвращает данные в зависимости от условий. Таким образом, вместо этого вы можете попробовать рефакторинг кода в качестве выражения регистра.

А иногда Рубокоп просто говорит о мусоре:-) (не специально, но "измерить" стиль кода сложно!)

Другие вопросы по тегам