Sharing the Code

Programming stuff that might be useful to others

Setting up a client-side workflow using Drupal Omega 4 on OSX

The Omega 4 theme is a responsive Drupal theme that supports the latest client-side tools like Sass, Compass, Susy, Grunt and Bower. It’s great that it supports all these tools but it’s not the easiest to set them up. Sass and the frameworks built on it – Compass and Susy require Ruby. Grunt, and if you want to use CoffeeScript require Node.js. Below is a brief description of these tools.

  • Sass – is like a super CSS that makes writing CSS easier. You write it in a language similar to CSS that compiles down to CSS.
  • Compass – is a Sass framework that amongst other things generates browser specific properties for CSS3.
  • Susy – another Sass framework that provides responsive grids.
  • Grunt – a build system that will convert your Sass to CSS and your CoffeeScript to Javascript continuously in the background.
  • Bower – provides client-side package management.
  • CoffeeScript – a cross between Ruby and Python that compiles down to JavaScript. It’s not supported out of the box by Omega but can be added easily.

Drush

The first thing you should install is Drush. It makes creating an Omega sub-theme a bit easier. Drush requires PEAR – a package repository for PHP.

To install type the following in a terminal:

pear channel-discover pear.drush.org
pear install drush/drush

If you don’t have PEAR installed you can install Drush manually – see the instructions here.

Omega

Omega is a responsive Drupal theme. You basically use it as a base theme if you want to make your own responsive theme. You can download it with Drush:

cd sites/my_site
drush pm-download omega --destination=sites/all/themes
drush pm-enable omega

Leave off destination if you want to install the module in your site.

Create an Omega sub-theme

To create a sub-theme, type the following in a terminal:

drush omega-wizard

If it complains that it can’t find omega-wizard, which might occur if you have just installed omega, type the following:

drush cache-clear drush

and then try running the wizard again:

drush omega-wizard

In Omega you can have different layouts, maybe one layout for the homepage and another for the inside pages – similar to having multiple page.tpl.php. You can find out more on how to create custom layouts here. I found that the name of the layouts had to be unique in the Drupal installation so I add a prefix to the layout name e.g. my_site_default or my_site_homepage. Once you’ve created your custom layout you can set it in Appearance > My Theme > Settings > Layouts.

To get the layout to change on a certain condition you need to install Context and Context Omega:

drush pm-download context --destination=sites/all/modules
drush pm-enable context, context_ui, context_layouts
drush pm-download context_omega --destination=sites/all/modules
drush pm-enable context_omega

Go down to Context UI in Modules and click on “Configure”. Add a new context and you might set the condition to “Node type” and check one of the node types. For the “Reactions” you would select “Blocks and Omega” and then select your layout from the drop-down list.

RVM

There are two files in your sub theme that specify the Ruby version (.ruby-version) and the gemset (.ruby-gemset). Gems are Ruby’s version of modules and a gemset is a collection of gems. The idea behind these two files is that if you have RVM (Ruby version manager) installed and cd into the project directory it will change to the Ruby version specified in .ruby-version and will use the gemset specified in .ruby-gemset. This makes sure that the theme won’t brake if you install a different version of Ruby or install different versions of gems for other projects.

To install RVM type:

curl -L https://get.rvm.io | sudo bash -s stable

This is a multi-user install – it will install RVM for all users on your computer. Don’t run this as root. To install it for the current user type:

curl -L https://get.rvm.io | bash -s stable

You will probably now need to close the terminal window and open it again.

To find out the version of Ruby that the sub-theme uses type:

cat .ruby-version

And then to install that version type:

rvm install 1.9.3 --with-gcc=clang

1.9.3 might need to be replaced by what is in .ruby-version. It might be able to download a binary but if it has to compile Ruby from source you will need to install XCode which is free and comes with a command line C compiler as an additional download. To download the C compiler open XCode and goto XCode > Preferences > Downloads and click on the download button next to “Command Line Tools”.

Next you will need to create a gemset for your theme. See what the name of the gemset should be by typing:

cat .ruby-gemset

First make sure you are using the version of Ruby specified in .ruby-version:

rvm 1.9.3

And then create the gemset:

rvm gemset create omega.my_site

And then change to that ruby version/gemset:

rvm use 1.9.3@omega.my_site

When you cd into the theme’s directory in future it should automatically change to the ruby version/gemset specified in .ruby-version and .ruby-gemset.

Next type the following to make sure any requirements are installed:

rvm requirements --with-gcc=clang

