Rails has this thing called the “flash”. It’s like a special subset of the session hash. It’s a key/value store with an enforced short lifespan. Stuff you put in the hash lasts for exactly one render or redirect, and then goes away. It’s handy for stuff like notifications. You can set it in the controller:
# ... flash[:notice] = "Your order has been submitted." # ...
…and then you can reference it in a view:
<%= flash[:notice] %>
You can then be confident that after the view is shown, the message will go away and not be seen again.
Recently on the Ruby Rogues we talked to Michel Martens about keeping libraries small and simple. It was a good show and he had a lot of good things to say. You should give it a listen.
One of the assertions he made on the show is that the Rails flash is an example of pointless bloat. It adds hundreds of lines of code, but you can accomplish the exact same thing with just some basic Ruby knowledge.
That is, in the controller, you could just use the regular session store:
# ... session[:notice] = "Your order has been submitted." # ...
And then in the view, you could use Hash#delete() (or the equivalent on whatever Hash-like object Rails uses for the session) to render and delete the entry all at once.
<%= session.delete(:notice) %>
It’s as simple as that to render the flash feature completely superfluous.
…or is it?
Imagine we move the rendering code above into a view partial named notice . And then we use it in a view to render notices both at the top and the bottom of the page.
<%= render "notice" %> <!-- ...lots of page content goes here... --> <%= render "notice" %>
There’s now a bug here. And we might not even notice it for a while. But sooner or later someone is going to realize that notices are only displaying at the top of the page, and not the bottom.
Bugs like these can be tricky to diagnose, if we don’t have recent familiarity with the code. That’s because we tend to think of views as being referentially transparent, like a function: you pass data in, and you get HTML out. The same HTML for the same data. We don’t expect a view to modify the data going in.
This isn’t the only potential gotcha, either. Rails carefully manages the lifetime of flash messages, expiring them whether they are used or not. But this flash-free technique means that so long as the view that uses it isn’t rendered (for whatever reason), the notice may linger on. Until finally it is rendered, in a context where it no longer applies.
I can definitely think of scenarios in which this sequence of events might occur.
So using this approach, we can easily end up with a site which sometimes fails to render notices, and sometimes renders notices belatedly. This sort of thing tends to be perceived as “flaky” and low-quality by users.
Don’t get me wrong: I love small, sharp tools. I almost always choose Sinatra over Rails for my own projects.
But this is something that has happened to me over and over again in my programming career: I’ll look at a “fat” tool, and think “this is stupid. It’s obvious we don’t need all that code. I can just use the language better and avoid all that waste.”
So I’ll plow ahead, and then run into an edge case that I hadn’t thought of. And then another, and then another. And before I know it, I’ve recreated the original “fat” code, only badly.
The point here isn’t that Michel is wrong. If we are careful, we can get along just fine without the flash. And for many applications, we may never need it.
But Rails isn’t wrong either. The flash has a non-triviai implementation because it handles some non-obvious cases in a way that ensures the programmer never has to worry about them.
More often than not, code exists for a reason. This is especially true of code in libraries and frameworks that receive a lot of code review and discussion, as is the case for Rails. We can choose to make use of that code, or we can choose to go with smaller, sharper tools. But whichever way we go, it usually pays to take a little time and understand why the “fat” code exists.