templator 0.1 → 0.2
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.
- data/CHANGES +6 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +20 -10
- data/README.md +44 -40
- data/bin/templator +9 -39
- data/lib/templator/actions.rb +1 -1
- data/lib/templator/parameter_dsl.rb +157 -57
- data/lib/templator/{parameter_code_loader.rb → parameter_file_selector.rb} +2 -14
- data/lib/templator/parameters.rb +8 -8
- data/lib/templator/version.rb +3 -0
- data/spec/templator/actions_spec.rb +2 -2
- data/spec/templator/parameter_dsl_spec.rb +60 -19
- data/spec/templator/parameter_file_selector_spec.rb +29 -0
- data/spec/templator/parameters_spec.rb +6 -10
- metadata +197 -197
- data/spec/templator/parameter_code_loader_spec.rb +0 -29
data/CHANGES
CHANGED
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,22 +1,32 @@
|
|
1
1
|
GEM
|
2
2
|
remote: http://rubygems.org/
|
3
3
|
specs:
|
4
|
+
coderay (1.0.8)
|
4
5
|
diff-lcs (1.1.3)
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
6
|
+
fakefs (0.4.2)
|
7
|
+
method_source (0.8.1)
|
8
|
+
pry (0.9.10)
|
9
|
+
coderay (~> 1.0.5)
|
10
|
+
method_source (~> 0.8)
|
11
|
+
slop (~> 3.3.1)
|
12
|
+
rspec (2.12.0)
|
13
|
+
rspec-core (~> 2.12.0)
|
14
|
+
rspec-expectations (~> 2.12.0)
|
15
|
+
rspec-mocks (~> 2.12.0)
|
16
|
+
rspec-core (2.12.0)
|
17
|
+
rspec-expectations (2.12.0)
|
18
|
+
diff-lcs (~> 1.1.3)
|
19
|
+
rspec-mocks (2.12.0)
|
20
|
+
slop (3.3.3)
|
21
|
+
thor (0.16.0)
|
22
|
+
yard (0.8.3)
|
15
23
|
|
16
24
|
PLATFORMS
|
17
25
|
ruby
|
18
26
|
|
19
27
|
DEPENDENCIES
|
28
|
+
fakefs
|
29
|
+
pry
|
20
30
|
rspec
|
21
31
|
thor
|
22
32
|
yard
|
data/README.md
CHANGED
@@ -99,56 +99,56 @@ and :parameter2 is gotten by invoking the corresponding methods, parameter1 et p
|
|
99
99
|
|
100
100
|
The group method allows to define a subset of parameters.
|
101
101
|
|
102
|
-
group :my_group
|
102
|
+
group :my_group do
|
103
103
|
export :my_parameter => "my_value"
|
104
|
-
|
104
|
+
end
|
105
105
|
|
106
106
|
Nested group is also possible:
|
107
107
|
|
108
|
-
group :top
|
109
|
-
group :inner
|
108
|
+
group :top do
|
109
|
+
group :inner do
|
110
110
|
...
|
111
|
-
|
112
|
-
|
111
|
+
end
|
112
|
+
end
|
113
113
|
|
114
114
|
Value of parameters defined in other groups must be retrieved with
|
115
115
|
the fully qualified name of the parameter in dot notation.
|
116
116
|
|
117
|
-
group :foo_group
|
117
|
+
group :foo_group do
|
118
118
|
export :foo => "foo"
|
119
|
-
|
119
|
+
end
|
120
120
|
|
121
|
-
group :bar_group
|
121
|
+
group :bar_group do
|
122
122
|
export :bar => "bar"
|
123
|
-
|
123
|
+
end
|
124
124
|
|
125
|
-
group :foobar_group
|
125
|
+
group :foobar_group do
|
126
126
|
export :foobar => foo_group.foo + bar_group.bar
|
127
|
-
|
127
|
+
end
|
128
128
|
|
129
129
|
A group can de defined multiple times. The resulting group is a merge of all
|
130
130
|
definitions taking into account the order of the parsing:
|
131
131
|
|
132
132
|
#file1
|
133
|
-
group :my_group
|
133
|
+
group :my_group do
|
134
134
|
export :parameter1 => 1
|
135
135
|
export :parameter2 => 2
|
136
|
-
|
136
|
+
end
|
137
137
|
|
138
138
|
#file2
|
139
|
-
group :my_group
|
139
|
+
group :my_group do
|
140
140
|
export :parameter1 => 0.99999
|
141
141
|
export :parameter3 => 3
|
142
|
-
|
142
|
+
end
|
143
143
|
|
144
144
|
Assuming that file1 and file2 are parsed in this order, the resulting group
|
145
145
|
is semantically equivalent to this one:
|
146
146
|
|
147
|
-
group :my_group
|
147
|
+
group :my_group do
|
148
148
|
export :parameter1 => 0.99999
|
149
149
|
export :parameter2 => 2
|
150
150
|
export :parameter3 => 3
|
151
|
-
|
151
|
+
end
|
152
152
|
|
153
153
|
* __include_group__
|
154
154
|
|
@@ -158,21 +158,21 @@ It is conceptually equivalent to the well known Ruby include method.
|
|
158
158
|
|
159
159
|
Consider the following example:
|
160
160
|
|
161
|
-
group :mixin
|
161
|
+
group :mixin do
|
162
162
|
export :mixme => "some value"
|
163
|
-
|
163
|
+
end
|
164
164
|
|
165
|
-
group :my_group
|
165
|
+
group :my_group do
|
166
166
|
include_group :mixin
|
167
167
|
export :another_parameter => "another_value"
|
168
|
-
|
168
|
+
end
|
169
169
|
|
170
170
|
Thus, the resulting group is equivalent to :
|
171
171
|
|
172
|
-
group :my_group
|
172
|
+
group :my_group do
|
173
173
|
export :mixme => "some value"
|
174
174
|
export :another_parameter => "another_value"
|
175
|
-
|
175
|
+
end
|
176
176
|
|
177
177
|
Template Actions
|
178
178
|
----------------
|
@@ -183,15 +183,15 @@ In addition to the features provided by ERB, the following extra methods can be
|
|
183
183
|
|
184
184
|
* __param__
|
185
185
|
|
186
|
-
This method allows to retrieve the value of
|
186
|
+
This method allows to retrieve the value of parameters passed to Templator by the __-p__ swicth.
|
187
187
|
|
188
188
|
Here is a concrete example:
|
189
189
|
|
190
190
|
File _parameters.txt_:
|
191
191
|
|
192
|
-
group :my_group
|
193
|
-
export my_parameter => "my_value"
|
194
|
-
|
192
|
+
group :my_group do
|
193
|
+
export :my_parameter => "my_value"
|
194
|
+
end
|
195
195
|
...
|
196
196
|
|
197
197
|
File _template.txt_:
|
@@ -229,7 +229,7 @@ Here is an example that dynamically generates the name of the template to
|
|
229
229
|
include according to the value of a parameter:
|
230
230
|
|
231
231
|
blah blah blah
|
232
|
-
<%= include_file "#{param :my_parameter}.txt"
|
232
|
+
<%= include_file "#{param :my_parameter}.txt" %>
|
233
233
|
|
234
234
|
The path of the template to include is interpreted relatively from the path
|
235
235
|
of the source template.
|
@@ -255,23 +255,27 @@ file for three different hosts.
|
|
255
255
|
|
256
256
|
File _hosts.txt_:
|
257
257
|
|
258
|
-
group :
|
258
|
+
group :common_parameters do
|
259
|
+
export :gateway => "192.168.121.254"
|
260
|
+
end
|
261
|
+
|
262
|
+
group :host_a do
|
259
263
|
export :address => "192.168.121.1"
|
260
264
|
export :netmask => "255.255.255.0"
|
261
|
-
|
262
|
-
|
265
|
+
include_group :common_parameters
|
266
|
+
end
|
263
267
|
|
264
|
-
group :host_b
|
265
|
-
export :address => "192.168.
|
268
|
+
group :host_b do
|
269
|
+
export :address => "192.168.121.2"
|
266
270
|
export :netmask => "255.255.255.0"
|
267
|
-
|
268
|
-
|
271
|
+
include_group :common_parameters
|
272
|
+
end
|
269
273
|
|
270
|
-
group :host_c
|
271
|
-
export :address => "192.168.
|
274
|
+
group :host_c do
|
275
|
+
export :address => "192.168.121.3"
|
272
276
|
export :netmask => "255.255.255.0"
|
273
|
-
|
274
|
-
|
277
|
+
include_group :common_parameters
|
278
|
+
end
|
275
279
|
|
276
280
|
File _interfaces.txt_:
|
277
281
|
|
data/bin/templator
CHANGED
@@ -4,7 +4,9 @@ if RUBY_VERSION < "1.9"
|
|
4
4
|
require "rubygems"
|
5
5
|
end
|
6
6
|
|
7
|
-
|
7
|
+
lib_dir=File.expand_path(File.join(File.dirname(__FILE__), '../lib'))
|
8
|
+
$LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir)
|
9
|
+
|
8
10
|
require "thor"
|
9
11
|
require "templator/parameters"
|
10
12
|
require "templator/actions"
|
@@ -36,7 +38,12 @@ class TemplatorCli < Thor
|
|
36
38
|
@template = template
|
37
39
|
|
38
40
|
if options.has_key?("parameter-files")
|
39
|
-
|
41
|
+
begin
|
42
|
+
@parameters = Templator::Parameters.load_files *options["parameter-files"]
|
43
|
+
rescue Exception => e
|
44
|
+
error e
|
45
|
+
exit 1
|
46
|
+
end
|
40
47
|
end
|
41
48
|
|
42
49
|
template template, output
|
@@ -94,43 +101,6 @@ class TemplatorCli < Thor
|
|
94
101
|
def method_missing(method, *args)
|
95
102
|
@parameters.get(method)
|
96
103
|
end
|
97
|
-
|
98
|
-
# Check global consistency of provided options
|
99
|
-
def sanity_check
|
100
|
-
|
101
|
-
#mutually exclusive options
|
102
|
-
mandatory_and_mutually_exclusive "template-directory", "template-file"
|
103
|
-
optional_and_mutually_exclusive "output-directory", "output-file"
|
104
|
-
|
105
|
-
#dependent option
|
106
|
-
dependent "template-file", "output-file"
|
107
|
-
dependent "template-directory", "output-directory"
|
108
|
-
end
|
109
|
-
|
110
|
-
# Check that options hash has one and only one of two given keys
|
111
|
-
def mandatory_and_mutually_exclusive(key1, key2)
|
112
|
-
raise Thor::Error.new("One of --#{key1} or --#{key2} must be provided. Try again.") unless (options.has_key?(key1) ^ options.has_key?(key2))
|
113
|
-
end
|
114
|
-
|
115
|
-
# Check that options hash has zero or one of two given keys
|
116
|
-
def optional_and_mutually_exclusive(key1, key2)
|
117
|
-
raise Thor::Error.new("Only one of --#{key1} or --#{key2} must be provided. Try again.") unless (options.has_key(key1) || options.has_key?(key2)) || ! (options.has_key?(key1) && options.has_key?(key2))
|
118
|
-
end
|
119
|
-
|
120
|
-
# Check that options hash has child_key if it has parent_key.
|
121
|
-
def dependent(parent_key, child_key)
|
122
|
-
raise Thor::Error.new("--#{child_key} must be provided when using --#{parent_key}. Try again.") if (options.has_key?(parent_key) && ! options.has_key?(child_key))
|
123
|
-
end
|
124
|
-
|
125
|
-
# Build a list of template files from provided options
|
126
|
-
def templates
|
127
|
-
options.has_key?("template-file") ? [options["template-file"]] : options["template-directory"]
|
128
|
-
end
|
129
|
-
|
130
|
-
# Build the output path based on the given option and the current template file.
|
131
|
-
def output_path(template_file)
|
132
|
-
options.has_key?("output-directory") ? File.join(options["output-directory"], template_file) : options["output-file"]
|
133
|
-
end
|
134
104
|
end
|
135
105
|
end
|
136
106
|
|
data/lib/templator/actions.rb
CHANGED
@@ -28,7 +28,7 @@ module Templator
|
|
28
28
|
search_path.each do |dir|
|
29
29
|
path = File.join(dir, filename)
|
30
30
|
if File.exist?(path)
|
31
|
-
content = ERB.new(::File.read(path), nil, '-', '
|
31
|
+
content = ERB.new(::File.read(path), nil, '-', 'included_template').result(binding)
|
32
32
|
throw :file_found
|
33
33
|
end
|
34
34
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
module Templator
|
2
2
|
|
3
|
-
#
|
3
|
+
# Parse a the given files with respect to the Parameter DSL.
|
4
|
+
#
|
4
5
|
# Supported DSL methods are :
|
5
6
|
# * export(hash) : defines a list of parameters from the given hash
|
6
7
|
# * group(name, block) : defines a group of parameter
|
@@ -23,32 +24,142 @@ module Templator
|
|
23
24
|
# include_group "group2.group3"
|
24
25
|
# end
|
25
26
|
#
|
26
|
-
#
|
27
27
|
# param5 value can retrieved with the following :
|
28
|
-
# p =
|
28
|
+
# p = ParameterFileLoader.new.parse("path/to/parameter_file")
|
29
29
|
# p.group2.param5
|
30
30
|
#
|
31
|
-
|
32
|
-
class ParameterDsl
|
31
|
+
class ParameterFileLoader
|
33
32
|
|
34
|
-
#
|
35
|
-
|
33
|
+
# Parses the given files
|
34
|
+
# @param [String[]] files files to parse
|
35
|
+
# @return [Object] a dynamically built object
|
36
|
+
# whose methods allow to access values of parameters defined in given code.
|
37
|
+
def parse(*files)
|
38
|
+
files.each do |file|
|
39
|
+
begin
|
40
|
+
load file
|
41
|
+
rescue ::Exception => e
|
42
|
+
raise ParseError.new(e, file)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
DslContext.top_level_group
|
46
|
+
end
|
36
47
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
48
|
+
end
|
49
|
+
|
50
|
+
# Error wrapper of all errors occuring during the parsing of a parameter file.
|
51
|
+
# This wrapper provides convenient methods to retrieve the origin of the error.
|
52
|
+
class ParseError < Exception
|
53
|
+
|
54
|
+
attr_reader :file, :line
|
55
|
+
|
56
|
+
def initialize(original_exception, file)
|
57
|
+
@original_exception = original_exception
|
58
|
+
@file = file
|
59
|
+
|
60
|
+
process original_exception
|
41
61
|
end
|
42
62
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
@
|
63
|
+
|
64
|
+
def message_to_s
|
65
|
+
@message.sub(/\A.*:\d+:\s*/, "")
|
66
|
+
end
|
67
|
+
|
68
|
+
def origin_to_s
|
69
|
+
"in file #{file}" + (@line ? " line #{@line}" : "")
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
def to_s
|
74
|
+
"ParseError #{origin_to_s}: #{message_to_s}"
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def process(exception)
|
80
|
+
@message = exception.message
|
81
|
+
|
82
|
+
case exception
|
83
|
+
when LoadError, ::SyntaxError
|
84
|
+
trace = nil
|
85
|
+
else
|
86
|
+
trace = exception.backtrace.drop_while {|line| line.match(/#{__FILE__}/)}.first
|
87
|
+
end
|
88
|
+
|
89
|
+
@line = find_line_number_in_trace trace if trace
|
90
|
+
end
|
91
|
+
|
92
|
+
def find_line_number_in_trace(trace)
|
93
|
+
line = nil
|
94
|
+
matcher = trace.match(/:(\d+):/)
|
95
|
+
line = matcher[1].to_i if matcher
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
# Base class to define a group.
|
101
|
+
# A Group instance is created whenever a group method is parsed from the DSL code.
|
102
|
+
# Methods are dynamically created inside the singleton of the instance to access nested parameters and groups.
|
103
|
+
class Group
|
104
|
+
attr_reader :name
|
105
|
+
def initialize(name)
|
106
|
+
@name = name
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Context used by the DSL methods to retrieve and update
|
111
|
+
# the current group.
|
112
|
+
class DslContext
|
113
|
+
|
114
|
+
# Retrieve the current group from the context
|
115
|
+
# @return [Group] the current group
|
116
|
+
def self.current_group
|
117
|
+
group_stack.last
|
118
|
+
end
|
119
|
+
|
120
|
+
# Retrieve the top level group from the context
|
121
|
+
# @return [Group] the top level group
|
122
|
+
def self.top_level_group
|
123
|
+
group_stack.first
|
124
|
+
end
|
125
|
+
|
126
|
+
# Enter a new group.
|
127
|
+
# This method shall be called by the DSL methods whenever
|
128
|
+
# a new group is entered.
|
129
|
+
# @param [Group] group group entered.
|
130
|
+
def self.enter_group(group)
|
131
|
+
group_stack.push(group)
|
132
|
+
end
|
133
|
+
|
134
|
+
# Leave a group.
|
135
|
+
# This method shall be calles by the DSL methods whenever
|
136
|
+
# a group is left.
|
137
|
+
def self.leave_group
|
138
|
+
group_stack.pop
|
50
139
|
end
|
51
140
|
|
141
|
+
private
|
142
|
+
|
143
|
+
# Retrieve the stack of groups.
|
144
|
+
# The stack is automatically created on the first call
|
145
|
+
# and the top level group is inserted as the first element
|
146
|
+
# of the stack.
|
147
|
+
# @return [Array<Group>] the stack of groups.
|
148
|
+
def self.group_stack
|
149
|
+
if (@group_stack.nil?)
|
150
|
+
@group_stack = []
|
151
|
+
@group_stack.push Group.new(DslMethods::TOP_LEVEL_GROUP_NAME)
|
152
|
+
end
|
153
|
+
@group_stack
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
# Module that defines the Parameter DSL methods.
|
158
|
+
module DslMethods
|
159
|
+
|
160
|
+
# Name of the implicit top level group
|
161
|
+
TOP_LEVEL_GROUP_NAME = "__top__"
|
162
|
+
|
52
163
|
# Defines parameters providing a name and a value for each parameter.
|
53
164
|
# @param [Hash]params hash of parameter name and value
|
54
165
|
def export(params)
|
@@ -80,21 +191,21 @@ module Templator
|
|
80
191
|
|
81
192
|
private
|
82
193
|
|
83
|
-
# Gets the current group from the
|
194
|
+
# Gets the current group from the DslContext
|
84
195
|
def current_group
|
85
|
-
|
196
|
+
DslContext.current_group
|
86
197
|
end
|
87
198
|
|
88
199
|
# Manages the entry in a new group:
|
89
|
-
# * create a new Group instance
|
90
|
-
# * define a method in the current group to access
|
91
|
-
# *
|
92
|
-
# @param [#to_s] name of the new group
|
200
|
+
# * create a new Group instance (or retrieve it if it already exists in the current context)
|
201
|
+
# * define a method in the current group to access the new group
|
202
|
+
# * notify the context about the entry in a new group
|
203
|
+
# @param [#to_s] name name of the new group
|
93
204
|
#
|
94
205
|
def enter_group(name)
|
95
206
|
group = group_in_current_context(name) || Group.new(name)
|
96
207
|
define_method_in_current_group(name) {group}
|
97
|
-
|
208
|
+
DslContext.enter_group(group)
|
98
209
|
end
|
99
210
|
|
100
211
|
# Defines a method inside the current group
|
@@ -102,43 +213,32 @@ module Templator
|
|
102
213
|
# @param [Block] method_block block of the méthode to define
|
103
214
|
def define_method_in_current_group(method_name, &method_block)
|
104
215
|
(class << current_group; self; end).send(:define_method, method_name, method_block)
|
105
|
-
|
216
|
+
end
|
106
217
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
218
|
+
# Verify if a group belongs to the current group
|
219
|
+
# @param [#to_s] name name of the group to control
|
220
|
+
# @return the group with the given name if it belongs to the current group, nil otherwise
|
221
|
+
def group_in_current_context(name)
|
222
|
+
current_group.respond_to?(name) ? current_group.send(name) : nil
|
223
|
+
end
|
113
224
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
225
|
+
# Manages the exit from a group
|
226
|
+
def leave_group
|
227
|
+
DslContext.leave_group
|
228
|
+
end
|
118
229
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
230
|
+
# Manages the access to a parameter outside of the current group
|
231
|
+
def method_missing(name, *args)
|
232
|
+
DslContext.top_level_group.send(name, *args)
|
233
|
+
end
|
123
234
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
235
|
+
# Get a group from its fully qualified name
|
236
|
+
def get_group(fully_qualified_name)
|
237
|
+
fully_qualified_name.to_s.split('.').inject(DslContext.top_level_group) {|result, name| result.send(name)}
|
238
|
+
end
|
128
239
|
|
129
|
-
# Return the top level goup
|
130
|
-
def top_level_group
|
131
|
-
@group_stack.first
|
132
240
|
end
|
133
|
-
|
134
241
|
end
|
135
242
|
|
136
|
-
#
|
137
|
-
|
138
|
-
# Methods are dynamically created inside the singleton of the instance to access nested parameters and groups.
|
139
|
-
class Group
|
140
|
-
def initialize(name)
|
141
|
-
@name = name
|
142
|
-
end
|
143
|
-
end
|
144
|
-
end
|
243
|
+
#inject the DSL methos in the main object
|
244
|
+
extend Templator::DslMethods
|