public
Description: A simple and easy to use generator system for Ruby
Homepage: http://www.mackframework.com
Clone URL: git://github.com/markbates/genosaurus.git
Click here to lend your support to: genosaurus and make a donation at www.pledgie.com !
genosaurus / lib / genosaurus.rb
100644 197 lines (174 sloc) 6.7 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
require 'rubygems'
require 'mack_ruby_core_extensions'
require 'fileutils'
require 'erb'
require 'yaml'
require 'erubis'
 
class Genosaurus
 
  include FileUtils
  
  class << self
    
    # Instantiates a new Genosaurus, passing the ENV hash as options into it, runs the generate method, and returns the Genosaurus object.
    def run(options = ENV.to_hash)
      gen = self.new(options)
      gen.generate
      gen
    end
    
  end
  
  # Takes any options needed for this generator. If the generator requires any parameters an ArgumentError exception will be
  # raised if those parameters are found in the options Hash. The setup method is called at the end of the initialization.
  def initialize(options = {})
    @options = options
    self.class.required_params.each do |p|
      raise ::ArgumentError.new("The required parameter '#{p.to_s.upcase}' is missing for this generator!") unless param(p)
    end
    @generator_name = self.class.name
    @generator_name_underscore = @generator_name.underscore
    @templates_directory_path = nil
    @manifest_path = nil
    $".each do |f|
      if f.match(/#{@generator_name_underscore}\.rb$/)
        @templates_directory_path = File.join(File.dirname(f), "templates")
        @manifest_path = File.join(File.dirname(f), "manifest.yml")
      end
    else
      raise "Unable to dynamically figure out your templates_directory_path and manifest_path! Please implement these methods and let Genosaurus know where to find these things. Thanks."
    end
    setup
  end
  
  # Returns the path to the templates directory.
  # IMPORTANT: The location of the templates_directory_path is VERY important! Genosaurus will attempt to find this location
  # automatically, HOWEVER, if there is a problem, or you want to be special, you can override this method in your generator
  # and have it return the correct path.
  def templates_directory_path
    @templates_directory_path
  end
  
  # Returns the path to the manifest.yml. This is only used if you have a manifest.yml file, if there is no file, or this
  # method returns nil, then an implied manifest is used based on the templates_directory_path contents.
  # IMPORTANT: Genosaurus will attempt to find this location automatically, HOWEVER, if there is a problem, or you want to
  # be special, you can override this method in your generator and have it return the correct path.
  def manifest_path
    @manifest_path
  end
  
  # To be overridden in subclasses to do any setup work needed by the generator.
  def setup
    # does nothing, unless overridden in subclass.
  end
  
  # To be overridden in subclasses to do work before the generate method is run.
  def before_generate
  end
  
  # To be overridden in subclasses to do work after the generate method is run.
  # This is a simple way to call other generators.
  def after_generate
  end
  
  # Returns the manifest for this generator, which is used by the generate method to do the dirty work.
  # If there is a manifest.yml, defined by the manifest_path method, then the contents of that file are processed
  # with ERB and returned. If there is not manifest.yml then an implied manifest is generated from the contents
  # of the templates_directory_path.
  def manifest
    ivar_cache do
      if File.exists?(manifest_path)
        # run using the yml file
        template = ERB.new(File.open(manifest_path).read, nil, "->")
        man = YAML.load(template.result(binding))
      else
        files = Dir.glob(File.join(templates_directory_path, "**/*.template"))
        man = {}
        files.each_with_index do |f, i|
          output_path = f.gsub(templates_directory_path, "")
          output_path.gsub!(".template", "")
          output_path.gsub!(/^\//, "")
          man["template_#{i+1}"] = {
            "type" => File.directory?(f) ? "directory" : "file",
            "template_path" => f,
            "output_path" => Erubis::Eruby.new(output_path, :pattern => '% %').result(binding)
          }
        end
      end
      # puts man.inspect
      man
    end
  end
  
  # Used to define arguments that are required by the generator.
  def self.require_param(*args)
    required_params << args
    required_params.flatten!
  end
  
  # Returns the required_params array.
  def self.required_params
    @required_params ||= []
  end
 
  # Returns a parameter from the initial Hash of parameters.
  def param(key)
    (@options[key.to_s.downcase] ||= @options[key.to_s.upcase])
  end
 
  # Takes an input_file runs it through ERB and
  # saves it to the specified output_file. If the output_file exists it will
  # be skipped. If you would like to force the writing of the file, use the
  # :force => true option.
  def template(input_file, output_file, options = @options)
    output_file = template_copy_common(output_file, options)
    unless output_file.nil?
      File.open(output_file, "w") {|f| f.puts ERB.new(File.open(input_file).read, nil, "->").result(binding)}
      puts "Wrote: #{output_file}"
    end
  end
  
  # Creates the specified directory.
  def directory(output_dir, options = @options)
    if $genosaurus_output_directory
      output_dir = File.join($genosaurus_output_directory, output_dir)
    end
    if File.exists?(output_dir)
      puts "Exists: #{output_dir}"
      return
    end
    mkdir_p(output_dir)
    puts "Created: #{output_dir}"
  end
  
  def copy(input_file, output_file, options = @options)
    output_file = template_copy_common(output_file, options)
    FileUtils.cp(input_file, output_file)
    puts "Copied: #{output_file}"
  end
  
  # This does the dirty work of generation.
  def generate
    generate_callbacks do
      manifest.each_value do |info|
        case info["type"]
        when "file"
          template(info["template_path"], info["output_path"])
        when "directory"
          directory(info["output_path"])
        when "copy"
          copy(info["template_path"], info["output_path"])
        else
          raise "Unknown 'type': #{info["type"]}!"
        end
      end
    end
  end
  
  def method_missing(sym, *args)
    p = param(sym)
    return p if p
    raise NoMethodError.new(sym)
  end
  
  private
  def generate_callbacks
    before_generate
    yield
    after_generate
  end
  
  def template_copy_common(output_file, options)
    if File.exists?(output_file)
      unless options[:force]
        puts "Skipped: #{output_file}"
        return nil
      end
    end
    # incase the directory doesn't exist, let's create it.
    directory(File.dirname(output_file))
    if $genosaurus_output_directory
      output_file = File.join($genosaurus_output_directory, output_file)
    end
    output_file
  end
  
end # Genosaurus