Class: FunWith::Templates::TemplateEvaluator
- Inherits:
-
Object
- Object
- FunWith::Templates::TemplateEvaluator
- Defined in:
- lib/fun_with/templates/template_evaluator.rb
Constant Summary collapse
- TEMPLATE_FILE_REGEX =
/\.(fw)?template$/
- VARIABLE_SUBSTITUTION_REGEX =
/%(0+)?([a-zA-Z][A-Za-z0-9_]*)(?:\.([a-zA-Z][A-Za-z0-9_]*))?%/
- VERBOSE =
false
Instance Attribute Summary collapse
-
#children ⇒ Object
readonly
Returns the value of attribute children.
-
#content ⇒ Object
readonly
Returns the value of attribute content.
-
#parent ⇒ Object
Returns the value of attribute parent.
-
#path ⇒ Object
readonly
Returns the value of attribute path.
-
#vars ⇒ Object
readonly
Returns the value of attribute vars.
Class Method Summary collapse
- .destination_filename(src_file, src_root, dest_root, vars) ⇒ Object
-
.result_to_file(src, dst, vars = {}) ⇒ Object
src: Either a file to be read, or a string dst: An output file (exists or not, will be overwritten) vars: A hash: { :var1 => “Value 1”, :var2 => “Value 2”} In the template, these can be accessed in the ERB way: <%= @var1 %>, <%= @var2 %>.
-
.write(src, dest, vars = {}) ⇒ Object
A simple interface for filling in and cloning an entire directory.
Instance Method Summary collapse
-
#destination(dest_root = nil) ⇒ Object
if no destination root is given, then a relative path from the src_root is given if this calculated dest is a directory, while the template @path is a file, then the template’s basename is appended to the dest and filled in.
- #each_node {|_self| ... } ⇒ Object
- #each_node_with_destination(dest_root = :temp, &block) ⇒ Object
-
#initialize(content_or_path, vars = {}) ⇒ TemplateEvaluator
constructor
source: absolute filepath of the source template dest: absolute filepath of the destination.
- #is_template?(filename) ⇒ Boolean
- #loop_over(var, &block) ⇒ Object
- #loopable_object?(obj) ⇒ Boolean
- #loopable_variables?(var_info, vars) ⇒ Boolean
- #make_children ⇒ Object
-
#parse_filename_vars(path = @path) ⇒ Object
if the var found in the filename isn’t included in the set of variables given (@vars), no substitution will be performed.
- #relative_path_from_root ⇒ Object
-
#result ⇒ Object
only called on leaf/files.
- #result_to_file(dest) ⇒ Object
- #src_root ⇒ Object
-
#var_combos(var_data, vars, &block) ⇒ Object
given the vars and a list of which entries to loop over.
- #write(dest = :temp) ⇒ Object
Constructor Details
#initialize(content_or_path, vars = {}) ⇒ TemplateEvaluator
source: absolute filepath of the source template dest: absolute filepath of the destination. The %sequence_variable% must be intact, and the same as the source,
but the overall filename can be different.
content : either a string to be ERB evaluated or a filepath vars : the variables required by the template you’re filling in.
69 70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/fun_with/templates/template_evaluator.rb', line 69 def initialize( content_or_path, vars = {} ) # debugger # if content_or_path =~ /xhtml/ @vars = vars if content_or_path.is_a?( FunWith::Files::FilePath ) # && content.file? @path = content_or_path make_children # only directly creates first level. Rest are handled by recursion else @path = nil @content = content_or_path end end |
Instance Attribute Details
#children ⇒ Object (readonly)
Returns the value of attribute children.
4 5 6 |
# File 'lib/fun_with/templates/template_evaluator.rb', line 4 def children @children end |
#content ⇒ Object (readonly)
Returns the value of attribute content.
4 5 6 |
# File 'lib/fun_with/templates/template_evaluator.rb', line 4 def content @content end |
#parent ⇒ Object
Returns the value of attribute parent.
5 6 7 |
# File 'lib/fun_with/templates/template_evaluator.rb', line 5 def parent @parent end |
#path ⇒ Object (readonly)
Returns the value of attribute path.
4 5 6 |
# File 'lib/fun_with/templates/template_evaluator.rb', line 4 def path @path end |
#vars ⇒ Object (readonly)
Returns the value of attribute vars.
4 5 6 |
# File 'lib/fun_with/templates/template_evaluator.rb', line 4 def vars @vars end |
Class Method Details
.destination_filename(src_file, src_root, dest_root, vars) ⇒ Object
283 284 285 286 |
# File 'lib/fun_with/templates/template_evaluator.rb', line 283 def self.destination_filename( src_file, src_root, dest_root, vars ) relative_path = src_file.relative_path_from( src_root ).gsub( TEMPLATE_FILE_REGEX, "" ) dest = dest_root.join( relative_path ) end |
.result_to_file(src, dst, vars = {}) ⇒ Object
src: Either a file to be read, or a string dst: An output file (exists or not, will be overwritten) vars: A hash: { :var1 => “Value 1”, :var2 => “Value 2”} In the template, these can be accessed in the ERB way: <%= @var1 %>, <%= @var2 %>
277 278 279 280 281 |
# File 'lib/fun_with/templates/template_evaluator.rb', line 277 def self.result_to_file( src, dst, vars = {} ) dst = dst.join( src.basename ) if dst.directory? self.new( src, vars ).result_to_file( dst ) end |
.write(src, dest, vars = {}) ⇒ Object
A simple interface for filling in and cloning an entire directory. The entire directory shares a single set of variables… may be made more flexible later, with an ability to specify special behaviors based on file matching regexes?
For now you feed the function the template directory, the destination you want it cloned into. The vars is a hash, with symbols as the variable names. If you declare { :monkey_name => “Bongo” }, you can use <%= @monkey_name %> in any of the templates, and ‘%monkey_name%’ in the filename.
If a file is named with the ‘.template’ or ‘.fwtemplate’ file extension, it will undergo variable substitution.
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. But if the variable name isn’t in the filename, the whole enumerable object gets passed in. I’m not totally happy with the inconsistency.
Filename variables:
A few examples:
- %i% : filled in. If the variable :i is enumerable, then the template gets evaluated multiple times
- %character.name% : The object { :character => Character.new("barry") } gets .name() called on it.
- %000k% : The number gets leading zeros.
- %hello_world% : You can use underscores.
Example: chapter-%0000chap_count%.markdown.template_seq
==> chapter-0001.markdown
==> chapter-0002.markdown
==> chapter-0003.markdown
...
==> chapter-0099.markdown
In this case, :chap_count would be the key in vars.
Example (no leading 0s): chapter%i%.html.template
==> chapter1.html
==> chapter2.html
==> chapter3.html
...
==> chapter99.html
The point of the leading zeros is to specify that the file’s number will be padded with zeros.
When declaring this in vars, you can use any of the following:
vars = {:i => 0..19} (will generate 0 - 19)
vars = {:i => %w(ocelot mouse kitten puppy velociraptor)} # @i will be filled in with the given strings. leading 0s will be ignored.
The directory structure is cloned.
Files named with any other extension will simply be copied as-is.
60 61 62 |
# File 'lib/fun_with/templates/template_evaluator.rb', line 60 def self.write( src, dest, vars = {} ) self.new( src, vars ).write( dest ) end |
Instance Method Details
#destination(dest_root = nil) ⇒ Object
if no destination root is given, then a relative path from the src_root is given if this calculated dest is a directory, while the template @path is a file, then the template’s basename is appended to the dest and filled in
181 182 183 184 185 186 187 188 |
# File 'lib/fun_with/templates/template_evaluator.rb', line 181 def destination( dest_root = nil ) dest = dest_root ? dest_root.join( self.relative_path_from_root ) : self.relative_path_from_root dest = dest.join( @path.basename ) if dest.directory? && @path.file? dest = dest.gsub( TEMPLATE_FILE_REGEX, "" ) FilenameVarData.fill_in_path( dest, parse_filename_vars, @vars ) end |
#each_node {|_self| ... } ⇒ Object
167 168 169 170 171 172 173 174 175 |
# File 'lib/fun_with/templates/template_evaluator.rb', line 167 def each_node( &block ) yield self for child in self.children child.each_node do |node| yield node end end end |
#each_node_with_destination(dest_root = :temp, &block) ⇒ Object
190 191 192 193 194 195 196 197 198 199 200 201 |
# File 'lib/fun_with/templates/template_evaluator.rb', line 190 def each_node_with_destination( dest_root = :temp, &block ) dest_root = FunWith::Files::FilePath.tmpdir if dest_root == :temp self.each_node do |node| dst = node.destination( dest_root ) if dst # if the filename needs variable replacing yield [node, dst] else warn( "Warning: file #{node.path} was not returned.") if FunWith::Templates.gem_verbose? end end end |
#is_template?(filename) ⇒ Boolean
288 289 290 |
# File 'lib/fun_with/templates/template_evaluator.rb', line 288 def is_template?( filename ) !!((filename) =~ TEMPLATE_FILE_REGEX ) end |
#loop_over(var, &block) ⇒ Object
301 302 303 304 |
# File 'lib/fun_with/templates/template_evaluator.rb', line 301 def loop_over( var, &block ) var = [var] unless loopable_object?( var ) var.each(&block) end |
#loopable_object?(obj) ⇒ Boolean
292 293 294 295 |
# File 'lib/fun_with/templates/template_evaluator.rb', line 292 def loopable_object?( obj ) obj.is_a?( Array ) || obj.is_a?( Range ) # obj.respond_to?(:each) && !obj.is_a?(String) && !obj.is_a?(Hash) end |
#loopable_variables?(var_info, vars) ⇒ Boolean
297 298 299 |
# File 'lib/fun_with/templates/template_evaluator.rb', line 297 def loopable_variables?( var_info, vars ) var_info.map(&:name).detect{ |name| loopable_object?( vars[name]) } end |
#make_children ⇒ Object
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/fun_with/templates/template_evaluator.rb', line 82 def make_children @children = [] # TODO: Fix fwf so that recursiveness can be turned off. if @path.directory? child_paths = @path.glob(:all, :recursive => false).select{|entry| entry.dirname == @path} elsif parse_filename_vars.fwf_blank? || ! loopable_variables?( parse_filename_vars, @vars ) # The current template is leaf? child_paths = [] else # we need to make the pathname variants child_paths = [@path] end for entry in child_paths combos = var_combos( parse_filename_vars( entry ), @vars ) if combos.fwf_blank? # Then you don't need to go any deeper? child = TemplateEvaluator.new( entry, @vars.clone ) child.parent = self @children << child else for narrowed_var_set in combos narrowed_var_set.inspect child = TemplateEvaluator.new( entry, @vars.clone.merge( narrowed_var_set ) ) child.parent = self @children << child end end end end |
#parse_filename_vars(path = @path) ⇒ Object
if the var found in the filename isn’t included in the set of variables given (@vars), no substitution will be performed
256 257 258 259 260 261 262 263 264 265 266 267 268 269 |
# File 'lib/fun_with/templates/template_evaluator.rb', line 256 def parse_filename_vars( path = @path ) var_matches = path.scan( VARIABLE_SUBSTITUTION_REGEX ) return [] if var_matches.fwf_blank? var_matches.inject([]) do |memo, var_match| # only return the matches where the name of the variable is in @vars if @vars.keys.include?( var_match[1].to_sym ) memo << FilenameVarData.new( var_match[1], var_match[2], var_match[0] ) end memo end end |
#relative_path_from_root ⇒ Object
163 164 165 |
# File 'lib/fun_with/templates/template_evaluator.rb', line 163 def relative_path_from_root @path.relative_path_from( self.src_root ) end |
#result ⇒ Object
only called on leaf/files
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 |
# File 'lib/fun_with/templates/template_evaluator.rb', line 222 def result if @path.nil? || is_template?( @path ) begin # formerly @template_evaluator_current_content. Don't know if removing the @ makes a diff. template_evaluator_current_content = self.content # In case someone using the templates uses @content template_evaluator_set_local_vars( @vars ) do ERB.new( template_evaluator_current_content ).result( binding ) end rescue Exception => e warn( "Template #{ @path } could not be filled in properly (using vars: #{@vars.inspect}). Returning error as result." ) result = ["FunWith::Templates::TemplateEvaluator ERROR"] result << "" result << "path: #{@path}" result << "" result << "vars: #{@vars.inspect}" result << "" result << "#{e.class}: #{e.}" result += e.backtrace.map{|line| "\t#{line}" } FunWith::Templates.say_if_verbose( result.join("\n") ) result.join("\n") end elsif @path.file? # just copy if it's not a template @path.read end end |
#result_to_file(dest) ⇒ Object
250 251 252 253 |
# File 'lib/fun_with/templates/template_evaluator.rb', line 250 def result_to_file( dest ) dest = dest.fwf_filepath. dest.write( self.result ) end |
#src_root ⇒ Object
159 160 161 |
# File 'lib/fun_with/templates/template_evaluator.rb', line 159 def src_root self.parent ? self.parent.src_root : @path end |
#var_combos(var_data, vars, &block) ⇒ Object
given the vars and a list of which entries to loop over
Should yield combinations of entries in the multi_entry variables. for example comboize( [ :i, :j, :k], { :i => 0..2, :j => 0..2, :k => 0..2 } ) would yield { :i => 0, :j => 0, :k => 0 }
then { :i => 0, :j => 0, :k => 1 }
then { :i => 0, :j => 0, :k => 2 }
then { :i => 0, :j => 1, :k => 0 } ...
The results can just be merged into the cloned variable set for a given child.
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
# File 'lib/fun_with/templates/template_evaluator.rb', line 124 def var_combos( var_data, vars, &block ) return [] if var_data.fwf_blank? var_name = var_data.shift if var_name.nil? raise "Recursed too far!" elsif var_data.fwf_blank? # last variable in the list, so start yielding combos = [] loop_over( vars[ var_name.name ] ) do |item| hash = { var_name.name => item } combos << hash end return combos else # recurse into other variables # Order doesn't matter, so take the results of the next recursion, pop from the front, # create a subarray with the variations, and push to the back. Stop when the key is found. partial_combos = var_combos( var_data, vars ) until partial_combos.length == 0 || partial_combos.first.keys.include?( var_name.name ) hash = partial_combos.shift filled_combos = [] loop_over( vars[var_name.name] ) do |item| h = hash.clone h[var_name.name] = item filled_combos << h end partial_combos += filled_combos end return partial_combos end end |
#write(dest = :temp) ⇒ Object
203 204 205 206 207 208 209 210 211 212 213 214 215 |
# File 'lib/fun_with/templates/template_evaluator.rb', line 203 def write( dest = :temp ) dest = FunWith::Files::FilePath.tmpdir if dest == :temp self.each_node_with_destination( dest ) do |node, destination| if node.path.directory? destination.touch_dir else destination.write( node.result ) end end dest end |