Two screencasts, two ways to eradicate Ruby nil values

 values are the worst. They crop up where you didn't expect them. Their presence forces you to litter your code with conditionals: checking, checking, and checking again if a variable contains  .

Today I'm making available a classic screencast from the RubyTapas archives. In it you'll see a way to remove nils while dramatically simplifying some typical web application logic, using the Special Case Pattern.

But! There's more. Today by special permission I'm also bringing you a video from the Upcase Weekly Iteration archives! In this bonus video, you'll watch Ben Orenstein and Joe Ferris apply a variant of the Special Case pattern, the Null Object pattern, in order to clean up some Rails 4 code.



So that's the Special Case pattern.

Sometimes, you have a special case where the “special” behavior you need is defined as: “always do nothing”. There's a name for this “special case of Special Case”: the Null Object Pattern.

Now here's Ben Orenstein and Joe Ferris of Upcase, with a demonstration of how to apply the Null Object pattern to a Rails project:



Did you enjoy this pairing of short & sweet RubyTapas with longer, more Rails-centric Upcase content?

If so, I have some very exciting news for you:

This week only, you can get an entire year of RubyTapas AND Upcase for a fantastic price. Go here to find out more.

There are also some nifty bonuses to be had, as well as special deals for teams.

I've never done a bundle like this before (and I don't know if/when I'll do it again). The deal expires February 6, 2017. If you want to supercharge your Ruby, Rails, object design, refactoring, and testing skills this year… go check it out!

Episode Script

For those who prefer reading to watching. This is for the RubyTapas episode only, not the Upcase video.

Here's a typical helper method from a web application. It looks to find a current user by checking in a session object. If it can find one it returns it; otherwise it implicitly returns nil.

Let's look at some places where this method is used.

Here's some code to greet the user when they first arrive at the site. It checks to see if there is a current user. If so it uses their name; otherwise it uses the name “guest”.

Here's a case where we render either a “log in” or “log out” button depending on whether there is a user.

Here's some code that optionally renders an admin panel. Before it can check to see if the user is an admin, it first has to check that there is a user at all.

In some cases we might want to show different results depending on what a user is allowed to see. Once again, we switch on the presence of a user object.

So far we've just been querying the user object. Here's some code that updates an attribute on the user. But first it has to check that the user is non-nil.

Here's a snippet of code that adds a product to the user's shopping cart. If they are logged in, it should go into their persistent user cart. Otherwise it should go into a special session-based cart.

All of the code we've been looking at has a common characteristic: it's uncertain about whether there will be a user object available. As a result, it keeps checking over and over again.

Let's see if we can get rid of this uncertainty. Instead of representing an anonymous session as a nil value, let's write a class to represent that case. We'll call it GuestUser.

We rewrite the #current_user method to return an instance of this class when there is no :user_id recorded.

We add a #name attribute to GuestUser.

This nicely simplifies the greeting code.

In the case where we render either “Log in” or “Log out” buttons, we can't get rid of the conditional completely. But what we can do is add an #authenticated? predicate method to both User and GuestUser.

By using this predicate method in the code for rendering the button, we end up with code that states its intent a little better.

Next let's take a look at the case where we check if the user has admin privileges. We add an implementation of #has_role? to GuestUser. Since an anonymous user has no special privileges, we make it return false for any role given.

This simplifies the role-checking code. No more check to see if the current user exists.

Now what about the code for showing different listings to different people? We implement a #visible_listings method on GuestUser which simply returns the publicly-visible result set.

Then we can reduce the listings code to a one-liner.

We also implement attribute setter methods as no-ops.

This enables us to eliminate another conditional.

In order to implement a shopping cart for users who haven't yet logged in, we make the GuestUser‘s cart attribute return an instance of the SessionCart type that we talked about earlier.

Now the code for adding an item to the cart also becomes a one-liner.

What we've done here is identify a special case—the case where there is no logged-in user. And then we represented that special case as an object in its own right. As a result, we were able to simplify quite a bit of our code. And it's not just simpler—it reads better too!

There's a name for this, and unsurprisingly it is the “Special Case” pattern. It's one application of a more broad observation: anytime a program keeps switching on the same condition over and over again, that's a good indication that there's a new kind of object waiting and wanting to be discovered.

That's it for today. Happy hacking!