Update 2
Incidentally, as I was typing Update 1
@muffinista posted the same conclusion I came to in Update 1
and I just wanted to add clarification that I am not sniping his answer and updating the question randomly. After thinking it through some more after having posted the question, I realized what the issue was. While @muffinista posted the cause, his solution was inadequate - that's why I am leaving Update 1
intact - because I found a better solution and it makes sense for others to get the full context.
Update 1
So I figured what is causing this error, an infinite loop.
I am trying to save the client
record within an after_save
callback on the Client record. So it keeps trying to save the client record and executing the after_save callback which tries to save the client_record.
How can I achieve what I want, i.e. updating this client.weighted_score
attribute whenever the record is updated without jumping into this loop?
Original Question
I have this callback:
after_save :calculate_weighted_score, :if => Proc.new { |c| c.score.present? }
def calculate_weighted_score
# Sum products of weight & scores of each attribute
client = self
weight = Weight.first
score = self.score
client.weighted_score = (weight.firm_size * score.firm_size) + (weight.priority_level * score.priority_level) +
(weight.inflection_point * score.inflection_point) + (weight.personal_priority * score.personal_priority) +
(weight.sales_priority * score.sales_priority) + (weight.sales_team_priority * score.sales_team_priority) +
(weight.days_since_contact * score.days_since_contact) + (weight.does_client_vote * score.does_client_vote) +
(weight.did_client_vote_for_us * score.did_client_vote_for_us) + (weight.days_until_next_vote * score.days_until_next_vote) +
(weight.does_client_vote_ii * score.does_client_vote_ii) + (weight.did_client_vote_ii_for_us * score.did_client_vote_ii_for_us) +
(weight.days_until_vote_ii * score.days_until_vote_ii)
client.o
# self.save
# client.update_attributes(:weighted_score => weighted_score)
end
This is an example of the state of a Client record before this callback is run:
#<Client:0x007fe00dbcea90> {
:id => 10,
:name => "Manta-Jar Gale",
:email => "mj@gmail.com",
:phone => 8769876435,
:firm_id => 1,
:created_at => Fri, 23 Nov 2012 23:50:09 UTC +00:00,
:updated_at => Tue, 27 Nov 2012 17:50:01 UTC +00:00,
:user_id => 1,
:personal_priority => true,
:last_contact => Sat, 08 Jan 2011,
:vote => true,
:vote_for_user => false,
:next_vote => Thu, 02 Jan 2014,
:vote_ii => true,
:vote_ii_for_us => true,
:next_vote_ii => Mon, 01 Jul 2013,
:weighted_score => nil,
:firm_size => 100.0
}
Notice the weighted_score => nil
attribute.
After the callback, this same record looks like this:
#<Client:0x007fe00dbcea90> {
:id => 10,
:name => "Manta-Jar Gale",
:email => "mj@gmail.com",
:phone => 8769876435,
:firm_id => 1,
:created_at => Fri, 23 Nov 2012 23:50:09 UTC +00:00,
:updated_at => Tue, 27 Nov 2012 17:50:01 UTC +00:00,
:user_id => 1,
:personal_priority => true,
:last_contact => Sat, 08 Jan 2011,
:vote => true,
:vote_for_user => false,
:next_vote => Thu, 02 Jan 2014,
:vote_ii => true,
:vote_ii_for_us => true,
:next_vote_ii => Mon, 01 Jul 2013,
:weighted_score => 9808,
:firm_size => 100.0
}
Notice the weighted_score => 9808
attribute.
So I know that the callback calculate_weighted_score
is being run, and the entire callback seems to be correct up until the assignment of the client.weighted_score
. The issue is, the log shows no UPDATE
db transaction for that attribute:
Started PUT "/clients/10" for 127.0.0.1 at 2012-11-27 12:50:01 -0500
Processing by ClientsController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"J172LuZQ=", "client"=>{"name"=>"Manta-Jar Gale", "email"=>"mj@gmail.com", "phone"=>"8769876435", "firm_id"=>"1", "topic_ids"=>["1", "2", "9"], "personal_priority"=>"1", "last_contact"=>"2011-01-08", "vote"=>"1", "vote_for_user"=>"0", "next_vote"=>"2014-01-02", "vote_ii"=>"1", "vote_ii_for_us"=>"1"}, "commit"=>"Update Client", "id"=>"10"}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 LIMIT 1
Client Load (0.2ms) SELECT "clients".* FROM "clients" WHERE "clients"."user_id" = 1 AND "clients"."id" = ? LIMIT 1 [["id", "10"]]
Topic Load (0.3ms) SELECT "topics".* FROM "topics"
(0.1ms) begin transaction
Topic Load (0.2ms) SELECT "topics".* FROM "topics" WHERE "topics"."id" IN (1, 2, 9)
Topic Load (0.1ms) SELECT "topics".* FROM "topics" INNER JOIN "clients_topics" ON "topics"."id" = "clients_topics"."topic_id" WHERE "clients_topics"."client_id" = 10
(0.5ms) UPDATE "clients" SET "name" = 'Manta-Jar Gale', "updated_at" = '2012-11-27 17:50:01.856893' WHERE "clients"."id" = 10
Firm Load (0.2ms) SELECT "firms".* FROM "firms" WHERE "firms"."id" = 1 LIMIT 1
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 LIMIT 1
Score Load (0.2ms) SELECT "scores".* FROM "scores" WHERE "scores"."client_id" = 10 LIMIT 1
SalesTeam Load (0.1ms) SELECT "sales_teams".* FROM "sales_teams" WHERE "sales_teams"."id" = 2 LIMIT 1
PriorityLevel Load (0.1ms) SELECT "priority_levels".* FROM "priority_levels" WHERE "priority_levels"."id" = 1 LIMIT 1
CACHE (0.0ms) SELECT "scores".* FROM "scores" WHERE "scores"."client_id" = 10 LIMIT 1
Max Load (0.2ms) SELECT "maxes".* FROM "maxes" WHERE "maxes"."user_id" = 1 LIMIT 1
CACHE (0.0ms) SELECT "scores".* FROM "scores" WHERE "scores"."client_id" = 10 LIMIT 1
Weight Load (0.2ms) SELECT "weights".* FROM "weights" LIMIT 1
(3.2ms) commit transaction
Redirected to http://localhost:3000/clients
Completed 302 Found in 796ms (ActiveRecord: 26.4ms)
The only UPDATE
transaction is for the edit to the name I made to test the callback.
I know there is no UPDATE
transaction, because technically I am not saving the record.
But when I try to do any of the commented out statements - i.e. client.save
or client.update_attributes(....)
I get a Stack Level Too Deep
Error.
What is causing this and how can I save this record?