我可以在rails模型中为计算值使用实例方法吗?

时间:2017-01-22 04:33:23

标签: ruby-on-rails ruby

我有一个模型,其时间长度(分钟和秒列)作为其数据的一部分。我想添加一个方法来返回格式为mm:ss的时间(如09:08)。我已经做了一个方法来做这个并使用irb进行测试。但是当我尝试从我的视图中访问该值时,看起来实例变量没有被初始化并且我得到了" 00:00"对于所有的价值观。在这里和其他地方看,看起来我可以创建一个类方法(通过添加self。)并传递值进行格式化,但这似乎是多余的。模型实例应该已经拥有数据。使用类方法的ruby方式?或者我在做其他明显错误的事情?

class Segment  < ApplicationRecord
  belongs_to :course

  def time
    m = pad @min
    s = pad @sec
    m + ":" + s
  end 

  def pad(x)
    x.to_s.rjust(2,"0")
  end
 end

 view:
  <%= segment.time %> # returns 00:00
  <%= segment.min %>:<%= segment.sec %> # returns correct non-padded values

我使用的是Rails 5.0.1和Ruby 2.2.6p386。

3 个答案:

答案 0 :(得分:2)

有一种更优雅的方式来处理持续时间。

只需使用单个整数列,并将持续时间存储在所需的最小精度级别(秒)。在此示例中,我们调用列duration

class Segment < ApplicationRecord
  # custom getter which returns an ActiveSupport::Duration
  def duration
    self[:duration].seconds
  end

  def duration_as_time
    Time.at(self[:duration]).utc
  end

  def time
    duration_as_time.strftime("%M:%S")
  end
end

请注意,我们使用的是方括号访问器[]。 Ruby允许您在任何类上定义这些访问器 - 在ActiveRecord中,它们用于获取/设置列值。

duration_as_time方法为我们提供了一个Time对象 unix epoch加上持续时间:

irb(main):025:0> Time.at(360).utc
=> 1970-01-01 00:06:00 UTC

拥有一个时间对象让我们可以使用专用的方法,如strftime,而不是重新发明轮子。

可以说,格式化时间不是模型的工作,应由助手或装饰者处理。如果您localizing times因为模型不能识别区域设置,则尤其如此。

答案 1 :(得分:1)

为什么要尝试使用实例变量?如果minsec是您的表格的列,您可以直接使用它们(隐式接收者将是self,因此它更简短地说{{1}和self.min)。

这应该有效:

self.sec

一般情况下,虽然我建议您考虑使用辅助工具和/或装饰工具,因此您现在正在做的事情是为模型添加2种仅用于显示目的的方法。

答案 2 :(得分:1)

问题在于您尝试访问ActiveRecord属性,并假设每个数据字段都存储在实例变量中。当属性作为@attributes结构存储在ActiveRecord::AttributeSet中时,这不起作用。

您可以使用segment.instance_method_get(:@attributes)

查看结构

在此结构中操作原始数据非常麻烦。因此AR为类中的每个属性定义属性方法。在您的情况下,#min#min=#sec#sec=(以及其他一些方法)。

通过访问器方法访问数据而不是直接访问实例变量是一种很好的做法。无论您是从课堂内外调用该方法,都是如此。

如果您在课外调用该方法,则需要明确说明访问者方法的接收者,该方法将是您的segment对象。

segment.min

在你的班级中,接收器被隐式定义为self(如Michael Kohl所提到的),因此你可以直接使用

来调用访问者
class Segment
  def padded_min
    min to_s.rjust(2,"0")
  end
end

对于以=结尾的方法,隐式定义的工作方式略有不同 - Ruby假设您正在创建局部变量。如果您尝试调用setter方法,则需要明确定义接收器。

class Segment
  def update_time(time)
    self.time = (time)
  end
end