aws-cfn-dsl 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -13
- data/Gemfile +0 -2
- data/aws-cfn-dsl.gemspec +3 -2
- data/lib/aws/cfn/dsl/base.rb +55 -443
- data/lib/aws/cfn/dsl/main.rb +2 -17
- data/lib/aws/cfn/dsl/mixins/dsl.rb +52 -0
- data/lib/aws/cfn/dsl/mixins/maintainer.rb +42 -0
- data/lib/aws/cfn/dsl/mixins/options.rb +126 -0
- data/lib/aws/cfn/dsl/mixins/output.rb +53 -0
- data/lib/aws/cfn/dsl/mixins/prettyprint.rb +296 -0
- data/lib/aws/cfn/dsl/mixins/simplify.rb +59 -0
- data/lib/aws/cfn/dsl/template.rb +0 -164
- data/lib/aws/cfn/dsl/version.rb +1 -1
- metadata +51 -25
data/lib/aws/cfn/dsl/main.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'slop'
|
2
1
|
require "aws/cfn/dsl/base"
|
3
2
|
|
4
3
|
module Aws
|
@@ -8,25 +7,11 @@ module Aws
|
|
8
7
|
|
9
8
|
def run
|
10
9
|
|
11
|
-
|
12
|
-
on :j, :template=, 'The template to convert', as: String, argument: true
|
13
|
-
on :o, :output=, 'The directory to output the DSL to.', as: String, argument: true
|
14
|
-
on :O, :overwrite, 'Overwrite existing generated source files. (HINT: Think twice ...)', { as: String, optional_argument: true, default: 'off', match: %r/0|1|yes|no|on|off|enable|disable|set|unset|true|false|raw/i }
|
15
|
-
end
|
16
|
-
|
17
|
-
@config[:overwrite] = if @opts[:overwrite].downcase.match %r'^(1|true|on|yes|enable|set)$'
|
18
|
-
true
|
19
|
-
else
|
20
|
-
false
|
21
|
-
end
|
22
|
-
|
23
|
-
unless @opts[:template]
|
24
|
-
abort! @opts
|
25
|
-
end
|
10
|
+
parse_options
|
26
11
|
|
27
12
|
load_template(@opts[:template])
|
28
13
|
|
29
|
-
save_dsl(@opts[:
|
14
|
+
save_dsl(@opts[:directory])
|
30
15
|
|
31
16
|
end
|
32
17
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Aws
|
2
|
+
module Cfn
|
3
|
+
module Dsl
|
4
|
+
module DSL
|
5
|
+
|
6
|
+
# Attempt to figure out what fragment of the template we have. This is imprecise and can't
|
7
|
+
# detect Mappings and Outputs sections reliably, so it doesn't attempt to.
|
8
|
+
def detect_type(val)
|
9
|
+
if val.is_a?(Hash) && val['AWSTemplateFormatVersion']
|
10
|
+
:template
|
11
|
+
elsif val.is_a?(Hash) && /^(String|Number)$/ =~ val['Type']
|
12
|
+
:parameter
|
13
|
+
elsif val.is_a?(Hash) && val['Type']
|
14
|
+
:resource
|
15
|
+
elsif val.is_a?(Hash) && val.values.all? { |v| detect_type(v) == :parameter }
|
16
|
+
:parameters
|
17
|
+
elsif val.is_a?(Hash) && val.values.all? { |v| detect_type(v) == :resource }
|
18
|
+
:resources
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def add_brick(subdir,name)
|
23
|
+
if @config[:directory]
|
24
|
+
#file = rb_file(subdir, name).gsub(%r'^#{@config[:directory]}/','')
|
25
|
+
#writeln " file '#{file}'"
|
26
|
+
s = subdir.downcase.gsub(%r's$','')
|
27
|
+
writeln " #{s} '#{name}'"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def rb_file(subdir, name)
|
32
|
+
path = File.join(@config[:directory], subdir)
|
33
|
+
unless File.directory? path
|
34
|
+
Dir.mkdir path
|
35
|
+
end
|
36
|
+
file = File.join(path, "#{name}.rb")
|
37
|
+
end
|
38
|
+
|
39
|
+
def module_name(parts=-1)
|
40
|
+
name = self.class.to_s.split("::")
|
41
|
+
name[0..parts-1].join('::')
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
def self.included(includer)
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Aws
|
2
|
+
module Cfn
|
3
|
+
module Dsl
|
4
|
+
module Maintainer
|
5
|
+
|
6
|
+
def maintainer(parts=-1)
|
7
|
+
"maintainer: #{module_name parts}"
|
8
|
+
end
|
9
|
+
|
10
|
+
def maintainer_comment(indent=' ')
|
11
|
+
"#{indent}# WARNING: This code is generated. Your changes may be overwritten!\n" +
|
12
|
+
"#{indent}# Remove this message and/or set the 'maintainer: <author name>' when you need your changes to survive.\n" +
|
13
|
+
"#{indent}# Abscence of the 'maintainer: ' will be considered conscent to overwrite.\n" +
|
14
|
+
"#{indent}# #{maintainer 3}\n" +
|
15
|
+
"#\n"
|
16
|
+
end
|
17
|
+
|
18
|
+
def print_maintainer(indent=' ')
|
19
|
+
writeln maintainer_comment(indent)
|
20
|
+
end
|
21
|
+
|
22
|
+
def i_am_maintainer(file)
|
23
|
+
# mod = module_name 2
|
24
|
+
if File.exists?(file)
|
25
|
+
src = IO.read(file)
|
26
|
+
mtc = src.match(%r'#{maintainer 2}')
|
27
|
+
iam = (not mtc.nil? or src.match(%r'#\s*maintainer:').nil?)
|
28
|
+
ovr = @config[:overwrite]
|
29
|
+
iam or ovr
|
30
|
+
else
|
31
|
+
true
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.included(includer)
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
require 'slop'
|
2
|
+
module Aws
|
3
|
+
module Cfn
|
4
|
+
module Dsl
|
5
|
+
module Options
|
6
|
+
attr_reader :opts
|
7
|
+
attr_reader :config
|
8
|
+
|
9
|
+
def setup_options
|
10
|
+
@on_off_regex = %r/0|1|yes|no|on|off|enable|disable|set|unset|true|false|raw/i
|
11
|
+
@format_regex = %r/ruby|rb|yaml|yml|json|js/i
|
12
|
+
@on_yes_regex = %r'^(1|true|on|yes|enable|set)$'
|
13
|
+
|
14
|
+
@opts = Slop.new(help: true) do
|
15
|
+
on :t, :template=, 'The template', as: String
|
16
|
+
on :d, :directory=, 'The directory with template components.', as: String
|
17
|
+
|
18
|
+
on :l, :log_level=, "Logging level. [#{::Logging::LEVELS.keys.join('|')}]", {as: String,
|
19
|
+
default: 'step',
|
20
|
+
match: %r/#{::Logging::LEVELS.keys.join('|')}/i}
|
21
|
+
|
22
|
+
on :n, :functions=, 'Enable function use.', { as: String,
|
23
|
+
default: 'off',
|
24
|
+
match: @on_off_regex } do |_|
|
25
|
+
me = @options.select { |o|
|
26
|
+
o.long == 'functions'
|
27
|
+
}[0]
|
28
|
+
me.config[:default] = 'on'
|
29
|
+
end
|
30
|
+
on :x, :expandedpaths, 'Show expanded paths in output', {as: String,
|
31
|
+
optional_argument: true,
|
32
|
+
default: 'off',
|
33
|
+
match: @on_off_regex } do |objects|
|
34
|
+
me = @options.select { |o|
|
35
|
+
o.long == 'expandedpaths'
|
36
|
+
}[0]
|
37
|
+
me.config[:default] = 'on'
|
38
|
+
end
|
39
|
+
on :O, :overwrite, 'Overwrite existing generated source files. (HINT: Think twice ...)', {as: String,
|
40
|
+
optional_argument: true,
|
41
|
+
default: 'off',
|
42
|
+
match: @on_off_regex } do |objects|
|
43
|
+
me = @options.select { |o|
|
44
|
+
o.long == 'overwrite'
|
45
|
+
}[0]
|
46
|
+
me.config[:default] = 'on'
|
47
|
+
end
|
48
|
+
on :force, 'Continue processing and ignore warnings', {as: String,
|
49
|
+
optional_argument: true,
|
50
|
+
default: 'off',
|
51
|
+
match: @on_off_regex } do |objects|
|
52
|
+
me = @options.select { |o|
|
53
|
+
o.long == 'force'
|
54
|
+
}[0]
|
55
|
+
me.config[:default] = 'on'
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
def parse_options
|
62
|
+
|
63
|
+
setup_options
|
64
|
+
|
65
|
+
@opts.parse!
|
66
|
+
|
67
|
+
unless @opts[:directory]
|
68
|
+
puts @opts
|
69
|
+
abort! "Missing required option --directory"
|
70
|
+
end
|
71
|
+
|
72
|
+
unless @opts[:template]
|
73
|
+
puts @opts
|
74
|
+
abort! "Missing required option --template"
|
75
|
+
end
|
76
|
+
|
77
|
+
setup_config
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
def setup_config
|
82
|
+
|
83
|
+
[:overwrite, :functions, :force, :expandedpaths ].each { |cfg|
|
84
|
+
@config[cfg] = (not @opts[cfg].downcase.match(@on_yes_regex).nil?)
|
85
|
+
}
|
86
|
+
|
87
|
+
@opts.options.each{ |opt|
|
88
|
+
key = opt.long.to_sym
|
89
|
+
unless @config.has_key?(key)
|
90
|
+
@config[key] = opt.value unless opt.value.nil?
|
91
|
+
end
|
92
|
+
}
|
93
|
+
|
94
|
+
lcs = ::Logging::ColorScheme.new( 'compiler', :levels => {
|
95
|
+
:trace => :blue,
|
96
|
+
:debug => :cyan,
|
97
|
+
:info => :green,
|
98
|
+
:step => :green,
|
99
|
+
:warn => :yellow,
|
100
|
+
:error => :red,
|
101
|
+
:fatal => :red,
|
102
|
+
:todo => :purple,
|
103
|
+
})
|
104
|
+
scheme = lcs.scheme
|
105
|
+
scheme['trace'] = "\e[38;5;33m"
|
106
|
+
scheme['fatal'] = "\e[38;5;89m"
|
107
|
+
scheme['todo'] = "\e[38;5;55m"
|
108
|
+
lcs.scheme scheme
|
109
|
+
@config[:log_opts] = lambda{|mlll| {
|
110
|
+
:pattern => "%#{mlll}l: %m %C\n",
|
111
|
+
:date_pattern => '%Y-%m-%d %H:%M:%S',
|
112
|
+
:color_scheme => 'compiler'
|
113
|
+
}
|
114
|
+
}
|
115
|
+
@config[:log_level] ||= :info
|
116
|
+
@logger = getLogger(@config)
|
117
|
+
end
|
118
|
+
|
119
|
+
def self.included(includer)
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Aws
|
2
|
+
module Cfn
|
3
|
+
module Dsl
|
4
|
+
module Output
|
5
|
+
|
6
|
+
def write(*s)
|
7
|
+
if s.is_a?(Array)
|
8
|
+
s = s.join('')
|
9
|
+
end
|
10
|
+
if @output.size > 0
|
11
|
+
@output[0].write s
|
12
|
+
else
|
13
|
+
print s
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def writeln(s='')
|
18
|
+
if @output.size > 0
|
19
|
+
@output[0].puts s
|
20
|
+
else
|
21
|
+
puts s
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def open_output(subdir,name)
|
26
|
+
if @config[:directory]
|
27
|
+
file = rb_file(subdir, name)
|
28
|
+
if i_am_maintainer(file)
|
29
|
+
@output.unshift File.open(file, 'w')
|
30
|
+
true
|
31
|
+
else
|
32
|
+
false
|
33
|
+
end
|
34
|
+
else
|
35
|
+
true
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def close_output()
|
40
|
+
if @config[:directory] and @output.size > 0
|
41
|
+
fp = @output.shift
|
42
|
+
fp.close
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.included(includer)
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,296 @@
|
|
1
|
+
module Aws
|
2
|
+
module Cfn
|
3
|
+
module Dsl
|
4
|
+
module PrettyPrint
|
5
|
+
|
6
|
+
def pprint(val)
|
7
|
+
logStep "Pretty print ..."
|
8
|
+
case detect_type(val)
|
9
|
+
when :template
|
10
|
+
pprint_cfn_template(val)
|
11
|
+
when :parameter
|
12
|
+
pprint_cfn_section 'parameter', 'TODO', val, 'Parameters'
|
13
|
+
when :resource
|
14
|
+
pprint_cfn_resource 'TODO', val
|
15
|
+
when :parameters
|
16
|
+
val.each { |k, v| pprint_cfn_section 'parameter', k, v, 'Parameters' }
|
17
|
+
when :resources
|
18
|
+
val.each { |k, v| pprint_cfn_resource k, v }
|
19
|
+
else
|
20
|
+
pprint_value(val, '')
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def pprint_cfn_template(tpl)
|
25
|
+
file = File.join(@config[:directory],File.basename(@config[:template].gsub(%r'\.(json|yaml|js|yaml)$'i, '.rb')))
|
26
|
+
filn = if @config[:expandedpaths]
|
27
|
+
File.expand_path(file)
|
28
|
+
else
|
29
|
+
file
|
30
|
+
end
|
31
|
+
file = File.basename(@config[:template].gsub(%r'\.(json|yaml|js|yaml)$'i, '.rb'))
|
32
|
+
# noinspection RubyParenthesesAroundConditionInspection
|
33
|
+
if (iam = open_output('', file.gsub(%r'\.(json|yaml|js|yml|rb)'i, '')))
|
34
|
+
logStep "Saving #{filn}"
|
35
|
+
writeln "#!/usr/bin/env ruby"
|
36
|
+
print_maintainer('')
|
37
|
+
writeln
|
38
|
+
if @config[:directory]
|
39
|
+
writeln "$:.unshift(File.dirname(__FILE__))"
|
40
|
+
# noinspection RubyExpressionInStringInspection
|
41
|
+
writeln '$:.unshift File.absolute_path("#{File.dirname(__FILE__)}/../lib")'
|
42
|
+
end
|
43
|
+
writeln "require 'bundler/setup'"
|
44
|
+
writeln "require 'aws/cfn/dsl/template'"
|
45
|
+
#writeln "require 'cloudformation-ruby-dsl/spotprice'"
|
46
|
+
#writeln "require 'cloudformation-ruby-dsl/table'"
|
47
|
+
writeln
|
48
|
+
writeln "template do"
|
49
|
+
writeln
|
50
|
+
tpl.each do |section, v|
|
51
|
+
case section
|
52
|
+
when 'Parameters'
|
53
|
+
when 'Mappings'
|
54
|
+
when 'Resources'
|
55
|
+
when 'Outputs'
|
56
|
+
else
|
57
|
+
write " value #{fmt_key(section)} => "
|
58
|
+
pprint_value v, ' '
|
59
|
+
writeln
|
60
|
+
writeln
|
61
|
+
end
|
62
|
+
end
|
63
|
+
else
|
64
|
+
@logger.warn "Not overwriting template: '#{file}'"
|
65
|
+
end
|
66
|
+
%w(Mappings Parameters Resources Outputs).each do |section|
|
67
|
+
writeln " # #{section}" if iam
|
68
|
+
v = tpl[section]
|
69
|
+
case section
|
70
|
+
when 'Parameters'
|
71
|
+
v.each { |name, options| pprint_cfn_section 'parameter', name, options, 'Parameters', iam }
|
72
|
+
when 'Mappings'
|
73
|
+
v.each { |name, options| pprint_cfn_section 'mapping', name, options, 'Mappings', iam }
|
74
|
+
when 'Resources'
|
75
|
+
v.each { |name, options| pprint_cfn_resource name, options, iam }
|
76
|
+
when 'Outputs'
|
77
|
+
v.each { |name, options| pprint_cfn_section 'output', name, options, 'Outputs', iam }
|
78
|
+
else
|
79
|
+
abort! "Internal Error: Unexpected section '#{section}'"
|
80
|
+
end
|
81
|
+
writeln if iam
|
82
|
+
end
|
83
|
+
writeln "end.exec!" if iam
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
def prelude_code(indent=' ')
|
88
|
+
"scope = Aws::Cfn::Compiler.binding[File.basename(File.dirname(__FILE__))][File.basename(__FILE__, '.rb')]\n"+
|
89
|
+
"template = scope[:template]\n"+
|
90
|
+
"\n"+
|
91
|
+
"# noinspection RubyStringKeysInHashInspection\n"+
|
92
|
+
"template." +
|
93
|
+
""
|
94
|
+
end
|
95
|
+
|
96
|
+
def print_with_wrapper(code,indent=' ')
|
97
|
+
write prelude_code(indent)+code.gsub(%r'^\s+','')
|
98
|
+
end
|
99
|
+
|
100
|
+
def pprint_cfn_section(section, name, options, subdir, brick=true)
|
101
|
+
filn = rb_file(subdir, name)
|
102
|
+
filn = File.expand_path(filn) if @config[:expandedpaths]
|
103
|
+
if open_output(subdir,name)
|
104
|
+
@logger.info "Pretty print #{section} '#{name}' to '#{filn}'"
|
105
|
+
print_maintainer ''
|
106
|
+
print_with_wrapper "#{section} #{fmt_string(name)}"
|
107
|
+
indent = ' ' + (' ' * section.length) + ' '
|
108
|
+
hang = true
|
109
|
+
options.each do |k, v|
|
110
|
+
if hang
|
111
|
+
writeln ','
|
112
|
+
hang = false
|
113
|
+
end
|
114
|
+
write indent, fmt_key(k), " => "
|
115
|
+
pprint_value v, indent
|
116
|
+
hang = true
|
117
|
+
end
|
118
|
+
writeln
|
119
|
+
writeln
|
120
|
+
close_output
|
121
|
+
add_brick(subdir,name) if brick
|
122
|
+
else
|
123
|
+
@logger.warn "NOT overwriting existing source file '#{filn}'"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def pprint_cfn_resource(name, options, brick=true)
|
128
|
+
subdir = 'Resources'
|
129
|
+
filn = rb_file(subdir, name)
|
130
|
+
filn = File.expand_path(filn) if @config[:expandedpaths]
|
131
|
+
if open_output(subdir,name)
|
132
|
+
@logger.info "Pretty print resource '#{name}' to '#{filn}'"
|
133
|
+
print_maintainer ''
|
134
|
+
print_with_wrapper "resource #{fmt_string(name)}"
|
135
|
+
indent = ' '
|
136
|
+
hang = true
|
137
|
+
options.each do |k, v|
|
138
|
+
if hang
|
139
|
+
writeln ','
|
140
|
+
hang = false
|
141
|
+
end
|
142
|
+
|
143
|
+
case k
|
144
|
+
when /^(Metadata|Properties)$/
|
145
|
+
write "#{indent}#{fmt_key(k)} => "
|
146
|
+
pprint_value options[k], indent
|
147
|
+
hang = true
|
148
|
+
else
|
149
|
+
write "#{indent}#{fmt_key(k)} => "
|
150
|
+
write "#{fmt(v)}"
|
151
|
+
hang = true
|
152
|
+
end
|
153
|
+
end
|
154
|
+
writeln
|
155
|
+
close_output
|
156
|
+
add_brick(subdir,name) if brick
|
157
|
+
else
|
158
|
+
@logger.warn "NOT overwriting existing source file '#{filn}'"
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def pprint_value(val, indent)
|
163
|
+
# Prefer to write the value on a single line if it's reasonable to do so
|
164
|
+
single_line = is_single_line(val) || is_single_line_hack(val)
|
165
|
+
if single_line && !is_multi_line_hack(val)
|
166
|
+
s = fmt(val)
|
167
|
+
if s.length < 120 || is_single_line_hack(val)
|
168
|
+
write s
|
169
|
+
return
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
# Print the value across multiple lines
|
174
|
+
if val.is_a?(Hash)
|
175
|
+
writeln "{"
|
176
|
+
val.each do |k, v|
|
177
|
+
write "#{indent} #{fmt_key(k)} => "
|
178
|
+
pprint_value v, indent + ' '
|
179
|
+
writeln ","
|
180
|
+
end
|
181
|
+
write "#{indent}}"
|
182
|
+
|
183
|
+
elsif val.is_a?(Array)
|
184
|
+
writeln "["
|
185
|
+
val.each do |v|
|
186
|
+
write "#{indent} "
|
187
|
+
pprint_value v, indent + ' '
|
188
|
+
writeln ","
|
189
|
+
end
|
190
|
+
write "#{indent}]"
|
191
|
+
|
192
|
+
elsif val.is_a?(FnCall) && val.multiline && @config[:functions] != 'raw'
|
193
|
+
write val.name, "("
|
194
|
+
args = val.arguments
|
195
|
+
sep = ''
|
196
|
+
sub_indent = indent + ' '
|
197
|
+
if val.name == 'join' && args.length > 1
|
198
|
+
pprint_value args[0], indent + ' '
|
199
|
+
args = args[1..-1]
|
200
|
+
sep = ','
|
201
|
+
sub_indent = indent + ' '
|
202
|
+
end
|
203
|
+
unless args.empty?
|
204
|
+
args.each do |v|
|
205
|
+
writeln sep
|
206
|
+
write sub_indent
|
207
|
+
pprint_value v, sub_indent
|
208
|
+
sep = ','
|
209
|
+
end
|
210
|
+
if val.name == 'join' && args.length > 1
|
211
|
+
write ","
|
212
|
+
end
|
213
|
+
writeln
|
214
|
+
write indent
|
215
|
+
end
|
216
|
+
write ")"
|
217
|
+
|
218
|
+
else
|
219
|
+
write fmt(val)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
def is_single_line(val)
|
224
|
+
if val.is_a?(Hash)
|
225
|
+
is_single_line(val.values)
|
226
|
+
elsif val.is_a?(Array)
|
227
|
+
val.empty? ||
|
228
|
+
(val.length == 1 && is_single_line(val[0]) && !val[0].is_a?(Hash)) ||
|
229
|
+
val.all? { |v| v.is_a?(String) }
|
230
|
+
else
|
231
|
+
true
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
# Emo-specific hacks to force the desired output formatting
|
236
|
+
def is_single_line_hack(val)
|
237
|
+
is_array_of_strings_hack(val)
|
238
|
+
end
|
239
|
+
|
240
|
+
# Emo-specific hacks to force the desired output formatting
|
241
|
+
def is_multi_line_hack(val)
|
242
|
+
val.is_a?(Hash) && val['email']
|
243
|
+
end
|
244
|
+
|
245
|
+
# Emo-specific hacks to force the desired output formatting
|
246
|
+
def is_array_of_strings_hack(val)
|
247
|
+
val.is_a?(Array) && val.all? { |v| v.is_a?(String) } && val.grep(/\s/).empty? && (
|
248
|
+
val.include?('autoscaling:EC2_INSTANCE_LAUNCH') ||
|
249
|
+
val.include?('m1.small')
|
250
|
+
)
|
251
|
+
end
|
252
|
+
|
253
|
+
def fmt(val)
|
254
|
+
if val == {}
|
255
|
+
'{}'
|
256
|
+
elsif val == []
|
257
|
+
'[]'
|
258
|
+
elsif val.is_a?(Hash)
|
259
|
+
'{ ' + (val.map { |k,v| fmt_key(k) + ' => ' + fmt(v) }).join(', ') + ' }'
|
260
|
+
elsif val.is_a?(Array) && is_array_of_strings_hack(val)
|
261
|
+
'%w(' + val.join(' ') + ')'
|
262
|
+
elsif val.is_a?(Array)
|
263
|
+
'[ ' + (val.map { |v| fmt(v) }).join(', ') + ' ]'
|
264
|
+
elsif val.is_a?(FnCall) && val.arguments.empty?
|
265
|
+
val.name
|
266
|
+
elsif val.is_a?(FnCall)
|
267
|
+
val.name + '(' + (val.arguments.map { |v| fmt(v) }).join(', ') + ')'
|
268
|
+
elsif val.is_a?(String)
|
269
|
+
fmt_string(val)
|
270
|
+
elsif val == nil
|
271
|
+
'null'
|
272
|
+
else
|
273
|
+
val.to_s # number, boolean
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
def fmt_key(s)
|
278
|
+
':' + (/^[a-zA-Z_]\w+$/ =~ s ? s : fmt_string(s)) # returns a symbol like :Foo or :'us-east-1'
|
279
|
+
end
|
280
|
+
|
281
|
+
def fmt_string(s)
|
282
|
+
if /[^ -~]/ =~ s
|
283
|
+
s.dump # contains, non-ascii or control char, return double-quoted string
|
284
|
+
else
|
285
|
+
'\'' + s.gsub(/([\\'])/, '\\\\\1') + '\'' # return single-quoted string, escape \ and '
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
def self.included(includer)
|
290
|
+
|
291
|
+
end
|
292
|
+
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|