2

Model "One"

class One < ActiveRecord::Base
  before_save :do_stuff

  private
    def do_stuff
      two = Two.find(8)
      two.field2 = 'Value'
      two.save!
    end
end

Model "Two"

class Two < ActiveRecord::Base
  before_save :do_stuff

  private
    def do_stuff
      one = One.find(7)
      one.field2 = 'SomeValue'
      one.save!
    end
end

Executing:

two = Two.find(1)
two.somefield = 'NewVal'
two.save!

Infinite loop will start. What would be most ruby-on-rails way to implement two models that must change each other on before_save callback?

Jonas
  • 4,683
  • 4
  • 45
  • 81
  • What exactly are you trying to achieve here? – Sergio Tulentsev Mar 14 '12 at 14:23
  • 2
    Surely you have problem with design, even if you would resolve the current question, this will lead you to the next problem – megas Mar 14 '12 at 14:44
  • +1 even if this looks like a lack in design I have found my self more than once in this kind of _cycle callback reference hell_. Could be helpful to have the possibility to [Avoid callbacks adhoc](http://stackoverflow.com/questions/632742/how-can-i-avoid-running-activerecord-callbacks) – fguillen Mar 14 '12 at 14:57

2 Answers2

2

On the hopefully rare occasions you need to do this, you might want to disable your before_save filter using an attr_accessor or move it to an after_save block to avoid looping.

For example:

class One < ActiveRecord::Base
  attr_accessor :not_doing_stuff
  before_save :do_stuff,
    :unless => :not_doing_stuff

private
  def do_stuff
    two = Two.find(8)
    two.field2 = 'Value'
    two.save!
  end
end

You'd disable the trigger on at least one of them:

class Two < ActiveRecord::Base
  before_save :do_stuff

private
  def do_stuff
    one = One.find(7)
    one.not_doing_stuff = true
    one.field2 = 'SomeValue'
    one.save!
  end
end

Things like this are always very ugly, so try and avoid it unless you can think of no other way. If you need it, make sure you've written enough unit tests to ensure it won't lock into an endless loop under some edge cases.

tadman
  • 208,517
  • 23
  • 234
  • 262
1

Don't call save in before_save: it will trigger an infinite loop. Return true or false instead -- true if whatever you put in the before_save succeeded, false if it failed. Returning false will cancel the save and all other callbacks.

Veraticus
  • 15,944
  • 3
  • 41
  • 45