Tuesday, 31 March 2009

Using Haml & Sass from a Rake task

Haml logoSome time ago I had the 'lightning' idea to implement another Rake automation to support my current blogging workflow, which at the moment consists of finding a sparkling idea to blog about, write it out in WriteRoom and refine the post in TextMate before publishing. As this process was a recurring and copy & paste driven event, I strove for an automation supporting this workflow. So unsurprisingly the post will show my current solution to achieve this goal by utilizing Rake, Haml and Sass.

So what's that Haml and Sass thingy?

Haml (HTML Abstraction Markup Language) is a templating language/engine with the primary goal to make Markup DRY, beautiful and readable again. It has a very shallow learning curve and therefor is perfectly suited for programmers and designers alike. Haml is primarily targeted at making the views of Ruby on Rails, Merb or Sinatra web applications leaner, but as you will see later the Ruby implementation also can be used framework independently.

Sass (Syntactically Awesome StyleSheets) is a module which comes bundled with Haml providing a meta-language/abstraction on top of CSS sharing the same goals and advantages as Haml.

Gluing Haml and Sass into a Rake task

To get going you first have to install Haml and Sass by running the gem command shown next.
sudo gem install haml
With Haml and Sass available it's about time to identify and outline the parts you want to automate, in my case it's the creation of a WriteRoom and/or a XHTML draft document for initial editings. So the parameters to pass into the task to come are the targeted editor(s), the title of the blog post to draft and a list of associated and whitespace separated category tags.

The XHTML document skeleton content and it's inline CSS are defined each in a separate Haml and Sass template file and will be rendered into the outcoming document along with the content passed into the Rake task. While the document skeleton for the WriteRoom draft document, due to it's brevity, is defined inside of the task itself. The following snippets are showing the mentioned Haml and Sass templates for the XHTML draft output file, which are located in the same directory as the Rake file.

 Haml
!!! 1.1
%html
%head
%title= "#{title} - Draft"
%style{ :type => 'text/css' }= inline_css
%body
%h3= title
%h4.custom sub headline
%pre.consoleOutput console command
%pre.codeSnippet code snippet
%br/
= "Tags: #{tags.join ', '}"
 Sass
body
:margin 5
:line-height 1.5em
:font small Trebuchet MS, Verdana, Arial, Sans-serif
:color #000000
h4
:margin-bottom 0.3em
.consoleOutput
:padding 6px
:background-color #000
:color rgb(20, 218, 62)
:font-size 12px
:font-weight bolder
.codeSnippet
:padding 3px
:background-color rgb(243, 243, 243)
:color rgb(93, 91, 91)
:font-size small
:border 1px solid #6A6565
To inject the dynamic content into the Haml template and have it rendered into the outcoming document, the values i.e. draft_title, draft_tags and draft_inline_css have to be made available to the template engine by passing them in a bundling Hash into the to_html alias method of the Haml Engine object like shown in the next Rake task.
task :default do
Rake::Task['blog_utils:create_draft_doc'].invoke
end

namespace :blog_utils do

desc 'Create a new draft document for a given title, category tags and editor'
task :create_draft_doc, [:title, :tags, :editor] do |t, args|
draft_title = args.title
draft_tags = args.tags.split(' ')
draft_target_editor = args.editor

raise_message = 'No title for draft provided'
raise raise_message if draft_title.nil?

raise_message = 'No tags for draft provided'
raise raise_message if draft_tags.nil?

draft_target_editor = '*' if draft_target_editor.nil?

raise_message = 'Unsupported target editor provided'
raise raise_message unless draft_target_editor == 'Textmate' ||
draft_target_editor == 'Writeroom' || draft_target_editor == '*'

if draft_target_editor == 'Writeroom' || draft_target_editor == '*'
draft_output_file = draft_title.gsub(' ', '_') + '.txt'

File.open(draft_output_file, 'w') do |draft_file_txt|
draft_file_txt.puts draft_title
draft_file_txt.puts
draft_file_txt.puts "Tags: #{draft_tags.join ', '}"
end
end

if draft_target_editor == 'Textmate' || draft_target_editor == '*'

template_sass_content, template_haml_content = ''

['haml', 'sass'].each do |template_type|
template = File.dirname(__FILE__) + "/draft_template.#{template_type}"
raise_message = "#{template_type.capitalize} template '#{template}' not found"
raise raise_message if !File.exists?(template)

template_sass_content = File.read(template) if template_type === 'sass'
template_haml_content = File.read(template) if template_type === 'haml'
end

require 'sass'
require 'haml'

draft_inline_css = Sass::Engine.new(template_sass_content).to_css
draft_document_content = Haml::Engine.new(template_haml_content).to_html(
Object.new, { :title => draft_title , :tags => draft_tags ,
:inline_css => draft_inline_css } )


draft_output_file = draft_title.gsub(' ', '_') + '.html'
File.open(draft_output_file, 'w') do |draft_file_html|
draft_file_html.puts(draft_document_content)
end
end

end
end

Easing invocation pain with alias

Now as the Rake task is implemented and waiting for demands it can be invoked by calling the task as shown in the next console snippet.
sudo rake -f $HOME/Automations/Rakefile.rb blog_utils:create_draft_doc['Title','Tag1 TagN','Editor']
As I'm not even close to being a console ninja and probably will have forgotten the task call structure before initiating the next blog post, I decided to add an easing and more memorizable alias to $HOME/.profile as shown next.
alias createdraft='sudo rake -f $HOME/Automations/Rakefile.rb blog_utils:create_draft_doc[$title,$tags,$editor]'
The created alias now allows to invoke the Rake task in a nice and easy way as shown in the next console command.
createdraft title='Using Haml & Sass from a Rake task' tags='Rake Ruby' editor='Textmate'

Taking a peek at the generated draft document

After running the described Rake task I end up with the XHTML document shown in the outro code snippet, which then can be used for the further editing process. Of course I could have setup a TextMate Snippet to get me going, but that way I would have missed the opportunity to mess around with another amazing Ruby tool.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html>
<head>
<title>Using Haml & Sass from a Rake task - Draft</title>
<style type='text/css'>
body {
margin: 5;
line-height: 1.5em;
font: small Trebuchet MS, Verdana, Arial, Sans-serif;
color: #000000; }

h4 {
margin-bottom: 0.3em; }

.consoleOutput {
padding: 6px;
background-color: #000;
color: rgb(20, 218, 62);
font-size: 12px;
font-weight: bolder; }

.codeSnippet {
padding: 3px;
background-color: rgb(243, 243, 243);
color: rgb(93, 91, 91);
font-size: small;
border: 1px solid #6A6565; }

</style>
</head>
<body>
<h3>Using Haml & Sass from a Rake task</h3>
<h4>sub headline</h4>
<pre class='consoleOutput'>console command</pre>
<pre class='codeSnippet'>code snippet</pre>
<br />
Tags: Rake, Ruby
</body>
</html>

No comments: