【问题标题】:How to write a guard clause with multiple conditions in Ruby?如何在 Ruby 中编写具有多个条件的保护子句?
【发布时间】:2015-12-05 01:36:25
【问题描述】:

在针对这段代码运行 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

谢谢

【问题讨论】:

  • "针对此代码运行 Rubocop" - 真的是 this 代码吗?我没有收到该消息。

标签: ruby guard-clause


【解决方案1】:

您的 sn-p 不是“保护条款”的有效示例。没什么好防备的。它只是选择数据。 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 都可以/应该转换为这种形式。只在合适的地方。

【讨论】:

  • 谢谢,如果不满足第一个条件,为什么我会退出该方法,我会保持原样,我想我将不得不关闭 @987654328 @一旦我都检查过了
  • “没有什么可防备的” - 好吧,您可以检查 ENV 有一个 'ENV' 键,并且 ENV['ENV'] 有一个有效值。跨度>
  • @Stefan:嗯,这两个条件也涵盖了。我认为无需进行多余的检查。
  • @SergioTulentsev 而不是重复检查,我会将检查 移动 到保护子句中。查看我的答案(评论代码太多)。
【解决方案2】:

这一切都取决于您的实际代码,但是对于给定的 sn-p,您可以使用保护子句来确保有效的 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

作为noted by Sergio Tulentsev,将您的凭据存储在ENV(而不是环境名称)中可能会更好:

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

如果在ENV 中找不到给定的密钥,fetch 将引发KeyError

【讨论】:

  • 不确定我会在我的代码中写这样的东西。我想我会选择 case/when (很明显哪个 env 对应于哪个凭据,不需要心理编译)或直接从 ENV 获取凭据,无需分支(12 因素应用程序最佳实践)
  • @SergioTulentsev 这只是一个示例,OP 的实际代码可能看起来不同。但是当然,将凭据存储在 ENV 中要好得多,我已经更新了我的答案。
【解决方案3】:

保护子句通常是这样的:

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 中,否则请为此使用众多出色的宝石之一:


关于 Rubocop 的一句话: 对它的建议持保留态度。例如,您的代码实际上并不是保护子句的情况,它只是根据条件返回数据。因此,您也可以尝试将代码重构为case expression

有时,Rubocop 只会说垃圾话 :-)(不是故意的,但“衡量”代码风格很难!)

【讨论】:

  • 对于双倍奖励积分,将整个内容提取到 ENV vars 中,这样凭据就不会存储在代码中。 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-13
  • 2023-03-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多