Reputation: 43
Using Rails 6 and Ruby 2.7
I run calculations using callbacks, which works on create; however, this does not work on update. Unless marked complete, my calculation gets stuck in a loop. So, in order to edit/update I'm attempting to use the before_update to mark complete as false, and then run the same calculation as create and then marking it back to true. I can get it to break the loop and update the attributes, however, it does not run the calculations like it does upon create.
I've been trying so many variations to get this to work and feel absolutely lost. I've tried various callbacks and have gone through documentation and have been searching for an answer. If anyone can give me a clue, hint, or any insight, please. I am not receiving any errors of any kind.
For the .previous method, I am using the By_star gem and for the .second_to_last method, I am using the groupdate gem.
Thank you for your time.
belongs_to :user
belongs_to :store
after_create :create_calculations
before_update :before_update_calculations
after_update :update_calculations
def create_calculations
store = Store.find(self.store_id)
sheet = Sheet.find(self.id)
past_sheet = store.sheets.second_to_last
unless store.sheets.last
s = past_sheet.draw - sheet.returned
else
s = sheet.sold
end
unless past_sheet
s = store.sheets.last.draw - sheet.returned
else
s = past_sheet.draw - sheet.returned
end
if sheet.complete != true && sheet.store.vending_box == true
sheet.update(complete: true)
sheet.update(sold: s)
short = (sheet.sold * 0.75) - sheet.collected
sheet.update(:shortage => short)
pilferage = (sheet.shortage / 0.75)
sheet.update(:pilferage => pilferage)
elsif sheet.complete != true && sheet.store.vending_box == false
sheet.update(complete: true)
sheet.update(sold: s)
short = (sheet.sold * 0.55) - sheet.collected
sheet.update(:shortage => short)
pilferage = (sheet.shortage / 0.55)
sheet.update(:pilferage => pilferage)
else
end
end
def before_update_calculations
unless Sheet.find(self.id).complete == true
Sheet.find(self.id).update(complete: false)
end
end
def update_calculations
store = Store.find(self.store_id)
sheet = Sheet.find(self.id)
past_sheet = store.sheets.find(self.id).previous
# will add back conditionals to deal with the .previous method, for when
# it's the first record and as no previous item, such as in the create.
s = past_sheet.draw - sheet.returned
if sheet.complete != true && sheet.store.vending_box == true
sheet.update(complete: true)
sheet.update(:sold => s)
short = (sheet.sold * 0.75) - sheet.collected
sheet.update(:shortage => short)
pilferage = (sheet.shortage / 0.75)
sheet.update(:pilferage => pilferage)
elsif sheet.complete != true && sheet.store.vending_box == false
sheet.update(complete: true)
sheet.update(:sold => s)
short = (sheet.sold * 0.55) - sheet.collected
sheet.update(:shortage => short)
pilferage = (sheet.shortage / 0.55)
sheet.update(:pilferage => pilferage)
else
end
end
end```
Upvotes: 0
Views: 94
Reputation: 5740
A good way to avoid infinite update loops on callbacks, is to use a variable that is only used to that purpose. This variable should work as a flag. When up, run the callback. Otherwise, just skip it.
Thus, you could do, for your example, do the following:
attr_accessor :this_is_the_flag
after_update update_calculations, :if => "this_is_the_flag.nil?"
def update_calculations
[...]
self.this_is_the_flag = true # invalidate the callback
end
This is the general idea, you can adjust this to your need. Also, please, make sure the syntax is correct, because callback syntax tends to change regularly.
Upvotes: 0
Reputation: 31
You call update
in your after_update
which starts a new cycle of update
callbacks.
I'd rather use methods which I call explicitly over callbacks but if you want to stick to callbacks, try assigning values in a before_update
callback the data will be updated once.
Upvotes: 1