如何重构重复的虚拟属性?

时间:2014-08-27 15:33:29

标签: ruby-on-rails refactoring getter-setter virtual-attribute

我遇到了一个问题,我在模型中有非常相似的虚拟属性。简而言之,他们扮演的是转换器"对于某些属性。以下是其中一些虚拟属性的示例:

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

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

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

3 个答案:

答案 0 :(得分: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

答案 1 :(得分: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

答案 2 :(得分:0)

尝试对以下方面进行调查: method_missing,define_method并发送ruby。

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