Skip to content

Instantly share code, notes, and snippets.

@spuder
Forked from bastman/README.md
Last active July 21, 2019 19:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save spuder/37543db2e7d3c5a24af8be32d7be6857 to your computer and use it in GitHub Desktop.
Save spuder/37543db2e7d3c5a24af8be32d7be6857 to your computer and use it in GitHub Desktop.
parse erb templates from command line (standalone, cli, json)

Goal

  • provide cli command to render erb template
  • template params (bindings) to be provided using json format
  • use inputs from file (template.erb, params.json)
  • use inputs from cli options / parameters
  • render output to stdout OR to file

Why ?

  • helps creating build & deploy pipelines

Usage

Help:

$ ruby json_erb_render.rb --help

Examples:

$ ruby json_erb_render.rb -i ./example/foo.erb -d ./example/config.json

$ ruby json_erb_render.rb -i ./example/foo.erb -d ./example/config.json -o ./out.txt

$ ruby json_erb_render.rb -t "FOO_BAR=<%= @config['foo'] %>" -d ./example/config.json

$ ruby json_erb_render.rb -t "FOO_BAR=<%= @config['foo'] %>" -p "{"foo":"bar"}"

$ ruby json_erb_render.rb -t "FOO_BAR=<%= @config['foo'] %>" -p "{"foo":"bar"}" -o ./out.txt

Docker

  • no need to install ruby

  • just run against official ruby image

    $ docker run --rm -it
    -e "$$PATH=$(PATH)"
    --volume "$(PWD)":"/src" -w /src
    ruby:2.3
    ruby /src/example/json_erb_render.rb -i /src/example/foo.erb -d /src/example/config.json

Limitations

  • no proper validations implemented, ruby will throw its exceptions if sth. is wrong
#!/usr/bin/env ruby
# usage:
# ruby json_erb_render.rb OPTIONS
# help:
# ruby json_erb_render.rb --help
# examples:
# ruby json_erb_render.rb -i ./example/foo.erb -d ./example/config.json
# ruby json_erb_render.rb -i ./example/foo.erb -d ./example/config.json -o ./out.txt
# ruby json_erb_render.rb -t "FOO_BAR=<%= @config['foo']['bar'] %>" -d ./example/config.json
# ruby json_erb_render.rb -t "FOO_BAR=<%= @config['foo']['bar'] %>" -p "{\"foo\":\"bar\"}"
# ruby json_erb_render.rb -t "FOO_BAR=<%= @config['foo']['bar'] %>" -p "{\"foo\":\"bar\"}" -o ./out.txt
require 'erb'
require 'json'
require 'yaml'
require 'pathname'
require 'optparse'
require 'ostruct'
options = OpenStruct.new
OptionParser.new do |opt|
opt.on('-i', '--input_file TEMPLATE_FILE', 'The template file being processed. e.g.: ./example/foo.erb') { |o| options['input_file'] = o }
opt.on('-t', '--template_text TEMPLATE_TEXT', 'The template text being processed. e.g.: "FOO_BAR=<%= @config[\'foo\'][\'bar\'] %>"') { |o| options['template_text'] = o }
opt.on('-d', '--data_file DATA_FILE', 'The config file (json) being processed. e.g.: ./example/config.json') { |o| options['data_file'] = o }
opt.on('-p', '--template_params TEMPLATE_PARAMS', 'The template params (json) being processed. e.g.: -p "{\"foo\":\"bar\"}"') { |o| options['template_params'] = o }
opt.on('-o', '--output_file SINK_FILE', 'The output file. if not specified, output gets rendered to stdout. e.g.: ./out.txt') { |o| options['output_file'] = o }
end.parse!
data_file = options['data_file']
data_text = options['template_params']
template_file = options['input_file']
template_text = options['template_text']
output_file = options['output_file']
class TemplateBuilder
attr_accessor :config
def initialize(config)
@config = config
end
# Expose private binding() method.
def get_binding
binding()
end
end
if template_file == nil
template=template_text
else
template=File.read(template_file)
end
if data_file == nil
params=JSON.parse(data_text)
else
if File.extname(data_file) == '.json'
puts "Detected json"
params=JSON.parse(File.read(data_file))
elsif [ '.yaml', '.yml'].include? File.extname(data_file)
puts "Detected yaml"
params=YAML.load_file(data_file)
else
raise "Unkown file extension for #{data_file}"
end
end
renderer = ERB.new(template)
builder = TemplateBuilder.new(params)
result = renderer.result(builder.get_binding)
# Extract strings '<%= -%>' and '<%= %>'
variables = template.scan(/<%= ?([^>]+) -?%>/).flatten
# Ensure there is a value at @config or ENV for all templates
# Because we are using eval() here, which is super dangerous, verfiy only
# variables that start with @config or ENV are evaluated
variables.each do |variable|
if variable.start_with?("@config")
variable.sub!("@config", "")
@config2 = builder.instance_variable_get("@config")
puts "WARNING: Undefined variable: @config#{variable}" unless eval("@config2#{variable}")
elsif variable.start_with?("ENV")
puts "WARNING: Undefined variable: #{variable}" unless eval(variable)
else
raise "Variable '#{variable}' not allowed"
end
end
if output_file==nil
puts result
else
output = File.new(output_file, 'w')
output.puts(result)
output.close
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment