All Case Studies Design Development Interviews Our Way Project Management

How To Use Decent Exposure Gem In Your App

In Rails, the default way of passing objects into views is to set instance variables in controllers. This is often confusing and breaks encapsulation (instance variables are meant to be private for the object). In addition, many such variables are set in the common ways, increasing boilerplate code (e.g. find record by id, find collection scoped to 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, like 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 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 existing or instantiate new models. This is how a controller using decent_exposure looks like:

[code]

decent_exposure took care of:

  • Instantiating new post object in new and edit 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 views

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

 

Step 3 - converting existing controllers

How to refactor 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 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, then you’re probably using 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 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 private method, keeping the header of the controller slim, like this:

[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 not used in view, only in controller, extract private method instead:

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

     

  • Controller with multiple responsibilities
    Many view exposures in 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 are a great pattern for managing presentational code of your models. For tutorial on implementing decorator classes, see draper gem.

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

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

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

 


More resources

Follow Netguru
Join our Newsletter

READ ALSO FROM
Read also
Need a successful project?
Estimate project or contact us