Iteration 2: Objects and Operations That Spam Requests

Learn to classify spam requests using objects and operations

Flash: communicating between actions

When we use redirect_to() to transfer control to another action, the browser generates a separate request to invoke that action. That request will be handled by our application in a fresh instance of a controller object. Instance variables that were set in the original action are not available to the code handling the redirected action. Sometimes we need to communicate between these two instances, though. We can do this using a facility called the flash.

The flash is a temporary scratchpad for values. It is organized like a hash and stored in the session data, so we can store values associated with keys and later retrieve them. It also has one special property. By default, values stored into the flash during the processing of a request will be available during the processing of the immediately following request. Once that second request has been processed, those values are removed from the flash.

Probably the most common use of the flash is to pass error and informational strings from one action to the next. The intent here is that the first action notices some condition, creates a message describing that condition, and redirects to a separate action. By storing the message in the flash, the second action is able to access the message text and use it in a view.

It is sometimes convenient to use the flash as a way of passing messages into a template in the current action. For example, our display() method might want to output a cheery banner if there isn’t another, more pressing note. It doesn’t need that message to be passed to the next action, because it’s for use in the current request only. To do this, it could use flash.now, which updates the flash but does not add to the session data.

While flash.now creates a transient flash entry, flash.keep does the opposite, making entries that are currently in the flash stick around for another request cycle. If we pass no parameters to flash.keep, then all the flash contents are preserved.

Flashes can store more than just text messages—we can use them to pass all kinds of information between actions. Obviously, for longer-term information we’d want to use the session in conjunction with our database, to store the data, but the flash is great if we want to pass parameters from one request to the next.

Since the flash data is stored in the session, all the usual rules apply. In particular, every object must be serializable. We strongly recommend passing only basic objects like strings or hashes in the flash.

Callbacks

Callbacks enable us to write code in our controllers that wrap the processing performed by actions—we can write a chunk of code once and have it be called before or after any number of actions in our controller (or our controller’s subclasses). This turns out to be a powerful facility. Using callbacks, we can implement authentication schemes, logging, response compression, and even response customization.

Rails support three types of callbacks: before, after, and around. Such call-backs are called just prior to and/or just after the execution of actions. Depending on how we define them, they either run as methods inside the controller or are passed the controller object when they are run. Either way, they get access to details of the request and response objects, along with the other controller attributes.

Before and after callbacks

As their names suggest, before and after callbacks are invoked before or after an action. Rails maintain two chains of callbacks for each controller. When a controller is about to run an action, it executes all the callbacks on the before chain. It executes the action before running the callbacks on the after chain.

Callbacks can be passive, monitoring activity performed by a controller. They can also take a more active part in request handling. If a before-action callback returns false, then processing of the callback chain terminates, and the action is not run. A callback may also render output or redirect requests, in which case the original action never gets invoked.

We saw an example of callbacks in use for authorization in the administration part of our store example in Limiting Access. We defined an authorization method that redirected to a login screen if the current session didn’t have a logged-in user. We then made this method a before action callback for all the actions in the administration controller.

Callback declarations also accept blocks and the names of classes. If a block is specified, it will be called with the current controller as a parameter. If a class is given, its filter() class method will be called with the controller as a parameter.

By default, callbacks apply to all actions in a controller (and any subclasses of that controller). We can modify this with the :only option, which takes one or more actions on which the callback is invoked, and the :except option, which lists actions to be excluded from callback.

The before_action and after_action declarations append to the controller’s chain of callbacks. Use the variants prepend_before_action() and prepend_after_action() to put callbacks at the front of the chain.

After callbacks can be used to modify the outbound response, changing the headers and content if required. Some applications use this technique to perform global replacements in the content generated by the controller’s templates. For example, they can substite customer’s name for the string <customer/> in the response body). Another use might be compressing the response if the user’s browser supports it.

Around callbacks wrap the execution of actions. We can write an around callback in two different styles. In the first, the callback is a single chunk of code. That code is called before the action is executed. If the callback code invokes yield, the action is executed. When the action completes, the callback code continues executing.

Thus, the code before the yield is like a before-action callback, and the code after is the after-action callback. If the callback code never invokes yield, the action is not run. This is the same as a before-action callback return false.

The benefit of around callbacks is that they can retain context across the invocation of the action.

As well as passing around_action the name of a method, we can pass it a block or a filter class.

If we use a block as a callback, it will be passed the controller object and a proxy for the action. Use call() on this second parameter to invoke the original action.

A second form allows us to pass an object as a callback. This object should implement a method called filter(). This method will be passed to the controller object. It yields to invoke the action.

Like before and after callbacks, around callbacks take :only and :except parameters.

Around callbacks are (by default) added to the callback chain differently. The first around action callback added is executed first. All subsequent around callbacks will be nested within the existing around callbacks.

Callback inheritance

If we subclass a controller containing callbacks, the callbacks will be run on the child objects as well as in the parent. However, callbacks defined in the children will not run in the parent.

If we don’t want a particular callback to run in a child controller, we can override the default processing with the skip_before_action and skip_after_action declarations. These accept the :only and :except parameters.

We can use skip_action to skip any action callback (before, after, and around). However, it works only for callbacks that were specified as the name of a method.

We made use of skip_before_action in Limiting Access.

Get hands-on with 1200+ tech skills courses.