I had my first WTF moment with Ruby and Rails over the past couple of days. I'm working on a personal project (you'll see it soon), and I'm using Rails. It's an opportunity to learn a new language, and see if the buzz around Rails is true.
Anyways, I was having problem with a model, here's the code I had:
class Call < ActiveRecord::Base
def my_status(user)
if (a_party == user)
a_party_status
else
b_party_status
end
end
def update_my_status(user, status)
if (a_party == user)
a_party_status = status
else
b_party_status = status
end
end
end
My controller looked like this:
@call = Call.find(params[:id])
@call.update_my_status(current_user(),params[:status])
This didn't work. No matter what I did, I couldn't modify call. I put a tonne of debug lines in, and eventually got to this:
@call = Call.find(params[:id])
@call.update_my_status(@call.a_party, true);
if !@call.a_party_status
@call.a_party_status = true
if @call.a_party_status
# Hrm, first method didn't work, but the second one did?
else
# They both failed!
end
end
Amazingly, setting the value outside of the class worked, but setting it inside of the method in the class didn't. I was stumped. Google wasn't any help, it seems this is something that no one else runs into as a problem.
Finally, I found a tutorial that showed me what was going wrong, even though it didn't say it explicitly... It had the following example code:
>> player = Player.new
=> #<Player:0x28ef720 @attributes={"team_id"=>nil, "name"=>nil, "number"=>nil,
"position"=>nil},
@new_record=true>
Looking at that, it all clicked. ActiveRecord makes extensive use of the "method missing" functionality available in Ruby. Essentially, it looks at what you are trying to invoke and performs the action on the fly if Ruby can't figure it out itself. That explains what is going on with the "update_my_status" method. Since it is an assignment, Ruby will create a local variable, "method missing" isn't invoked and the class attributes are never changed! The solution? Put "self." in front of the attribute assignments:
def update_my_status(user, status)
if (a_party == user)
self.a_party_status = status
else
self.b_party_status = status
end
end
That was extremely frustrating. I need to see if there is a "warnings as errors" option. It's also an indication that I need to start working on my own stuff earlier in the evening. My brain seems to shut down after 9:30.
Oddly, it seems that I'm the only one who has this problem. After talking a friend, he said, "I always put self. in front of everything out of convention".
Nuts.
Oh, and Rails? It Rocks.