yamlr 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.autotest +25 -0
- data/.gemtest +0 -0
- data/History.txt +10 -0
- data/Manifest.txt +35 -0
- data/README.txt +53 -0
- data/Rakefile +24 -0
- data/bin/yamlr +68 -0
- data/lib/yamlr.rb +46 -0
- data/lib/yamlr/defaults.rb +78 -0
- data/lib/yamlr/errors.rb +8 -0
- data/lib/yamlr/indicators.rb +25 -0
- data/lib/yamlr/reader.rb +33 -0
- data/lib/yamlr/reader/builder.rb +249 -0
- data/lib/yamlr/reader/format.rb +116 -0
- data/lib/yamlr/reader/node.rb +53 -0
- data/lib/yamlr/reader/parser.rb +68 -0
- data/lib/yamlr/version.rb +3 -0
- data/lib/yamlr/writer.rb +47 -0
- data/lib/yamlr/writer/builder.rb +93 -0
- data/test/files/2009.yml +3 -0
- data/test/files/arrays.yml +15 -0
- data/test/files/blank.yml +0 -0
- data/test/files/comments.yml +17 -0
- data/test/files/hashes.yml +26 -0
- data/test/files/malformed.yml +3 -0
- data/test/files/mixed.yml +12 -0
- data/test/files/nested.yml +9 -0
- data/test/files/split.yml +9 -0
- data/test/test_reader.rb +210 -0
- data/test/test_reader_builder.rb +342 -0
- data/test/test_reader_format.rb +393 -0
- data/test/test_reader_parser.rb +190 -0
- data/test/test_writer.rb +133 -0
- data/test/test_writer_builder.rb +150 -0
- data/test/test_yamlr.rb +185 -0
- data/yamlr.gemspec +36 -0
- metadata +121 -0
@@ -0,0 +1,116 @@
|
|
1
|
+
module Yamlr
|
2
|
+
module Reader
|
3
|
+
module Format
|
4
|
+
# format datatypes in the parsed hash
|
5
|
+
#
|
6
|
+
def self.format(phs)
|
7
|
+
self.adjust_options(phs)
|
8
|
+
self.stripper(phs)
|
9
|
+
self.key_to_int(phs)
|
10
|
+
self.val_to_int(phs)
|
11
|
+
self.key_to_sym(phs)
|
12
|
+
self.val_to_sym(phs)
|
13
|
+
self.key_to_true(phs)
|
14
|
+
self.val_to_true(phs)
|
15
|
+
phs
|
16
|
+
end
|
17
|
+
|
18
|
+
# strips keys and values
|
19
|
+
#
|
20
|
+
def self.stripper(phs)
|
21
|
+
psp = phs[:opt][:strip]
|
22
|
+
psk = phs[:opt][:strip_keys]
|
23
|
+
psv = phs[:opt][:strip_vals]
|
24
|
+
phs[:key] = phs[:key].to_s.strip if psp or psk
|
25
|
+
phs[:val] = phs[:val].to_s.strip if psp or psv
|
26
|
+
end
|
27
|
+
|
28
|
+
# adjusts options
|
29
|
+
#
|
30
|
+
def self.adjust_options(phs)
|
31
|
+
if phs[:opt][:auto_sym]
|
32
|
+
phs[:opt][:auto_sym_keys] = true
|
33
|
+
phs[:opt][:auto_sym_vals] = true
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# true key
|
38
|
+
#
|
39
|
+
def self.key_to_true(phs)
|
40
|
+
x = []
|
41
|
+
x << phs[:opt][:auto_true].is_a?(TrueClass)
|
42
|
+
x << phs[:opt][:auto_true_keys].is_a?(TrueClass)
|
43
|
+
if x.any?
|
44
|
+
case
|
45
|
+
when (phs[:key].to_s.strip == "true") then phs[:key] = true
|
46
|
+
when (phs[:key].to_s.strip == "false") then phs[:key] = false
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# true val
|
52
|
+
#
|
53
|
+
def self.val_to_true(phs)
|
54
|
+
x = []
|
55
|
+
x << phs[:opt][:auto_true].is_a?(TrueClass)
|
56
|
+
x << phs[:opt][:auto_true_vals].is_a?(TrueClass)
|
57
|
+
if x.any?
|
58
|
+
case
|
59
|
+
when (phs[:val].to_s.strip == "true") then phs[:val] = true
|
60
|
+
when (phs[:val].to_s.strip == "false") then phs[:val] = false
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# symbolize key
|
66
|
+
#
|
67
|
+
def self.key_to_sym(phs)
|
68
|
+
is_str = !phs[:key].is_a?(Integer)
|
69
|
+
x = []
|
70
|
+
x << (phs[:opt][:sym_str] && is_str).is_a?(TrueClass)
|
71
|
+
x << (phs[:opt][:sym_str_keys] && is_str).is_a?(TrueClass)
|
72
|
+
x << phs[:opt][:symbolize].is_a?(TrueClass)
|
73
|
+
x << (phs[:ask] && phs[:opt][:auto_sym_keys] && is_str).is_a?(TrueClass)
|
74
|
+
x << phs[:opt][:symbolize_keys].is_a?(TrueClass)
|
75
|
+
phs[:key] = phs[:key].to_s.to_sym if x.any?
|
76
|
+
end
|
77
|
+
|
78
|
+
# symbolize val
|
79
|
+
#
|
80
|
+
def self.val_to_sym(phs)
|
81
|
+
is_str = !phs[:val].is_a?(Integer)
|
82
|
+
x = []
|
83
|
+
x << (phs[:opt][:sym_str] && is_str).is_a?(TrueClass)
|
84
|
+
x << (phs[:opt][:sym_str_vals] && is_str).is_a?(TrueClass)
|
85
|
+
x << phs[:opt][:symbolize].is_a?(TrueClass)
|
86
|
+
x << (phs[:asv] && phs[:opt][:auto_sym_vals] && is_str).is_a?(TrueClass)
|
87
|
+
x << phs[:opt][:symbolize_vals].is_a?(TrueClass)
|
88
|
+
phs[:val] = phs[:val].to_s.to_sym if (x.any? && !phs[:val].to_s.strip.empty?)
|
89
|
+
end
|
90
|
+
|
91
|
+
# key is parsed as string, so try to_i
|
92
|
+
#
|
93
|
+
def self.key_to_int(phs)
|
94
|
+
x = []
|
95
|
+
x << phs[:opt][:int].is_a?(TrueClass)
|
96
|
+
x << phs[:opt][:int_keys].is_a?(TrueClass)
|
97
|
+
phs[:key] = self.string_to_int(phs[:key]) if x.any?
|
98
|
+
end
|
99
|
+
|
100
|
+
# val is parsed as string, so try to_i
|
101
|
+
#
|
102
|
+
def self.val_to_int(phs)
|
103
|
+
x = []
|
104
|
+
x << phs[:opt][:int].is_a?(TrueClass)
|
105
|
+
x << phs[:opt][:int_vals].is_a?(TrueClass)
|
106
|
+
phs[:val] = self.string_to_int(phs[:val]) if x.any?
|
107
|
+
end
|
108
|
+
|
109
|
+
# returns int if string is convertable
|
110
|
+
#
|
111
|
+
def self.string_to_int(string)
|
112
|
+
string.to_s.strip =~ /^\d+$/ ? string.to_s.to_i : string
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Yamlr
|
2
|
+
module Reader
|
3
|
+
module Node
|
4
|
+
# hashpair
|
5
|
+
#
|
6
|
+
def self.hashpair(hsh, sym, spc)
|
7
|
+
/^(#{spc}*)(\S*)#{hsh}#{spc}*(.*)#{spc}*$/
|
8
|
+
end
|
9
|
+
|
10
|
+
# hashpair, both key and val are symbol
|
11
|
+
#
|
12
|
+
def self.hashpair_sym_all(hsh, sym, spc)
|
13
|
+
/^(#{spc}*)(#{sym}\S*)#{hsh}#{spc}*(#{sym}.*)#{spc}*$/
|
14
|
+
end
|
15
|
+
|
16
|
+
# hashpair, key is symbol
|
17
|
+
#
|
18
|
+
def self.hashpair_sym_key(hsh, sym, spc)
|
19
|
+
/^(#{spc}*)(#{sym}\S*)#{hsh}#{spc}*(.*)#{spc}*$/
|
20
|
+
end
|
21
|
+
|
22
|
+
# hashpair, value is symbol
|
23
|
+
#
|
24
|
+
def self.hashpair_sym_val(hsh, sym, spc)
|
25
|
+
/^(#{spc}*)(\S*)#{hsh}#{spc}*(#{sym}.*)#{spc}*$/
|
26
|
+
end
|
27
|
+
|
28
|
+
# hashkey is symbol
|
29
|
+
#
|
30
|
+
def self.hashkey_sym(hsh, sym, spc)
|
31
|
+
/^(#{spc}*)(#{sym}\S*)#{hsh}#{spc}*$/
|
32
|
+
end
|
33
|
+
|
34
|
+
# hashkey is awesome
|
35
|
+
#
|
36
|
+
def self.hashkey(hsh, spc)
|
37
|
+
/^(#{spc}*)(\S*)#{hsh}#{spc}*$/
|
38
|
+
end
|
39
|
+
|
40
|
+
# docstart and docterm
|
41
|
+
#
|
42
|
+
def self.document(idc, spc)
|
43
|
+
/^#{spc}*#{idc}#{spc}*$/
|
44
|
+
end
|
45
|
+
|
46
|
+
# comments and arrays
|
47
|
+
#
|
48
|
+
def self.left_match(idc, spc)
|
49
|
+
/^(#{spc}*)#{idc}(#{spc}*)(.*)#{spc}*$/
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Yamlr
|
2
|
+
module Reader
|
3
|
+
module Parser
|
4
|
+
class IndentError < StandardError; end
|
5
|
+
|
6
|
+
def self.parse(line, opts, num)
|
7
|
+
results = self.parse_line(line, opts)
|
8
|
+
phs = self.results_to_hash(results, opts)
|
9
|
+
self.check_indent(line, phs[:spc], opts[:indent], num)
|
10
|
+
phs
|
11
|
+
end
|
12
|
+
|
13
|
+
# invalid number of spaces, line num and truncated line for context
|
14
|
+
#
|
15
|
+
def self.check_indent(line, spc, idt, num)
|
16
|
+
raise IndentError, "#{num} #{line[1..50]}" if spc % idt != 0
|
17
|
+
end
|
18
|
+
|
19
|
+
# parses line, returns array, dependent on Node module
|
20
|
+
#
|
21
|
+
def self.parse_line(line, opts)
|
22
|
+
nod = Yamlr::Reader::Node
|
23
|
+
spc = opts[:space]
|
24
|
+
hsh = opts[:hash]
|
25
|
+
sym = opts[:symbol]
|
26
|
+
dcs = opts[:doc_start]
|
27
|
+
dct = opts[:doc_term]
|
28
|
+
arr = opts[:array]
|
29
|
+
com = opts[:comment]
|
30
|
+
|
31
|
+
begin
|
32
|
+
case line
|
33
|
+
when nod.left_match(arr, spc) then [:arr, Regexp.last_match.captures]
|
34
|
+
when nod.left_match(com, spc) then [:com, Regexp.last_match.captures]
|
35
|
+
when nod.hashkey_sym(hsh, sym, spc) then [:hky, Regexp.last_match.captures, nil, true]
|
36
|
+
when nod.hashkey(hsh, spc) then [:hky, Regexp.last_match.captures]
|
37
|
+
when nod.hashpair_sym_all(hsh, sym, spc) then [:hpr, Regexp.last_match.captures, true, true]
|
38
|
+
when nod.hashpair_sym_key(hsh, sym, spc) then [:hpr, Regexp.last_match.captures, true]
|
39
|
+
when nod.hashpair_sym_val(hsh, sym, spc) then [:hpr, Regexp.last_match.captures, nil, true]
|
40
|
+
when nod.hashpair(hsh, sym, spc) then [:hpr, Regexp.last_match.captures]
|
41
|
+
when /^\s*$/ then [:bla]
|
42
|
+
when nod.document(dcs, spc) then [:dcs]
|
43
|
+
when nod.document(dct, spc) then [:dct]
|
44
|
+
else
|
45
|
+
raise 'MALFORMED'
|
46
|
+
end
|
47
|
+
rescue => e
|
48
|
+
# log or whatever
|
49
|
+
[:mal, '', nil, line.strip]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# creates hash with array vals, includes options
|
54
|
+
#
|
55
|
+
def self.results_to_hash(results, opt)
|
56
|
+
#raise results.flatten.to_s + " results.class => #{results.class}"
|
57
|
+
msg, spc, key, val, ask, asv = results.flatten
|
58
|
+
{ :msg => msg,
|
59
|
+
:spc => (spc.nil? ? 0 : spc.length),
|
60
|
+
:key => key.to_s.sub(/^:/, ''),
|
61
|
+
:val => val.to_s.sub(/^:/, ''),
|
62
|
+
:ask => ask,
|
63
|
+
:asv => asv,
|
64
|
+
:opt => opt}
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/lib/yamlr/writer.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'yamlr/writer/builder'
|
2
|
+
|
3
|
+
module Yamlr
|
4
|
+
module Writer
|
5
|
+
class EmptyFilenameError < StandardError; end
|
6
|
+
class EmptyInputError < StandardError; end
|
7
|
+
class InvalidInputError < StandardError; end
|
8
|
+
class InvalidFilenameError < StandardError; end
|
9
|
+
|
10
|
+
# array, hash, or string to array of lines of .yml file
|
11
|
+
#
|
12
|
+
def self.build(object, options)
|
13
|
+
raise Yamlr::Writer::EmptyInputError if object.is_a?(String) && object.strip.empty?
|
14
|
+
raise Yamlr::Writer::InvalidInputError unless object.is_a?(String) or object.is_a?(Array) or object.is_a?(Hash)
|
15
|
+
Yamlr::Writer::Builder.build([], object, options)
|
16
|
+
end
|
17
|
+
|
18
|
+
# array, hash, or string to file
|
19
|
+
#
|
20
|
+
def self.write(object, filename, options)
|
21
|
+
filename = "#{ENV['HOME']}/#{File.basename(filename.to_s)}" if (filename =~ /^~/)
|
22
|
+
raise Yamlr::Writer::EmptyInputError if (object.is_a?(String) && object.strip.empty?)
|
23
|
+
raise Yamlr::Writer::InvalidInputError unless (object.is_a?(String) or object.is_a?(Array) or object.is_a?(Hash))
|
24
|
+
raise Yamlr::Writer::EmptyFilenameError if File.basename(filename.to_s).strip.empty?
|
25
|
+
raise Yamlr::Writer::InvalidFilenameError unless File.exists?(File.dirname(filename))
|
26
|
+
File.open(filename, "w") do |file|
|
27
|
+
self.build(object, options).each {|line|
|
28
|
+
file.print("#{line}\n")
|
29
|
+
}
|
30
|
+
end
|
31
|
+
filename
|
32
|
+
end
|
33
|
+
|
34
|
+
# write a yamlr dotfile to home dir
|
35
|
+
#
|
36
|
+
def self.dotfile(options, home)
|
37
|
+
filename = "#{home.chomp("/")}/.yamlrc"
|
38
|
+
new_name = nil
|
39
|
+
if File.exists?(filename)
|
40
|
+
new_name = "#{filename}.#{Time.now.strftime("%Y%m%d%H%M%S")}"
|
41
|
+
File.rename(filename, new_name)
|
42
|
+
end
|
43
|
+
self.write(options, filename, Yamlr::Indicators.options)
|
44
|
+
new_name ? new_name : filename
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module Yamlr
|
2
|
+
module Writer
|
3
|
+
module Builder
|
4
|
+
def self.build(arr, inp, opt)
|
5
|
+
spc = (opt[:space] * opt[:indent])
|
6
|
+
case
|
7
|
+
when inp.is_a?(Hash) then self.hash_to_array(arr, inp, opt, spc)
|
8
|
+
when inp.is_a?(Array) then self.array_to_lines(arr, inp, opt, spc)
|
9
|
+
when inp.is_a?(String) then self.string_to_array(arr, inp, opt, spc)
|
10
|
+
end
|
11
|
+
arr
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.sym_to_str(x, y = nil)
|
15
|
+
a = x.is_a?(Symbol) ? ":#{x}" : x
|
16
|
+
b = y.is_a?(Symbol) ? ":#{y}" : y
|
17
|
+
[a, b]
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.string_to_array(arr, inp, opt, spc)
|
21
|
+
self.array_to_lines(arr, inp.split(opt[:line_end]), opt, spc)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.hash_to_array(arr, inp, opt, spc)
|
25
|
+
inp.each do |key,val|
|
26
|
+
case
|
27
|
+
when val.is_a?(Hash)
|
28
|
+
key = self.sym_to_str(key)
|
29
|
+
arr << opt[:doc_start] if opt[:docs]
|
30
|
+
arr << "#{key.join}#{opt[:hash]}" unless opt[:docs]
|
31
|
+
self.hash_to_lines(arr, val, val.keys, opt, spc, (opt[:docs] ? 0 : 1))
|
32
|
+
when val.is_a?(Array)
|
33
|
+
arr << opt[:doc_start] if opt[:docs]
|
34
|
+
self.array_to_lines(arr, val, opt)
|
35
|
+
else
|
36
|
+
key, val = self.sym_to_str(key, val)
|
37
|
+
arr << "#{key}#{opt[:hash]} #{val}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# line is array, push each indice to array, assumes every indice is a string
|
43
|
+
#
|
44
|
+
def self.array_to_lines(arr, val, opt, spc, idt = nil)
|
45
|
+
idt = 0 if idt.nil?
|
46
|
+
ist = spc * idt
|
47
|
+
ind = opt[:array]
|
48
|
+
until val.empty? do
|
49
|
+
x = val.shift
|
50
|
+
case
|
51
|
+
when x.is_a?(Hash)
|
52
|
+
arr << "#{ist}#{ind}"
|
53
|
+
self.hash_to_lines(arr, x, x.keys, opt, spc, (idt + 1))
|
54
|
+
when x.is_a?(Array)
|
55
|
+
i = idt
|
56
|
+
arr << "#{ist}#{ind}" if opt[:yaml]
|
57
|
+
i += 1
|
58
|
+
self.array_to_lines(arr, x, opt, spc, i)
|
59
|
+
else
|
60
|
+
x = self.sym_to_str(x)
|
61
|
+
arr << "#{ist}#{ind} #{x.join}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# recursively add lines to array
|
67
|
+
#
|
68
|
+
def self.hash_to_lines(arr, hsh, kys, opt, spc, idt = nil)
|
69
|
+
idt = 0 if idt.nil?
|
70
|
+
while !kys.empty?
|
71
|
+
ist = spc * idt
|
72
|
+
key = kys.shift
|
73
|
+
val = hsh[key]
|
74
|
+
ind = opt[:hash]
|
75
|
+
case
|
76
|
+
when val.is_a?(Hash)
|
77
|
+
# push key into lines_array
|
78
|
+
arr << "#{ist}#{key}#{ind}"
|
79
|
+
# step indent
|
80
|
+
# call this again (the recursive part)
|
81
|
+
self.hash_to_lines(arr, val, val.keys, opt, spc, (idt + 1))
|
82
|
+
when val.is_a?(Array)
|
83
|
+
arr << "#{ist}#{key}#{ind}"
|
84
|
+
self.array_to_lines(arr, val, opt, spc, (idt + 1))
|
85
|
+
else
|
86
|
+
# if not Writer or Array, it must be String
|
87
|
+
arr << "#{ist}#{key}#{ind} #{val}"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
data/test/files/2009.yml
ADDED
File without changes
|
@@ -0,0 +1,26 @@
|
|
1
|
+
grocery_list:
|
2
|
+
beer:
|
3
|
+
1:
|
4
|
+
model: 24 pack - Coors Lite
|
5
|
+
count: 1
|
6
|
+
meatsez:
|
7
|
+
1:
|
8
|
+
model: Spam
|
9
|
+
count: 5
|
10
|
+
2:
|
11
|
+
model: Log of ground beef
|
12
|
+
count: 1
|
13
|
+
cigarettes:
|
14
|
+
1:
|
15
|
+
model: Carton - Basic Ultra Menthol Box 100
|
16
|
+
count: 2
|
17
|
+
other:
|
18
|
+
1:
|
19
|
+
model: Economy-Size Pork & Beans
|
20
|
+
count: 2
|
21
|
+
2:
|
22
|
+
model: Jumbo Miracle Whip
|
23
|
+
count: 1
|
24
|
+
3:
|
25
|
+
model: White Wonder Bread
|
26
|
+
count: 2
|