Tuesday, 23 March 2010

Rails - Day 8 - Down and Dirty with HAML and SASS

Rails  - Day 1 - The RubyGem Love Story
Rails -  Day 10 - Testing Rails Controllers
Rails -  Day 13 -ActiveRecord Relationships Part II
Rails - Day 15 - Rails Forms Part II
In this post, I want to create a simple view and try and give justice to the power of HAML, SASS and Compass.  

In the previous post, we created a simple index action for our UsersController:


As you can see from the above, we are simply (in .NET terms) Response.Write'ing our output.

We will now add our first view.  Guess what?  View files are retrieved and the correct template handler (viewengine) is selected by using...........a rails convention!!!

Shock, horror, whatever next?  No xml configuration setting or line of code is needed.  It is down to the file location and the file extension.  In .NET convention over configuration rarely leaves the realms of assembly namespaces.

We have stated several hundred times now, how Rails promotes convention over configuration and the default rendering is an excellent example of this.  By default, controllers in Rails automatically render views with names that correspond to actions.  In our UsersController example, Rails will render a View if it is found in app/views/users/index.html.haml.  It just so happens in our example that we are rendering plain text as you can see in the index action at the top of the post.

It is important to know that the file extension of our view controls the choice of template handler.  A template handler is analgous to the ASP.NET MVC ViewEngine.  We are going to use HAML, so we add a file named index.html.haml to the location we mentioned above:


HAML

As mentioned in a previous post, HAML uses a weird almost python like syntax to seriously reduce the amount of text that is entered into a view.  HAML is most definitely not everyone's cup of tea and you will just have to judge for yourself if you would be more comfortable with something else like ERB.  

So without further a do, let us see some HAML in the context of our sample project.  By way of an entrée, I am just going to show you how the following part of the HTML document looks in HAML.  Below is the HTML we want to create in HAML.


Here is the index.html.haml that this HTML was compiled from:


I hope you can see from the above how the <meta> and <link> tags are identified as nested inside the <head> tag by using indentation.  

Also, if we look at the meta tag syntax, we can see how HAML uses a hash to build up the attribute pairs:

%meta{'http-equiv' => 'Content-Type', :content => 'text/html; charset=utf-8'}

becomes:

<meta content='text/html; charset=utf-8' http-equiv='Content-Type' />

Also note how the % sign is start of a new element.  If the HAML code makes you want to puke then you might want to look at ERB or something else.  The lack of angle brackets is a big plus for me but not for everyone.

Compass and SASS

You can see from the HAML and the HTML above that we are pointing to the stylesheets that are compiled from the SASS files we talked about in this post.  

Compass is not a framework in the true sense of the word, it is put crudely, a lot of code you can use in your current project.   In a previous post, I mentioned how Compass created for us when we ran the compass installer, the following files in the app/stylesheets folder:



These sass files compile down to the CSS we are referencing in our HAML index.html.haml file and are located here:




We never update the CSS directly and only make changes to the SASS which compiles down to the CSS.

I also mentioned the compass command line tool we can leave running that listens for changes in our sass files and compile into css automatically:



The main SASS file is the app/stylesheets/screen.sass and here is mine, it should be noted that this has been altered from that which was created by the compass installer:



What we have here at the top of the file are a number of @import statements which will include some shared libraries in the form of SASS mixins from various code files of the compass gem.

You can see that we are using the mixin semantics here by adding a mixin under the #container selector.  A sass mixin allows us to reuse CSS attributes.

For example, I might declare the following mixin in my sass file:


I can then reuse this mixin by using it in one or more selector like this where I am reusing it for a label class:



Which will compile down to the following css:

01 .label {
02    font-weightbold;
03    font-size126%;
04    text-decorationunderline}

We are then free to reuse this mixin with other selectors.  This is the power of SASS, the same reuse semantics just do not exist in CSS or perhaps SASS would not exist.

So the first thing we declare in our screen.sass file is the container selector and use the +container mixin that will give us the overall container css of the whole web page from the blueprint grid framework. 

We declare the following SASS style rule:



If we look at the compiled version of the screen.css, we can see what the css has been compiled from the above SASS.  


We can see that the container class style rule will set the width of the blueprint grid we are going to use on the page:

The value of the width of the container selector is 950px, this size is set in the app/stylesheets/partials/_base.sass file:



The sass variable used in the +container mixin is the statement !blueprint_container_size = 950px

OK, now we have created the container selector and added the container mixin, we can create the <body> tag and add a <div> tag to our index.html.haml file with an id of container.



The #container HAML will render a div element like this <div id="container"></div> and pick up the css style rules from the #container selector.

We are now going to add a header element to our page and add the sass to style the header.  So first of all, we add the following to our index.html.haml file that simply renders an unordered list as an example of perhaps some top level navigation:



We then add the following sass to style the header element:



Here we are using the +column mixin that defines a selector as a column.  The first argument is the number of grid units to span and there is also an optional second argument which is a boolean and indicates whether the column is the last of the current row.  We are setting the number of columns to 24 which is the maximum amount of columns at our disposal as is stated in the app/stylesheets/partials/_base.sass file.

Here is how the resulting HTML looks in our browser:



We are now going to create a sidebar which you can think of as a left menu element that a lot of web applications have.  We add the following haml which will render a div with a child unordered list:

We then add the following sass to style the sidebar:


We only want the left menu to take up 5 columns worth of width, so we pass 5 into the column mixin.  

We then want to add an element and some styling the main content of our page.  We add the following HAML which is just a place holder for now:



We add the following SASS to style the content:



All that is left is to add the footer.  We add the following footer HAML:

The footer is remarkably like the header we early created.  This is an ideal candidate for creating a mixin that we can reuse.  With that decided, we create the following mixin declaration:



I like to think of sass mixins as anonymous methods that we then add to our parent selectors.

We then refactor our header sass selector to look like this:


We can then easily create the footer sass selector easily as a carbon copy of the header:




This was painlessly easy and the end result is cross browser CSS.  The code in our SASS files are so much more clean and maintainable than the ugly sprawls that go under the name of CSS files.  The HAML is bereft of inline styling and to me is much more elegant than the angle bracket soup you get in most other ViewEngines including the new ALT.NET darling view engine spark.

This is the basics of using SASS, HAML, Compass and the blueprint framework.

Before we leave the crazy world of Compass and his friends, I want to show the +showgrid mixin in action.  

If we update the container selector to add the +showgrid mixin:


This will give us a proper feel for the grid layout we are trying to achieve by setting the +column mixin and other values that move our elements left, right and up and down the page.

After adding the +showgrid mixin, our web page looks like this:



This is a great visual aid to deciding where to place your elements.

We have covered a lot of ground and I will be surprised if anybody has actually made it this far and has not instead moved onto another blogger in your reader selection.  

I am fairly sure that is what I would have done by now.

In the next post, I am going to touch on rendering content from a database which is really what a lot of us do.  I include myself in this less than elite group.





2 comments: