【问题标题】:Is there a better alternative to this Ruby idiom?这个 Ruby 习语有更好的替代方法吗?
【发布时间】:2011-01-05 07:20:49
【问题描述】:

我发现自己经常在控制器中编写这段代码:

params[:task][:completed_at] = Time.parse(params[:task][:completed_at]) if params[:task][:completed_at]

不要纠结于我在这里具体做什么,因为原因每次都在变化;但在很多情况下,我需要检查 params 中的值并在将其交给 createupdate_attributes 之前对其进行更改。

重复params[:task][:completed_at] 三次感觉很糟糕。有没有更好的方法来做到这一点?

【问题讨论】:

  • 为什么要更改参数值?我问的是一般意义上的问题,以及这个具体的例子。
  • 这并没有真正回答您的问题,但这似乎是模型端的before_createbefore_save 中应该发生的事情类型。它也会看起来更漂亮一些,因为您不会拥有所有 params[:task] 业务。
  • @DougR:修复意味着模型将其属性值作为预期类型,这意味着哈希可以直接传递给#create()。正如所指出的,它不需要在控制器中完成。
  • @DougR:最常见的是当我需要在服务器端做一些我不想暴露给客户端的事情时。一个例子是用户在保存之前对模型进行标记:模型无法执行此操作,因为它看不到 current_user,所以我必须在控制器中自动执行此操作,以防止有人恶意更改关联用户。

标签: ruby-on-rails ruby idioms


【解决方案1】:

稍微缩短一点的方法是:

if c = params[:task][:completed_at]
  params[:task][:completed_at] = Time.parse(c)
end

或者,您可能更喜欢这个:

params[:task][:completed_at] &&= Time.parse(params[:task][:completed_at])

在第二种情况下,仅当左侧为“真”时才会发生分配。

【讨论】:

  • &&= 特别棒。以前从未见过。虽然,起初,它看起来有点时髦。
  • 我也没见过!我突然想到它会起作用。
  • 我也没见过&&=这样用过。不知道是不是有什么问题,还是之前没有人想到?
  • 出于某种原因&& 使用非布尔值的操作让我的大脑受伤,难怪我没有想到这一点。我什至一直使用||= 成语。很酷。
  • 我认为||= 是相当标准的(如果左侧是“假的”,则分配),所以我相当有信心这可以按预期工作。记住布尔运算符是如何定义的:|| 如果为真则返回其第一个参数,否则返回第二个参数; && 如果为 false,则返回其第一个参数,否则返回第二个参数。换句话说,每个返回需要计算的最后一个操作数以确定表达式的值。因此,如果第二个操作数是一个赋值,如果被赋值者为假,它将被评估为||,如果为真,则为&&
【解决方案2】:

我想你可以考虑做这样的事情。

在 String 和 NilClass 上实现 #to_time,可能在 extensions.rb 中(如在 Ruby Best Practices 中推荐的那样,例如

require 'time'
class String
  def to_time
    Time.parse(self) # add error/exception handling to taste
  end
end

class NilClass
  def to_time
    nil
  end
end

然后你可以打电话给params[:task][:created_at].to_time,重复就没有了。

我完全不确定这是否一定构成“最佳实践”,但恕我直言,它符合问题的目标......

【讨论】:

    【解决方案3】:

    我对 Ruby 不是很熟悉,但由于它有 Perl 根源,可能有一个结构允许您这样编写它:

    $_ = Time->parse($_) for params[:task][:completed_at] || ();
    

    基本上利用 for 循环为变量创建别名(如果存在)

    可能是这样的:

    (params[:task][:completed_at] || ()).each { |i| i = Time.parse(i) }
    

    编辑:

    我看到 Ruby 有一个 alias 关键字。我对它不够熟悉,无法给出一个 Ruby 示例,但在 Perl 中,也可以编写上述内容:

    local *_ = \$params[$task][$completed_at];
    
    $_ = Time->parse($_) if defined;
    

    指定$_ 将是$params[$task][$completed_at] 的别名

    我尝试在 Ruby 中简单地使用它,但没有看到为标识符起别名的方法,只是全局变量。

    【讨论】:

    • 这是行不通的,因为当:completed_at 为 nil 时,你会为 nil:NilClass` 得到 NoMethodError: undefined method each'。每个仅在迭代器类上可用。
    • () 替换为{} 将消除异常,但不会覆盖:completed_at 的值。
    猜你喜欢
    • 2018-10-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多