Class: Tap::Generator::Base

Inherits:
Task
  • Object
show all
Extended by:
Helpers
Defined in:
lib/tap/generator/base.rb

Overview

:startdoc:::- Base provides the basic structure of a generator and custom generators inherit from it. Base is patterned after the Ruby on Rails generators, but obviously takes on all the advantages of Tasks.

Usage

Tap generators define a manifest method that defines what files and directories are created by the generator. Then, at execution time, a mixin with the appropriate funtion (ie Generate or Destroy) is overlaid to figure out how to roll those actions forward or backwards.

Generators are identified using the ::generator flag rather than ::task, so that generators are available to the generate/destroy commands and not run.

Typically, generators live in a directory structure like this:

root
|- lib
|   `- sample.rb
|
`- templates
    `- sample
        `- template_file.erb

Tap generators keep templates out of lib and under templates, in a directory is named after the generator class. Generators themselves take the form:

[sample.rb]
require 'tap/generator/base'

# ::generator generates a directory, and two files
#
# An extended description of the
# generator goes here...
#
class Sample < Tap::Generator::Base

  config :key, 'value'       # a sample config

  def manifest(m, *args)
    # make a directory
    m.directory('path/to/dir')

    # make a file
    m.file('path/to/file.txt') do |file|
      file << "some content"
    end

    # template a file
    m.template('path/to/result.txt', 'template_file.erb', config.to_hash)
  end
end

The arguments that a generator receives are specified by manifest (minus the ‘m’ argument which is standard) rather than process. Creating directories and files is straightforward, as above. Template renders the erb source file using attributes specified in the last argument; in the example template uses the generator configurations.

:startdoc:::+

Instance Attribute Summary collapse

Attributes included from Helpers

#helper_registry

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Helpers

cache_helpers, helpers

Constructor Details

#initialize(config = {}, app = Tap::App.current) ⇒ Base

Returns a new instance of Base.



149
150
151
152
153
# File 'lib/tap/generator/base.rb', line 149

def initialize(config={}, app=Tap::App.current)
  super
  @prompt_in = $stdin
  @prompt_out = $stdout
end

Instance Attribute Details

#prompt_inObject

The IO used to pull prompt inputs (default: $stdin)



144
145
146
# File 'lib/tap/generator/base.rb', line 144

def prompt_in
  @prompt_in
end

#prompt_outObject

The IO used to prompt users for input (default: $stdout)



147
148
149
# File 'lib/tap/generator/base.rb', line 147

def prompt_out
  @prompt_out
end

Class Method Details

.build(spec = {}, app = Tap::App.current) ⇒ Object



101
102
103
104
105
106
107
108
109
# File 'lib/tap/generator/base.rb', line 101

def build(spec={}, app=Tap::App.current)
  obj = new(spec['config'] || {}, app)
  
  if mixin = spec['mixin']
    obj.extend app.env.constant(mixin)
  end
  
  obj
end

.convert_to_spec(parser, args) ⇒ Object



111
112
113
114
115
116
# File 'lib/tap/generator/base.rb', line 111

def convert_to_spec(parser, args)
  {
    'config' => parser.nested_config,
    'mixin'  => args.shift
  }
end

.parse_as(mixin, argv = ARGV, app = Tap::App.current, &block) ⇒ Object



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/tap/generator/base.rb', line 78

def parse_as(mixin, argv=ARGV, app=Tap::App.current, &block)
  if argv.empty?
    raise "no generator specified"
  end
  
  if argv[0] == '--help'
    desc = mixin.desc
    lines = ["#{mixin}#{desc.empty? ? '' : ' -- '}#{desc.to_s}"]
    lines << '-' * 80
    lines.concat desc.wrap(77, 2, nil).collect {|line| "  #{line}"}
    lines << '-' * 80
    lines << "usage: tap #{mixin.to_s.underscore} generator *args"
    lines << ''
    lines.concat Tasks::List.new(:types => ['generator']).manifest
    raise lines.join("\n")
  end
  
  argv = argv.dup
  generator = argv.shift
  argv.unshift mixin
  app.env.constant(generator, 'generator').parse(argv, app, &block)
end

Instance Method Details

