Ruby on rails 我可以对rails模型中的计算值使用实例方法吗?

Ruby on rails 我可以对rails模型中的计算值使用实例方法吗?,ruby-on-rails,ruby,Ruby On Rails,Ruby,我有一个模型,它的数据中有一个时间长度(分和秒列)。我想添加一个方法来返回格式为mm:ss(如09:08)的时间。我已经做了一个方法来实现这一点,并使用irb进行了测试。但是,当我试图从视图中访问该值时,看起来实例变量没有被初始化,所有值的值都是“00:00”。在这里和其他地方,看起来我可以创建一个类方法(通过添加self)并将值传递给格式化,但这似乎是多余的。模型实例应该已经有了数据。使用类方法是ruby方式吗?还是我在做其他明显错误的事情 class Segment < Applic

我有一个模型,它的数据中有一个时间长度(分和秒列)。我想添加一个方法来返回格式为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。

为什么要尝试使用实例变量?如果
min
sec
是表中的列,您可以直接使用它们(隐式接收器将是
self
,因此这是表示
self.min
self.sec
的较短方式)

这应该起作用:

def time
  "#{pad(min)}:#{pad(sec)}"
end

一般来说,尽管我建议您考虑使用helpers和/或decorator,但您现在所做的是在模型中添加两种方法,它们仅用于显示目的。

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

您可以使用
segment.instance\u method\u get(:@attributes)

在这个结构中操作原始数据相当麻烦。所以AR为类中的每个属性定义属性方法。在您的例子中,
#min
#min=
#sec
#sec=
(以及一系列其他方法)

最好通过访问器方法访问数据,而不是直接访问实例变量。无论是从类内部还是外部调用该方法,这都是正确的

如果在类之外调用该方法,则需要显式地声明访问器方法的接收者,该方法将是您的
对象

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

有一种更优雅的方法来处理时间长度

只需使用一个整数列,并以所需的最小精度级别(秒)存储持续时间。在本例中,我们调用列
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
拥有一个时间对象可以让我们使用专门构建的方法,比如
strftime
,而不是重新发明轮子


尽管可以说格式化时间不是模型的工作,应该由助手或装饰师来处理。如果您是不了解区域设置的模型,则尤其如此。

我认为实例变量不是问题所在。我猜这与你在
时间
中构建字符串的方式有关。正如答案所示,尝试字符串插值。就类方法而言,您可以编写整个程序,将所有内容作为参数传递,而不存储实例状态。Thid需要更长的编写时间,但是让每个方法都是自包含的可以帮助解耦代码。最好的办法是尝试两种方法,看看你喜欢哪一种。你已经解决了我困惑的根源!非常感谢你!非常感谢。我一定会用它来重构我所拥有的。
irb(main):025:0> Time.at(360).utc
=> 1970-01-01 00:06:00 UTC