How to Use Decent Exposure Gem in Your App

Photo of Tomek Pewiński

Tomek Pewiński

Jul 18, 2016 • 6 min read

In Rails, the default way of passing objects into views is to set instance variables in the controllers. This is often confusing and breaks encapsulation (instance variables are meant to be private for the object). In addition, a number of variables are set in the common ways, increasing the boilerplate code (e.g. find a record by its id, find a collection scoped to the current user).

Decent_exposure is a small utility gem to improve exposing objects in controllers to views.

Pros

  • Removes the need for instance variables in controllers, improving encapsulation.

  • Makes the variables used in views clearly visible at the top of the controller.

  • Compatible with most common use-cases, e.g. strong_parameters, decorators.

  • Exposes code smells, e.g. controllers with multiple responsibilities (pun not intended).

Cons

  • Lazy-loading of exposed variables may cause reasoning about complex controller flow more difficult (e.g. when exactly is the model fetched from the database?)

Step 1 - Installation

Installing is as simple as adding gem ‘decent_exposure’ to the Gemfile and running bundle install.

Step 2 - Adding a New Controller

In its basic form, decent_exposure provides an expose macro to be used in rails controllers. Based on the name and request params, it will automatically fetch the existing or instantiate the new models. This is what a controller using decent_exposure looks like:

[code]

decent_exposure took care of:

  • instantiating new post object in the new and the edited actions (they are not needed to be defined explicitly anymore);

  • assigning attributes from params in create and update actions (we just have to call save);

  • finding correct post by id (Post.find(params[:id])) for show action and listing posts for index action (again, no need to define these actions explicitly);

  • adding “post” and “posts” methods available both in this controller and the views.

The variables defined by expose are memorised - meaning you can reference ‘post’ variable many times but it will be assigned only once.

Step 3 - Converting Existing Controllers

How to refactor the existing controller with decent_exposure:

  1. Make sure there are tests in place to cover the controller’s functionality.

  2. Add expose macros at the top of the controller definition.

  3. Remove before_actions setting instance variables.

  4. Replace all references to instance variables by methods (without @) in controller and views.

  5. Modify controller tests using assigns:
    assigns(:post) → controller.post

Step 4 - Advanced Features

To scope finders to particular context, e.g. get current_user.posts.find() rather than Post.find(), use ancestor option:

 expose(:post, ancestor: :current_user)

To expose objects with nonstandard fetching methods or scoped collections, use the block passed to expose:

expose(:post) { Post.find_by_slug(params[:id]) }
expose(:posts) { Post.recent }

To expose paginated collections, use block with a default argument:

expose(:posts) { |default| default.page(params[:page]).per(params[:per]) }

Tips

Tip 1: Usage with Strong_parameters

If you’re on Rails 4, you probably use strong_parameters. It was added as a default way to prevent accidentally updating sensitive model attributes (i.e. admin field). It requires all the params to be whitelisted - read more about it here.

Decent exposure has built-in support for passing strong parameters: see an example in the documentation.

Tip 2: Long Code in Expose Block

Sometimes expose block contains a lot of code, like in this example:

[code]

In this situation, it is nice to extract the code to a private method, keeping the header of the controller slim:

[code]

Tip 3: Dealing with Overexposure

The ideal number of exposures is 2 per controller (one for singular resource, e.g. post, one for plural collection, e.g. posts). If the top of the controller class is crowded with expose statements, two code smells might occur:

  • Exposing variables not used in view.
    When the variable is used not in view but in controller, you should extract the private method instead:

    [code]
    This way, the details regarding controller implementation won't leak on the outside, and it is clearer which variables are used by views.

  • Controller with multiple responsibilities
    Numerous view exposures in a single controller is often an indication that the controller in question is too big or handles too many actions. Look for the opportunities to extract parts of functionality to separate controllers.

Tip 4: Usage with Decorators

Decorators comprise a great pattern for managing the presentational code of your models. For the tutorial on implementing decorator classes, see draper gem.

You can utilise expose with block feature to decorate the object:

expose(:post) { |default| PostDecorator.new(default) }

Or instead, you can take a look at decent_decoration gem which streamlines the process.


More Resources:

Photo of Tomek Pewiński

More posts by this author

Tomek Pewiński

Tomek is one hell of a web developer and an open-source contributor. In between devouring popular...
How to build products fast?  We've just answered the question in our Digital Acceleration Editorial  Sign up to get access

We're Netguru!

At Netguru we specialize in designing, building, shipping and scaling beautiful, usable products with blazing-fast efficiency
Let's talk business!

Trusted by:

  • Vector-5
  • Babbel logo
  • Merc logo
  • Ikea logo
  • Volkswagen logo
  • UBS_Home