ActiveHelper 0.1.0 released

Finally – helpers with proper encapsulation, delegation, interfaces and inheritance!

When I was prototyping a new JavaScript generator library using Rails’ helpers I came to a conclusion: Helpers suck.

Typically helpers in ruby frameworks are implemented as simple modules. However, despite the word simple, they suffer from two problems:

  • Dependency helper modules assume there are other helpers mixed into the target and blindly use foreign methods
  • Dependency helper modules rely on instance variables from the target they were mixed into.

ActiveHelper - An example

ActiveHelper puts helpers back to where they belong: classes.

Here’s how a typical MVC helper would look like with ActiveHelper. And, hey, if this is too brief, go and check out the README.

class View
  include ActiveHelper
 
  def https_request?; false; end
end

We have an exemplary View class that knows if an HTTP request is secure or not.

use

The include pendant for helpers and target instances is use.

> view.use FormHelper

The instance can use a helper to gain he-man’s form building capabilities.

class FormHelper < TagHelper
  provides :form_tag
  uses UrlHelper
 
  def form_tag(destination)
    destination = url_for(destination)  # in UrlHelper.
    tag(:form, "action=#{destination}") # in TagHelper.
  end
end

provides

The FormHelper exposes its interface as it provides a method #form_tag that itself uses two foreign methods #tag and #url_for.

The #tag method is simply inherited from the TagHelper, which provides :tag again. See the inheritance?

The latter, #url_for, is implemented in UrlHelper, so the form lib references that dependency when it uses the required helper.

needs

Let’s look into that url thing helper.

class UrlHelper < ActiveHelper::Base
  provides  :url_for
  needs     :https_request?
 
  def url_for(url)
    protocol = https_request? ? 'https' : 'http'
    "#{protocol}://#{url}"
  end
end

You already got it, don’t you?

The UrlHelper relies on a mysterious method #https_request?. There is no magical inclusion somewhere. As soon as it needs a method an accessor will be created delegated all calls to the View instance, which was the target.

Handling dependencies

So it’s the importing instance that has to handle methods declared with needs.

If the view wouldn’t expose the required method, it would be like

> view.form_tag('apotomo.de')
# => 11:in `url_for': undefined method `https_request?' for #<View:0xb749d4fc> (NoMethodError)

which is a clear, debugable issue.

Come on!

The approach found with ActiveHelper is completely generic, not bound to a specific framework like Rails, Merb or Sinatra. It may (and should!) be used in order to make helpers cleaner, use OOP features and make them easier to debug.

What’s your impression, what’s your problems and experiences with helpers? Don’t be shy, tell us!

Curious about the source? Check out my repository at the github, trans.

Cheers!

2 Responses to “ActiveHelper 0.1.0 released”

  1. trans Says:

    Very nice. I’ve wondered about something like this myself. Glad to see someone doing it.

    Couple of thoughts: 1) Can public methods automatically be ‘provides’? 2) naming the library ActiveHelpers pretty much shouts “RAILS”, so despite being generally useful I am not sure you’ll get a lot of traction outside Rails. Just a thought.

    Lastly, a link to the source code/ repository would be great.

  2. nick Says:

    yo trans,
    thanks for your post!

    1) what about some
    provides public_instance_methods
    or
    provides :public_methods => true
    or so? i enjoy being explicit.

    2) yeah i know, you’re definitly right. i wanted to relate it to ActiveSupport, which is pretty useful and completely rails independent. you using rails? :-D

    3) link to the repository is at the top, i’ll add another one in a second.

    thanks again,
    nick

Leave a Reply