When I started using Rails years ago I found helpers extremely cool. I could call a method in a view and it would help me by doing something. The method was simply there, no need to worry about its source and how to access it, just call it.

I got older, wiser, and more opinionated. I still like the concept of helpers – of methods. However, the way helpers are implemented in Rails sucks. Also, having object-disoriented functions in your view brings us back to the years where OOP still had to be invented.

In this post I’d like to discuss why I dislike Rails helpers and how to get out of that misery.

What’s a helper?

In Rails, a helper is a function.

<h2>
  Hello, <%= capitalize @user.name %>
</h2>

Here, the #capitalize method helps me capitalizing the username, which is freaking awesome. As this is pretty simple behaviour, let’s call helpers like this utility methods. They modify the input parameter, compute something or escape strings. Pretty straight-forward.

As a second example, I’d like to show a more complex helper.

<div id="sidebar">
  <%= render_news_for @user %>
</div>

This helper will iterate through news items for a particular user and render markup, maybe using several partials. Since it actively renders templates, let’s call this a view component.

Why are helpers shit?

At first sight, using helpers rocks. Capitalizing a string works like a charm – I simply call a function and it happens.

However, looking at the first helper I can identify several drawbacks.

module StringHelper
  def capitalize(string)
    string.capitalize
  end
  • Helpers in Rails are modules, which do not allow inheritance. If I’d need a foreign method I’d have to include another module into the helper module. Not a big deal.
  • Using the #capitalize methods happens without a receiver. The method is globally available in the view since Rails somehow mixes the helper into the view. So, what happens if I have two #capitalize methods in two different helpers mixed in the same view? I don’t have a clue. Do you?

Before getting to solutions, let me discuss another issue with helpers: Another real problem is the implementation in Rails – how these functions are made available to the view.

Helpers in Rails

Again, I’m not talking about how #form_for or #url_for are written internally, I’m talking about how these methods get into the view.

In Rails 3, all helpers are mixed into the view automatically, you still can insert additional modules using the controller’s helper facilities.

class HomeController < ApplicationController
  helper StringHelper

It’s not that the implementation as-it is bad code or something, it is the idea of magically mixing methods into the view instance to make them globally available. This adds complexity to the Rails core, namely around 280 LOCs. Just to mix some methods into the view.

Helpers are shit.

I desperately tried to demonstrate the major disadvantages of helpers in Rails. To summarize.

  1. I like utility methods. There is nothing wrong with having those little “helpers” in your view. What I don’t like is that they are called without an obvious receiver – they look and feel like functions. This is wrong.
  2. The way Rails mixes helpers into the view is error-prone and sucks. Following a slightly different approach there’s no need for all that complexity.
  3. Complex helpers suck. I do believe in view components and the need for those but they shouldn’t be rendering helper methods.

Moaning is fine, but let’s see how things could be changed.

Solution 1: Push Utility Methods into Decorators.

Luckily, a bunch of people feel uncomfy about the current helper architecture. My friend Steve Klabnik wrote a nice article about Jeff Casimir’s draper gem which introduces the Decorator pattern into Rails’ view layer.

Basically, the draper gem wraps existing model instances and provides utility (“helper”) methods on the decorated instance. Here’s an example.

class ArticleDecorator < ApplicationDecorator
  decorates :article
 
  def published_at
    model.published_at.strftime("%A, %B %e")
  end

Now that we defined the Decorator we can use it to wrap the actual model.

@article = ArticleDecorator.decorate(Article.find(1))

The wrapped model can then be used in the view.

<li>
  <%= @article.published_at %>
</li>

The interesting point is that we call the utility helper on the wrapped model which clearly states a receiver. No need for a homeless, global helper function. This way, we can have cleanly separated, domain-focused helpers for models. Decorators also allow inheritance and all other OOP features, since they are just objects.

Decorators are a solid technique when it comes to – well – decorating models. What can we do if there’s no matching model, for instance, when we need to call #url_for?

Solution 2: Use the Controller Instance as View Context

To learn more about that we should peek at the rendering cycle in Rails. What happens when a controller renders a template?

  1. An ActionView instance is created (this will be the “context”).
  2. The controller manages a magical module that contains all helper methods. This module is now mixed into the ActionView instance to make helpers available. I already discussed the need for hundreds of lines of code in order to achieve this “knowledge transfer” from the controller to the view.
  3. Next, instance variables from the controller are copied to the view instance as well.

