If you are using haml as the template language in a Rails project, it can be very annoying if some Rails plugin generator creates a bunch of erb files. Or you have an existing Rails project where you want to migrate from erb to haml. For converting between these languages, there is the excellent html2haml tool, which used to be bundled with the haml gem but has just been moved to its own gem called html2haml.

With this tool, you can convert a single erb file to haml like this:

html2haml app/views/layout/application.html.erb app/views/layout/application.html.haml

While this works great for single files, it sucks when you have many templates that you want to convert many templates. Many blog posts suggest you to add a task to your Rakefile like this (untested):

require 'find'

desc "Convert all erb templates to haml"
task :html2haml do
  Find.find('app/views').select { |f| f =~ /\.html\.erb$/ }.each do |filename|
    sh "bundle exec html2haml #{filename} #{filename.sub(/erb$/, 'haml')}"
  end
end

But if you are using zsh, you can do this even easier:

for f (**/*.html.erb) html2haml $f $f:r.haml

That could not be shorter. Zsh has advanced globbing where you can use ** to match the current and all subdirectories. So **/*.html.erb searches recursively through all subdirectories for files that end in .html.erb. With the for loop for f (**/*.html.erb), we can execute a command for every file matching that pattern using $f for the filename. Now comes the best part: zsh has build in modifiers which you can append to a variable. We use the r modifier, which removes the last extension from a filename. If $f is set to foo.html.erb, $f:r is evaluated to foo.html. Now we can append .haml to get your target filename. $f:r.haml thus evaluates the the filename with the last extension (which is .erb) replaced by .haml. Once you understand the zsh modifiers, you can do a lot of tricks with your shell.

If don’t mind loosing the original erb files, you can delete them in one go:

for f (**/*.html.erb) { html2haml $f $f:r.haml; rm $f }