Feb 122017
 

My colleagueReid Cooper and I discovered a nice little trick of controller concerns, something we sometimes call “behaviors” in our app (typically implemented as modules). We found a trick from this link that lets us mix in behavior into both a controller and view helper, but first a brief introduction to controller concerns.

Concerns were born in Rails 4 as a nod to the limitations, vis-a-vi the domain model, of a a “strict” interpretation MVC as implemented by Rails. Around 2012 or 2013, most experience Rails developers would explain that the MVC structure created by default doesn’t necessarily dictate a strict MVC paradigm. Thanks in part to DCI architecture -- which complements but does not replace MVC -- a more modern understanding of larger apps includes a domain layer, i.e., where you put the business domain that is not in the traditional Rails models.

There are various options, and in a small nod to the problem the Rails core team added a blank empty folder to default Rails installs. You might notice this folder at app/controllers/concerns. What, the Rails newbie says, am I to do with a blank empty folder?

Good question. You would do well do study the excellent work of Sandi Metz and James Coplien, who cover domain abstraction (and a specific pattern the latter calls “DCI,” or domain-context interaction) in two excellent books (POODR and Lean Archtecture, respectively). The scope of these is well beyond this blog post, but since they are such my heroes I want to take an opportunity to plug these excellent books.

Reid and I wanted a behavior, a-la “concern”, that we could mix into a controller to inherit instance methods for the controller. We also wanted a view helper automagically mixed into our views for the view to access while it is rendering. To the rescue: the obscure included hook that gets called after modules are included into controllers, where you can re-access the controller itself and add both helper and actions (formerly known as filters.)

app/controllers/abc_controller.rb

class AbcController < ApplicationController
 include FancyConcern
 
 def index

 end 
end

and here’s the magic, in app/controllers/concern/fancy_concern.rb

module FancyConcern
 def self.included(base)
  # http://www.railstips.org/blog/archives/2009/05/15/include-vs-extend-in-ruby/
  base.helper FancyConcernViewHelper
  base.before_action :set_my_instance_variable
 end
 
 def set_my_instance_variable
  @my_instance_variable = “_instance variable value_”
 end 
end

app/views/abc/index.html

Hello world!

<fieldset>
<legend>An instance variable set in a before_filter</legend>
<%= @my_instance_variable %>
</fieldset>

<fieldset>
<legend>A call to the view</legend>
<%= my_view_helper_method %>
</fieldset>

Check out the full test app.

I don’t have a demo up & running, but it works (I took a screenshot below). If you want you can pull it locally and run it yourself to see.

 Posted by at 11:14 am  Tagged with:

 Leave a Reply