robert hahn

a darn good web developer

July 22, 2007

Three Methods for simulating render_component in Camping

If you have used the Camping microframework for any length of time, you might have quickly found out that there’s no feature available like render_component, which is found in Ruby on Rails. render_component allows you to call a controller method (with its associated view) for the purposes of rendering a portion of your page. For example, you might want a page that primarily displays data for one employee, and show a list of employees in a side column - ideally, you’d like the controller responsible for generating a list of employees to continue to be responsible for that, even if you’re in the controller for displaying employee data. So I ended up working out three different methods for solving this problem.

Let’s go back to my running example here: I’ve got this employee management app, where people might want to add or delete employees, and on almost every page, I need to see a list of employees’ names, each linked to their profiles, on a side column. The controller class for retrieving this list could look something like this:

class Employees < R
  def get
    @employees = Employees.find :all, :order => "name asc"
    render :employee_nav_list
  end
end

Method 1: Calling methods from other classes

My first cut at trying to build render_component was the hardest thing I had to solve. The Employees class, with its get method for generating the list was already written, so what I really wanted to do was somehow instantiate the class and call get. In my previous article, we learned that the return values of these methods are always a string; if I could somehow instantiate that class, and call its get method, then I’d have a string containing a chunk of HTML that I can store in an instance variable to be used in my current view.

Calling Employees.new.get, unfortunately, wasn’t that simple. Hints to what the Employee’s constructor looked like was kind of buried in the Camping microframework itself, and after poring over the code and testing things out, I was able to come up with the following module:

module Component_Support
  def render_component( classname, method, payload, args )
    classname.new( payload, @env, method.to_s ).send(method.to_sym, *args)
  end
end

To use this, you’d have a bit of code in your app that looks like this:

module YourApp
  include Component_Support
end

and then you’re free to call render_component wherever you like. Let’s break it down. render_component, here, takes 4 arguments:

classname
The name of the class you want to instantiate
method
The method you want to call in that class
payload
If you’re making a `GET` request, this should be empty. If you’re making a `POST` request, the payload should consist of `form-encoded` data (which typically looks like this: `name1=value&name2=value&name3=value…`)
args
These would be the regular arguments the method requires to execute, if there are any.

The body of the method instantiates the object correctly, calls the method that we’re interested in, and returns the result. If I was displaying an employee profile page, and I wanted to show that list of employees down the side, all I need to do is add the following line to my profile controller:

@employee_list = render_component(Employees, 'GET', '', '')

And I’ll have a nice fragment of HTML to include in my output.

Method 3: Define methods for sharing data

I don’t know about you, but up until recently, I built the classes in Camping using only get and post methods because I thought that was the way you were supposed to do it. It was “the rules”. It never occurred to me that I could easily add other methods if I needed to.

The thing is, it’s just a class, like any other, so if you really needed to, you could move code that’s common to both get and post methods into their own method calls and call them.

using the above code snippet, we could basically build something like this:

class Profile < R '/profile'
  def get
    # code specific to get

    @employee_list = build_emp_list

    render :profile
  end

  def post
    # code specific to post

    @employee_list = build_emp_list

    render :profile
  end

  private

  def build_emp_list
    @employees = Employees.find :all, :order => "name asc"
    render :employee_nav_list
  end
end

Method 3: Putting components in modules

As a variation on a theme, suppose you have a number of controller classes, and most of them needs to display the same type of data on a page. You’d like to be as DRY as possible, and you don’t want to use the render_component code up above – maybe because the component you want to render doesn’t properly belong to one of the controller classes. All you’d need to do is make two changes to code that looks like method 2:

Here’s an example:

module MyApp::Controllers
  module Employee_Components

    # some components defined here...

    def build_emp_list
      @employees = Employees.find :all, :order => "name asc"
      render :employee_nav_list
    end
  end

  class Profile < R '/profile'
    include Employee_Components

    def get
      # code specific to get

      @employee_list = build_emp_list

      render :profile
    end

    def post
      # code specific to post

      @employee_list = build_emp_list

      render :profile
    end
  end
end

Conclusion

So, with this article, you’re well equipped to add render_component functionality to your Camping apps, which will help in making your code more DRY. Which method you use should depend on how you can best make your code readable. If the code you want to reuse resides in a related class, then I’ve provided you with a means to instantiate that class and call the method. If the only time you require this method is in one class, define it as its own function. Otherwise, some clever module management might do the trick.

decorative image of trees

Copyright © 2009
Robert Hahn.
All Rights Reserved unless otherwise indicated.