3

考虑一个ActiveRecord名为的模型Movie,它有一个持续时间属性,以分钟为单位表示电影的长度。在我看来,打印此属性的最佳方法是什么?

我希望能够写出这样的东西:

= l(@movie.duration, unit: :minutes) # Should return "105 minutes"

但如果我能写出来,那就更棒了:

= l(@movie.duration)

甚至

= @movie.duration

但是,这需要某种方式来声明在持续时间属性中使用的单位,它只是数据库中的一个整数。

似乎 Rails I18n 有一种翻译属性的方法,但只有它们的名称,而不是它们的值。

与 I18n 配合得很好的解决方案是可取的。

4

3 回答 3

2

Rails 不知道该值以分钟为单位。

这个功能属于一个助手,或者(我的偏好)一个装饰器,带有 I18N 标签。

于 2013-05-15T23:17:48.513 回答
1

我认为没有任何理由在这里使事情复杂化。你甚至没有在不同的单位之间进行转换:

在您看来:

<%= t :'models.movie.duration', :count => @movie.duration %>

在你的config/locales/en.yml

en:
  models:
    movie:
      duration:
        one: '%{count} minute'
        other: '%{count} minutes'
于 2013-05-16T17:27:40.890 回答
1

差不多 7 年前我问过这个问题,现在仍然偶尔有活动。

因此,对于那些像我一样思考这个话题的(初级)开发人员,我想补充一些想法。

我认为这个问题试图解决多个问题。第一个问题是 PostgreSQL 没有用于存储持续时间的本机数据类型(至少不是我熟悉的那种)。

因此,在处理持续时间时,我们被迫在持续时间和 PostgreSQL 提供的数据类型之间进行转换。就我而言,我使用了整数类型。

所以有两个后续问题。

在没有本机持续时间类型的情况下,我应该使用什么数据类型?

我会说整数或小数。这取决于应用程序的需求和数据的质量。如果您喜欢存储为分钟,但需要(毫)秒精度,请使用小数。但总的来说,我发现使用整数更容易,所以最好使用一个足够小的单位,使整数足够精确。

我应该如何在应用程序中的数字类型(整数/小数)和持续时间类型之间进行转换?

根据您希望如何处理数据,我认为最好封装有关持续时间如何转换为数据库数据类型的知识,而不是将其泄漏到应用程序的视图(和其他地方)中。如果这些知识泄露,以后更改底层数据类型会变得更加困难——例如,如果 PostgreSQL/Rails 添加了更好的数据类型。

所以我可能会做一个价值类。这意味着创建一个使用数据库值初始化的持续时间类,但可以作为持续时间进行查询和修改。

简单的例子:

class Duration

  attr_accessor :seconds
  def initialize(seconds)
    @seconds = seconds
  end

  def minutes
    (seconds / 60.0).round
  end

  def minutes=(minutes)
    self.seconds = minutes * 60
  end

  def hours
    (seconds / (60.0 * 60)).round
  end

  def hours=(hours)
    self.seconds = hours * 60 * 60
  end

  def to_s(unit = :minutes) # Valid values are :seconds, :minutes and :hours.
    I18n.t("value_classes.duration.units.#{unit}", value: send(unit))
  end

end

如果您将它与 ActiveRecord 类上的自定义访问器结合使用,那么您将获得相当可靠的封装。

class Movie < ApplicationRecord

  def duration
    Duration.new(duration_in_seconds) # Name of database attribute
  end

  def duration=(duration_instance)
    self.duration_in_seconds = duration_instance.seconds
  end

end

在一个视图中:

<p>Duration: <%= @movie.duration %></p>

您可能希望向 Duration 类添加一些额外的格式化方法,因此您可以根据设计者在特定视图中的需要在“127 分钟”、“2 小时 7 分钟”和“2:07”之间进行选择。

该解决方案还包含访问器,因此您的控制器可以执行以下操作

@movie.duration = Duration.new.tap do |duration|
  duration.minutes = params[:movie][:duration_as_minutes]
end
于 2020-01-17T16:28:10.493 回答