Why isn't ActiveRecord's autosave working on my association?
callback, rails-activerecord, ruby, ruby-on-rails
Solution
The problem here is that `autosave: true` simply sets up a normal `before_save` callback, and `before_save` callbacks are run in the order that they're created.**
Therefore, it tries to save the `bar`, which has no changes, then it calls `modify_bar`.
The solution is to ensure that the `modify_bar` callback runs before the autosave.
One way to do that is with the `prepend` option.
class Foo
belongs_to :bar, autosave: true
before_save :modify_bar, prepend: true
...
end
Another way would be to put the `before_save` statement before the `belongs_to`.
Another way would be to explicitly save `bar` at the end of the `modify_bar` method and not use the `autosave` option at all.
Thanks to Danny Burkes for the helpful blog post.
** Also, they're run after all `after_validation` callbacks and before any `before_create` callbacks - see the docs.
Update
Here's one way to check the order of such callbacks.
describe "sequence of callbacks" do
let(:sequence_checker) { SequenceChecker.new }
before :each do
foo.stub(:bar).and_return(sequence_checker)
end
it "modifies bar before saving it" do
# Run the before_save callbacks and halt before actually saving
foo.run_callbacks(:save) { false }
# Test one of the following
#
# If only these methods should have been called
expect(sequence_checker.called_methods).to eq(%w[modify save])
# If there may be other methods called in between
expect(sequence_checker.received_in_order?('modify', 'save')).to be_true
end
end
Using this supporting class:
class SequenceChecker
attr_accessor :called_methods
def initialize
self.called_methods = []
end
def method_missing(method_name, *args)
called_methods << method_name.to_s
end
def received_in_order?(*expected_methods)
expected_methods.map!(&:to_s)
called_methods & expected_methods == expected_methods
end
end
Problem
I have an ActiveRecord class that looks something like this. ``` class Foo belongs_to :bar, autosave: true before_save :modify_bar ... end ``` If I do some logging, I see that the `bar` is being modified, but its changes are not saved. What's wrong?