Next you will need to install the gems for the theme. The gems are specified in Gemfile.

bundle install

If you get the following error:

ERROR: Gem bundler is not installed, run `gem install bundler` first.

Install the Gem bundler with:

gem install bundler

And again try:

bundle install

Grunt

Grunt requires npm which is the Node package manager. You can download Node.js installers from here. To install Grunt run the following as root:

npm install -g grunt-cli

Log back into your user account and type:

npm install

This is the Node version of bundle install and it installs all the modules specified in package.json.

To get Grunt to automatically convert your Sass to CSS type:

grunt watch

LiveReload

LiveReload reloads your web page automatically when you change source files so you don’t have to keep hitting the refresh button. Grunt supports LiveReload but for some reason it’s turned off for Sass by default in Omega’s Gruntfile.js. To enable it remove the options section of sass in Gruntfile.js.

Gruntfile.js:
[sourcecode language=”html”]

sass: {
files: [‘sass/{,**/}*.{scss,sass}’],
tasks: [‘compass:dev’]
},

[/sourcecode]

And then restart “grunt watch” for the changes to take effect.

CoffeeScript

To add support for CoffeeScript you will need to install the Grunt plug-in grunt-contrib-coffee:

npm install grunt-contrib-coffee --save-dev

The suffix –save-dev adds an entry to devDependencies in package.json.

Then add a new section to the watch section in Gruntfile.js and add a coffee section like below:
[sourcecode language=”html”]

watch: {
options: {
livereload: true
},

coffee : {
files: [‘coffeescript/**/*.coffee’],
tasks: [‘coffee:dev’]
},
},

coffee: {
options: {
join: true
},
dev: {
options : {
sourceMap : true
},
files : {
‘js/my-theme-behaviors.js’ : [‘coffeescript/my-theme-behaviors.coffee’]
}
},
dist : {
options : {
sourceMap : false
},
files : {
‘js/my-theme-behaviors.normal.js’ : [‘coffeescript/my-theme-behaviors.coffee’]
}
}
}
});

grunt.loadNpmTasks(‘grunt-contrib-coffee’);

[/sourcecode]

This setup is on the presumption that your CoffeeScript files live in a directory called coffeescript. if you have multiple CoffeeScript files you will need to put them in the correct order so that dependencies come first. There is a Grunt plug-in called Percolator that handles this for you but source maps don’t work properly with it. See here for an introduction to Grunt.

Source Maps

Source maps are files that map the CoffeeScript to the generated JavaScript so when there is an error in the code, you can go to to the offending line in the CoffeeScript rather than doing it manually by looking at the generated JavaScript. It’s supported in Google Chrome and partially supported in Firefox. In Firefox you can put breakpoints in the CoffeeScript but when there is an error it shows the JavaScript rather than the CoffeeScript.

Deploying to a Production Server

When you want to deploy the theme to a server you would want to minify the CSS and Javascript, package it up, upload it, unpack it on the server and clear the cache. This can be done in Grunt. First you will need to install a few Grunt plug-ins:

npm install grunt-shell --save-dev
npm install grunt-ssh --save-dev

It’s useful to keep the properties for these tasks in package.json and refer to them in Gruntfile.js:

