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 

在這種情況下,我有一個名爲「加班」和「shift_drop_cutoff」兩列。這兩列都是以秒爲單位的整數。但是,我不想在幾秒鐘內向用戶顯示這些屬性。相反,我想將它們轉換爲幾小時。因此,這是虛擬屬性的目的。

正如你所看到的,這些虛擬屬性的getter/setter幾乎是相同的。有沒有人有關於如何重構這個的提示?

回答

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 
+0

非常感謝你!我會贊成,但我還沒有足夠的聲望。我是一個noob :(。 – benbartling 2014-08-28 21:22:16

0

設法使一些調查: 的method_missing,define_method和紅寶石發送。

這裏是一個很好的tutorial,可以幫助您

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