happycoffeebean
happycoffeebean

Reputation: 43

Using ActiveRecord callbacks

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

Answers (2)

Ruby Racer
Ruby Racer

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

Uepsilon
Uepsilon

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

Related Questions