package.json (complete):
[sourcecode language=”javascript”]
{
“name”: “my_theme”,
“version”: “1.0.0”,
“dependencies”: {},
“devDependencies”: {
“grunt”: “~0.4.0”,
“grunt-contrib-watch”: “~0.4.3”,
“grunt-contrib-compass”: “~0.2.0”,
“grunt-contrib-jshint”: “~0.1.1”,
“grunt-contrib-uglify”: “~0.2.0”,
“grunt-shell”: “~0.3.1”,
“grunt-contrib-coffee”: “~0.7.0”,
“grunt-ssh”: “~0.7.0”
},
“engines”: {
“node”: “>=0.8.0”
},
“archive”: “my_theme.tgz”,
“buildPath”: “../../../../../build”,
“exclude”: “–exclude=js/*.normal.js –exclude=js/*.map –exclude=js/*.coffee –exclude=sass –exclude=.sass-cache –exclude=coffeescript –exclude=node_modules –exclude=bower.json –exclude=config.rb –exclude=Gemfile –exclude=Gemfile.lock –exclude=Gruntfile.js –exclude=Guardfile –exclude=libraries.make –exclude=package.json –exclude=readme.txt”,
“remote”: {
“host”: “example.com”,
“username”: “user”,
“password”: “password”,
“basePath”: “/path/to/website”,
“path”: “web/sites/my_site/themes/my_theme”,
}
}
[/sourcecode]

I’ve set the build path to ../../../../../build so that it is outside Drupal.

Gruntfile.js (complete):
[sourcecode language=”javascript”]
‘use strict’;

module.exports = function(grunt) {

grunt.initConfig({
pkg : grunt.file.readJSON(‘package.json’),

watch : {
options : {
livereload : true
},
sass : {
files : [‘sass/{,**/}*.{scss,sass}’],
tasks : [‘compass:dev’],
},
coffee : {
files : [‘coffeescript/**/*.coffee’],
tasks : [‘coffee:dev’]
},
registry : {
files : [‘*.info’, ‘{,**}/*.{php,inc}’],
tasks : [‘shell:clear-theme-registry’],
options : {
livereload : false
}
}
},

shell : {
“clear-theme-registry” : {
command : ‘drush cache-clear theme-registry’
},
“package” : {
command : ‘tar -zcf <%= pkg.buildPath %>/<%= pkg.archive %> <%= pkg.exclude %> *’
}
},

sftp : {
deploy : {
“./” : “<%= pkg.buildPath %>/<%= pkg.archive %>”
},
options : {
path : ‘<%= pkg.remote.basePath %>‘,
host : ‘<%= pkg.remote.host %>‘,
username : ‘<%= pkg.remote.username %>‘,
password : ‘<%= pkg.remote.password %>‘,
srcBasePath : ‘<%= pkg.buildPath %>‘
}
},

sshexec : {
deploy : {
command : “tar -zx -C <%= pkg.remote.basePath %>/<%= pkg.remote.path %> -f <%= pkg.remote.basePath %>/<%= pkg.archive %>;rm <%= pkg.remote.basePath %>/<%= pkg.archive %>;cd <%= pkg.remote.basePath %>/<%= pkg.remote.path %>; drush cache-clear all”,
options : {
host : ‘<%= pkg.remote.host %>‘,
username : ‘<%= pkg.remote.username %>‘,
password : ‘<%= pkg.remote.password %>‘
}
}
},

compass : {
options : {
config : ‘config.rb’,
bundleExec : true
},
dev : {
options : {
environment : ‘development’,
force : true
}
},
dist : {
options : {
environment : ‘production’,
force : true
}
}
},

coffee : {
options : {
join : true
},
dev : {
options : {
sourceMap : true
},
files : {
‘js/my-theme-behaviors.js’ : [‘coffeescript/my-theme-behaviors.coffee’]
}
},
dist : {
options : {
sourceMap : false
},
files : {
‘js/my-theme-behaviors.normal.js’ : [‘coffeescript/my-theme-behaviors.coffee’]
}
}
},

jshint : {
options : {
jshintrc : ‘.jshintrc’
},
all : [‘js/{,**/}*.js’, ‘!js/{,**/}*.min.js’]
},

uglify : {
dev : {
options : {
mangle : false,
compress : false,
beautify : true
},
files : [{
expand : true,
cwd : ‘js’,
src : [‘**/*.js’, ‘!**/*.min.js’],
dest : ‘js’,
ext : ‘.min.js’
}]
},
dist : {
options : {
mangle : true,
compress : true
},
files : [{
expand : true,
cwd : ‘js’,
src : [‘**/*.normal.js’, ‘!**/*.min.js’],
dest : ‘js’,
ext : ‘.js’
}]
}
}
});

grunt.loadNpmTasks(‘grunt-contrib-watch’);
grunt.loadNpmTasks(‘grunt-contrib-compass’);
grunt.loadNpmTasks(‘grunt-contrib-jshint’);
grunt.loadNpmTasks(‘grunt-contrib-uglify’);
grunt.loadNpmTasks(‘grunt-shell’);
grunt.loadNpmTasks(‘grunt-contrib-coffee’);
grunt.loadNpmTasks(‘grunt-ssh’);

grunt.registerTask(‘build’, [‘coffee:dist’, ‘uglify:dist’, ‘compass:dist’]);
grunt.registerTask(‘deploy’, [‘build’, ‘shell:package’, ‘sftp:deploy’, ‘sshexec:deploy’]);
};
[/sourcecode]

So, to deploy to a server, you would type:

grunt deploy

The CoffeeScript compiler can’t minify so I output the CoffeeScript to my-theme-behaviors.normal.js and then uglify minifies this to my-theme-behaviors.js. Uglify normally outputs a file with .min.js extension but this would require changing the .info file for production.

Comments are closed