以下に2つのモデルがあります。シフトのインデックスビューは、結合を行う必要があるため、給与計算を行う場合と同様にレンダリングするのが非常に困難です。時計には多くの属性があり、シフトを必要なだけ動的にし、移動、休憩、仕事を含めるには、時計を個別に保存する必要があります。
パフォーマンスの問題を回避するために、最初と最後のクロックの日時(これはこの属性の不適切な名前です)は、シフトの合計期間だけでなく、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
問題を浮き彫りにするために、コードをできるだけ単純化しようとしました。何か足りないものがあるかどうか、またはこれをより良くするためのまったく異なる方法があるかどうかを教えてください。