#actionObject

Returns the action for self (ie :generate or :destroy)

Raises:

  • (NotImplementedError)


249
250
251
# File 'lib/tap/generator/base.rb', line 249

def action
  raise NotImplementedError
end

#directories(root, targets, options = {}) ⇒ Object

Makes (or destroys) the root and each of the targets, relative to root. Options are passed onto directory.



201
202
203
204
205
206
207
# File 'lib/tap/generator/base.rb', line 201

def directories(root, targets, options={})
  results = [directory(root, options)]
  targets.each do |target|
    results << directory(File.join(root, target), options)
  end
  results
end

#directory(target, options = {}) ⇒ Object

Peforms a directory action (ex generate or destroy). Must be overridden by one of the action mixins (ex Generate or Destroy).

Raises:

  • (NotImplementedError)


187
188
189
# File 'lib/tap/generator/base.rb', line 187

def directory(target, options={})
  raise NotImplementedError
end

#file(target, options = {}) ⇒ Object

Peforms a file action (ex generate or destroy). Calls to file specify input for a target by providing a block; the block recieves an IO and pushes content to it. Must be overridden by one of the action mixins (ex Generate or Destroy).

Raises:

  • (NotImplementedError)


195
196
197
# File 'lib/tap/generator/base.rb', line 195

def file(target, options={}) # :yields: io
  raise NotImplementedError
end

#iterate(actions) ⇒ Object

Peforms each of the input actions in order, and collects the results. The process method returns these results.



176
177
178
# File 'lib/tap/generator/base.rb', line 176

def iterate(actions)
  actions.collect {|action| yield(action) }
end

#log_relative(action, path) ⇒ Object

Logs the action with the relative filepath from destination_root to path.



254
255
256
257
# File 'lib/tap/generator/base.rb', line 254

def log_relative(action, path)
  relative_path = destination_root.relative_path(path)
  app.log(action, relative_path || path)
end

#manifest(m, *argv) ⇒ Object

Overridden in subclasses to add actions to the input Manifest. Any arguments passed to process will be passed to manifest unchanged.

Raises:

  • (NotImplementedError)


170
171
172
# File 'lib/tap/generator/base.rb', line 170

def manifest(m, *argv)
  raise NotImplementedError
end

#on(*actions, &block) ⇒ Object

Calls the block when specified by the action for self.



240
241
242
243
244
245
246
# File 'lib/tap/generator/base.rb', line 240

def on(*actions, &block)
  if actions.include?(action)
    block.call
  else
    nil
  end
end

#path(*paths) ⇒ Object

Constructs a path relative to destination_root.



181
182
183
# File 'lib/tap/generator/base.rb', line 181

def path(*paths)
  destination_root.path(*paths)
end

#process(*argv) ⇒ Object

Builds the manifest, then executes the actions of the manifest. Process returns the results of iterate, which normally will be an array of files and directories created (or destroyed) by self.



158
159
160
161
162
163
164
165
# File 'lib/tap/generator/base.rb', line 158

def process(*argv)
  actions = []
  manifest(Manifest.new(actions), *argv)
  
  iterate(actions) do |action, args, block|
    send(action, *args, &block)
  end.compact
end

#template(target, source, attributes = {}, options = {}) ⇒ Object

Makes (or destroys) the target by templating the source using the specified attributes. Source is expanded relative to template_root. Options are passed onto file.



212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/tap/generator/base.rb', line 212

def template(target, source, attributes={}, options={})
  template_path = template_root.path(source)
  templater = Templater.new(File.read(template_path), attributes)
  
  (options[:helpers] || self.class.helpers).each do |helper|
    templater.extend(helper)
  end
  
  file(target, options) do |file| 
    file << templater.build(nil, template_path)
  end
end

#template_filesObject

Yields each source file under template_root to the block, with a target path of the source relative to template_root.



227
228
229
230
231
232
233
234
235
236
237
# File 'lib/tap/generator/base.rb', line 227

def template_files
  targets = []
  template_root.glob('**/*').sort.each do |source|
    next unless File.file?(source)
    
    target = template_root.relative_path(source)
    yield(source, target)
    targets << target
  end
  targets
end