require 'pathname' require 'fileutils' require 'erb' module Puppet::ModuleTool module Applications class Generator < Application def initialize(full_module_name, options = {}) begin @metadata = Metadata.new(:full_module_name => full_module_name) rescue ArgumentError msg = "Could not generate directory #{full_module_name.inspect}, you must specify a dash-separated username and module name." raise $!, msg, $!.backtrace end super(options) end def skeleton @skeleton ||= Skeleton.new end def get_binding binding end def run if destination.directory? raise ArgumentError, "#{destination} already exists." end Puppet.notice "Generating module at #{Dir.pwd}/#{@metadata.dashed_name}" files_created = [] skeleton.path.find do |path| if path == skeleton destination.mkpath else node = Node.on(path, self) if node node.install! files_created << node.target else Puppet.notice "Could not generate from #{path}" end end end # Return an array of Pathname objects representing file paths of files # and directories just generated. This return value is used by the # module_tool face generate action, and displayed on the console. # # Example return value: # # [ # #<Pathname:puppetlabs-apache>, # #<Pathname:puppetlabs-apache/tests>, # #<Pathname:puppetlabs-apache/tests/init.pp>, # #<Pathname:puppetlabs-apache/spec>, # #<Pathname:puppetlabs-apache/spec/spec_helper.rb>, # #<Pathname:puppetlabs-apache/spec/spec.opts>, # #<Pathname:puppetlabs-apache/README>, # #<Pathname:puppetlabs-apache/Modulefile>, # #<Pathname:puppetlabs-apache/metadata.json>, # #<Pathname:puppetlabs-apache/manifests>, # #<Pathname:puppetlabs-apache/manifests/init.pp" # ] # files_created end def destination @destination ||= Pathname.new(@metadata.dashed_name) end class Node def self.types @types ||= [] end def self.inherited(klass) types << klass end def self.on(path, generator) klass = types.detect { |t| t.matches?(path) } if klass klass.new(path, generator) end end def initialize(source, generator) @generator = generator @source = source end def read @source.read end def target target = @generator.destination + @source.relative_path_from(@generator.skeleton.path) components = target.to_s.split(File::SEPARATOR).map do |part| part == 'NAME' ? @generator.metadata.name : part end Pathname.new(components.join(File::SEPARATOR)) end def install! raise NotImplementedError, "Abstract" end end class DirectoryNode < Node def self.matches?(path) path.directory? end def install! target.mkpath end end class ParsedFileNode < Node def self.matches?(path) path.file? && path.extname == '.erb' end def target path = super path.parent + path.basename('.erb') end def contents template = ERB.new(read) template.result(@generator.send(:get_binding)) end def install! target.open('w') { |f| f.write contents } end end class FileNode < Node def self.matches?(path) path.file? end def install! FileUtils.cp(@source, target) end end end end end