I keep a note pad with features I want to add, things I need to fix and other reminders to myself whenever I'm coding an application. I've got a few methods defined in my models that will calculate totals. Needless to say, these methods aren't going to do anything but complain if the data fields they use to calculate a total either don't exist or aren't integers. What to do?
Validation to the rescue! But I had a problem. A minor problem. The models I speak of all belong to a main Character model. Take my Initiative model for example. It belongs_to Character, and Character has_one Initiative. I have a few fields in Initiative: :dex, :misc, and :speed. These refer to a Character's Dexterity modifier, any miscellaneous modifiers and how many feet that Character can move in a round. So I have this method defined in Initiative.rb:
def total
dex + misc
end
This way, I can simply call Initiative.total in a view and Rails will calculate the total for me. Cool, right? And this is where validation comes in handy. However, here's the issue I had. My validation worked, but it was showing an error BEFORE any data had been entered. Definitely better than not working, or allowing a user to enter a B instead of an 8, but I'd rather not show an error message if no error has been made. So what was the solution?
First, the validation I wrote, in my Initiative model.
validates_presence_of :dex, :misc, :speed
validates_numericality_of :dex, :misc, :speed
This all looks good. I suspected the error was in the way my new method was written in Intiatives controller, but I couldn't figure it out, so I asked for help on StackOverflow. User
m_x was kind enough to point me in
the right direction.
def new
@character = Character.find(params[:character_id])
@initiative = @character.create_initiative(params[:initiative])
end
This bit of code was the culprit. Easy enough to fix, though.
def new
@character = Character.find(params[:character_id])
@initiative = @character.build_initiative(params[:initiative])
end
It turns out the reason my validation was being called before a user had entered any data is because the create method creates a new object and attempts to save it. In this case, it wasn't saving because it failed the validation, because nothing had been entered yet. On the other hand, the build method creates a new object, but does NOT save it. Easy enough to remember! As an added bonus, this gives me some ideas on how to tackle another problem I've been thinking about.
This mistake feels like something I should have known before hand, considering how much time I've spent reading through the Rails API, but I have a hard time holding on to information until it becomes practical. But now that I've used this, and cemented exactly what the difference is between build_model and create_model, I won't make the same mistake again.
One less thing, right?