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
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:
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.
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
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:
#build_emp_list
into a module, along with other methods you want to re-use.include Your_new_modulename
to the class.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
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.
Copyright © 2009
Robert Hahn.
All Rights Reserved unless otherwise indicated.