Rendering partial .rxml templates

Posted by JP Mon, 28 May 2007 01:09:00 GMT

Partials are an essential part of the Rails philosophy. They are a key component of the (Don't Repeat Yourself) DRY principle. A partial is a page fragment that you can reuse. It's an important of part of Rails's views.

Let's say you're writing an application (yes I will too use the blogging application to write this example). You're rendering a blog post and you want to print its comments (as a partial) as well. You'll probably do something like this:

<!-- In post.rhtml -->
<h1><%= h post.title %></h1>
<p><%= h post.body %></p>
<h2>Comments</h2>
<%= render: partial => 'comments', :collection => post.comments %>

<!-- _comment.rhtml -->
<h3><%= h comment.title %></h3>
<p><%= h comment.body =><p>

There you go, partials. Then, if you have another page that needs to display blog comments, you can re-use the post partial instead of re-writing that code. DRY FTW!

Now, what if you want to do the same thing using a .rxml template instead of a .rhtml template:

# In post.rxml
xml.post do
  xml.title post.title
  xml.body post.body
  xml.comments do
    render :partial => 'comments', 
      :collection => post.comments
  end
end

# In _comment.rxml
xml.comment do
  xml.title comment.title
  xml.body comment. body
end

You would expect that to work, wouldn't you? Unfortunately, it doesn't. You won't get an error, but you won't get any output either; the section in your output document will be empty. How sad and unsatisfying!

The issue at hand is that - note that I'm not familiar with the template rendering's inner workings so let's pretend this is true - the xml object in the _comment.rxml partial has a different context than the xml object in the post.rxml file. It seems that Rails instanciates a new xml for every template it renders. In other words, your partial's output is not going where you're expecting it to go.

How to fix this? It's pretty simple actually. We need to tell the partial that we want it to use the same instance of xml. We do that by passing it the current instance of xml using render's :locals parameter and then by setting the value of xml by hand:

# In post.rxml
xml.post do
  xml.title post.title
  xml.body post.body
  xml.comments do
    render :partial => 'comments', 
      :collection => post.comments, 
      :locals => {:xml_instance => xml}
  end
end

# In _comment.rxml
xml = xml_instance unless xml_instance.nil?
xml.comment do
  xml.title comment.title
  xml.body comment. body
end

That's it! One note, you need to use a different name (I used xml_instance in that example) because if you use xml like this:

render [...], :locals => {:xml => xml}

It won't work. I suspect that the auto-instanciated xml object has a different scope, which superceedes the assignment made by the :locals parameter. Again, I'm not familiar with how things work under the hood, but I think the :locals variable assignments don't work if a variable with the same name already exists in the current scope.

That's it, I hope that was useful, and please Dont Repeat Youself. Always use partials!

Posted in  | Tags , , , , , , , ,  | 4 comments

Good Web Application Development Practices

Posted by JP Tue, 22 May 2007 02:03:00 GMT

Or, how to decrease the probability that you shoot yourself in the foot.

Here's a checklist of things you might want to do early on during a web application. Doing these simple things will save you trouble, save time and help you put out a better quality product. The earlier you do them, the more time you'll save.

1 - Commit Source Code to Subversion

If you prefer another source-control system, use another one. The point is, use source control software. If you don't, bad things can and will happen.

Source control will help you:

  • Track regression bugs.
  • Remember what your code looked like before after you change your mind.
  • Bring other people on board. Having more than one person modify a code base without source-control is a surefire way to attract problems.
  • Document what you did and when you did it.
  • Undo errors. Yours or others'.

2 - Write Automated Tests

Automated tests are a key component of Quality Assurance. If you can't hire QA people because you're running a small operation, fine - but do the minimum.

Automated tests let you:

  • Find bugs as soon as they are created.
  • Know when you break an interface.
  • Find bugs before you deploy.
  • Find bugs before your client/co-workers/boss finds them for you.
  • Test your code in a relatively thorough fashion in a fraction of the time you would do it by hand.
  • Increase the quality of your projects constantly.

3 - Set up a staging environment

Sooner or later, you'll need to deploy things that can't be tested in a development environment: server configuration changes, database server upgrades, whatever. The only way to test this reliably is to do it using a staging environment.

While testing your deployment on the staging environment, take note of everything you do so the real deployment will be smooth. To be safe, you can even stage the deployment twice. The second time around, you'll be sure that you didn't forget anything.

A staging environment will help you

  • Deploy your software easily.
  • Test the deployment procedure itself somewhere else than your production environment.
  • Document changes done to the production environment.
  • Get up and running quicker should you need to restore your entire production environment.

4 - Automate your deployment procedure

Scripts are faster than you are and if they're set up properly, they don't make mistakes. If your scripts are intelligent enough, they let you undo the deployment. Write them to be what you want them to be. My advice on the subject is to do it good enough so you save time without going overboard.

Automating your deployment procedure will let you:

  • Minimize your application's downtime.
  • Deploy urgent fixes quickly. It happens.
  • Deploy more often by removing overhead. The easier it is, the more you'll do it. Your product will be incrementally better, faster.

5 - Fix problems as soon as they show up

Procrastinating is bad. Procrastinating bug fixes is especially bad. Fix problems while they are fresh in you mind. Don't wait two months to fix something that you could fix now. That way, you won't forget it, and it will take less time ot fix.

Out of lazyness, no bullet list for this item!

6 - Backup everything

On-site, and off-site. Your local machine and the servers. On site backups aren't enough: they won't protect-you against theft, fire, or whatever mother nature can cook up these days.

My friend's father who's a graphic designer used to backup his work on a Zip Drive. Too bad his backup disk was in his computer when it was stolen. Not only did he have to deal with the theft itself, but he had to redo a few weeks worth of work, with the ensuing consequences. Let's all learn something from this anecdote. I know I have.


Now, Business consultants charge four figures to walk into a conference room and tell you the same things I just told you with PowerPoint slides. One could say that they are crooks for doing so. One could also say that the return on investment will make up for that cost.

Maybe your project isn't there yet, fine, but as soon as a project becomes a source of revenue - or expenses, you want do be following these tips to make sure you're not burning your profits, potential revenue, or even worst, money out of your pocket.

The bottom line is that if you're not doing these things, you're lazy, but in a bad way. Lazing is an art. Do it well.

Please share any other ideas/suggestions you might have.

Posted in  | Tags , , , , , , ,  | no comments

Useful Regular Expressions

Posted by JP Tue, 03 Apr 2007 21:00:00 GMT

I use Regular Expressions quite often as possible because they are the most efficient (code-wise) way to validate and extract information from text strings.

Here's a few of my favorites. Feel free to use them and let me know if you find a glitch:

Date validation/extraction in "2007-12-31" format. This regexp makes sure that the number of days in the month is valid (february always allows 29 days regardless of the year, which is good enough for most applications):

/^([1-9]\d{3})-(?:
(02)-(0[1-9]|[12]\d)
|(0[469]|11)-(0[1-9]|[12]\d|30)
|(0[13578]|1[02])-(0[1-9]|[12]\d|3[01]))$/

Time of day validation/extration in 24h format, from 00:00 (midnight) to 23:59. Hours' leading zero is optional:

/^([0-1]?\d|2[0-3]):([0-5][0-9])$/

Canadian postal code validation, case insensitive:

/^[a-z]\d[a-z] \d[a-z]\d$/i

Phone number validation, with mandatory area code and optional trailing extension in any format (e.g: "514-555-1234 x303"):

/^(\d{3}-){2}\d{4}( .+)?$/

Note that the above regexps should not contain any line breaks. Remove them as you copy/paste them in your code.

Enjoy!

Tags , , , , ,  | no comments