Monday, October 20, 2008

Creating a new Merb stack with Templater

In trying to remove Merb's dependency on ActiveSupport, Jonas Nicklas developed Templater. I'm going to show you briefly how to use it to create an ActiveRecord, TestUnit, and Prototype based Merb stack.

I'd like to make clear at this point that this is more for learning about Merb, generators, and to use as a reference point for creating other stacks. I prefer DataMapper, Rspec, and JQuery, and as such this example stack won't be supported officially or unofficially (unless someone else would like to...if so, let me know).

You can find the code here: http://github.com/jackdempsey/merb-ar-stack/tree/master

The first important piece is the Generators file:

scope 'merb-gen' do
dir = File.join(File.dirname(__FILE__), 'lib', 'generators/')
Merb.add_generators dir + 'merb_ar_stack'
end



This file is picked up by Templater and as such, when merb-gen is run, you'll see it listed as one of the available generators. Lets take a look at what merb_ar_stack actually is then.

merb-ar-stack/lib/generators/merb_ar_stack.rb


module Merb
module Generators
class MerbArStackGenerator < AppGenerator
#
# ==== Paths
#

def self.source_root
File.join(super, 'application', 'merb_ar_stack')
end

def self.common_templates_dir
File.expand_path(File.join(File.dirname(__FILE__), 'templates', 'application', 'common'))
end

def destination_root
File.join(@destination_root, base_name)
end

def common_templates_dir
self.class.common_templates_dir
end

def testing_framework
:test_unit
end

def orm
:activerecord
end

#
# ==== Generator options
#

option :template_engine, :default => :erb,
:desc => 'Template engine to prefer for this application (one of: erb, haml).'

desc <<-DESC
This generates a "prepackaged" (or "opinionated") Merb application that uses ActiveRecord,
TestUnit, helpers, assets, mailer, caching, slices and merb-auth all out of the box.
DESC

first_argument :name, :required => true, :desc => "Application name"

#
# ==== Common directories & files
#

empty_directory :gems, 'gems'
file :thorfile do |file|
file.source = File.join(common_templates_dir, "merb.thor")
file.destination = "tasks/merb.thor"
end

template :rakefile do |template|
template.source = File.join(common_templates_dir, "Rakefile")
template.destination = "Rakefile"
end

file :gitignore do |file|
file.source = File.join(common_templates_dir, 'dotgitignore')
file.destination = ".gitignore"
end

file :htaccess do |file|
file.source = File.join(common_templates_dir, 'dothtaccess')
file.destination = 'public/.htaccess'
end

file :doctask do |file|
file.source = File.join(common_templates_dir, 'doc.thor')
file.destination = 'tasks/doc.thor'
end

file :prototype do |file|
file.source = File.join(common_templates_dir, 'prototype.js')
file.destination = 'public/javascripts/prototype.js'
end

directory :test_dir do |directory|
dir = testing_framework == :rspec ? "spec" : "test"

directory.source = File.join(source_root, dir)
directory.destination = dir
end

#
# ==== Layout specific things
#

# empty array means all files are considered to be just
# files, not templates
glob! "app"
glob! "autotest"
glob! "config"
glob! "doc", []
glob! "public"
glob! "lib"
glob! "merb"

invoke :layout do |generator|
generator.new(destination_root, options, 'application')
end
end

add 'ar-app', MerbArStackGenerator
end
end


Pretty straightforward, right? You'll see a couple places easily configured to be whatever you like, as well as listing of various files you want to include (like the prototype.js reference).

Beyond that, you'll see inside lib/generators/templates an application directory that holds a directory for the common files you'll use as well as a directory that actually lays out what the generated app will look like.

The final important piece is inside the Rakefile:


gems = [
["merb-core", "~> #{GEM_VERSION}"],
["merb-more", "~> #{GEM_VERSION}"],
["activerecord", "~> 2.1.0"]
]


This array is used later in the rake file by add_dependency. It takes care of bringing in the various gems needed for your stack. Take a look here to see what the official stack depends upon.

Thats pretty much it. There are other pieces that you'll want to configure things (like in the config/init.rb file inside your templates/application/merb_ar_stack folder for instance). I'd recommend forking this repo and tinkering with things a bit to get a feel for how it all fits together.

Things have been a bit busy recently, so I haven't had as much time for blogging as I'd like. I hope to write more in the near future when 1.0 is out and people are looked to really dig into things.