These are 3 completely useless steps. Completely. Every template engine, whether it be Rails’ internal or tilt requires a so called view context whenever a template is rendered. Both instance variables and methods (that is, helper calls) used within the template are looked up on this view context instance.

Now, there is absolutely no reason for having a separate ActionView instance as view context! We can simply use the controller instance as context object and everything would work. No need to copy over variables, no need to transfer “helpers” to the view instance.

“Helpers” would be modules mixed into the controller – and that’s it.

class HomeController < ApplicationController
  include UrlMethods
 
  def show
    @link = link_to(home_url)

Notice how we can use the mixed-in “helper” methods in the controller instance – we simply included them.

<a href="<%= home_path %>">

The cool thing is we can also use the utility methods in the view which will be invoked on the controller instance, again. No magic copying, just modules.

The Cells project currently is experimenting with this approach and things work out fine. Will blog.

I can hear people now moan about too many mixed-in methods in their ActionController – and they are right! Again, this is due to Rails’ monolithic view/controller design. If one single controller is responsible for rendering an entire web page, then this controller has a lot of responsibilities – too many. That’s why we should use Cells to split up the view into components, which is discussed next.

Solution 3: Use View Components instead of Complex Helpers.

Helpers that compute data and render partials are scary. Often, there is too much concerns in the little helper.

def render_news_for(user)
  items = user.find_news
  render "shared/news", :items => items
end

Let’s assume the _news partial should be reusable throughout your application, needs some special helper function #sanitize and does caching.

<% cache do
  <%- for item in items %>
    <%= sanitize item.text %>
  <% end %>
<% end %>

Several problems here.

  • Every controller has to take care of requiring the special SanitizerHelper for the partial.
  • Caching happens by using helpers, again, which is no good .

Moving the partial and its behaviour into a cell would cleanly separate concerns. The cell could be used as view context and thus provide utility methods itself.

class NewsCell < Cell::Base
  cache :show
 
  def show(items)
    @items = items
    render
  end
 
  def sanitize(string)
    # ...
  end

This creates a reusable view component with a defined scope. Intentionally, I keep the cells discussion briefly as this would break the mold.

Combining Decorators and Cells

