Class: Templater::Generator

Inherits:
Object
  • Object
show all
Includes:
CaptureHelpers
Defined in:
lib/templater/generator.rb

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from CaptureHelpers

#_erb_buffer, #capture, #concat

Constructor Details

#initialize(destination_root, options = {}, *args) ⇒ Generator

Create a new generator. Checks the list of arguments agains the requirements set using argument.

Parameters

destination_root<String>

The destination, where the generated files will be put.

options<Hash=> Symbol>

Options given to this generator.

*arguments<String>

The list of arguments. These must match the declared requirements.

Raises

Templater::ArgumentError

If the arguments are invalid



452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
# File 'lib/templater/generator.rb', line 452

def initialize(destination_root, options = {}, *args)
  @destination_root = destination_root
  @arguments = []
  @options = options

  # Initialize options to their default values.
  self.class.options.each do |option|
    @options[option.name] ||= option.options[:default]
  end

  args.each_with_index do |arg, n|
    set_argument(n, arg)
  end

  self.class.arguments.each_with_index do |argument, i|
    # Initialize arguments to their default values.
    @arguments[i] ||= argument.options[:default]
    # Check if all arguments are valid.
    argument.valid?(@arguments[i])
  end
end

Class Attribute Details

.manifoldObject

Returns the value of attribute manifold.



11
12
13
# File 'lib/templater/generator.rb', line 11

def manifold
  @manifold
end

Instance Attribute Details

#argumentsObject

Instance methods



441
442
443
# File 'lib/templater/generator.rb', line 441

def arguments
  @arguments
end

#destination_rootObject

Returns the destination root that is given to the generator on initialization. If the generator is a command line program, this would usually be Dir.pwd.

Returns

String

The destination root



441
442
443
# File 'lib/templater/generator.rb', line 441

def destination_root
  @destination_root
end

#optionsObject

Instance methods



441
442
443
# File 'lib/templater/generator.rb', line 441

def options
  @options
end

Class Method Details

.actionsObject

Returns an Hash that maps the type of action to a list of ActionDescriptions.

Returns

HashActionDescription

A Hash of actions



50
# File 'lib/templater/generator.rb', line 50

def actions; @actions ||= {} end

.argument(n, name, options = {}, &block) ⇒ Object

Assign a name to the n:th argument that this generator takes. An accessor with that name will automatically be added to the generator. Options can be provided to ensure the argument conforms to certain requirements. If a block is provided, when an argument is assigned, the block is called with that value and if :invalid is thrown, a proper error is raised

Parameters

n<Integer>

The index of the argument that this describes

name<Symbol>

The name of this argument, an accessor with this name will be created for the argument

options<Hash>

Options for this argument

&block<Proc>

Is evaluated on assignment to check the validity of the argument

Options (opts)

:default<Object>

Specify a default value for this argument

:as<Symbol>

If set to :hash or :array, this argument will ‘consume’ all remaining arguments and bundle them

Use this only for the last argument to this generator.
:required<Boolean>

If set to true, the generator will throw an error if it initialized without this argument

:desc<Symbol>

Provide a description for this argument



108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/templater/generator.rb', line 108

