The Accelerate HR Blog
Help! My Booleans Are Not Nil ! (Mon Nov 26 2007)
I come from a land where boolean is either true or false.
And in Ruby on Rails it's been hard to come to terms with the fact that boolean can be true or false OR nil. Hard, but extremely useful !
I was setting up the basic rules and parameters for a payroll. To get started, the user needs to answer a series of questions. Like 'Does personal taxation apply in this location?' (Yes, there are still countries where there's no tax! ) 'Does national insurance apply?' 'Do some employees earn a gratuity at the end of service?' 'Do you have a staff savings or pension scheme?' Their answers are going to be used to make the payroll set-up a breeze.
What I could have done is set a default on each of the boolean fields to true or false. But that's not what I wanted. I need to see evidence that users have actually considered and answered each of the questions; it's important that they should get this right before they move on. So the default is nil (or NULL if you want to MySql it). And then, in the model, I set up a method to find out whether all the questions have been answered before allowing them to move on.
It all seemed easy enough at first. I created a model payrollparameter and migrated in fields like this:
t.column :taxation, :boolean
t.column :insurance, :boolean
t.column :gratuity, :boolean
......
..............
And then I needed to set up the method in the location model. Something like this, I thought: -
if @p.taxation.nil? and @p.insurance.nil? and @p.gratuity.nil?
And finally in the payrollparameter controller:
if @payrollparameter.update_attributes(params[ :payrollparameter])
redirect_to :action => 'index'
redirect_to :controller => ..................
Nice idea ... but it doesn't work. It's fine while you haven't answered all the questions. Click on 'Save changes' and the update action correctly refreshes the index page with the warning that you haven't answered all the questions.
But it all breaks down when you've answered all the questions, when everything is either true or false. Then there's a nasty error message telling you that Ruby's not coping with the nil values on the boolean fields.
So what to do? Well, I could have decided not to use a boolean, I guess. Set the fields to an integer, with 0 as unanswered, 1 as false and 2 as true. But somehow that just didn't feel right.
So instead I changed the method name to has_payroll_parameters? The question-mark shows Ruby that the method is expected to return either true or false, so we can get rid of the if ... return false ... else ... return true.
Next, to test whether or not the fields have a value, it's sufficient to say:
So the revised method in the model is:
@p.taxation and @p.insurance and @p.gratuity
Better. Shorter. Neater.
So on to the controller. And here I had a little trouble working things out at first. In the 3rd line of the update action, I tried:
Still not right. When I answered all the questions, the controller was still sending me back to the index page with the error message.
And this is where the ever-useful console came to the rescue. Firing up ruby script/console, I set the location:
>> @location = Location.find_by_id(3)
Then with all the questions answered:
>> @p = @location.has_payroll_parameters?
-> false
So then I tried again, but this time I restored some of the question-fields to their original NULL settings. The console returned a different result:
>> @p = @location.has_payroll_parameters?
-> nil
Aha. So the first time, when I'd answered all the questions, some with true and some with false as the response, the method returned a false because not all the statements were true. The second time it returned a nil because not all the questions had been answered. has_payroll_parameters? doesn't have a value yet.
And that immediately gave me what I needed and I was able to correct the controller:-
if @payrollparameter.update_attributes(params[ :payrollparameter])
if @pp.nil?
redirect_to :action => 'index'
redirect_to :controller => ..................
It's not rocket science, but it helped me. And hopefully if you're just starting out with Ruby and Rails, it'll help you. Want more posts like this with details of how I'm trying to deal with the problems I meet? Then just let me know. And if you've got a better way - then I'm all ears... well, eyes really.