Using draper’s decorators within cells is what I figure a fantastic option. Where the decorator cleanly wraps the model object and provides utility methods for tweaking model data the cell separates the concern into a reusable view component, provides a limited scope and generic helper methods (like #url_for), and even caching!

I really don’t care whether draper, cells, or whatever replaces helpers – all I want is less magic code, more object-orientation and rock-solid software. This was a long post – gimme some feedback in the comments section or tweet me

Tags: , , ,

35 Responses to “Rails Misapprehensions: Helpers are shit.”


  1. Daniel

    I find the decorator pattern useful for more than this, but the draper gem seems overkill. Plain Ruby is just as simple and even less magic!

    class Article
      def self.find(*args)
        Article.new
      end
     
      def published_at
        Time.now
      end
    end
     
     
    module ArticleDecorator 
      def self.extended(mod)
        mod.class.send(:alias_method, :old_published_at, :published_at)
      end
     
      def published_at
        old_published_at.strftime("%A, %B %e")
      end
    end
     
    @article = Article.find(1).extend ArticleDecorator
    p @article.published_at
  2. You’re touching some important problems here.

    Draper and the idea of decorators is interesting, indeed. It’s better than the traditional Rails way. Still, it concerns me that we’re creating a new object here (a decorator) where in my understanding we still have the same base object – an article in this case.

    Why don’t we use the idea of “extending the object runtime” here?

    @article = blog.articles.find(1).extend(ArticleView)

    where ArticleView is a role (in the DCI sense) implemented as a module:

    module ArticleView
      def published_at
        published_at.strftime("%A, %B %e")
      end 
    end

    In short – let’s not create new objects, but use the existing ones and use roles. It means that in this request the article knows how to represent itself.

    Similarly, with the #url_for method. It becomes just article.url (injected by ArticleView as well).


  3. Justin Ko

    Andrzej Krzywda – I think inheritance is the better option here. And what’s wrong with creating more objects?

  4. Good insight, Nick. The Rails way doesn’t scale with regards to code maintainance as the application gets bigger.

    Chances of conflicts as the codebase evolves increases, specially for helpers. That’s why I always avoided them.

    Nowadays, with lots of logic moving to the client-side, this is less of a problem with views being delegated to tools like Handlebars, Knockout, Batman, etc.

    That’s probably the reason why no one seems to be really concerned about this issue.

    By the way, using frameworks like Cell or Apotomo is the way to go for separating concerns the right way, in my opinion. Keep up with the good work!

  5. Justin: I try to reflect the user mental model in the object design. If you ask the user what is the thing that he’s reading now – he’s going to say “I’m reading an article”. Not an article_decorator.

    As for inheritance, it is a possible way of implementing it but it just doesn’t sound well to me.

    Rodrigo is right here – we move most of the logic to the client-side, especially the view stuff. I think that’s the right direction – with client-side we can do a real MVC with better UI modularization than with Rails.


  6. Mark S

    News.for_user(@user)

    Nice and easy, obvious where the method exists and easily testable.

    If you end up needing logic in a view directly or via a helper, maybe have two versions of the view and render the appropriate one.

  7. I recently wanted to use helpers in a way that would allow helpers defined later overwrite methods defined by helpers included earlier (that’s an expected behavior I believe). It was total pain in the ass for two reasons:
    a) rails by default loads all helpers for every controller
    b) the helper which is named after controller is included into view context firstly (not lastly) and trying to add it later has no effect because it is already included.

    It took me a while to figure out a nasty workaround: https://gist.github.com/1234553 . So not only helpers are way behind the rest of rails code and totally not OO, they are also very hard to customize (meaning “to use in a non-standard way”). The whole point of Rails 3 was to make Rails more modular and allow developers to do things “their way” instead of “rails way” when they need. I think that this point was not achieved when it comes to helpers. They are pretty much the same thing since Rails 1.

    I totally agree with you that there is a problem. However the second solution scares me a lot. Every other way is a better way :-)


  8. Frederic

    This week episode on RailCasts.com is about the Draper gem, and shows how it helps to clean up your code.

    Ryan BATES also introduces this week the “pro” declination of his screencasts ($9/month) whose first episode is titled “Presenters from Scratch” and explains how to implement Draper like features… from scratch (with all the flexibility it offers) and with testing.


  9. nick

    @Daniel, @Andrzej: I also like plain Ruby best, but I guess the draper Decorators are made without any magic, so I’d recommend checking it out. Definitely, I love the idea of Andrzej, using a DCI approach to extend the model instance dynamically for the view. This could be a different approach for draper.

    @Rodrigo: Would love to see an example how JS helps avoiding helpers!

    @Robert: Agreed. Why do you hate the 2nd approach, using the controller instance as view context? That’s the way Sinatra does it, btw.

    @Frederic: Thanks, will have a look at that!

  10. @nick – I think that the second approach would make controllers a giant classes with too much responsibility. As for instance variables being copied into view context. I like decent_exposure approach to that problem. We should explicitly expose data for views via methods (not instance variables). I even use this approach with apotomo.

    Another thing that I would consider is exposing data for view in separate object:

    def show
    view_data.post = Post.find(1)
    end

    This view data could be a simple container or it could have more responsibilites.


  11. Justin Ko

    @Daniel, @Andrzej, @nick – ah, but don’t tie decorators so tightly to models!

    Because Draper uses classes, you can use them without tying them to a model.

    So, with the composition approach, how would you extend a non-model? You’d have to create a class or object:

    https://gist.github.com/1269680

    Might as well just use classes from the beginning. Also, including a “base” module into other modules *and* classes feels dirty to me. Include (or define) it in the superclass and be done with it.


  12. Justin Ko

    Nick, btw, your website is shit! lol – no follow up emails for comments!


  13. nick

    @Justin: The DCI approach isn’t bound to any “models”, you just add instance methods temporarily at run-time to decorate an object.

    The point is you’d really just use

    object.extend(ViewDecorator)

    to decorate it, nothing more. What’s so dirty about that?

    I installed email notifications, let’s see if that works :-)


  14. nick

    @Robert: The ActionController already is a giant class with way too many responsibilities – that’s why I say, split that into several cells! Why should I mix in #published_at into the AC controller if that method is needed once in a small fragment, only? That could be modeled into a separate cell.

    You can still use decent_exposure, which would be a good idea for an Apotomo screencast, btw!!!


  15. Justin Ko

    @nick – my point was how do you come up with that object? You have to create a class or do Object.new.extend

    Most likely you would not use Object.new because you probably need to pass some data/state to the decorator. Therefore, you would use a class. So, you would have modules for your models, and classes for your non-models. Inconsistent.

  16. Rails helpers move us away from OO design patterns whereas Cells and Draper move us obviously closer. Putting decent_exposure (or something like it) in the core and not copying instance variables would put us in the right direction without changing too much.

    The other downside with helpers in most rails apps is that we set “helper :all” in the application controller because the community (myself included at the time) wanted to use helpers to organize code rather than extend a specific instance of the view.

    Andrzej is right to point out that the mental model is important too. I wrote a bit about the DCI approach and why you would want to use it.


  17. nick

    The automatic mail follow-ups should work now.


  18. emma

    Little typo: “Solution 1: Push Utitlity Methods”. Should be “Utility”.


  19. qwerty

    I am confused how helpers are not OO. Modules like everything in Ruby is an object.

    How modules get mixed into the view is not a mystery.

    ActionView creates the object that the view is in, and mixes in the appropriate helpers: application_helper and current_view_helper.

    Perhaps you need to study how mixins work?


  20. qwerty

    As long as the “conflicting” methods are not in either the current_view_helper or application_helper there is no conflict, if they do, well it follows the ruby object model obviously, so it should be clear which one get called. If it isn’t, then bone up on the Ruby object model, which is a little complex, but not massively so.

    Since Modules are a Class, and an Object you can mixin as many other modules as need. Saying you can’t include other modules is rank ignorance.


  21. nick

    @qwerty: Frankly, I’m sick of the “In Ruby, everything is an object” excuse for making things wrong. Yes, modules are objects, even mee knows that.

    Nevertheless, in OOP every method call needs a receiver.

    Why is the receiver magically the ActionView instance and not the controller when using helpers? Why do tons of helper methods still need to re-delegate to the controller if it makes so much sense to use the ActionView as context? Why do so many people complain that gem xyz suddenly overwrites helper #abc since we have one global namespace for all the application helpers?

    Telling me to “bone up on the Ruby object model” doesn’t help, I’m more concerned about the architecture of helpers in Rails and not about the implementation as-it.


  22. QWERTY

    If you were more concerned about knowing a little more about Ruby’s object model you would know what the receiver is.

    Try this

    module T
    def foo= foo
    @foo=foo
    end
    end

    Class Q
    include T
    def foo
    @foo
    end
    end

    Inside an object the self receiver is almost always implicit, outside it never is. View code doesn’t need an explicit receiver because the modules are mixed into the view object. It is the same reason you can directly access instance variables.

    It is not a global namespace in the same way the Kernel module is not global. Ruby isn’t a hodgepodge of poorly implemented feature like PHP.

    Essentially, you actually have to learn Ruby to understand advanced Ruby concepts. Shocking I know, but still very true.

    Telling you to done is very helpful, because your answer lies in the object model.

    Seriously, read Metaprogramming Ruby, it will make all this stuff very clear.


  23. nick

    @QWERTY: Thx for the examples! The problem is not my misunderstandings of the Ruby object model but the fact that I don’t want to access the ActionView instance. Seriously, all helper calls go to a monolithic, global view instance – which is about the same as a PHP script.


  24. Kevin Triplett

    I’m using draper and in my Test::Unit tests, I have to add the following:

    setup do
    ApplicationController.new.set_current_view_context
    end

    This provides draper with the view context.

    I am waiting anxiously for the day when the Rails core team announces a better way to render views!


  25. Kevin Triplett

    When I say Test::Unit tests, I meant Test::Unit widget tests. :b


  26. wtf

    @nick,

    You seriously misunderstand the ruby object model.

    Do you think the Kernel module is a monolithic instance?

    If yes, you don’t understand Ruby.

    If no, how are helpers any different?

    You can mix in modules from inside or outside the object, maybe that is what is screwing you up.


  27. wtf

    @kevin

    That is an issue with Draper not rails.

    Besides, Test::Unit is pretty weak, use Rspec.


  28. nick

    @wtf: I re-read this post having your insightful additions in mind – couldn’t follow since this article is about Rails helper architecture and not Ruby module internals. You and me are talking about two different abstraction layers.

    The global monolithic ActionView instance is my problem, not the Kernel.

    > Besides, Test::Unit is pretty weak, use Rspec.
    Sounds like a well-proven selling point.

  29. [...] 相关资源:Draper: View Models for Railshttp://asciicasts.com/episodes/286-draperhttp://blog.steveklabnik.com/2011/09/09/better-ruby-presenters.htmlhttp://nicksda.apotomo.de/2011/10/rails-misapprehensions-helpers-are-shit/ 未分类你一定, 对象, 属性, 方法, 是一个, 显示, 格式化, 相关, 职责, 逻辑 ← JS 面向对象学习笔记 对于抽象稳定等价原则的深入思考 → 发表评论?0 条评论。 [...]


  30. Alexey

    What is wrong with helpers as functions? As far as i understand, OOP is needed only when a function is operating on a big and obscure data value, the new value is only slightly different from the original one, and the original value is discarded afterwards, like in:

    user.update_email(new_email)

    instead of

    user = update_user_email(user, new_email)

    OOP also conveniently allows to reuse the same method name for different functions based on the receiver type.

    Otherwise i see no reason not to use pure functions. However, helper functions are not pure functions. I am not familiar with details, but i believe that the receiver of a helper method is the view/controller context, isn’t it? Then it looks very natural to me, because it is the responsibility of the view to decide how things are formatted or capitalized.

    Decorators/delegators look to me like a bad secretary: they pass through all phone calls to which they cannot answer. Sending a message to a decorator, you are not sure who would answer, and if you are sure, then maybe using a decorator was not an appropriate choice?

    Decorators look to me like a convenient hack to reuse code without posing too many questions about which code is responsible for what.

  31. IMO, those criticizing Nick, and pointing out that Kernel is an object that is not technically global, and view helpers are the same and therefore ok, are missing the point. The point is that in order to make our applications simpler (and therefore more maintainable, extensible, etc.), we should always be seeking to make the accessibility to code and data as narrow as possible. This minimizes the number of possible interactions, and therefore complexity. Whether or not a helper is an object is not the issue; it’s that it’s so widely accessible.

    I’ve been looking at functional programming lately (I wrote a blog article introducing FP in Ruby at http://www.bbs-software.com/blog/2012/11/05/intro-to-functional-programming-in-ruby/). Given that, I decided to try creating a function in the controller and passing it to the view in an instance variable. (If they got to be more numerous, I could put them in a hash (maybe even call it ‘helpers’ ;) ), where the value was the function and the key was a name by which to access it.)

    This may seem kludgy, and I haven’t thought much about how it would scale, but the benefit is that it narrows the scope about as much as one can. The way I did it was to create the function in the controller’s constructor, and assign it to an instance variable. Then, it’s available to the view. Treating functions as values is rare in Ruby (except when we pass them to functions), and I’m wondering if we should do it more often. Thoughts?

    - Keith

  32. [...] of their code has to go into a model, view or controller. Some frameworks provide view helpers (but these are evil) and maybe a place for custom plugins or components, so that’s maybe 5 or 6 types of [...]


  33. Nick

    I also hate helpers in rails. I think stuff should go in the controller or the view. Helpers make it very hard to figure out where it is and what the scope is. If your view gets too complicated, then pull some of it out into a partial. I guess my approach isn’t kosher because then I am accessing models from the views, but that seems like a small price to pay for avoiding the helpers. And I think it makes the code simpler and more easy to maintain.

  34. […] anyone remember the provocatively titled Rails article from 2011: Helpers are sh*t? Well the author (Nick Sutterer) has decided it’s time for a follow up post detailing a […]

  35. ,塑料角码; 现代人在家具消费上更注重衣柜的设计,以及 塑料角码 衣柜的选材用料,根本没有花心思在五金配件上。其实五金配件在衣柜中,用户的使用体验感是直接挂钩的。

    五金
    衣柜五金配件分类

    在整个衣柜的组架中,包含的五金配件包括:三合一连接件、合叶铰链滑轨类(如柜门铰链)、装饰拉手类、装饰锁具类(如抽屉锁)、挂衣杆、裤架等。五金件的价格、档次与寿命是制约衣柜身价的关键因素,国外一些高档橱柜、衣柜五金件的价格,已接近或超过衣柜本身的价格。好 今得利五金 的五金件,可以保证衣柜的使用寿命,而劣质五金件会使柜门开启不灵活,推不动、关不严,给消费者带来烦恼与不便。

    看五金配件辨

Leave a Reply