def argument(n, name, options={}, &block)
  self.arguments[n] = ArgumentDescription.new(name.to_sym, options, &block)
  class_eval <<-CLASS
    def #{name}
      get_argument(#{n})
    end

    def #{name}=(arg)
      set_argument(#{n}, arg)
    end
  CLASS
end

.argumentsObject

Returns an array of hashes, where each hash describes a single argument.

Returns

Array

A list of arguments



18
# File 'lib/templater/generator.rb', line 18

def arguments; @arguments ||= []; end

.desc(text = nil) ⇒ Object

If the argument is omitted, simply returns the description for this generator, otherwise sets the description to the passed string.

Parameters

text<String>

A description

Returns

String

The description for this generator



85
86
87
88
# File 'lib/templater/generator.rb', line 85

def desc(text = nil)
  @text = text.realign_indentation if text
  return @text
end

.directoriesObject

Returns an array of ActionDescriptions, where each describes a single directory.

Returns

Array

A list of file descriptions.



68
# File 'lib/templater/generator.rb', line 68

def directories; actions[:directories] ||= []; end

.directory(name, *args, &block) ⇒ Object

Adds a directory that is copied directly. This method is usedful when globbing is not exactly what you want, or you need to access options to decide what source or destination should be.

Parameters

name<Symbol>

The name of this template

source<String>

The source template, can be omitted

destination<String>

The destination where the result will be put.

options<Hash>

Options for this template

&block<Proc>

A block to execute when the generator is instantiated

Options

:before<Symbol>

Name of a method to execute before this template is invoked

:after<Symbol>

Name of a method to execute after this template is invoked



277
278
279
280
281
282
283
284
285
286
287
# File 'lib/templater/generator.rb', line 277

def directory(name, *args, &block)
  options = args.last.is_a?(Hash) ? args.pop : {}
  source, destination = args
  source, destination = source, source if args.size == 1

  directories << ActionDescription.new(name, options) do |generator|
    directory = Actions::Directory.new(generator, name, source, destination, options)
    generator.instance_exec(directory, &block) if block
    directory
  end
end

.directory_list(list) ⇒ Object

An easy way to add many non-rendering templates to a generator. The provided list can be either an array of Strings or a Here-Doc with templates on individual lines.

Parameters

list<String|Array>

A list of non-rendering templates to be added to this generator

Examples

class MyGenerator < Templater::Generator
  directory_list <<-LIST
    path/to/directory
    another/directory
  LIST
  directory_list ['a/third/directory', 'and/a/fourth']
end


373
374
375
376
377
378
379
# File 'lib/templater/generator.rb', line 373

def directory_list(list)
  list = list.to_lines if list.is_a?(String)
  list.each do |item|
    item = item.to_s.chomp.strip
    self.directory(item.gsub(/[\.\/]/, '_').to_sym, item)
  end
end

.empty_directoriesObject

Returns an array of ActionDescriptions, where each describes a single empty directory created by generator.

Returns

Array

A list of empty directory descriptions.



74
# File 'lib/templater/generator.rb', line 74

def empty_directories; actions[:empty_directories] ||= []; end

.empty_directory(name, *args, &block) ⇒ Object

Adds an empty directory that will be created when the generator is run.

Parameters

name<Symbol>

The name of this empty directory

destination<String>

The destination where the empty directory will be created

options<Hash>

Options for this empty directory

&block<Proc>

A block to execute when the generator is instantiated

Options

:before<Symbol>

Name of a method to execute before this template is invoked

:after<Symbol>

Name of a method to execute after this template is invoked



300
301
302
303
304
305
306
307
308
309
# File 'lib/templater/generator.rb', line 300

def empty_directory(name, *args, &block)
  options = args.last.is_a?(Hash) ? args.pop : {}
  destination = args.first

  empty_directories << ActionDescription.new(name, options) do |generator|
    directory = Actions::EmptyDirectory.new(generator, name, destination, options)
    generator.instance_exec(directory, &block) if block
    directory
  end
end

.file(name, *args, &block) ⇒ Object

Adds a template that is not rendered using ERB, but copied directly. Unlike Templater::Generator.template this will not append a ‘t’ to the source, otherwise it works identically.

Parameters

name<Symbol>

The name of this template

source<String>

The source template, can be omitted

destination<String>

The destination where the result will be put.

options<Hash>

Options for this template

&block<Proc>

A block to execute when the generator is instantiated

Options

:before<Symbol>

Name of a method to execute before this template is invoked

:after<Symbol>

Name of a method to execute after this template is invoked



251
252
253
254
255
256
257
258
259
260
261
# File 'lib/templater/generator.rb', line 251

def file(name, *args, &block)
  options = args.last.is_a?(Hash) ? args.pop : {}
  source, destination = args
  source, destination = source, source if args.size == 1

  files << ActionDescription.new(name, options) do |generator|
    file = Actions::File.new(generator, name, source, destination, options)
    generator.instance_exec(file, &block) if block
    file
  end
end

.file_list(list) ⇒ Object

An easy way to add many non-rendering templates to a generator. The provided list can be either an array of Strings or a Here-Doc with templates on individual lines.

Parameters

list<String|Array>

A list of non-rendering templates to be added to this generator

Examples

class MyGenerator < Templater::Generator
  file_list <<-LIST
    path/to/file.jpg
    another/file.html.erb
  LIST
  file_list ['a/third/file.gif', 'and/a/fourth.rb']
end


350
351
352
353
354
355
356
# File 'lib/templater/generator.rb', line 350

def file_list(list)
  list = list.to_lines if list.is_a?(String)
  list.each do |item|
    item = item.to_s.chomp.strip
    self.file(item.gsub(/[\.\/]/, '_').to_sym, item)
  end
end

.filesObject

Returns an array of ActionDescriptions, where each describes a single file.

Returns

Array

A list of file descriptions.



62
# File 'lib/templater/generator.rb', line 62

def files; actions[:files] ||= []; end

.first_argument(*args) ⇒ Object

A shorthand method for adding the first argument, see Templater::Generator.argument



21
# File 'lib/templater/generator.rb', line 21

def first_argument(*args); argument(0, *args); end

.fourth_argument(*args) ⇒ Object

A shorthand method for adding the fourth argument, see Templater::Generator.argument



30
# File 'lib/templater/generator.rb', line 30

def fourth_argument(*args); argument(3, *args); end

.generatorsObject

Returns a list of the classes of all generators (recursively) that are invoked together with this one.

Returns

Array

an array of generator classes.



414
415
416
417
418
419
420
421
422
423
# File 'lib/templater/generator.rb', line 414

def generators
  generators = [self]
  if manifold
    generators += invocations.map do |i|
      generator = manifold.generator(i.name)
      generator ? generator.generators : nil
    end
  end
  generators.flatten.compact
end

.glob!(dir = nil, template_extensions = %w(rb css js erb html yml Rakefile TODO LICENSE README),, options = {}) ⇒ Object

Search a directory for templates and files and add them to this generator. Any file whose extension matches one of those provided in the template_extensions parameter is considered a template and will be rendered with ERB, all others are considered normal files and are simply copied.

A hash of options can be passed which will be assigned to each file and template. All of these options are matched against the options passed to the generator.

Parameters

source<String>

The directory to search in, relative to the source_root, if omitted

the source root itself is searched.
template_destination<Array>

A list of extensions. If a file has one of these

extensions, it is considered a template and will be rendered with ERB.
options<HashSymbol=>Object>

A list of options.



396
397
398
399
400
401
402
403
404
405
406
407
408
# File 'lib/templater/generator.rb', line 396

def glob!(dir = nil, template_extensions = %w(rb css js erb html yml Rakefile TODO LICENSE README), options={})
  ::Dir[::File.join(source_root, dir.to_s, '**/*')].each do |action|
    unless ::File.directory?(action)
      action = action.sub("#{source_root}/", '')

      if template_extensions.include?(::File.extname(action.sub(/\.%.+%$/,''))[1..-1]) or template_extensions.include?(::File.basename(action))
        template(action.downcase.gsub(/[^a-z0-9]+/, '_').to_sym, action, action)
      else
        file(action.downcase.gsub(/[^a-z0-9]+/, '_').to_sym, action, action)
      end
    end
  end
end

.invocationsObject

Returns an array of hashes, where each hash describes a single invocation.

Returns

Array

A list of invocations



43
# File 'lib/templater/generator.rb', line 43

def invocations; @invocations ||= []; end

.invoke(name, options = {}, &block) ⇒ Object

Adds an invocation of another generator to this generator. This allows the interface to invoke any templates in that target generator. This requires that the generator is part of a manifold. The name provided is the name of the target generator in this generator’s manifold.

A hash of options can be passed, all of these options are matched against the options passed to the generator.

If a block is given, the generator class is passed to the block, and it is expected that the block yields an instance. Otherwise the target generator is instantiated with the same options and arguments as this generator.

Parameters

name<Symbol>

The name in the manifold of the generator that is to be invoked

options<Hash>

A hash of requirements that are matched against the generator options

&block<Proc>

A block to execute when the generator is instantiated

Examples

class MyGenerator < Templater::Generator
  invoke :other_generator
end

class MyGenerator < Templater::Generator
  def random
    rand(100000).to_s
  end

  # invoke :other_generator with some
  invoke :other_generator do |generator|
    generator.new(destination_root, options, random)
  end
end

class MyGenerator < Templater::Generator
  option :animal
  # other_generator will be invoked only if the option 'animal' is set to 'bear'
  invoke :other_generator, :animal => :bear
end


183
184
185
# File 'lib/templater/generator.rb', line 183

def invoke(name, options={}, &block)
  self.invocations << InvocationDescription.new(name.to_sym, options, &block)
end

.option(name, options = {}) ⇒ Object

Adds an accessor with the given name to this generator, also automatically fills that value through the options hash that is provided when the generator is initialized.

Parameters

name<Symbol>

The name of this option, an accessor with this name will be created for the option

options<Hash>

Options for this option (how meta!)

Options (opts)

:default<Object>

Specify a default value for this option

:as<Symbol>

If set to :boolean provides a hint to the interface using this generator.

:desc<Symbol>

Provide a description for this option



132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/templater/generator.rb', line 132

def option(name, options={})
  self.options << Description.new(name.to_sym, options)
  class_eval <<-CLASS
    def #{name}
      get_option(:#{name})
    end

    def #{name}=(arg)
      set_option(:#{name}, arg)
    end
  CLASS
end

.optionsObject

Returns an array of options, where each hash describes a single option.

Returns

Array

A list of options



37
# File 'lib/templater/generator.rb', line 37

def options; @options ||= []; end

.second_argument(*args) ⇒ Object

A shorthand method for adding the second argument, see Templater::Generator.argument



24
# File 'lib/templater/generator.rb', line 24

def second_argument(*args); argument(1, *args); end

.source_rootObject

This should return the directory where source templates are located. This method must be overridden in any Generator inheriting from Templater::Source.

Raises

Templater::SourceNotSpecifiedError

Always raises this error, so be sure to override this method.



430
431
432
# File 'lib/templater/generator.rb', line 430

def source_root
  raise Templater::SourceNotSpecifiedError, "Subclasses of Templater::Generator must override the source_root method, to specify where source templates are located."
end

.template(name, *args, &block) ⇒ Object

Adds a template to this generator. Templates are named and can later be retrieved by that name. Templates have a source and a destination. When a template is invoked, the source file is rendered, passing through ERB, and the result is copied to the destination. Source and destination can be specified in different ways, and are always assumed to be relative to source_root and destination_root.

If only a destination is given, the source is assumed to be the same destination, only appending the letter ‘t’, so a destination of ‘app/model.rb’, would assume a source of ‘app/model.rbt’

Source and destination can be set in a block, which makes it possible to call instance methods to determine the correct source and/or desination.

A hash of options can be passed, these options are matched against the options passed to the generator. Some special options, for callbacks for example, are also supported, see below.

Parameters

name<Symbol>

The name of this template

source<String>

The source template, can be omitted

destination<String>

The destination where the result will be put.

options<Hash>

Options for this template

&block<Proc>

A block to execute when the generator is instantiated

Options

:before<Symbol>

Name of a method to execute before this template is invoked

:after<Symbol>

Name of a method to execute after this template is invoked

Examples

class MyGenerator < Templater::Generator
  def random
    rand(100000).to_s
  end

  template :one, 'template.rb' # source will be inferred as 'template.rbt'
  template :two, 'source.rbt', 'template.rb' # source expicitly given
  template :three do
    source('source.rbt')
    destination("#{random}.rb")
  end
end


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

def template(name, *args, &block)
  options = args.last.is_a?(Hash) ? args.pop : {}
  source, destination = args
  source, destination = source + 't', source if args.size == 1

  templates << ActionDescription.new(name, options) do |generator|
    template = Actions::Template.new(generator, name, source, destination, options)
    generator.instance_exec(template, &block) if block
    template
  end
end

.template_list(list) ⇒ Object

An easy way to add many templates to a generator, each item in the list is added as a template. The provided list can be either an array of Strings or a Here-Doc with templates on individual lines.

Parameters

list<String|Array>

A list of templates to be added to this generator

Examples

class MyGenerator < Templater::Generator
  template_list <<-LIST
    path/to/template1.rb
    another/template.css
  LIST
  template_list ['a/third/template.rb', 'and/a/fourth.js']
end


327
328
329
330
331
332
333
# File 'lib/templater/generator.rb', line 327

def template_list(list)
  list = list.to_lines if list.is_a?(String)
  list.each do |item|
    item = item.to_s.chomp.strip
    self.template(item.gsub(/[\.\/]/, '_').to_sym, item)
  end
end

.templatesObject

Returns an array of ActionDescriptions, where each describes a single template.

Returns

Array

A list of template descriptions.



56
# File 'lib/templater/generator.rb', line 56

def templates; actions[:templates] ||= []; end

.third_argument(*args) ⇒ Object

A shorthand method for adding the third argument, see Templater::Generator.argument



27
# File 'lib/templater/generator.rb', line 27

def third_argument(*args); argument(2, *args); end

Instance Method Details

#actions(type = nil) ⇒ Object

Finds and returns all templates and files for this generators whose options match its options.

Parameters

type<Symbol>

The type of actions to look up (optional)

Returns

[Templater::Actions::*]

The found templates and files.



548
549
550
551
552
553
554
# File 'lib/templater/generator.rb', line 548

def actions(type=nil)
  actions = type ? self.class.actions[type] : self.class.actions.values.flatten
  actions.inject([]) do |actions, description|
    actions << description.compile(self) if match_options?(description.options)
    actions
  end
end

#after_deletionObject



608
609
610
# File 'lib/templater/generator.rb', line 608

def after_deletion
  # override in subclasses if necessary
end

#after_generationObject



604
605
606
# File 'lib/templater/generator.rb', line 604

def after_generation
  # override in subclasses if necessary
end

#after_runObject



600
601
602
# File 'lib/templater/generator.rb', line 600

def after_run
  # override in subclasses if necessary
end

#all_actions(type = nil) ⇒ Object

Finds and returns all templates and files for this generators and any of those generators it invokes, whose options match that generator’s options.

Returns

[Templater::Actions::File, Templater::Actions::Template]

The found templates and files.



561
562
563
564
565
# File 'lib/templater/generator.rb', line 561

def all_actions(type=nil)
  all_actions = actions(type)
  all_actions += invocations.map { |i| i.all_actions(type) }
  all_actions.flatten
end

#empty_directoriesObject

Finds and returns all empty directories generator creates.

Returns

[Templater::Actions::File]

The found files.



526
527
528
# File 'lib/templater/generator.rb', line 526

def empty_directories
  actions(:empty_directories)
end

#empty_directory(name) ⇒ Object

Finds and returns all empty directories whose options match the generator options.

Returns

[Templater::Actions::EmptyDirectory]

The found empty directories that generator creates.



502
503
504
# File 'lib/templater/generator.rb', line 502

def empty_directory(name)
  empty_directories.find { |d| d.name == name }
end

#file(name) ⇒ Object

Finds and returns the file of the given name. If that file’s options don’t match the generator options, returns nil.

Parameters

name<Symbol>

The name of the file to look up.

Returns

Templater::Actions::File

The found file.



494
495
496
# File 'lib/templater/generator.rb', line 494

def file(name)
  files.find { |f| f.name == name }
end

#filesObject

Finds and returns all files whose options match the generator options.

Returns

[Templater::Actions::File]

The found files.



518
519
520
# File 'lib/templater/generator.rb', line 518

def files
  actions(:files)
end

#invocationsObject

Finds and returns all templates whose options match the generator options.

Returns

[Templater::Generator]

The found templates.



534
535
536
537
538
539
540
# File 'lib/templater/generator.rb', line 534

def invocations
  return [] unless self.class.manifold

  self.class.invocations.map do |invocation|
    invocation.get(self) if match_options?(invocation.options)
  end.compact
end

#invoke!Object

Invokes the templates for this generator



568
569
570
# File 'lib/templater/generator.rb', line 568

def invoke!
  all_actions.each { |t| t.invoke! }
end

#render!Object

Renders all actions in this generator. Use this to verify that rendering templates raises no errors.

Returns

[String]

The results of the rendered actions



576
577
578
# File 'lib/templater/generator.rb', line 576

def render!
  all_actions.map { |t| t.render }
end

#source_rootObject

Returns this generator’s source root

Returns

String

The source root of this generator.

Raises

Templater::SourceNotSpecifiedError

IF the source_root class method has not been overridden.



587
588
589
# File 'lib/templater/generator.rb', line 587

def source_root
  self.class.source_root
end

#template(name) ⇒ Object

Finds and returns the template of the given name. If that template’s options don’t match the generator options, returns nil.

Parameters

name<Symbol>

The name of the template to look up.

Returns

Templater::Actions::Template

The found template.



482
483
484
# File 'lib/templater/generator.rb', line 482

def template(name)
  templates.find { |t| t.name == name }
end

#templatesObject

Finds and returns all templates whose options match the generator options.

Returns

[Templater::Actions::Template]

The found templates.



510
511
512
# File 'lib/templater/generator.rb', line 510

def templates
  actions(:templates)
end