Wednesday, January 6, 2016

Rendering and the DRY principle



Imagine you want to render this piece of code as a partial view

<ul class="nav navbar-nav navbar-right">
  <li>
    <a href="/user/dashboard">Dashboard</a>
  </li>
  <li>
    <a href="/user/profile">Profile</a>
  </li>
  <li>
    <a href="/user/courses">Courses</a>
  </li>
  <li>
    <a href="/user/schedules">Schedules</a>
  </li>
  <li>
    <a href="/user/invoices">Invoices</a>
  </li>
  <li>
    <a href="/user/tickets">Tickets</a>
  </li>
</ul>

Then you put it in app/views/users/_menu.html.erb and inside app/views/users/view_one.html.erb you put something like

<%= render 'menu' %>

This works perfectly well, of course, and the menu will show up exactly where you want it to. 

But now, imagine another situation. You want to show this very same menu in a view of another controller, let us say app/views/customers/view_two.html.erb.

In Rails, partials are relative to the path where the controller has its views. Convention, not configuration, remember?

So, if you simply put 

<%= render 'menu' %> 

somewhere inside app/views/customers/view_two.html.erb, you'll get an error. The partial _menu won't be found in the scope of the views of Customers controller.

One may, of course, just issue the command

$ cp app/views/users/_menu.html.erb app/views/customers

and then the error will be gone. But then, what about the DRY principle?

Don't Repeat Yourself (DRY) is one of the principles guiding Ruby on Rails and violating it is not a good idea, for many reasons. Code repetition makes systems hard to mantain, among other things. You may read a bit more about this in 3 Key Software Principles You Must Understand.

The situation described is a problem, of course, but I have good news! You don't need to violate the DRY principle.


All you have to do is change a little bit your app/views/customers/view_two.html.erb file. Instead of 

<%= render 'menu' %>

you just need to write

<%= render 'users/menu' %>

and Rails rendering engine will look for the partial inside the correct directory!

But be careful with one thing.

When one sees such an easy way to deal with this problem, a malicious idea comes do mind: putting things to be repeated here and there inside a special directory like app/views/commons or something like that.

This is not a good idea. If you do so, there is a great risk of ending up with lots of loose code, not belonging to any of the entities of your system.