yamlr 2.0.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.
- 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
|