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.)
and here’s the magic, in app/controllers/concern/fancy_concern.rb
@my_instance_variable = “_instance variable value_”
<legend>An instance variable set in a before_filter</legend>
<%= @my_instance_variable %>
<legend>A call to the view</legend>
<%= my_view_helper_method %>
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.