Class: DirectoryTemplate

Inherits:
Object show all
Defined in:
lib/directory_template.rb,
lib/directory_template/version.rb,
lib/directory_template/processor.rb,
lib/directory_template/blank_slate.rb,
lib/directory_template/erb_template.rb,
lib/directory_template/process_data.rb,
lib/directory_template/processor/erb.rb,
lib/directory_template/processor/stop.rb,
lib/directory_template/processor/format.rb,
lib/directory_template/processor/markdown.rb

Overview

DirectoryTemplate Create directory structures from template directory structures or template data.

Preregistered processors

  • :stop: Stops the preprocessing chain, it’s advised to add that to all files for

    future-proofness.
    

Author:

Version:

  • 1.0.1

Defined Under Namespace

Classes: BlankSlate, ErbTemplate, ProcessData, Processor

Constant Summary collapse

Processors =

All registered processors

[]
StandardPathProcessor =

The standard path processor, just replaces ‘%sprintf_style_variables` with their values.

Processor::Format
DefaultOptions =

The default options used by DirectoryTemplate

{
  :verbose        => false,
  :silent         => false,
  :out            => $stdout,
  :processors     => Processors,
  :path_processor => StandardPathProcessor,
  :source         => '(unknown)',
  :meta           => {},
}
Version =

The version of DirectoryTemplate

Gem::Version.new("1.0.1")

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(data, options = nil) ⇒ DirectoryTemplate

Create a new DirectoryTemplate

Parameters:

  • data (Hash)

    A hash with the two keys :directories and :files, where :directories contains an Array of all directory names, and :files contains a Hash of all file names and their unprocessed content.

  • options (Hash, nil) (defaults to: nil)

    An options hash, @see DirectoryTemplate::DefaultOptions for a list of all available options.

Raises:

  • (ArgumentError)

See Also:

  • To create a DirectoryTemplate from an existing directory structure.
  • To create a DirectoryTemplate from a description in a YAML file.


151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/directory_template.rb', line 151

def initialize(data, options=nil)
  options               = options ? DefaultOptions.merge(options) : DefaultOptions.dup
  @directories          = data[:directories] || []
  @files                = data[:files] || []
  @source               = options.delete(:source)
  @meta                 = options.delete(:meta)
  @verbose              = options.delete(:verbose)
  @silent               = options.delete(:silent)
  @out                  = options.delete(:out)
  @processors           = options.delete(:processors)
  @path_processor       = options.delete(:path_processor)
  @dry_run              = false
  raise ArgumentError, "Unknown options: #{options.keys.join(', ')}" unless options.empty?
end

Instance Attribute Details

#directoriesArray (readonly)

Returns All directories of the template.

Returns:

  • (Array)

    All directories of the template



112
113
114
# File 'lib/directory_template.rb', line 112

def directories
  @directories
end

#dry_run(in_path = '.', env = {}, &on_collision) ⇒ Object (readonly)

Same as #materialize, but doesn’t actually do anything, except print the debug and info messages. It additionally prints an info message, containing the file content of files that would be created.



128
129
130
# File 'lib/directory_template.rb', line 128

def dry_run
  @dry_run
end

#filesHash<String,String> (readonly)

Returns All files of the template and their content.

Returns:

  • (Hash<String,String>)

    All files of the template and their content.



115
116
117
# File 'lib/directory_template.rb', line 115

def files
  @files
end

#metaObject (readonly)

Meta information can be used by processors. There’s no requirements on them, except that the toplevel container is a hash.



109
110
111
# File 'lib/directory_template.rb', line 109

def meta
  @meta
end

#outIO, #puts (readonly)

Returns The object on which info and debug messages are printed.

Returns:

  • (IO, #puts)

    The object on which info and debug messages are printed



124
125
126
# File 'lib/directory_template.rb', line 124

def out
  @out
end

#path_processor#call (readonly)

Returns The path processor used by this template.

Returns:

  • (#call)

    The path processor used by this template.



121
122
123
# File 'lib/directory_template.rb', line 121

def path_processor
  @path_processor
end

#processorsArray<DirectoryTemplate::Processor> (readonly)

Returns The content processors used by this template.

Returns:



118
119
120
# File 'lib/directory_template.rb', line 118

def processors
  @processors
end

#silentObject

When true, will not even output info messages



131
132
133
# File 'lib/directory_template.rb', line 131

def silent
  @silent
end

#verboseObject

When true, will additionally output debug messages



134
135
136
# File 'lib/directory_template.rb', line 134

def verbose
  @verbose
end

Class Method Details

.convert_recursive_structure(current, stack = [], dirs = [], files = {}) ⇒ Object

Converts a recursive hash into a suitable data structure for DirectoryTemplate::new



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/directory_template.rb', line 70

def self.convert_recursive_structure(current, stack=[], dirs=[], files={})
  current.each do |segment, content|
    new_stack = stack+[segment]
    path      = new_stack.join('/')
    case content
      when String,nil
        files[path] = content || ''
      when Hash
        dirs << path
        convert_recursive_structure(content, new_stack, dirs, files)
      else
        raise "Invalid structure"
    end
  end

  return dirs, files
end

.directory(template_path, options = {}) ⇒ Object

Create a DirectoryTemplate from an existing directory structure.



55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/directory_template.rb', line 55

def self.directory(template_path, options={})
  data    = Dir.chdir(template_path) {
    paths   =  Dir['**/{*,.*}']
    paths  -= paths.grep(/(?:^|\/)\.\.?$/)
    directories, files = paths.sort.partition { |path| File.directory?(path) }
    filemap = Hash[files.map { |path| [path, File.read(path)] }]

    {:directories => directories, :files => filemap}
  }

  new(data, options.merge(:source => template_path))
end

.from_hash(hash, options = nil) ⇒ Object

Create a DirectoryTemplate from a nested hash structure. The hash should just be a recursive hash of strings. Use an empty hash to indicate an empty directory. Leaf-strings are considered to be the content of a file. Use nil to indicate an empty file.



92
93
94
95
96
97
# File 'lib/directory_template.rb', line 92

def self.from_hash(hash, options=nil)
  dirs, files = convert_recursive_structure(hash)
  data        = {:directories => dirs, :files => files}

  new(data, options)
end

.register(processor) ⇒ Object

You can register custom processors for templates. They’re triggered based on the pattern. A processor can change the ProcessData struct passed to it, which will be reflected when creating the file or directory

Parameters:



34
35
36
# File 'lib/directory_template.rb', line 34

def self.register(processor)
  Processors << processor
end

.yaml_file(path, options = {}) ⇒ Object

Create a DirectoryTemplate from a YAML file. The yaml should just be a recursive hash of strings. Use an empty hash to indicate an empty directory. Leaf-strings are considered to be the content of a file. Use nil to indicate an empty file.



103
104
105
# File 'lib/directory_template.rb', line 103

def self.yaml_file(path, options={})
  from_hash(YAML.load_file(path), options.merge(:source => template_path))
end

Instance Method Details

#create_directory(path, mode = 0755, &message) ⇒ Object

Note:

The mode param is currently unused.

Create the given directory and emit an info message (unless in dry_run mode).



255
256
257
258
259
260
# File 'lib/directory_template.rb', line 255

def create_directory(path, mode=0755, &message)
  unless File.exist?(path) then
    info(&message)
    FileUtils.mkdir_p(path) unless @dry_run
  end
end

#create_file(path, content = "", mode = 0644, &message) ⇒ Object

Note:

The mode param is currently unused.

Create the given file and emit an info message (unless in dry_run mode).



266
267
268
269
270
271
272
273
274
275
# File 'lib/directory_template.rb', line 266

def create_file(path, content="", mode=0644, &message)
  info(&message)
  if @dry_run then
    info { "  Content:\n#{content.gsub(/^/, '    ')}" }
  else
    File.open(path, 'wb:binary') do |fh|
      fh.write(content)
    end
  end
end

#debugObject

Emit a debug string (the return value of the block). Will only be emitted if DirectoryTemplate#debug is true



297
298
299
# File 'lib/directory_template.rb', line 297

def debug
  @out.puts yield if @verbose
end

#infoObject

Emit an info string (the return value of the block). Will not be emitted if DirectoryTemplate#silent is true



290
291
292
# File 'lib/directory_template.rb', line 290

def info
  @out.puts yield unless @silent
end

#inspectObject

See Object#inspect



303
304
305
# File 'lib/directory_template.rb', line 303

def inspect
  sprintf "#<%s:0x%x source=%p>", self.class, object_id<<1, @source
end

#materialize(in_path = '.', env = {}, &on_collision) ⇒ Object

Creates all the directories and files from the template in the given path.

Parameters:

  • in_path (String) (defaults to: '.')

    The directory within which to generate the structure.

  • env (Hash) (defaults to: {})

    A hash with various information used to generate the structure. Most important one being the :variables value.

Options Hash (env):

  • :variables (Hash)

    A hash with variables available to both, path and content processing

  • :path_variables (Hash)

    A hash with variables available to path processing

  • :content_variables (Hash)

    A hash with variables available to content processing

See Also:



192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/directory_template.rb', line 192

def materialize(in_path='.', env={}, &on_collision)
  in_path = File.expand_path(in_path)
  create_directory(in_path) { "Creating root '#{in_path}'" }

  Dir.chdir(in_path) do
    if @directories.empty? then
      info { "No directories to create" }
    else
      info { "Creating directories" }
      @directories.each do |source_dir_path|
        target_dir_path = process_path(source_dir_path, env)
        create_directory(target_dir_path) { "  #{target_dir_path}" }
      end
    end

    if @files.empty? then
      info { "No files to create" }
    else
      info { "Creating files" }
      @files.each do |source_file_path, content|
        target_file_path  = process_path(source_file_path, env)
        data              = process_content(target_file_path, content, env)
        if File.exist?(data.path) then
          if block_given? && yield(data) then
            create_file(data.path, data.content) { "  #{data.path} (overwrite)" }
          else
            info { "  #{data.path} (exists already)" }
          end
        else
          create_file(data.path, data.content) { "  #{data.path} (new)" }
        end
      end
    end
  end

  self
end

#process_content(path, content, env) ⇒ Object

Preprocesses the given content



238
239
240
241
242
243
244
245
246
247
248
249
# File 'lib/directory_template.rb', line 238

def process_content(path, content, env)
  data = ProcessData.new(self, path, content, env)
  catch(:stop_processing) {
    #p :process_content => path, :available => @processors.size, :processors => processors_for(data).tap { |x| x && x.size }
    while processor = processor_for(data)
      debug { "  -> Applying #{processor.name}" }
      processor.call(data)
    end
  }

  data
end

#process_path(path, env) ⇒ Object

Preprocesses the given path



232
233
234
# File 'lib/directory_template.rb', line 232

def process_path(path, env)
  @path_processor.call(ProcessData.new(self, path, nil, env)).path
end

#processor_for(data) ⇒ Processor?

Returns the processor or nil

Parameters:

  • data (ProcessData)

    The data which the processor should apply to.

Returns:

  • (Processor, nil)

    Returns the processor or nil



283
284
285
# File 'lib/directory_template.rb', line 283

def processor_for(data)
  @processors.find { |processor| processor === data }
end