Class: Lono::Template::DSL

Inherits:
Object
  • Object
show all
Defined in:
lib/lono/template/dsl.rb

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ DSL

Returns a new instance of DSL.



2
3
4
5
6
7
8
9
# File 'lib/lono/template/dsl.rb', line 2

def initialize(options={})
  @options = options
  @config_path = "#{Lono.root}/config"
  Lono::ProjectChecker.check
  @templates = []
  @results = {}
  @detected_format = nil
end

Instance Method Details

#build_templatesObject



82
83
84
85
86
87
# File 'lib/lono/template/dsl.rb', line 82

def build_templates
  options = @options.merge(detected_format: @detected_format)
  @templates.each do |t|
    @results[t[:name]] = Lono::Template::Template.new(t[:name], t[:block], options).build
  end
end

#detect_formatObject

Detects the format of the templates. Checks the extension of all the templates files. All the templates must be of the same format, either all json or all yaml.



71
72
73
74
75
76
# File 'lib/lono/template/dsl.rb', line 71

def detect_format
  extensions = Dir.glob("#{Lono.root}/templates/**/*").map do |path|
    File.extname(path).sub(/^\./,'')
  end.reject(&:empty?).uniq
  extensions.include?('yml') ? 'yml' : 'json' # defaults to yml - falls back to json
end

#ensure_parent_dir(path) ⇒ Object



154
155
156
157
# File 'lib/lono/template/dsl.rb', line 154

def ensure_parent_dir(path)
  dir = File.dirname(path)
  FileUtils.mkdir_p(dir) unless File.exist?(dir)
end

#evaluate_folder(folder) ⇒ Object



27
28
29
30
31
32
# File 'lib/lono/template/dsl.rb', line 27

def evaluate_folder(folder)
  paths = Dir.glob("#{@config_path}/templates/#{folder}/**/*")
  paths.select{ |e| File.file?(e) }.each do |path|
    evaluate_template(path)
  end
end

#evaluate_template(path) ⇒ Object



34
35
36
37
38
39
40
41
42
# File 'lib/lono/template/dsl.rb', line 34

def evaluate_template(path)
  begin
    instance_eval(File.read(path), path)
  rescue Exception => e
    template_evaluation_error(e)
    puts "\nFull error:"
    raise
  end
end

#evaluate_templatesObject

Instance eval’s all the files within each folder under

config/lono/base and config/lono/[Lono.env]

Base gets base first and then the Lono.env configs get evaluate second. This means the env specific configs override the base configs.



21
22
23
24
25
# File 'lib/lono/template/dsl.rb', line 21

def evaluate_templates
  evaluate_folder("base")
  evaluate_folder(Lono.env)
  @detected_format = detect_format
end

#output_format(text) ⇒ Object



136
137
138
# File 'lib/lono/template/dsl.rb', line 136

def output_format(text)
  @options[:pretty] ? prettify(text) : text
end

#prettify(text) ⇒ Object

Input text is either yaml or json. Do not prettify yaml format because it removes the !Ref like CloudFormation notation



142
143
144
# File 'lib/lono/template/dsl.rb', line 142

def prettify(text)
  @detected_format == "json" ? JSON.pretty_generate(JSON.parse(text)) : yaml_format(text)
end

#run(options = {}) ⇒ Object



11
12
13
14
15
# File 'lib/lono/template/dsl.rb', line 11

def run(options={})
  evaluate_templates
  build_templates
  write_output
end

#template(name, &block) ⇒ Object



78
79
80
# File 'lib/lono/template/dsl.rb', line 78

def template(name, &block)
  @templates << {name: name, block: block}
end

#template_evaluation_error(e) ⇒ Object

Prints out a user friendly task_definition error message



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/lono/template/dsl.rb', line 45

def template_evaluation_error(e)
  error_info = e.backtrace.first
  path, line_no, _ = error_info.split(':')
  line_no = line_no.to_i
  puts "Error evaluating #{path}:".colorize(:red)
  puts e.message
  puts "Here's the line in #{path} with the error:\n\n"

  contents = IO.read(path)
  content_lines = contents.split("\n")
  context = 5 # lines of context
  top, bottom = [line_no-context-1, 0].max, line_no+context-1
  spacing = content_lines.size.to_s.size
  content_lines[top..bottom].each_with_index do |line_content, index|
    line_number = top+index+1
    if line_number == line_no
      printf("%#{spacing}d %s\n".colorize(:red), line_number, line_content)
    else
      printf("%#{spacing}d %s\n", line_number, line_content)
    end
  end
end

#validate(text, path) ⇒ Object



106
107
108
109
110
111
112
# File 'lib/lono/template/dsl.rb', line 106

def validate(text, path)
  if @detected_format == "json"
    validate_json(text, path)
  else
    validate_yaml(text, path)
  end
end

#validate_json(json, path) ⇒ Object



125
126
127
128
129
130
131
132
133
134
# File 'lib/lono/template/dsl.rb', line 125

def validate_json(json, path)
  begin
    JSON.parse(json)
  rescue JSON::ParserError => e
    puts "Invalid json.  Output written to #{path} for debugging".colorize(:red)
    puts "ERROR: #{e.message}".colorize(:red)
    File.open(path, 'w') {|f| f.write(json) }
    exit 1
  end
end

#validate_yaml(yaml, path) ⇒ Object



114
115
116
117
118
119
120
121
122
123
# File 'lib/lono/template/dsl.rb', line 114

def validate_yaml(yaml, path)
  begin
    YAML.load(yaml)
  rescue Psych::SyntaxError => e
    puts "Invalid yaml.  Output written to #{path} for debugging".colorize(:red)
    puts "ERROR: #{e.message}".colorize(:red)
    File.open(path, 'w') {|f| f.write(yaml) }
    exit 1
  end
end

#write_outputObject



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/lono/template/dsl.rb', line 89

def write_output
  output_path = "#{Lono.root}/output"
  FileUtils.rm_rf(output_path) if @options[:clean]
  FileUtils.mkdir(output_path) unless File.exist?(output_path)
  puts "Generating CloudFormation templates:" unless @options[:quiet]
  @results.each do |name,text|
    path = "#{output_path}/#{name}".sub(/^\.\//,'') # strip leading '.'
    path += ".#{@detected_format}"
    puts "  #{path}" unless @options[:quiet]
    ensure_parent_dir(path)
    validate(text, path)
    File.open(path, 'w') do |f|
      f.write(output_format(text))
    end
  end
end

#yaml_format(text) ⇒ Object



146
147
148
149
150
151
152
# File 'lib/lono/template/dsl.rb', line 146

def yaml_format(text)
  comment =<<~EOS
    # This file was generated with lono. Do not edit directly, the changes will be lost.
    # More info: http://lono.cloud
  EOS
  "#{comment}#{text}"
end