我在下面有两个模型。由于必须进行联接,因此很难像进行工资计算一样呈现班次的索引视图。时钟有很多属性,有必要将它们分开存储,以使轮班在我们需要时具有动态性,包括旅行、休息和工作。
为避免性能问题,第一个和最后一个时钟的日期时间(我知道这个属性的名称不好)在班次中存储为started_at 和finished_at 以及班次的总持续时间。直到下一个时钟的持续时间存储在每个时钟中。
问题:保存时钟当前会回调其 Shift 以重新计算其属性,然后保存它们,这会再次自动保存所有时钟。这不是无限期的,因为回调中的 attribute_changed 检查,但它会重新计算并保存多次。
问题:如何确保所有模型都保持正确的缓存值,而无需在保存父级或子级时重新计算多次?
换档型号:
class Shift < ActiveRecord::Base
has_many :clocks, autosave: true, dependent: :destroy
attr_accessible :employee_id, :started_at, :finished_at, :duration, :clocks_attributes
before_save :calculate_cached_attributes
def calculate_cached_attributes
clocks.sort_by!(&:datetime)
self.started_at = clocks.first.datetime
# assign finished_at if shift is finished and check that there isn't one finished clock
if clocks.last.activity == :finished && clocks.first != clocks.last
self.finished_at = clocks.last.datetime
else
self.finished_at = nil
end
following_clock = nil
shift_duration = 0
clocks.reverse.each do |clock|
if following_clock
clock.duration = following_clock.datetime - clock.datetime
else
clock.duration = 0
end
shift_duration += clock.duration unless clock.activity == :break
following_clock = clock
end
self.duration = shift_duration
end
def update_cached_attributes!
if clocks.any? # check if being called because of clocks dependent destroy callback
save!
end
end
end
时钟型号:
class Clock < ActiveRecord::Base
belongs_to :shift
attr_accessible :shift_id, :datetime, :duration, :activity
symbolize :activity, in: [:work, :travel, :break, :finished] # symbolize gem
after_save :update_shift_attributes_after_save!
after_destroy :update_shift_attributes_after_destroy!
def update_shift_attributes_after_save!
if (datetime_changed? || activity_changed?) && shift
shift.update_cached_attributes!
end
end
def update_shift_attributes_after_destroy!
if shift
shift.reload.update_cached_attributes!
end
end
end
需要(重新)计算的示例:
shift.clocks.last.update_attribute(:datetime, Time.now)
# shift may no longer be complete if activity changed from :finished
shift.clocks.last.update_attribute(:activity, :work)
shift.clocks.last.datetime = Time.now
shift.save!
Shift.create!(employee_id: 1, clocks_attributes: [....] )
shift.clocks.last.destroy
我试图尽可能简化代码以突出问题。让我知道是否缺少某些东西,或者是否有完全不同的方法可以更好地做到这一点。