fun_with_templates 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.document +5 -0
- data/CHANGELOG.markdown +6 -0
- data/Gemfile +18 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +67 -0
- data/Rakefile +45 -0
- data/VERSION +1 -0
- data/lib/fun_with/core_extensions/kernel.rb +18 -0
- data/lib/fun_with/templates/directory_builder.rb +36 -0
- data/lib/fun_with/templates/filename_var_data.rb +42 -0
- data/lib/fun_with/templates/template_evaluator.rb +307 -0
- data/lib/fun_with_templates.rb +4 -0
- data/test/helper.rb +57 -0
- data/test/templates/00/a.rb.template +7 -0
- data/test/templates/00/dir/subdir/style.css +4 -0
- data/test/templates/00/seqfiles/page%0000i%.html.template +12 -0
- data/test/templates/00/seqfiles/page%j%.html.template +12 -0
- data/test/templates/00/seqfiles/page_about_%critter_name%.html.template +15 -0
- data/test/templates/01/%string.length%/%string%-%string.length%.nontemplate +1 -0
- data/test/templates/01/%string.length%/%string%-%string.length%.txt.template +1 -0
- data/test/templates/02/%class%.rb.template +6 -0
- data/test/templates/03/coordinates.%i%-%j%-%k%.html.template +14 -0
- data/test/templates/03/dir%0000i%/dir%0000j%/file%0000k%.py.template +1 -0
- data/test/templates/03/index.html.template +22 -0
- data/test/templates/epf/book/afterword.markdown +4 -0
- data/test/templates/epf/book/chapter-%0000chapter%.markdown.template +4 -0
- data/test/templates/epf/book/cover.xhtml.template +13 -0
- data/test/templates/epf/book/foreword.markdown +6 -0
- data/test/templates/epf/book/images/cover.png +0 -0
- data/test/templates/epf/book/stylesheets/stylesheet.css +2 -0
- data/test/templates/epf/book/title_page.markdown.template +5 -0
- data/test/templates/epf/notes/character.%character.name_for_file%.markdown.template +15 -0
- data/test/templates/epf/notes/images/cover.png +0 -0
- data/test/templates/epf/notes/stylesheets/stylesheet.css +2 -0
- data/test/templates/epf/settings/actions/local_action.rb.example +14 -0
- data/test/templates/epf/settings/config.rb.template +52 -0
- data/test/templates/epf/settings/htmlizers.rb +83 -0
- data/test/templates/epf/settings/wordcount.template +6 -0
- data/test/test_directory_builder.rb +24 -0
- data/test/test_filename_var_data.rb +51 -0
- data/test/test_fun_with_templates.rb +21 -0
- data/test/test_parse_filename_vars.rb +25 -0
- data/test/test_string_templates.rb +14 -0
- data/test/test_write_example_templates.rb +149 -0
- metadata +202 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
ZDQ2ZTFmYzJhZTUxMDRhYWE1OTNhODUxZTkyYTQ2MmU0ODU4OGE1ZQ==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
OTQ0ZDAyYmJjODYxNDZmZDE1NjY4ZDgxZmE5M2ExODdiYjM4YTA2MQ==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
MTZmNWE5MTE4YTlmZjRjNmNiZjgzYTc3ZDQzYTcxMTI3MGJkOWFhYTE5ZGI5
|
10
|
+
OWZjMjFkZjhjNGMxYmI2MTZkNDQzM2NlNzhlN2NjNTgwMjViMTRmMTM3ZTcw
|
11
|
+
ZDhmZDBkMDBkMDViMDFjZjk4MmY5ZjEzYTU3YThjNDIxODQxZGY=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
NGI3MjNlZDI0ZjczYWE2MzczZmZmNDEyZWVkODdmZmIxOWU3ZjEwOTRhN2Q3
|
14
|
+
YmFiNWVjMDJjNzJmNTk5YWY3MzY3MzRmYjAxNDFlZWE3OTNjYmY2Y2Y0NjZm
|
15
|
+
MTFiYTc4OTA2ODkyZDFlYWQyMWUyNjA1OTA1YTY4NmI2NzMwNjI=
|
data/.document
ADDED
data/CHANGELOG.markdown
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
# Add dependencies required to use your gem here.
|
3
|
+
# Example:
|
4
|
+
# gem "activesupport", ">= 2.3.5"
|
5
|
+
|
6
|
+
# Add dependencies to develop your gem here.
|
7
|
+
# Include everything needed to run rake, tests, features, etc.
|
8
|
+
group :development do
|
9
|
+
gem "shoulda", "~> 3.5"
|
10
|
+
gem "rdoc", "~> 3.12"
|
11
|
+
gem "bundler", "~> 1.5"
|
12
|
+
gem "jeweler", "~> 2.0"
|
13
|
+
gem "debugger", "~> 1.6"
|
14
|
+
gem "fun_with_testing", "~> 0.0"
|
15
|
+
end
|
16
|
+
|
17
|
+
gem 'fun_with_gems', "~> 0.0"
|
18
|
+
gem 'fun_with_string_colors'
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2013 Bryce Anderson
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
= fun_with_templates
|
2
|
+
|
3
|
+
A simple interface for filling in and cloning an entire directory. The entire directory shares a single set of variables.
|
4
|
+
|
5
|
+
For now you feed the function the template directory, the destination you want it cloned into.
|
6
|
+
The vars is a hash, with symbols as the variable names. If you declare { :monkey_name => "Bongo" },
|
7
|
+
you can use <%= @monkey_name %> in any of the templates, and '%monkey_name%' in the filename.
|
8
|
+
|
9
|
+
If a file is named with the '.template' or '.fwtemplate' file extension, it will undergo variable substitution.
|
10
|
+
|
11
|
+
If the variable is an Array or Range or other enumeratableable object, and the filename has a variable embedded in it, the template will be evaluated once for each item enumerated, leading to multiple destination files. If the variable name isn't in the filename, the whole enumerable object gets passed in. This is weird, but it gives one advantage: in the templates where you don't mention the variable in the filename, you can access the whole range.
|
12
|
+
|
13
|
+
Filename variables:
|
14
|
+
|
15
|
+
A few examples:
|
16
|
+
- %i% : filled in. If the variable :i is enumerable, then the template gets evaluated multiple times
|
17
|
+
- %character.name% : The object { :character => Character.new("barry") } gets .name() called on it.
|
18
|
+
You can also make :character a hash with the :name key ( {:character => {:name => "barry" } } ), and 'barry' will get substituted in.
|
19
|
+
- %000k% : The number gets leading zeros.
|
20
|
+
- %hello_world% : You can use underscores.
|
21
|
+
|
22
|
+
Example: chapter-%0000chap_count%.markdown.template_seq
|
23
|
+
==> chapter-0001.markdown
|
24
|
+
==> chapter-0002.markdown
|
25
|
+
==> chapter-0003.markdown
|
26
|
+
...
|
27
|
+
==> chapter-0099.markdown
|
28
|
+
|
29
|
+
In this case, :chap_count would be the key in vars.
|
30
|
+
|
31
|
+
Example (no leading 0s): chapter%i%.html.template
|
32
|
+
==> chapter1.html
|
33
|
+
==> chapter2.html
|
34
|
+
==> chapter3.html
|
35
|
+
...
|
36
|
+
==> chapter99.html
|
37
|
+
|
38
|
+
The point of the leading zeros is to specify that the file's number will be padded with that number of zeros.
|
39
|
+
|
40
|
+
When declaring this in vars, you can use any of the following:
|
41
|
+
vars = {:i => 0..19} (will generate 0 - 19)
|
42
|
+
vars = {:i => %w(ocelot mouse kitten puppy velociraptor)} # @i will be filled in with the given strings. leading 0s will be ignored, because the items in the array aren't numbers.
|
43
|
+
|
44
|
+
The directory structure is cloned, though variable substitution can be done in directories as well as files. If the variable embedded in the directory is an Array or Range object, it will create a separate directory for each item. Beware combinatorial explosions.
|
45
|
+
|
46
|
+
Files named with extensions other than .fwtemplate and .template will simply be copied as-is.
|
47
|
+
|
48
|
+
|
49
|
+
== Feature ideas
|
50
|
+
|
51
|
+
.. may be made more flexible later, with an ability to specify special behaviors based on file-matching regexes? Also, maybe having template evaluators other than ERB would be a good idea. I wonder if there's demand for it.
|
52
|
+
|
53
|
+
== Contributing to fun_with_templates
|
54
|
+
|
55
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
56
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
57
|
+
* Fork the project.
|
58
|
+
* Start a feature/bugfix branch.
|
59
|
+
* Commit and push until you are happy with your contribution.
|
60
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
61
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
62
|
+
|
63
|
+
== Copyright
|
64
|
+
|
65
|
+
Copyright (c) 2013 Bryce Anderson. See LICENSE.txt for
|
66
|
+
further details.
|
67
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
+
gem.name = "fun_with_templates"
|
18
|
+
gem.homepage = "http://github.com/darthschmoo/fun_with_templates"
|
19
|
+
gem.license = "MIT"
|
20
|
+
gem.summary = "Templates made stupid"
|
21
|
+
gem.description = "A simple approach to 'fill in the blank' file templates, which may be useful for some tasks."
|
22
|
+
gem.email = "keeputahweird@gmail.com"
|
23
|
+
gem.authors = ["Bryce Anderson"]
|
24
|
+
# dependencies defined in Gemfile
|
25
|
+
end
|
26
|
+
Jeweler::RubygemsDotOrgTasks.new
|
27
|
+
|
28
|
+
require 'rake/testtask'
|
29
|
+
Rake::TestTask.new(:test) do |test|
|
30
|
+
test.libs << 'lib' << 'test'
|
31
|
+
test.pattern = 'test/**/test_*.rb'
|
32
|
+
test.verbose = true
|
33
|
+
end
|
34
|
+
|
35
|
+
task :default => :test
|
36
|
+
|
37
|
+
require 'rdoc/task'
|
38
|
+
Rake::RDocTask.new do |rdoc|
|
39
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
40
|
+
|
41
|
+
rdoc.rdoc_dir = 'rdoc'
|
42
|
+
rdoc.title = "fun_with_templates #{version}"
|
43
|
+
rdoc.rdoc_files.include('README*')
|
44
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
45
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.3
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Kernel
|
2
|
+
def template_evaluator_set_local_vars( locals = {}, &block )
|
3
|
+
old_local_vars = {}
|
4
|
+
|
5
|
+
for k, v in locals
|
6
|
+
var = :"@#{k}"
|
7
|
+
old_local_vars[k] = instance_variable_get( var )
|
8
|
+
instance_variable_set( var, v )
|
9
|
+
end
|
10
|
+
|
11
|
+
yield
|
12
|
+
ensure # make all as it once was
|
13
|
+
for k, v in old_local_vars
|
14
|
+
var = :"@#{k}"
|
15
|
+
instance_variable_set( var, v )
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# Add-on to FunWith::Files # Not going to implement just now. Having trouble deciding what it ought to do.
|
2
|
+
# module FunWith
|
3
|
+
# module Files
|
4
|
+
# class DirectoryBuilder
|
5
|
+
# def template( *args, &block )
|
6
|
+
# vars = args.last.is_a?(Hash) ? args.pop : {}
|
7
|
+
#
|
8
|
+
# if args.length == 2
|
9
|
+
# src = args.first
|
10
|
+
# dst = args.last
|
11
|
+
# elsif args.length == 1
|
12
|
+
# src = args.first
|
13
|
+
# dst = self.current_path
|
14
|
+
# else
|
15
|
+
# raise ArgumentError.new( "Wrong number of arguments: template(src, dst, variables)" )
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# if block_given?
|
19
|
+
# collector = FunWith::Templates::VarCollector.collect do |c|
|
20
|
+
# yield c
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# vars.merge!( collector )
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# FunWith::Templates::TemplateEvaluator.result_to_file( src, dst, vars )
|
27
|
+
#
|
28
|
+
# # if src.directory?
|
29
|
+
# # FunWith::Templates::TemplateEvaluator.evaluate( src, dest, vars )
|
30
|
+
# # elsif src.file?
|
31
|
+
# # FunWith::Templates::TemplateEvaluator.result_to_file( src, dest, vars )
|
32
|
+
# # end
|
33
|
+
# end
|
34
|
+
# end
|
35
|
+
# end
|
36
|
+
# end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module FunWith
|
2
|
+
module Templates
|
3
|
+
class FilenameVarData
|
4
|
+
attr_accessor :num_format, :var_name, :method_to_call
|
5
|
+
|
6
|
+
def initialize( var_name, method_to_call = nil, num_format = nil )
|
7
|
+
@num_format = num_format
|
8
|
+
@var_name = var_name.to_sym
|
9
|
+
@method_to_call = method_to_call.to_sym if method_to_call
|
10
|
+
end
|
11
|
+
|
12
|
+
alias :name :var_name
|
13
|
+
|
14
|
+
def original_string
|
15
|
+
"%#{self.num_format}#{self.name}" + (self.method_to_call ? ".#{method_to_call}" : "") + "%"
|
16
|
+
end
|
17
|
+
|
18
|
+
def fill_in_path( template_path, substitution )
|
19
|
+
return nil if substitution.nil?
|
20
|
+
substitution = call_method_on( substitution )
|
21
|
+
substitution = self.num_format ? sprintf("%0#{self.num_format.length}i", substitution ) : substitution
|
22
|
+
|
23
|
+
template_path.gsub( original_string, substitution.to_s )
|
24
|
+
end
|
25
|
+
|
26
|
+
# If necessary. If
|
27
|
+
def call_method_on( obj )
|
28
|
+
return obj if self.method_to_call.nil?
|
29
|
+
return obj[self.method_to_call] if obj.is_a?(Hash) && obj.has_key?(self.method_to_call)
|
30
|
+
return obj.send( self.method_to_call )
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.fill_in_path( template_path, var_data, vars )
|
34
|
+
var_data.each do |var_dat|
|
35
|
+
template_path = var_dat.fill_in_path( template_path, vars[var_dat.name] )
|
36
|
+
end
|
37
|
+
|
38
|
+
template_path
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,307 @@
|
|
1
|
+
module FunWith
|
2
|
+
module Templates
|
3
|
+
class TemplateEvaluator
|
4
|
+
attr_reader :content, :vars, :path, :children
|
5
|
+
attr_accessor :parent
|
6
|
+
|
7
|
+
TEMPLATE_FILE_REGEX = /\.(fw)?template$/
|
8
|
+
VARIABLE_SUBSTITUTION_REGEX = /%(0+)?([a-zA-Z][A-Za-z0-9_]*)(?:\.([a-zA-Z][A-Za-z0-9_]*))?%/
|
9
|
+
VERBOSE = false
|
10
|
+
|
11
|
+
# A simple interface for filling in and cloning an entire directory.
|
12
|
+
# The entire directory shares a single set of variables... may be made
|
13
|
+
# more flexible later, with an ability to specify special behaviors
|
14
|
+
# based on file matching regexes?
|
15
|
+
#
|
16
|
+
# For now you feed the function the template directory, the destination you want it cloned into.
|
17
|
+
# The vars is a hash, with symbols as the variable names. If you declare { :monkey_name => "Bongo" },
|
18
|
+
# you can use <%= @monkey_name %> in any of the templates, and '%monkey_name%' in the filename.
|
19
|
+
#
|
20
|
+
# If a file is named with the '.template' or '.fwtemplate' file extension, it will undergo variable substitution.
|
21
|
+
#
|
22
|
+
# If the variable is an Array or Range or other enumeratableable object, and the filename has a variable embedded
|
23
|
+
# in it, the template will be evaluated once for each item enumerated, leading to multiple destination files.
|
24
|
+
# But if the variable name isn't in the filename, the whole enumerable object gets passed in. I'm not totally
|
25
|
+
# happy with the inconsistency.
|
26
|
+
#
|
27
|
+
# Filename variables:
|
28
|
+
#
|
29
|
+
# A few examples:
|
30
|
+
# - %i% : filled in. If the variable :i is enumerable, then the template gets evaluated multiple times
|
31
|
+
# - %character.name% : The object { :character => Character.new("barry") } gets .name() called on it.
|
32
|
+
# - %000k% : The number gets leading zeros.
|
33
|
+
# - %hello_world% : You can use underscores.
|
34
|
+
#
|
35
|
+
# Example: chapter-%0000chap_count%.markdown.template_seq
|
36
|
+
# ==> chapter-0001.markdown
|
37
|
+
# ==> chapter-0002.markdown
|
38
|
+
# ==> chapter-0003.markdown
|
39
|
+
# ...
|
40
|
+
# ==> chapter-0099.markdown
|
41
|
+
#
|
42
|
+
# In this case, :chap_count would be the key in vars.
|
43
|
+
#
|
44
|
+
# Example (no leading 0s): chapter%i%.html.template
|
45
|
+
# ==> chapter1.html
|
46
|
+
# ==> chapter2.html
|
47
|
+
# ==> chapter3.html
|
48
|
+
# ...
|
49
|
+
# ==> chapter99.html
|
50
|
+
#
|
51
|
+
# The point of the leading zeros is to specify that the file's number will be padded with zeros.
|
52
|
+
#
|
53
|
+
# When declaring this in vars, you can use any of the following:
|
54
|
+
# vars = {:i => 0..19} (will generate 0 - 19)
|
55
|
+
# vars = {:i => %w(ocelot mouse kitten puppy velociraptor)} # @i will be filled in with the given strings. leading 0s will be ignored.
|
56
|
+
#
|
57
|
+
# The directory structure is cloned.
|
58
|
+
#
|
59
|
+
# Files named with any other extension will simply be copied as-is.
|
60
|
+
def self.write( src, dest, vars = {} )
|
61
|
+
self.new( src, vars ).write( dest )
|
62
|
+
end
|
63
|
+
|
64
|
+
# source: absolute filepath of the source template
|
65
|
+
# dest: absolute filepath of the destination. The %sequence_variable% must be intact, and the same as the source,
|
66
|
+
# but the overall filename can be different.
|
67
|
+
# content : either a string to be ERB evaluated or a filepath
|
68
|
+
# vars : the variables required by the template you're filling in.
|
69
|
+
def initialize( content_or_path, vars = {} )
|
70
|
+
# debugger # if content_or_path =~ /xhtml/
|
71
|
+
@vars = vars
|
72
|
+
|
73
|
+
if content_or_path.is_a?( FunWith::Files::FilePath ) # && content.file?
|
74
|
+
@path = content_or_path
|
75
|
+
make_children # only directly creates first level. Rest are handled by recursion
|
76
|
+
else
|
77
|
+
@path = nil
|
78
|
+
@content = content_or_path
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def make_children
|
83
|
+
@children = []
|
84
|
+
|
85
|
+
# TODO: Fix fwf so that recursiveness can be turned off.
|
86
|
+
if @path.directory?
|
87
|
+
child_paths = @path.glob(:all, :recursive => false).select{|entry| entry.dirname == @path}
|
88
|
+
elsif parse_filename_vars.fwf_blank? || ! loopable_variables?( parse_filename_vars, @vars ) # The current template is leaf?
|
89
|
+
child_paths = []
|
90
|
+
else # we need to make the pathname variants
|
91
|
+
child_paths = [@path]
|
92
|
+
end
|
93
|
+
|
94
|
+
for entry in child_paths
|
95
|
+
combos = var_combos( parse_filename_vars( entry ), @vars )
|
96
|
+
|
97
|
+
if combos.fwf_blank? # Then you don't need to go any deeper?
|
98
|
+
child = TemplateEvaluator.new( entry, @vars.clone )
|
99
|
+
child.parent = self
|
100
|
+
@children << child
|
101
|
+
else
|
102
|
+
for narrowed_var_set in combos
|
103
|
+
narrowed_var_set.inspect
|
104
|
+
|
105
|
+
child = TemplateEvaluator.new( entry, @vars.clone.merge( narrowed_var_set ) )
|
106
|
+
child.parent = self
|
107
|
+
@children << child
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# given the vars and a list of which entries to loop over
|
114
|
+
#
|
115
|
+
# Should yield combinations of entries in the multi_entry variables.
|
116
|
+
# for example
|
117
|
+
# comboize( [ :i, :j, :k], { :i => 0..2, :j => 0..2, :k => 0..2 } )
|
118
|
+
# would yield { :i => 0, :j => 0, :k => 0 }
|
119
|
+
# then { :i => 0, :j => 0, :k => 1 }
|
120
|
+
# then { :i => 0, :j => 0, :k => 2 }
|
121
|
+
# then { :i => 0, :j => 1, :k => 0 } ...
|
122
|
+
#
|
123
|
+
# The results can just be merged into the cloned variable set for a given child.
|
124
|
+
def var_combos( var_data, vars, &block )
|
125
|
+
return [] if var_data.fwf_blank?
|
126
|
+
var_name = var_data.shift
|
127
|
+
if var_name.nil?
|
128
|
+
raise "Recursed too far!"
|
129
|
+
elsif var_data.fwf_blank? # last variable in the list, so start yielding
|
130
|
+
combos = []
|
131
|
+
loop_over( vars[ var_name.name ] ) do |item|
|
132
|
+
hash = { var_name.name => item }
|
133
|
+
combos << hash
|
134
|
+
end
|
135
|
+
return combos
|
136
|
+
else # recurse into other variables
|
137
|
+
# Order doesn't matter, so take the results of the next recursion, pop from the front,
|
138
|
+
# create a subarray with the variations, and push to the back. Stop when the key is found.
|
139
|
+
partial_combos = var_combos( var_data, vars )
|
140
|
+
|
141
|
+
until partial_combos.length == 0 || partial_combos.first.keys.include?( var_name.name )
|
142
|
+
hash = partial_combos.shift
|
143
|
+
filled_combos = []
|
144
|
+
|
145
|
+
loop_over( vars[var_name.name] ) do |item|
|
146
|
+
h = hash.clone
|
147
|
+
h[var_name.name] = item
|
148
|
+
filled_combos << h
|
149
|
+
end
|
150
|
+
|
151
|
+
partial_combos += filled_combos
|
152
|
+
end
|
153
|
+
|
154
|
+
return partial_combos
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
|
159
|
+
def src_root
|
160
|
+
self.parent ? self.parent.src_root : @path
|
161
|
+
end
|
162
|
+
|
163
|
+
def relative_path_from_root
|
164
|
+
@path.relative_path_from( self.src_root )
|
165
|
+
end
|
166
|
+
|
167
|
+
def each_node( &block )
|
168
|
+
yield self
|
169
|
+
|
170
|
+
for child in self.children
|
171
|
+
child.each_node do |node|
|
172
|
+
yield node
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
|
178
|
+
# if no destination root is given, then a relative path from the src_root is given
|
179
|
+
# if this calculated dest is a directory, while the template @path is a file, then
|
180
|
+
# the template's basename is appended to the dest and filled in
|
181
|
+
def destination( dest_root = nil )
|
182
|
+
dest = dest_root ? dest_root.join( self.relative_path_from_root ) : self.relative_path_from_root
|
183
|
+
|
184
|
+
dest = dest.join( @path.basename ) if dest.directory? && @path.file?
|
185
|
+
|
186
|
+
dest = dest.gsub( TEMPLATE_FILE_REGEX, "" )
|
187
|
+
FilenameVarData.fill_in_path( dest, parse_filename_vars, @vars )
|
188
|
+
end
|
189
|
+
|
190
|
+
def each_node_with_destination( dest_root = :temp, &block )
|
191
|
+
dest_root = FunWith::Files::FilePath.tmpdir if dest_root == :temp
|
192
|
+
|
193
|
+
self.each_node do |node|
|
194
|
+
dst = node.destination( dest_root )
|
195
|
+
if dst # if the filename needs variable replacing
|
196
|
+
yield [node, dst]
|
197
|
+
else
|
198
|
+
warn( "Warning: file #{node.path} was not returned.") if FunWith::Templates.gem_verbose?
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def write( dest = :temp )
|
204
|
+
dest = FunWith::Files::FilePath.tmpdir if dest == :temp
|
205
|
+
|
206
|
+
self.each_node_with_destination( dest ) do |node, destination|
|
207
|
+
if node.path.directory?
|
208
|
+
destination.touch_dir
|
209
|
+
else
|
210
|
+
destination.write( node.result )
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
dest
|
215
|
+
end
|
216
|
+
|
217
|
+
def content
|
218
|
+
@content || ( ( @path && @path.file?) ? @path.read : "ERROR: 'content() meaningless for directory" )
|
219
|
+
end
|
220
|
+
|
221
|
+
# only called on leaf/files
|
222
|
+
def result
|
223
|
+
if @path.nil? || is_template?( @path )
|
224
|
+
begin
|
225
|
+
# formerly @template_evaluator_current_content. Don't know if removing the @ makes a diff.
|
226
|
+
template_evaluator_current_content = self.content # In case someone using the templates uses @content
|
227
|
+
template_evaluator_set_local_vars( @vars ) do
|
228
|
+
ERB.new( template_evaluator_current_content ).result( binding )
|
229
|
+
end
|
230
|
+
rescue Exception => e
|
231
|
+
warn( "Template #{ @path } could not be filled in properly (using vars: #{@vars.inspect}). Returning error as result." )
|
232
|
+
result = ["FunWith::Templates::TemplateEvaluator ERROR"]
|
233
|
+
result << ""
|
234
|
+
result << "path: #{@path}"
|
235
|
+
result << ""
|
236
|
+
result << "vars: #{@vars.inspect}"
|
237
|
+
result << ""
|
238
|
+
result << "#{e.class}: #{e.message}"
|
239
|
+
result += e.backtrace.map{|line| "\t#{line}" }
|
240
|
+
|
241
|
+
FunWith::Templates.say_if_verbose( result.join("\n") )
|
242
|
+
result.join("\n")
|
243
|
+
end
|
244
|
+
elsif @path.file?
|
245
|
+
# just copy if it's not a template
|
246
|
+
@path.read
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
def result_to_file( dest )
|
251
|
+
dest = dest.fwf_filepath.expand
|
252
|
+
dest.write( self.result )
|
253
|
+
end
|
254
|
+
|
255
|
+
# if the var found in the filename isn't included in the set of variables given (@vars), no substitution will be performed
|
256
|
+
def parse_filename_vars( path = @path )
|
257
|
+
var_matches = path.scan( VARIABLE_SUBSTITUTION_REGEX )
|
258
|
+
|
259
|
+
return [] if var_matches.fwf_blank?
|
260
|
+
|
261
|
+
var_matches.inject([]) do |memo, var_match|
|
262
|
+
# only return the matches where the name of the variable is in @vars
|
263
|
+
if @vars.keys.include?( var_match[1].to_sym )
|
264
|
+
memo << FilenameVarData.new( var_match[1], var_match[2], var_match[0] )
|
265
|
+
end
|
266
|
+
|
267
|
+
memo
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
|
272
|
+
# src: Either a file to be read, or a string
|
273
|
+
# dst: An output file (exists or not, will be overwritten)
|
274
|
+
# vars: A hash: { :var1 => "Value 1", :var2 => "Value 2"}
|
275
|
+
# In the template, these can be accessed in the ERB way:
|
276
|
+
# <%= @var1 %>, <%= @var2 %>
|
277
|
+
def self.result_to_file( src, dst, vars = {} )
|
278
|
+
dst = dst.join( src.basename ) if dst.directory?
|
279
|
+
|
280
|
+
self.new( src, vars ).result_to_file( dst )
|
281
|
+
end
|
282
|
+
|
283
|
+
def self.destination_filename( src_file, src_root, dest_root, vars )
|
284
|
+
relative_path = src_file.relative_path_from( src_root ).gsub( TEMPLATE_FILE_REGEX, "" )
|
285
|
+
dest = dest_root.join( relative_path )
|
286
|
+
end
|
287
|
+
|
288
|
+
def is_template?( filename )
|
289
|
+
!!((filename) =~ TEMPLATE_FILE_REGEX )
|
290
|
+
end
|
291
|
+
|
292
|
+
def loopable_object?( obj )
|
293
|
+
obj.is_a?( Array ) || obj.is_a?( Range )
|
294
|
+
# obj.respond_to?(:each) && !obj.is_a?(String) && !obj.is_a?(Hash)
|
295
|
+
end
|
296
|
+
|
297
|
+
def loopable_variables?( var_info, vars )
|
298
|
+
var_info.map(&:name).detect{ |name| loopable_object?( vars[name]) }
|
299
|
+
end
|
300
|
+
|
301
|
+
def loop_over( var, &block )
|
302
|
+
var = [var] unless loopable_object?( var )
|
303
|
+
var.each(&block)
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|