Thursday, March 29, 2007

A default behavior for failure or nil

I read "The Rails Way", mainly because Jamis Buck writes there. Three days ago, they had actually posted one thing that I had address just a few days prior--that is, how to prevent users from looking at other users' data.

I have to admit, I was rather tickled by my solution. It's nice when you figure things out. But alas, after reading Koz's post on association proxies, I have to admit, I like his solution better. At least I made it to anti-pattern #3.

So what did he do? He simply used the find in the association, and let things throw an exception otherwise.
def show
@todo_list = current_user.todo_lists.find(params[:id])
rescue ActiveRecord::RecordNotFound => e
flash[:warning] = "Stop playing around with your urls"
redirect_to '/'
end
It also takes care of the case where you have say, many items that belong to a todo_list. You can load it by using :condition in the association find. The only reason I can think of not to use it is if it happens to be a slow solution. But no use optimizing if you have no measurements and hard numbers.

Koz's solution let the default behavior of the method take care of things. This is a way of thinking that I need to start using more of.

I was use to nil being a failure state, something that you checked, and if it happened, everything's gotta stop--like the examples I often saw in C:
status = CallSomeMethod(with, some, parameters);
if status == NULL {
return ERROR_CODE
// or throw some exception here if you're using C++ or Java
}

Often times, these things got cumbersome, because NULL (or even an empty array or hash) was not considered to be a valid input for many functions, and would stop computation by returning error codes and throwing exceptions in C++.

One of the things that I found nice about the code from Ruby gurus was that it was conventional to do (and subsequently, I saw in Perl):
setting = params[:setting] || "default"
Instead of:
setting = params[:setting].nil? ? "default" : params[:setting]
Or
setting ||= "default"
Instead of:
setting = setting.nil? ? "default" : setting

It was because the operator || took nil by default, and had an appropriate behavior for dealing with nil, and that has made all the difference in being able to chain functions together. In addition, having default failure behaviors require you not have to write error checking code all over the place either.

I'm not sure what this is called (if anyone knows, enlighten me), but a completeness and liberal in what you accept for your input and strict in what you output also applies here to methods and functions. I think it makes code more readable, because it is not peppered with common sense error checking code.

No comments:

Post a Comment