ytemplate 0.0.1
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/.document +3 -0
- data/.gitignore +4 -0
- data/.rspec +2 -0
- data/CHANGES.md +8 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.md +71 -0
- data/Rakefile +3 -0
- data/lib/ytemplate.rb +391 -0
- data/lib/ytemplate/misc.rb +249 -0
- data/lib/ytemplate/version.rb +5 -0
- data/spec/spec_helper.rb +50 -0
- data/spec/ytemplate_spec.rb +42 -0
- data/ytemplate.gemspec +28 -0
- metadata +94 -0
data/.document
ADDED
data/.gitignore
ADDED
data/.rspec
ADDED
data/CHANGES.md
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Malo Skrylevo
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
# YAML-шаблонъ
|
2
|
+
|
3
|
+
YAML-шаблонъ есть расширитель YAML модели шаблонами. Онъ позволяетъ расшрирять любой YAML-документъ, предоставляя пользователю развёртывать и проверять его по опредѣлённому шаблону.
|
4
|
+
|
5
|
+
## Использованіе
|
6
|
+
|
7
|
+
### Простая развёртка
|
8
|
+
|
9
|
+
require 'ytemplate'
|
10
|
+
|
11
|
+
SampleTemplate = <<C
|
12
|
+
---
|
13
|
+
local=:
|
14
|
+
lkey: lvalue
|
15
|
+
key1: value
|
16
|
+
key2: %local
|
17
|
+
key3:
|
18
|
+
%local:
|
19
|
+
key4: value
|
20
|
+
C
|
21
|
+
|
22
|
+
tmpl = YAML::Template.new SampleTemplate
|
23
|
+
|
24
|
+
o = tmpl.deploy # => {"key1"=>"value", "key2"=>{"lkey"=>"lvalue"}, "key3"=>{"lkey"=>"lvalue", "key4"=>"value"}}
|
25
|
+
puts o.to_yaml
|
26
|
+
# ---
|
27
|
+
# key1: value
|
28
|
+
# key2:
|
29
|
+
# lkey: lvalue
|
30
|
+
# key3:
|
31
|
+
# lkey: lvalue
|
32
|
+
# key4: value
|
33
|
+
|
34
|
+
|
35
|
+
### Развёртка въ иной YAML-документъ
|
36
|
+
|
37
|
+
SampleFile = <<C
|
38
|
+
---
|
39
|
+
key1: value
|
40
|
+
key2:
|
41
|
+
lkey: lvalue
|
42
|
+
key3:
|
43
|
+
lkey: lvalue
|
44
|
+
key4: value
|
45
|
+
C
|
46
|
+
|
47
|
+
o = tmpl.deploy_to( YAML.load( SampleFile) ) # => {"key1"=>"+value", "key2"=>{"lkey"=>"+lvalue"}, "key3"=>{"lkey"=>"+lvalue", "key4"=>"+value"}}
|
48
|
+
puts o.to_yaml
|
49
|
+
# ---
|
50
|
+
# key1: +value
|
51
|
+
# key2:
|
52
|
+
# lkey: +lvalue
|
53
|
+
# key3:
|
54
|
+
# lkey: +lvalue
|
55
|
+
# key4: +value
|
56
|
+
|
57
|
+
### Провѣрка подобности
|
58
|
+
|
59
|
+
Можно провѣрить, подобенъ ли YAML-документъ нѣкоему шаблону. Выходомъ метода 'match' будетъ Наборъ ошибокъ, представленныхъ въ видѣ текста.
|
60
|
+
|
61
|
+
file = YAML.load( SampleFile )
|
62
|
+
o = tmpl.match( file ) # => []
|
63
|
+
|
64
|
+
file['key1'] = 'novalue'
|
65
|
+
o = tmpl.match( file ) # => [":key1 => novalue =~ value"]
|
66
|
+
|
67
|
+
# Права
|
68
|
+
|
69
|
+
Авторскія и исключительныя права (а) 2011 Малъ Скрылевъ
|
70
|
+
Зри LICENSE за подробностями.
|
71
|
+
|
data/Rakefile
ADDED
data/lib/ytemplate.rb
ADDED
@@ -0,0 +1,391 @@
|
|
1
|
+
#!/usr/bin/ruby -KU
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require 'yaml'
|
5
|
+
require 'rdoba/re'
|
6
|
+
require 'rdoba/debug'
|
7
|
+
require 'rdoba/deploy'
|
8
|
+
require 'rdoba/yaml'
|
9
|
+
|
10
|
+
module YAML
|
11
|
+
class Template
|
12
|
+
|
13
|
+
# def puts(*args)
|
14
|
+
# File.open('yaml_t.log','a') do |f| f.puts(*args) end
|
15
|
+
# end
|
16
|
+
|
17
|
+
@@errors = {
|
18
|
+
:dict_unallow => 'Словарь не допустим на уровне %i',
|
19
|
+
:var_undef => 'Переменная %s не определена',
|
20
|
+
:no_tmpl_str => 'Значение строки шаблона отсутствует',
|
21
|
+
:added => 'Переменная \'%s\' добавлена',
|
22
|
+
}
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def deploy_verility(content)
|
27
|
+
@verility_template = YAML.load( StringIO.new content.to_s ).deploy
|
28
|
+
deploy_value(@verility_template)
|
29
|
+
end
|
30
|
+
|
31
|
+
def deploy_value(value)
|
32
|
+
@levelno ? @levelno += 1 : @levelno = 0
|
33
|
+
res = case value.class.to_s
|
34
|
+
when 'Hash'
|
35
|
+
unless value.class == Hash
|
36
|
+
@levelno -= 1
|
37
|
+
raise @@errors[:dict_unallow] % @levelno
|
38
|
+
end
|
39
|
+
|
40
|
+
value.each_pair do |tkey, tvalue|
|
41
|
+
if tkey =~ /^(.*)=$/ and not @vars.keys.include? $1
|
42
|
+
dbp12 "[deploy_value]> #{@@errors[:added] % $1}"
|
43
|
+
@vars[$1] = tvalue
|
44
|
+
value.delete(tkey)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
err = []
|
49
|
+
unless (value.each_pair do |tkey, tvalue|
|
50
|
+
begin
|
51
|
+
if tkey =~ /^%(.*)$/
|
52
|
+
raise @@errors[:var_undef] % $1 unless @vars.keys.include? $1
|
53
|
+
value.delete(tkey)
|
54
|
+
@vars[$1].each_pair do |k,v| value[k] = v; end
|
55
|
+
else
|
56
|
+
break false unless deploy_value(tkey)
|
57
|
+
end
|
58
|
+
break false unless deploy_value(tvalue)
|
59
|
+
rescue
|
60
|
+
$stderr.puts "Exception: #{$!}\n\t#{$@.join("\n\t")}"
|
61
|
+
err << $!.to_s
|
62
|
+
break false
|
63
|
+
end
|
64
|
+
end)
|
65
|
+
@levelno -= 1
|
66
|
+
raise err.join(',')
|
67
|
+
end
|
68
|
+
|
69
|
+
when 'Array'
|
70
|
+
err = []
|
71
|
+
unless (value.each do |tvalue|
|
72
|
+
begin
|
73
|
+
break false unless deploy_value(tvalue)
|
74
|
+
rescue
|
75
|
+
$stderr.puts "Exception: #{$!}\n\t#{$@.join("\n\t")}"
|
76
|
+
err << $!.to_s
|
77
|
+
break false
|
78
|
+
end
|
79
|
+
end)
|
80
|
+
@levelno -= 1
|
81
|
+
raise err.join(',')
|
82
|
+
end
|
83
|
+
|
84
|
+
when 'String'
|
85
|
+
if value == ''
|
86
|
+
@levelno -= 1
|
87
|
+
raise @@errors[:no_tmpl_str]
|
88
|
+
end
|
89
|
+
|
90
|
+
else
|
91
|
+
end
|
92
|
+
|
93
|
+
@levelno -= 1
|
94
|
+
true
|
95
|
+
end
|
96
|
+
|
97
|
+
def initialize(verility_template)
|
98
|
+
@levels = []
|
99
|
+
@vars = {}
|
100
|
+
deploy_verility(verility_template)
|
101
|
+
end
|
102
|
+
|
103
|
+
public
|
104
|
+
|
105
|
+
def deploy
|
106
|
+
@verility_template.clone
|
107
|
+
end
|
108
|
+
|
109
|
+
def deploy_to(inval, options = {})
|
110
|
+
# options:
|
111
|
+
# required: array - field list in hash that is required to be shewn
|
112
|
+
# use_template: true | +false
|
113
|
+
# expand_level: integer - force expand upto the specified level( or -1 to full) the hash and array
|
114
|
+
dbp11 "[deploy_to] <<< #{inval.inspect}, #{options.inspect}"
|
115
|
+
options = { :required => [], :use_template => false, :expand_level => 0 } | options
|
116
|
+
options[:expand_level] = -1 if options[:expand_level].class != Fixnum
|
117
|
+
|
118
|
+
hash = @verility_template.clone
|
119
|
+
|
120
|
+
def deploy_array_to(form, tform, options = {})
|
121
|
+
dbp11 "[deploy_array_to] <<< form = #{form.inspect}, tform = #{tform.inspect}, options = #{options.inspect}"
|
122
|
+
tidx = -tform.size
|
123
|
+
expand = options[:expand_level]
|
124
|
+
form.reverse.each do |value|
|
125
|
+
expand = true
|
126
|
+
dbp14 "[deploy_array_to]> #{value.inspect} <<< #{tform[0].inspect}"
|
127
|
+
case value.class.to_s
|
128
|
+
when 'Hash'
|
129
|
+
deploy_hash_to(value, tform[0], options)
|
130
|
+
when 'Array'
|
131
|
+
deploy_array_to(value, tform[0], options)
|
132
|
+
when 'String'
|
133
|
+
if tform[0] =~ /@.*@(.*)$/
|
134
|
+
# - matched array value /@expr@/ =~ '@.*@'
|
135
|
+
value.replace(tform[0] + value)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
if options[:expand_level] < 0
|
141
|
+
tform.reverse.each do |tvalue|
|
142
|
+
dbp12 "[deploy_array_to]> Append: #{tvalue.inspect}"
|
143
|
+
form.push(tvalue)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
dbp11 "[deploy_array_to] >>> #{form.inspect}"
|
147
|
+
end
|
148
|
+
|
149
|
+
def crop_to(level, value)
|
150
|
+
dbp14 "[crop_to] <<< level = #{level.inspect}, value = #{value.inspect}"
|
151
|
+
level -= 1 if level > 0
|
152
|
+
res = case value.class.to_sym
|
153
|
+
when :Hash
|
154
|
+
res = {}
|
155
|
+
value.each do |k,v| res[k] = level != 0 && crop_to(level, v) || nil end if level != 0
|
156
|
+
res
|
157
|
+
when :Array
|
158
|
+
res = []
|
159
|
+
value.each do |v|
|
160
|
+
res << crop_to(level, v)
|
161
|
+
end if level != 0
|
162
|
+
res
|
163
|
+
else
|
164
|
+
value
|
165
|
+
end
|
166
|
+
dbp14 "[crop_to] >>> #{res.inspect}"
|
167
|
+
res
|
168
|
+
end
|
169
|
+
|
170
|
+
def deploy_hash_to(form, tform, options = {})
|
171
|
+
dbp11 "[deploy_hash_to] <<< form = #{form.inspect}, tform = #{tform.inspect}, options = #{options.inspect}"
|
172
|
+
rplc = {}
|
173
|
+
expand = options[:expand_level] || (not (options[:required].empty? ||
|
174
|
+
options[:required].each do |x| break false if tform.key?(x) end ))
|
175
|
+
form.each_pair do |key, value|
|
176
|
+
expand = true
|
177
|
+
tform.each_pair do |tkey, tvalue|
|
178
|
+
dbp12 "[deploy_hash_to]> Check #{tkey} => #{key}"
|
179
|
+
if tkey =~ /^\/([^\/]+)/ and key =~ /#{$1}/ and key !~ /^\//
|
180
|
+
# matched hash key /(sr|...)/ =~ 'sr'
|
181
|
+
p tkey, key, options
|
182
|
+
if options[:use_template]
|
183
|
+
nkey = tkey.gsub(key,"+#{key}")
|
184
|
+
dbp12 "[deploy_hash_to]> Replace key #{key} => #{nkey}"
|
185
|
+
rplc[key] = nkey
|
186
|
+
end
|
187
|
+
key = tkey
|
188
|
+
end
|
189
|
+
|
190
|
+
if tkey == key
|
191
|
+
dbp14 "[deploy_hash_to]> Try deploy #{key}: #{value.inspect} <= #{tvalue.inspect}"
|
192
|
+
break case value.class.to_s
|
193
|
+
when 'Hash'
|
194
|
+
deploy_hash_to(value, tvalue, options)
|
195
|
+
when 'Array'
|
196
|
+
deploy_array_to(value, tvalue, options)
|
197
|
+
when 'String'
|
198
|
+
value.insert(0, '+') if tvalue == value
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
rplc.each do |key,nkey| form[nkey] = form.delete(key); end
|
205
|
+
|
206
|
+
if expand
|
207
|
+
tform.each_pair do |tkey, tvalue|
|
208
|
+
dbp14 "[deploy_hash_to]> Template pair: #{tkey.inspect} => #{tvalue.inspect}"
|
209
|
+
if form.key?(tkey)
|
210
|
+
dbp14 "[deploy_hash_to]> Form has template key: #{tkey.inspect}"
|
211
|
+
basevalue = form[tkey]
|
212
|
+
if tvalue.class == String
|
213
|
+
dbp14 "[deploy_hash_to]> Try match string: #{basevalue.inspect} to string as re: #{tvalue.inspect}"
|
214
|
+
if tvalue =~ /^\/(.*)/u and (re_s = $1) =~ /[\(\)]/ and basevalue.match(/#{re_s}/u)
|
215
|
+
# matched value /(sr|...)/ =~ 'sr'
|
216
|
+
form[tkey] = tvalue.sub(basevalue, '+' + basevalue)
|
217
|
+
elsif tvalue =~ /@.*@(.*)$/
|
218
|
+
# matched value /@expr@/ =~ '@.*@'
|
219
|
+
form[tkey] = tvalue + basevalue + $1.to_s
|
220
|
+
end
|
221
|
+
end
|
222
|
+
else
|
223
|
+
dbp14 "[deploy_hash_to]> Form has NO template key: #{tkey.inspect}"
|
224
|
+
basekey = tkey =~ /(.*)\[.*\]/
|
225
|
+
form[tkey] = if basekey and form.key?($1)
|
226
|
+
basevalue = form.delete($1)
|
227
|
+
tvalue.sub(/#{basevalue}/, '+' + basevalue)
|
228
|
+
else
|
229
|
+
if options[:expand_level]
|
230
|
+
dbp14 "[deploy_hash_to]> Expand level >>> #{options[:expand_level].inspect}"
|
231
|
+
crop_to(options[:expand_level].to_i, tvalue)
|
232
|
+
elsif tvalue.class == String
|
233
|
+
tvalue
|
234
|
+
else
|
235
|
+
tvalue.dup.clear
|
236
|
+
end if options[:expand_level].to_i != 0
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
inhash = if inval.class == String
|
244
|
+
YAML.load(StringIO.new(inval)) || {}
|
245
|
+
elsif inval.class == Hash
|
246
|
+
inval
|
247
|
+
else
|
248
|
+
raise "The class '#{inval.class}' of the input parameter is undeploable"
|
249
|
+
end
|
250
|
+
|
251
|
+
begin
|
252
|
+
deploy_hash_to(inhash, hash, options)
|
253
|
+
rescue
|
254
|
+
$stderr.puts "Error #{$!}\t#{$@.join("\n\t")}"
|
255
|
+
return {}
|
256
|
+
end
|
257
|
+
|
258
|
+
dbp18 "[deploy_to]>>> output dump: vvv\n #{inhash.inspect}"
|
259
|
+
|
260
|
+
dbp11 "[deploy_to] >>> #{inhash.to_yml}"
|
261
|
+
|
262
|
+
inval.class == String ? inval.replace(inhash.to_yml) : inval.replace(inhash)
|
263
|
+
inhash
|
264
|
+
end
|
265
|
+
|
266
|
+
def match(inval, options = {}) #TODO add path to a base value to match... ":key1:key2", to match to => key3:value
|
267
|
+
# options:
|
268
|
+
# required: array - field list in hash that is required to be shewn
|
269
|
+
# use_template: true | +false
|
270
|
+
# expand_level: integer - force expand upto the specified level( or -1 to full) the hash and array
|
271
|
+
dbp11 "[match] <<< #{inval.inspect}, #{options.inspect}"
|
272
|
+
options = { :required => [], :use_template => false, :expand_level => 0 } | options
|
273
|
+
options[:expand_level] = -1 if options[:expand_level].class != Fixnum
|
274
|
+
|
275
|
+
hash = @verility_template.clone
|
276
|
+
|
277
|
+
err = []
|
278
|
+
|
279
|
+
def crop_to(level, value)
|
280
|
+
dbp14 "[crop_to] <<< level = #{level.inspect}, value = #{value.inspect}"
|
281
|
+
level -= 1 if level > 0
|
282
|
+
res = case value.class.to_sym
|
283
|
+
when :Hash
|
284
|
+
res = {}
|
285
|
+
value.each do |k,v| res[k] = level != 0 && crop_to(level, v) || nil end if level != 0
|
286
|
+
res
|
287
|
+
when :Array
|
288
|
+
res = []
|
289
|
+
value.each do |v|
|
290
|
+
res << crop_to(level, v)
|
291
|
+
end if level != 0
|
292
|
+
res
|
293
|
+
else
|
294
|
+
value
|
295
|
+
end
|
296
|
+
dbp14 "[crop_to] >>> #{res.inspect}"
|
297
|
+
res
|
298
|
+
end
|
299
|
+
|
300
|
+
def match_string(path, value, tvalue, err, options = {})
|
301
|
+
return if tvalue =~ /@.*@(.*)$/
|
302
|
+
|
303
|
+
if value != tvalue
|
304
|
+
err << "#{path.join(':')} => #{value} =~ #{tvalue}"
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
def match_array(path, form, tform, err, options = {})
|
309
|
+
dbp11 "[match_array] <<< form = #{form.inspect}, tform = #{tform.inspect}, options = #{options.inspect}"
|
310
|
+
tidx = -tform.size
|
311
|
+
expand = options[:expand_level]
|
312
|
+
size = from.size
|
313
|
+
rform = form.reverse.each_index do |idx|
|
314
|
+
value = form[-idx - 1]
|
315
|
+
expand = true
|
316
|
+
dbp14 "[match_array]> #{value.inspect} <<< #{tform[0].inspect}"
|
317
|
+
npath = path.dup << size - idx - 1
|
318
|
+
case value.class.to_s
|
319
|
+
when 'Hash'
|
320
|
+
match_hash(npath, value, tform[0], err, options)
|
321
|
+
when 'Array'
|
322
|
+
match_array(npath, value, tform[0], err, options)
|
323
|
+
when 'String'
|
324
|
+
match_array(npath, value, tform[0], err, options)
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
dbp11 "[match_array] >>> #{form.inspect}"
|
329
|
+
end
|
330
|
+
|
331
|
+
def match_hash(path, form, tform, err, options = {})
|
332
|
+
dbp11 "[match_hash] <<< form = #{form.inspect}, tform = #{tform.inspect}, options = #{options.inspect}"
|
333
|
+
rplc = {}
|
334
|
+
expand = options[:expand_level] || (not (options[:required].empty? ||
|
335
|
+
options[:required].each do |x| break false if tform.key?(x) end ))
|
336
|
+
form.each_pair do |key, value|
|
337
|
+
expand = true
|
338
|
+
tform.each_pair do |tkey, tvalue|
|
339
|
+
dbp12 "[match_hash]> Check #{tkey} => #{key}"
|
340
|
+
if tkey =~ /^\/([^\/]+)/ and key =~ /#{$1}/
|
341
|
+
# matched hash key /(sr|...)/ =~ 'sr'
|
342
|
+
if options[:use_template]
|
343
|
+
nkey = tkey.gsub(key,"+#{key}")
|
344
|
+
dbp12 "[match_hash]> Replace key #{key} => #{nkey}"
|
345
|
+
rplc[key] = nkey
|
346
|
+
end
|
347
|
+
key = tkey
|
348
|
+
end
|
349
|
+
|
350
|
+
if tkey == key
|
351
|
+
dbp14 "[match_hash]> Try match #{key} => #{value.inspect}"
|
352
|
+
npath = path.dup << key
|
353
|
+
break case value.class.to_sym
|
354
|
+
when :Hash
|
355
|
+
match_hash(npath, value, tvalue, err, options)
|
356
|
+
when :Array
|
357
|
+
match_array(npath, value, tvalue, err, options)
|
358
|
+
when :String
|
359
|
+
match_string(npath, value, tvalue, err, options)
|
360
|
+
end
|
361
|
+
end
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
end
|
366
|
+
|
367
|
+
inhash = if inval.class == String
|
368
|
+
YAML.load(StringIO.new(inval)) || {}
|
369
|
+
elsif inval.class == Hash
|
370
|
+
inval
|
371
|
+
else
|
372
|
+
raise "The class '#{inval.class}' of the input parameter is unmatchable"
|
373
|
+
end
|
374
|
+
|
375
|
+
begin
|
376
|
+
match_hash([''], inhash, hash, err, options)
|
377
|
+
rescue
|
378
|
+
$stderr.puts "Error #{$!}\t#{$@.join("\n\t")}"
|
379
|
+
return {}
|
380
|
+
end
|
381
|
+
|
382
|
+
dbp18 "[match]>>> output dump: vvv\n #{inhash.inspect}"
|
383
|
+
|
384
|
+
dbp11 "[match] >>> #{inhash.to_yml}"
|
385
|
+
|
386
|
+
inval.class == String ? inval.replace(inhash.to_yml) : inval.replace(inhash)
|
387
|
+
err
|
388
|
+
end
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
@@ -0,0 +1,249 @@
|
|
1
|
+
#!/usr/bin/ruby -KU
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require 'yaml'
|
5
|
+
require 'strscan'
|
6
|
+
require 'rdoba'
|
7
|
+
|
8
|
+
module YAML
|
9
|
+
class TemplateMisc
|
10
|
+
|
11
|
+
# def puts(*args)
|
12
|
+
# File.open('yaml_t.log','a') do |f| f.puts(*args) end
|
13
|
+
# end
|
14
|
+
|
15
|
+
@@errors = {
|
16
|
+
:unkn_class => 'Непонятный класс %s для присваиваемого ключа %s',
|
17
|
+
:dict_unallow => 'Словарь не допустим на уровне %i',
|
18
|
+
:var_undef => 'Переменная %s не определена',
|
19
|
+
:no_tmpl_str => 'Значение строки шаблона отсутствует',
|
20
|
+
:tmpl_undef => 'Не определён шаблон %s на уровне %i',
|
21
|
+
:git_unfound => 'Гит схов не найден в папке %s',
|
22
|
+
:fold_unfound => 'Папка %s для словаря не существует',
|
23
|
+
:dict_unfound => 'Словарь %s не найден во Гит схове',
|
24
|
+
:cant_save => 'Невозможно сохранить изменения лексемы %s по причине %s',
|
25
|
+
:create_ok => 'Лексема создана успешно',
|
26
|
+
:update_ok => 'Лексема обновлена успешно',
|
27
|
+
:added => 'Переменная \'%s\' добавлена',
|
28
|
+
}
|
29
|
+
|
30
|
+
def self.simply(hash, vars)
|
31
|
+
hash.keys.each do |key|
|
32
|
+
vars[$1] = hash.delete(key) if key =~ /(.*)=$/
|
33
|
+
end
|
34
|
+
|
35
|
+
hash.keys.each do |key|
|
36
|
+
value = hash[key]
|
37
|
+
if key =~ /^%(.*)/
|
38
|
+
var = vars[$1].clone
|
39
|
+
if var.class == Hash
|
40
|
+
self.simply(var, vars)
|
41
|
+
hash.replace( hash.merge(var) )
|
42
|
+
elsif var.class == String
|
43
|
+
hash[var] = nil
|
44
|
+
else
|
45
|
+
raise "Unsupported variable #{$1} value class #{var.class}"
|
46
|
+
end
|
47
|
+
hash.delete(key)
|
48
|
+
next
|
49
|
+
end
|
50
|
+
|
51
|
+
case value.class.to_s.to_sym
|
52
|
+
when :String
|
53
|
+
hash[key] = vars[$1].clone if value =~ /^%(.*)/
|
54
|
+
when :Hash
|
55
|
+
self.simply(value, vars)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def rule
|
61
|
+
### dbp21 "$$$ Строка '#{invalue}' == '#{tinvalue}'"
|
62
|
+
# parse conditiones
|
63
|
+
tvalue = ''
|
64
|
+
ss = StringScanner.new(tinvalue)
|
65
|
+
|
66
|
+
while ss.scan_until(/\[(.*)\]/)
|
67
|
+
if ss[1] =~ /(.*)=(.*)/
|
68
|
+
ckey = $1
|
69
|
+
cvalue = $2
|
70
|
+
tvalue += ss.pre_match if begin
|
71
|
+
@levels[-2].class == Hash and @levels[-2].key?(ckey) and match(@levels[-2][ckey], cvalue)
|
72
|
+
rescue
|
73
|
+
false
|
74
|
+
end
|
75
|
+
|
76
|
+
### dbp22 "$$$ Добавок в правило: #{tvalue}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
tvalue += ss.rest
|
81
|
+
### dbp12 "$$$ Новое правило #{tvalue}"
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
def expand__(invalue, tinvalue)
|
86
|
+
@levels << invalue
|
87
|
+
res = case invalue.class.to_s
|
88
|
+
when 'Hash'
|
89
|
+
unless tinvalue.class == Hash
|
90
|
+
@levels.pop
|
91
|
+
raise "#{@levels.size}"
|
92
|
+
### raise "Словарь не допустим на уровне #{@levels.size}"
|
93
|
+
end
|
94
|
+
|
95
|
+
tinvalue.each_pair do |tkey, tvalue|
|
96
|
+
if tkey =~ /^(.*)=$/ and not @vars.keys.include? $1
|
97
|
+
### dbp22 "### Переменная '#{$1}' добавлена"
|
98
|
+
@vars[$1] = tvalue
|
99
|
+
tinvalue.delete(tkey)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
tinvalue.each_pair do |tkey, tvalue|
|
104
|
+
if tkey =~ /^%(.*)$/
|
105
|
+
raise "#{$1}" unless @vars.keys.include? $1
|
106
|
+
### raise "Переменная #{$1} не определена" unless @vars.keys.include? $1
|
107
|
+
tinvalue.delete(tkey)
|
108
|
+
@vars[$1].each_pair do |k,v| tinvalue[k] = v; end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
when 'Array'
|
113
|
+
when 'String'
|
114
|
+
if tinvalue == ''
|
115
|
+
@levels.pop
|
116
|
+
### raise "Значение строки шаблона отсутствует"
|
117
|
+
end
|
118
|
+
|
119
|
+
else
|
120
|
+
@levels.pop
|
121
|
+
### raise "Неизвестный класс #{value.class}"
|
122
|
+
end
|
123
|
+
|
124
|
+
@levels.pop
|
125
|
+
true
|
126
|
+
end
|
127
|
+
|
128
|
+
def match(invalue, tinvalue)
|
129
|
+
@levels << invalue
|
130
|
+
res = case invalue.class.to_s
|
131
|
+
when 'Hash'
|
132
|
+
unless tinvalue.class == Hash
|
133
|
+
@levels.pop
|
134
|
+
raise "1 #{@levels.size}"
|
135
|
+
### raise "Словарь не допустим на уровне #{@levels.size}"
|
136
|
+
end
|
137
|
+
|
138
|
+
tinvalue.each_pair do |tkey, tvalue|
|
139
|
+
if tkey =~ /^(.*)=$/ and not @vars.keys.include? $1
|
140
|
+
### dbp22 "### Переменная '#{$1}' добавлена"
|
141
|
+
@vars[$1] = tvalue
|
142
|
+
tinvalue.delete(tkey)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
tinvalue.each_pair do |tkey, tvalue|
|
147
|
+
if tkey =~ /^%(.*)$/
|
148
|
+
raise "#{$1}" unless @vars.keys.include? $1
|
149
|
+
### raise "Переменная #{$1} не определена" unless @vars.keys.include? $1
|
150
|
+
tinvalue.delete(tkey)
|
151
|
+
@vars[$1].each_pair do |k,v| tinvalue[k] = v; end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
invalue.each_pair do |key, value|
|
156
|
+
err = []
|
157
|
+
errup = tinvalue.each_pair do |tkey, tvalue|
|
158
|
+
begin
|
159
|
+
break false if match(key, tkey) and match(value, tvalue)
|
160
|
+
rescue
|
161
|
+
err << $!
|
162
|
+
break true
|
163
|
+
end
|
164
|
+
end
|
165
|
+
if errup
|
166
|
+
@levels.pop
|
167
|
+
key += ": #{value}" if value.class == String
|
168
|
+
### raise "Пара с ключём '#{key}' не удовлетворяет шаблонной на уровне #{@levels.size}\n <= #{err.join(',')}\t"
|
169
|
+
end
|
170
|
+
end
|
171
|
+
true
|
172
|
+
when 'Array'
|
173
|
+
invalue.each do |value|
|
174
|
+
err = []
|
175
|
+
errup = tinvalue.each do |tvalue|
|
176
|
+
begin
|
177
|
+
break false if match(value, tvalue)
|
178
|
+
rescue
|
179
|
+
err << $!
|
180
|
+
break true
|
181
|
+
end
|
182
|
+
end
|
183
|
+
if errup
|
184
|
+
@levels.pop
|
185
|
+
### val = " со значением #{value}" if value.class == String
|
186
|
+
### raise "Массив#{val} не удовлетворяет шаблонному на уровне #{@levels.size}\n <= #{err.join(',')}"
|
187
|
+
end
|
188
|
+
end
|
189
|
+
true
|
190
|
+
when 'String'
|
191
|
+
if tinvalue == ''
|
192
|
+
@levels.pop
|
193
|
+
raise "2"
|
194
|
+
### raise "Значение строки шаблона отсутствует"
|
195
|
+
end
|
196
|
+
if invalue == ''
|
197
|
+
@levels.pop
|
198
|
+
raise "3"
|
199
|
+
### raise "Входное значение сроки отсутствует"
|
200
|
+
end
|
201
|
+
|
202
|
+
### dbp22 "$$$ Строка '#{invalue}' == '#{tinvalue}'"
|
203
|
+
# parse conditiones
|
204
|
+
tvalue = ''
|
205
|
+
ss = StringScanner.new(tinvalue)
|
206
|
+
|
207
|
+
while ss.scan_until(/\[(.*)\]/)
|
208
|
+
if ss[1] =~ /(.*)=(.*)/
|
209
|
+
ckey = $1
|
210
|
+
cvalue = $2
|
211
|
+
tvalue += ss.pre_match if begin
|
212
|
+
@levels[-2].class == Hash and @levels[-2].key?(ckey) and match(@levels[-2][ckey], cvalue)
|
213
|
+
rescue
|
214
|
+
false
|
215
|
+
end
|
216
|
+
|
217
|
+
### dbp22 "$$$ Добавок в правило: #{tvalue}"
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
tvalue += ss.rest
|
222
|
+
### dbp22 "$$$ Новое правило #{tvalue}"
|
223
|
+
|
224
|
+
if tvalue != ''
|
225
|
+
|
226
|
+
# match
|
227
|
+
(tvalue = /#{$1}/) if tvalue =~ /^\/([^\/]+)/
|
228
|
+
|
229
|
+
### dbp22 "$$$ Правило '#{invalue}' =~ '#{tvalue}'"
|
230
|
+
|
231
|
+
res = (invalue =~ tvalue)
|
232
|
+
### dbp22 "$$$ Попало? #{not not res}"
|
233
|
+
|
234
|
+
res
|
235
|
+
else
|
236
|
+
false
|
237
|
+
end
|
238
|
+
else
|
239
|
+
@levels.pop
|
240
|
+
raise "333 #{value.class}"
|
241
|
+
### raise "Неизвестный класс #{value.class}"
|
242
|
+
end
|
243
|
+
|
244
|
+
@levels.pop
|
245
|
+
res
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
|
2
|
+
require File.expand_path('../../lib/ytemplate', __FILE__)
|
3
|
+
|
4
|
+
RSpec.configure do |c|
|
5
|
+
c.mock_with :rspec
|
6
|
+
end
|
7
|
+
|
8
|
+
SampleTemplate = <<C
|
9
|
+
---
|
10
|
+
@instance=:
|
11
|
+
ikey: ivalue
|
12
|
+
local=:
|
13
|
+
lkey: lvalue
|
14
|
+
key1: value
|
15
|
+
key2: %local
|
16
|
+
key3:
|
17
|
+
%local:
|
18
|
+
key4: value
|
19
|
+
C
|
20
|
+
|
21
|
+
SampleFile = <<C
|
22
|
+
---
|
23
|
+
key1: value
|
24
|
+
key2:
|
25
|
+
lkey: lvalue
|
26
|
+
key3:
|
27
|
+
lkey: lvalue
|
28
|
+
key4: value
|
29
|
+
C
|
30
|
+
|
31
|
+
SampleResultTemplate = <<C
|
32
|
+
---
|
33
|
+
key1: value
|
34
|
+
key2:
|
35
|
+
lkey: lvalue
|
36
|
+
key3:
|
37
|
+
lkey: lvalue
|
38
|
+
key4: value
|
39
|
+
C
|
40
|
+
|
41
|
+
SampleResultDocument = <<C
|
42
|
+
---
|
43
|
+
key1: +value
|
44
|
+
key2:
|
45
|
+
lkey: +lvalue
|
46
|
+
key3:
|
47
|
+
lkey: +lvalue
|
48
|
+
key4: +value
|
49
|
+
C
|
50
|
+
|
@@ -0,0 +1,42 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
require File.expand_path('../spec_helper', __FILE__)
|
4
|
+
|
5
|
+
describe 'Eventer' do
|
6
|
+
it "Expand of a template" do
|
7
|
+
tmpl = YAML::Template.new SampleTemplate
|
8
|
+
|
9
|
+
o = tmpl.deploy
|
10
|
+
|
11
|
+
raise "Resulted YAML-document is wrong" if o != YAML.load( SampleResultTemplate )
|
12
|
+
end
|
13
|
+
|
14
|
+
it "Expand of a document" do
|
15
|
+
tmpl = YAML::Template.new SampleTemplate
|
16
|
+
|
17
|
+
o = tmpl.deploy_to( YAML.load( SampleFile ), :use_template => true)
|
18
|
+
|
19
|
+
raise "Resulted YAML-document is wrong" if o != YAML.load( SampleResultDocument )
|
20
|
+
end
|
21
|
+
|
22
|
+
it "Document match to the template" do
|
23
|
+
tmpl = YAML::Template.new SampleTemplate
|
24
|
+
|
25
|
+
o = tmpl.match(YAML.load( SampleFile) )
|
26
|
+
|
27
|
+
raise "Resulted YAML-document is wrong" unless o.empty?
|
28
|
+
end
|
29
|
+
|
30
|
+
it "Erroneous document match to the template" do
|
31
|
+
tmpl = YAML::Template.new SampleTemplate
|
32
|
+
|
33
|
+
file = YAML.load( SampleFile )
|
34
|
+
file['key1'] = 'novalue'
|
35
|
+
|
36
|
+
o = tmpl.match( file )
|
37
|
+
|
38
|
+
raise "Resulted YAML-document is wrong" if o.empty?
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
|
data/ytemplate.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "ytemplate/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "ytemplate"
|
7
|
+
s.version = YAML::Template::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = [ 'Малъ Скрылёвъ (Malo Skrylevo)' ]
|
10
|
+
s.email = [ '3aHyga@gmail.com' ]
|
11
|
+
s.homepage = 'https://github.com/3aHyga/ytemplate'
|
12
|
+
s.summary = 'YAML template deployment extension'
|
13
|
+
s.description = 'ytemplate is YAML extension allowing deployment of YAML templates'
|
14
|
+
|
15
|
+
s.rubyforge_project = "ytemplate"
|
16
|
+
|
17
|
+
s.required_rubygems_version = '>= 1.6.0'
|
18
|
+
|
19
|
+
s.add_dependency 'rdoba', ">= 0.1"
|
20
|
+
|
21
|
+
s.add_development_dependency("bundler", ">= 1.0.0")
|
22
|
+
s.add_development_dependency("rspec", "~> 2.0.1")
|
23
|
+
|
24
|
+
s.files = `git ls-files`.split("\n")
|
25
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
26
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
27
|
+
s.require_paths = ["lib"]
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ytemplate
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Малъ Скрылёвъ (Malo Skrylevo)
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-05-18 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rdoba
|
16
|
+
requirement: &69624460 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0.1'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *69624460
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: bundler
|
27
|
+
requirement: &69624230 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 1.0.0
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *69624230
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rspec
|
38
|
+
requirement: &69624000 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 2.0.1
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *69624000
|
47
|
+
description: ytemplate is YAML extension allowing deployment of YAML templates
|
48
|
+
email:
|
49
|
+
- 3aHyga@gmail.com
|
50
|
+
executables: []
|
51
|
+
extensions: []
|
52
|
+
extra_rdoc_files: []
|
53
|
+
files:
|
54
|
+
- .document
|
55
|
+
- .gitignore
|
56
|
+
- .rspec
|
57
|
+
- CHANGES.md
|
58
|
+
- Gemfile
|
59
|
+
- LICENSE
|
60
|
+
- README.md
|
61
|
+
- Rakefile
|
62
|
+
- lib/ytemplate.rb
|
63
|
+
- lib/ytemplate/misc.rb
|
64
|
+
- lib/ytemplate/version.rb
|
65
|
+
- spec/spec_helper.rb
|
66
|
+
- spec/ytemplate_spec.rb
|
67
|
+
- ytemplate.gemspec
|
68
|
+
homepage: https://github.com/3aHyga/ytemplate
|
69
|
+
licenses: []
|
70
|
+
post_install_message:
|
71
|
+
rdoc_options: []
|
72
|
+
require_paths:
|
73
|
+
- lib
|
74
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
75
|
+
none: false
|
76
|
+
requirements:
|
77
|
+
- - ! '>='
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '0'
|
80
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 1.6.0
|
86
|
+
requirements: []
|
87
|
+
rubyforge_project: ytemplate
|
88
|
+
rubygems_version: 1.7.2
|
89
|
+
signing_key:
|
90
|
+
specification_version: 3
|
91
|
+
summary: YAML template deployment extension
|
92
|
+
test_files:
|
93
|
+
- spec/spec_helper.rb
|
94
|
+
- spec/ytemplate_spec.rb
|