1

我遇到了一个问题,我在非常相似的模型中具有虚拟属性。简而言之,它们充当某些属性的“转换器”。以下是其中一些虚拟属性的示例:

class Setting < ActiveRecord::Base
  validates :overtime, presence: true, numericality: { greater_than_or_equal_to: 0 }
  validates :shift_drop_cut_off, presence: true, numericality: { greater_than_or_equal_to: 0 }

  def overtime_hrs
    return 0 unless self.overtime.present?
    (self.overtime / 3600)
  end

  def overtime_hrs=(overtime_hrs)
    return 0 unless overtime_hrs.present?
    self.overtime = overtime_hrs.to_i * 3600
  end

  def shift_drop_cut_off_hrs
    return 0 unless self.shift_drop_cut_off.present?
    (self.shift_drop_cut_off / 3600)
  end

  def shift_drop_cut_off_hrs=(shift_drop_cut_off_hrs)
    return 0 unless shift_drop_cut_off_hrs.present?
    self.shift_drop_cut_off = shift_drop_cut_off_hrs.to_i * 3600
  end
end

在这种情况下,我有两列名为“overtime”和“shift_drop_cutoff”。这两列都是整数,以秒为单位表示时间。但是,我不想在几秒钟内向用户显示这些属性。相反,我想将它们转换为小时。因此,这就是虚拟属性的目的。

如您所见,这些虚拟属性 getter/setter 几乎相同。有没有人有关于我如何重构这个的提示?

4

3 回答 3

1

元编程ftw!

module ByHours
  extend ActiveSupport::Concern
  module ClassMethods
    def by_hours(name, base)
      define_method name do
        (send(base) || 0) / 3600
      end
      define_method "#{name}=" do |val|
        send("#{base}=", val * 3600)
      end
    end
  end
end

然后在您的设置类中:

class Setting
  by_hours :overtime_hrs, :overtime
  by_hours :shift_drop_cut_off_hrs, :shift_drop_cut_off
end
于 2014-08-27T16:03:39.747 回答
1

您可以定义类处理时间对话并在模型中使用它:

class Duration
  attr_reader :hours, :seconds

  def self.from_hours(hours)
    hours ||= 0
    seconds = hours * 3600

    new(seconds)
  end

  def self.from_seconds(seconds)
    seconds ||= 0

    new(seconds)
  end

  def initialize(seconds)
    @seconds = seconds
    @hours = @seconds / 3600
  end
end

然后在你的模型中:

def overtime_hrs
  Duration.from_seconds(self.overtime).hours
end

def overtime_hrs=(overtime_hrs)
  self.overtime = Duration.from_hours(overtime_hrs).seconds
end

def shift_drop_cut_off_hrs
  Duration.from_seconds(self.shift_drop_cut_off).hours
end

def shift_drop_cut_off_hrs=(shift_drop_cut_off_hrs)
  self.overtime = Duration.from_hours(shift_drop_cut_off_hrs).seconds
end
于 2014-08-27T16:23:49.987 回答
0

尝试对以下内容进行一些调查:method_missing、define_method 并发送 ruby​​。

这是一个很好的教程,可以帮助你

于 2014-08-27T16:07:56.380 回答