1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 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 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
|
# frozen_string_literal: true
require_relative '../tilt'
require 'optparse'
module Tilt::CLI
USAGE = (<<USAGE).freeze
Usage: tilt <options> <file>
Process template <file> and write output to stdout. With no <file> or
when <file> is '-', read template from stdin and use the --type option
to determine the template's type.
Options
-l, --list List template engines + file patterns and exit
-t, --type=<pattern> Use this template engine; required if no <file>
-y, --layout=<file> Use <file> as a layout template
-D<name>=<value> Define variable <name> as <value>
-d, --define-file=<file> Load YAML from <file> and use for variables
--vars=<ruby> Evaluate <ruby> to Hash and use for variables
-h, --help Show this help message
Convert markdown to HTML:
$ tilt foo.markdown > foo.html
Process ERB template:
$ echo "Answer: <%= 2 + 2 %>" | tilt -t erb
Answer: 4
Define variables:
$ echo "Answer: <%= 2 + n %>" | tilt -t erb --vars="{:n=>40}"
Answer: 42
$ echo "Answer: <%= 2 + n.to_i %>" | tilt -t erb -Dn=40
Answer: 42
USAGE
private_constant :USAGE
# Backbone of the tilt command line utility. Allows mocking input/output
# for simple testing. Returns program exit code.
def self.run(argv: ARGV, stdout: $stdout, stdin: $stdin, stderr: $stderr, script_name: File.basename($0))
pattern = nil
layout = nil
locals = {}
abort = proc do |msg|
stderr.puts msg
return 1
end
OptionParser.new do |o|
o.program_name = script_name
# list all available template engines
o.on("-l", "--list") do
groups = {}
Tilt.lazy_map.each do |pattern,engines|
engines.each do |engine,|
engine = engine.split('::').last.sub(/Template\z/, '')
(groups[engine] ||= []) << pattern
end
end
groups.sort { |(k1,v1),(k2,v2)| k1 <=> k2 }.each do |engine,files|
stdout.printf "%-20s %s\n", engine, files.sort.join(', ')
end
return 0
end
# the template type / pattern
o.on("-t", "--type=PATTERN", String) do |val|
abort.("unknown template type: #{val}") unless Tilt[val]
pattern = val
end
# pass template output into the specified layout template
o.on("-y", "--layout=FILE", String) do |file|
paths = [file, "~/.tilt/#{file}", "/etc/tilt/#{file}"]
layout = paths.
map { |p| File.expand_path(p) }.
find { |p| File.exist?(p) }
abort.("no such layout: #{file}") if layout.nil?
end
# define a local variable
o.on("-D", "--define=PAIR", String) do |pair|
key, value = pair.split(/[=:]/, 2)
locals[key.to_sym] = value
end
# define local variables from YAML or JSON
o.on("-d", "--define-file=FILE", String) do |file|
require 'yaml'
abort.("no such define file: #{file}") unless File.exist? file
hash = File.open(file, 'r:bom|utf-8') { |f| YAML.load(f.read) }
abort.("vars must be a Hash, not instance of #{hash.class}") unless hash.is_a?(Hash)
hash.each { |key, value| locals[key.to_sym] = value }
end
# define local variables using a Ruby hash
o.on("--vars=RUBY") do |ruby|
hash = eval(ruby)
abort.("vars must be a Hash, not instance of #{hash.class}") unless hash.is_a?(Hash)
hash.each { |key, value| locals[key.to_sym] = value }
end
o.on_tail("-h", "--help") do
stdout.puts USAGE
return 0
end
end.parse!(argv)
file = argv.first || '-'
pattern = file if pattern.nil?
abort.("template type not given. see: #{script_name} --help") if ['-', ''].include?(pattern)
engine = Tilt[pattern]
abort.("template engine not found for: #{pattern}") unless engine
template =
engine.new(file) {
if file == '-'
stdin.read
else
File.read(file)
end
}
output = template.render(self, locals)
# process layout
output = Tilt.new(layout).render(self, locals) { output } if layout
stdout.write(output)
0
end
end
|