public
Description: RubiGen alternative Generator thingy
Homepage:
Clone URL: git://github.com/jnicklas/templater.git
jnicklas (author)
Mon Jun 30 14:21:10 -0700 2008
commit  cb7a7dc4bdec96858b1c6ee2b37839d88fd24e23
tree    31ffe2610db06a6d3752d695db65eb451c2454a0
parent  cd83247a0dee0a37ba263eda84ec0521dfb1b2ea
name age message
file .gitignore Loading commit data...
file LICENSE
file README
file Rakefile
file TODO
directory lib/
directory spec/
README
= Templater

Templater is a system for generating files. Templater has the ability to both copy files from A to B and also to render 
templates using ERB. Templater consists of four parts:

- Actions (Files and Templates)
- Generators
- Manifolds
- The command line interface

Each manifold has many generator, and each generator has many actions.

== Example

This is how to create a very simple system for generating things:

    module MyGenerators

      extend Templater::Manifold

      class BlogGenerator < Templater::Generator

        def self.source_root
          File.join(File.dirname(__FILE__), 'templates')
        end

        template :blog, 'blog.rb'
        file :me, 'me.jpg'

      end

      class WikiGenerator < Templater::Generator

        def self.source_root
          File.join(File.dirname(__FILE__), 'templates')
        end

        template :wiki, 'wiki.rb'
        file :img, 'wiki.jpg'

      end
      
      add :blog, BlogGenerator
      add :wiki, WikiGenerator

    end

    MyGenerators.run_cli Dir.pwd, 'my_generators', '0.1', ARGV
  
The generator classes override the source_root method to specify where templates will be located. All subclasses of 
Templater::Generator that have any actions must do this. The +template+ and +file+ methods add actions to the generator. 
In the first case, a template that is rendered with ERB and then put in its destination location, in the other case a 
file that is copied.

The generators are added to the manifold, and assigned the names 'wiki' and 'blog'.

Neither manifolds or generators actually do anything by themselves, they are just abstract represenations. The last line 
invokes the command-line-interface, which fetches the desired generator, tells it to render its templates and checks 
with the user if there are any problems. The generators can easily be used without the command-line-interface, so it is 
easy to construct an alternative interface.

== Invoking other generators

Generators can invoke other generators, a WikiBlog generator that creates both a Wiki and a Blog could look like this:

    module MyGenerators

      extend Templater::Manifold

      class WikiBlogGenerator < Templater::Generator

        invoke :wiki
        invoke :blog

      end
      
      add :wiki_blog, WikiBlogGenerator

    end
    
It needs to source_root, since it has no actions. Not here that the generators are invoked by their name in the 
manifold, *not* by their class name. This gives the system a great deal of flexibility.

== Automatically adding actions

It can get tedious to declare each action, instead you can search in a given directory and automatically add all files 
to your generator, this is done with the glob! function.

    class MyGenerator < Templater::Generator
     
      def self.source_root
        File.join(File.dirname(__FILE__), 'templates')
      end
      
      glob!
    
    end
    
This will search the source root and add all files as actions.

== Templates

There are a lot of ways of adding templates:

    template :one_argument, 'source_and_destination.rb'
  
    template :two_arguments, 'source.rb', 'destination.rb'
  
    template :block do
      source('source.rb')
      destination(some_instance_method)
    end
  
    template :expression, 'source.rb' '%some_instance_method%.rb'

In the last example, the characters enclosed in percentage signs will be replaced with the results of the instance 
method +some_instance_method+

Inside the templates normal ERB can be used. The templates are rendered in the same context as the generator instance, 
so generator instance methods can be called from inside the template.

    <% if name %>
    puts "My name is <%= name %>"
    <% else %>
    puts "I have no name"
    <% end %>
    
IF you need to render templates where the result should contain actual erb, simply use a double percentage sign, this 
will prevent the statement from being executed.

    <%= 2 + 2 %>
    <%%= 2 + 2 %>

will result in

    4
    <%= 2 + 2 %>

== An advanced example

A generator for creating a model class, such as it used by Merb or Rails, could look like this:

    module Merb::Generators
  
      class ModelGenerator < ComponentGenerator

        def self.source_root
          File.join(super, 'model')
        end
    
        desc <<-DESC
          This is a model generator
        DESC
    
        option :testing_framework, :desc => 'Specify which testing framework to use (spec, test_unit)'
        option :orm, :desc => 'Specify which Object-Relation Mapper to use (none, activerecord, datamapper, sequel)'
    
        first_argument :name, :required => true
        second_argument :attributes, :as => :hash, :default => {}
    
        invoke :migration do |generator|
          generator.new(destination_root, options.merge(:model => true), name, attributes)
        end
    
        template :model, :orm => :none do
          source('model.rbt')
          destination('app/models/' + file_name + '.rb')
        end
    
        template :model_activerecord, :orm => :activerecord do
          source('model_activerecord.rbt')
          destination('app/models/' + file_name + '.rb')
        end
    
        template :model_datamapper, :orm => :datamapper do
          source('model_datamapper.rbt')
          destination('app/models/' + file_name + '.rb')
        end
    
        template :model_sequel, :orm => :sequel do
          source('model_sequel.rbt')
          destination('app/models/' + file_name + '.rb')
        end
    
        template :spec, :testing_framework => :rspec do
          source('spec.rbt')
          destination('spec/models/' + file_name + '_spec.rb')
        end
    
        template :test_unit, :testing_framework => :test_unit do
          source('test_unit.rbt')
          destination('test/models/' + file_name + '_test.rb')
        end
    
        def class_name
          self.name.camel_case
        end
    
        def test_class_name
          self.class_name + "Test"
        end
    
        def file_name
          self.name.snake_case
        end
    
      end
  
      add :model, ModelGenerator
  
    end