awesome-cheeba 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +6 -0
- data/Manifest.txt +96 -0
- data/README.txt +33 -0
- data/Rakefile +13 -0
- data/bin/cheeba +68 -0
- data/cheeba.gemspec +37 -0
- data/lib/cheeba.rb +50 -0
- data/lib/cheeba/defaults.rb +69 -0
- data/lib/cheeba/errors.rb +10 -0
- data/lib/cheeba/indicators.rb +25 -0
- data/lib/cheeba/reader.rb +33 -0
- data/lib/cheeba/reader/builder.rb +264 -0
- data/lib/cheeba/reader/format.rb +126 -0
- data/lib/cheeba/reader/node.rb +61 -0
- data/lib/cheeba/reader/parser.rb +64 -0
- data/lib/cheeba/version.rb +3 -0
- data/lib/cheeba/writer.rb +49 -0
- data/lib/cheeba/writer/builder.rb +95 -0
- data/test/files/arrays.cash +15 -0
- data/test/files/blank.cash +0 -0
- data/test/files/comments.cash +17 -0
- data/test/files/hashes.cash +26 -0
- data/test/files/malformed.cash +3 -0
- data/test/files/mixed.cash +12 -0
- data/test/files/split.cash +9 -0
- data/test/test_cheeba.rb +120 -0
- data/test/test_reader.rb +210 -0
- data/test/test_reader_builder.rb +345 -0
- data/test/test_reader_format.rb +316 -0
- data/test/test_reader_parser.rb +190 -0
- data/test/test_writer.rb +133 -0
- data/test/test_writer_builder.rb +150 -0
- metadata +102 -0
@@ -0,0 +1,264 @@
|
|
1
|
+
module Cheeba
|
2
|
+
module Reader
|
3
|
+
module Builder
|
4
|
+
#
|
5
|
+
# Abbreviations:
|
6
|
+
# adr = address
|
7
|
+
# phs = parsed hash
|
8
|
+
# hsh = hash
|
9
|
+
# lst = list
|
10
|
+
# met = meta
|
11
|
+
# cur = current
|
12
|
+
# spc = spaces
|
13
|
+
# idt = indent
|
14
|
+
# idx = index
|
15
|
+
#
|
16
|
+
# dcs = doc_start
|
17
|
+
# dct = doc_term
|
18
|
+
# hpr = hsh_pair
|
19
|
+
# hky = hsh_key
|
20
|
+
# arr = array
|
21
|
+
# com = comment
|
22
|
+
# mal = malformed
|
23
|
+
# msg = message
|
24
|
+
# bla = blank
|
25
|
+
#
|
26
|
+
class RootNodeError < StandardError; end
|
27
|
+
|
28
|
+
##
|
29
|
+
# adds parsed_hash of line to hash
|
30
|
+
#
|
31
|
+
def self.build(hsh, phs)
|
32
|
+
add = false
|
33
|
+
upd = nil
|
34
|
+
msg = phs[:msg]
|
35
|
+
self.doc_new(hsh) if hsh.empty? && msg != "doc"
|
36
|
+
val = phs[:val]
|
37
|
+
lst = hsh[:lst]
|
38
|
+
|
39
|
+
unless [:com, :mal, :bla].include?(msg)
|
40
|
+
cur = self.cur(hsh)
|
41
|
+
key = phs[:key]
|
42
|
+
spc = phs[:spc]
|
43
|
+
idt = phs[:opt][:indent]
|
44
|
+
adr = hsh[:adr]
|
45
|
+
idx = self.index(spc, idt)
|
46
|
+
upd = self.update(adr, idx)
|
47
|
+
las = self.adr_obj(hsh, hsh[:adr])
|
48
|
+
add = true
|
49
|
+
end
|
50
|
+
|
51
|
+
case msg
|
52
|
+
when :dcs: self.doc_start(hsh)
|
53
|
+
when :dct: self.doc_term(hsh)
|
54
|
+
when :hpr: self.hsh_pair(las, key, val)
|
55
|
+
when :hky: self.hsh_key(las, adr, key)
|
56
|
+
when :bla: self.blank(lst)
|
57
|
+
when :arr
|
58
|
+
raise RootNodeError if cur.is_a?(Hash) && !cur.empty? && spc == 0
|
59
|
+
if las.is_a?(Hash) && las.empty?
|
60
|
+
self.adr_obj_to_array(hsh, hsh[:adr])
|
61
|
+
las = self.adr_obj(hsh, hsh[:adr])
|
62
|
+
end
|
63
|
+
self.arr_parse(las, adr, val, upd)
|
64
|
+
when :com: self.comment(lst, val)
|
65
|
+
when :mal: self.malformed(lst, val)
|
66
|
+
end
|
67
|
+
|
68
|
+
self.add_to_list(lst, adr) if add
|
69
|
+
end
|
70
|
+
|
71
|
+
##
|
72
|
+
# sub-parses array message, and logic from update-method
|
73
|
+
#
|
74
|
+
def self.arr_parse(las, adr, val, upd)
|
75
|
+
case upd
|
76
|
+
when "eq": self.array_val(las, val)
|
77
|
+
when "lt": self.array_val(las, val)
|
78
|
+
when "gt": self.array_new(las, adr, val)
|
79
|
+
else; raise "error"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
##
|
84
|
+
# current hash, returns array of "doc"
|
85
|
+
#
|
86
|
+
def self.cur(hsh)
|
87
|
+
hsh[hsh.length - 2]
|
88
|
+
end
|
89
|
+
|
90
|
+
##
|
91
|
+
# address array to string usable in eval on root node
|
92
|
+
#
|
93
|
+
def self.to_adr(adr)
|
94
|
+
m = adr.map {|x|
|
95
|
+
case
|
96
|
+
when x.is_a?(String): "['#{x}']"
|
97
|
+
when x.is_a?(Fixnum): "[#{x}]"
|
98
|
+
end
|
99
|
+
}
|
100
|
+
m.to_s
|
101
|
+
end
|
102
|
+
|
103
|
+
##
|
104
|
+
# returns the actual object at an address in tree
|
105
|
+
#
|
106
|
+
def self.adr_obj(hsh, adr)
|
107
|
+
m = self.to_adr(adr)
|
108
|
+
eval("hsh#{m.to_s}")
|
109
|
+
end
|
110
|
+
|
111
|
+
##
|
112
|
+
# converts an object in tree to empty array
|
113
|
+
#
|
114
|
+
def self.adr_obj_to_array(hsh, adr)
|
115
|
+
m = self.to_adr(adr)
|
116
|
+
eval("hsh#{m.to_s} = []")
|
117
|
+
end
|
118
|
+
|
119
|
+
##
|
120
|
+
# calculates index based on spaces divided by indent unit
|
121
|
+
#
|
122
|
+
def self.index(spc, idt)
|
123
|
+
((spc % idt) != 0) ? 0 : spc / idt
|
124
|
+
end
|
125
|
+
|
126
|
+
# if indentation less than before, jump up tree, remove extra indices
|
127
|
+
#
|
128
|
+
def self.update(adr, idx)
|
129
|
+
ret = nil
|
130
|
+
len = (adr.length - 1)
|
131
|
+
if idx < len
|
132
|
+
# remove indices after current index
|
133
|
+
adr.replace(adr[0..idx])
|
134
|
+
ret = "lt"
|
135
|
+
elsif idx == len
|
136
|
+
ret = "eq"
|
137
|
+
elsif idx > len
|
138
|
+
ret = "gt"
|
139
|
+
end
|
140
|
+
ret
|
141
|
+
end
|
142
|
+
|
143
|
+
##
|
144
|
+
# create keypair for new doc
|
145
|
+
#
|
146
|
+
def self.doc_new(hsh)
|
147
|
+
hsh[:lst] ||= {}
|
148
|
+
hsh[:adr] ||= []
|
149
|
+
len = hsh.length - 1
|
150
|
+
hsh[len] = {}
|
151
|
+
hsh[:adr].clear
|
152
|
+
hsh[:adr] << len
|
153
|
+
end
|
154
|
+
|
155
|
+
##
|
156
|
+
# new array in tree, provides logic for last modified object
|
157
|
+
#
|
158
|
+
def self.array_new(las, adr, val)
|
159
|
+
case
|
160
|
+
when las.is_a?(Array) && las.empty?
|
161
|
+
x = [val]
|
162
|
+
las = x
|
163
|
+
adr << las.rindex(x)
|
164
|
+
when las.is_a?(Array) && las.last.is_a?(String) && las.last.empty?
|
165
|
+
x = [val]
|
166
|
+
las[-1] = [val]
|
167
|
+
adr << las.rindex(x)
|
168
|
+
when las.is_a?(Array)
|
169
|
+
x = [val]
|
170
|
+
las << x
|
171
|
+
adr << las.rindex(x)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
##
|
176
|
+
# start document
|
177
|
+
#
|
178
|
+
def self.doc_start(hsh)
|
179
|
+
self.doc_new(hsh)
|
180
|
+
self.add_to_list(hsh[:lst], "#DOC_START")
|
181
|
+
end
|
182
|
+
|
183
|
+
##
|
184
|
+
# document terminate
|
185
|
+
#
|
186
|
+
def self.doc_term(hsh)
|
187
|
+
self.add_to_list(hsh[:lst], "#DOC_TERM")
|
188
|
+
end
|
189
|
+
|
190
|
+
##
|
191
|
+
# add val to array in tree
|
192
|
+
#
|
193
|
+
def self.array_val(las, val)
|
194
|
+
case
|
195
|
+
# when x is a hash, it's already addressed
|
196
|
+
when las.is_a?(Array)
|
197
|
+
las << val
|
198
|
+
else
|
199
|
+
raise las.inspect
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
##
|
204
|
+
# add hashkey to tree
|
205
|
+
#
|
206
|
+
def self.hsh_key(las, adr, key)
|
207
|
+
case
|
208
|
+
when las.is_a?(Hash)
|
209
|
+
las[key] = {}
|
210
|
+
when las.is_a?(Array)
|
211
|
+
x = {key => {}}
|
212
|
+
las << x
|
213
|
+
adr << las.rindex(x)
|
214
|
+
end
|
215
|
+
adr << key
|
216
|
+
end
|
217
|
+
|
218
|
+
##
|
219
|
+
# add hashpair to tree
|
220
|
+
#
|
221
|
+
def self.hsh_pair(las, key, val)
|
222
|
+
case
|
223
|
+
when las.is_a?(Array)
|
224
|
+
x = {key => val}
|
225
|
+
las << x
|
226
|
+
when las.is_a?(Hash)
|
227
|
+
las[key] = val
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
##
|
232
|
+
# list stores hsh addresses of lines, comments, etc.
|
233
|
+
#
|
234
|
+
def self.add_to_list(lst, adr)
|
235
|
+
case adr.class.to_s
|
236
|
+
when "String": x = adr
|
237
|
+
when "Array": x = adr.join(",")
|
238
|
+
end
|
239
|
+
lst[lst.length + 1] = x
|
240
|
+
end
|
241
|
+
|
242
|
+
##
|
243
|
+
# add blank to list
|
244
|
+
#
|
245
|
+
def self.blank(lst)
|
246
|
+
self.add_to_list(lst, "#BLANK")
|
247
|
+
end
|
248
|
+
|
249
|
+
##
|
250
|
+
# add comment to list
|
251
|
+
#
|
252
|
+
def self.comment(lst, val)
|
253
|
+
lst[lst.length + 1] = "#COMMENT: #{val}"
|
254
|
+
end
|
255
|
+
|
256
|
+
##
|
257
|
+
# add malformed to list
|
258
|
+
#
|
259
|
+
def self.malformed(lst, val)
|
260
|
+
lst[lst.length + 1] = "#MALFORMED: #{val}"
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
module Cheeba
|
2
|
+
module Reader
|
3
|
+
module Format
|
4
|
+
##
|
5
|
+
# format datatypes in the parsed hash
|
6
|
+
#
|
7
|
+
def self.format(phs)
|
8
|
+
self.adjust_options(phs)
|
9
|
+
self.stripper(phs)
|
10
|
+
self.key_to_int(phs)
|
11
|
+
self.val_to_int(phs)
|
12
|
+
self.key_to_sym(phs)
|
13
|
+
self.val_to_sym(phs)
|
14
|
+
self.key_to_true(phs)
|
15
|
+
self.val_to_true(phs)
|
16
|
+
phs
|
17
|
+
end
|
18
|
+
|
19
|
+
##
|
20
|
+
# strips keys and values
|
21
|
+
#
|
22
|
+
def self.stripper(phs)
|
23
|
+
psp = phs[:opt][:strip]
|
24
|
+
psk = phs[:opt][:strip_keys]
|
25
|
+
psv = phs[:opt][:strip_vals]
|
26
|
+
phs[:key] = phs[:key].to_s.strip if psp or psk
|
27
|
+
phs[:val] = phs[:val].to_s.strip if psp or psv
|
28
|
+
end
|
29
|
+
|
30
|
+
##
|
31
|
+
# adjusts options
|
32
|
+
#
|
33
|
+
def self.adjust_options(phs)
|
34
|
+
if phs[:opt][:auto_sym]
|
35
|
+
phs[:opt][:auto_sym_keys] = true
|
36
|
+
phs[:opt][:auto_sym_vals] = true
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
##
|
41
|
+
# true key
|
42
|
+
#
|
43
|
+
def self.key_to_true(phs)
|
44
|
+
x = []
|
45
|
+
x << phs[:opt][:auto_true].is_a?(TrueClass)
|
46
|
+
x << phs[:opt][:auto_true_keys].is_a?(TrueClass)
|
47
|
+
if x.any?
|
48
|
+
case
|
49
|
+
when (phs[:key].to_s.strip == "true"): phs[:key] = true
|
50
|
+
when (phs[:key].to_s.strip == "false"): phs[:key] = false
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
##
|
56
|
+
# true val
|
57
|
+
#
|
58
|
+
def self.val_to_true(phs)
|
59
|
+
x = []
|
60
|
+
x << phs[:opt][:auto_true].is_a?(TrueClass)
|
61
|
+
x << phs[:opt][:auto_true_vals].is_a?(TrueClass)
|
62
|
+
if x.any?
|
63
|
+
case
|
64
|
+
when (phs[:val].to_s.strip == "true"): phs[:val] = true
|
65
|
+
when (phs[:val].to_s.strip == "false"): phs[:val] = false
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
##
|
71
|
+
# symbolize key
|
72
|
+
#
|
73
|
+
def self.key_to_sym(phs)
|
74
|
+
x = []
|
75
|
+
is_str = (phs[:key] =~ /^\d*$/).nil?
|
76
|
+
x << phs[:opt][:symbolize].is_a?(TrueClass)
|
77
|
+
x << (phs[:ask] && phs[:opt][:auto_sym_keys] && is_str).is_a?(TrueClass)
|
78
|
+
x << phs[:opt][:symbolize_keys].is_a?(TrueClass)
|
79
|
+
phs[:key] = phs[:key].to_sym if x.any?
|
80
|
+
end
|
81
|
+
|
82
|
+
##
|
83
|
+
# symbolize val
|
84
|
+
#
|
85
|
+
def self.val_to_sym(phs)
|
86
|
+
x = []
|
87
|
+
is_str = (phs[:val] =~ /^\d*$/).nil?
|
88
|
+
x << phs[:opt][:symbolize].is_a?(TrueClass)
|
89
|
+
x << (phs[:asv] && phs[:opt][:auto_sym_vals] && is_str).is_a?(TrueClass)
|
90
|
+
x << phs[:opt][:symbolize_vals].is_a?(TrueClass)
|
91
|
+
phs[:val] = phs[:val].to_sym if x.any? && !phs[:val].to_s.strip.empty?
|
92
|
+
end
|
93
|
+
|
94
|
+
##
|
95
|
+
# key is parsed as string, so try to_i
|
96
|
+
#
|
97
|
+
def self.key_to_int(phs)
|
98
|
+
x = []
|
99
|
+
x << phs[:opt][:int].is_a?(TrueClass)
|
100
|
+
x << phs[:opt][:int_keys].is_a?(TrueClass)
|
101
|
+
phs[:key] = self.string_to_int(phs[:key]) if x.any?
|
102
|
+
end
|
103
|
+
|
104
|
+
##
|
105
|
+
# val is parsed as string, so try to_i
|
106
|
+
#
|
107
|
+
def self.val_to_int(phs)
|
108
|
+
x = []
|
109
|
+
x << phs[:opt][:int].is_a?(TrueClass)
|
110
|
+
x << phs[:opt][:int_vals].is_a?(TrueClass)
|
111
|
+
phs[:val] = self.string_to_int(phs[:val]) if x.any?
|
112
|
+
end
|
113
|
+
|
114
|
+
##
|
115
|
+
# returns int if string is convertable
|
116
|
+
#
|
117
|
+
def self.string_to_int(string)
|
118
|
+
if string.to_i.to_s == string
|
119
|
+
string.to_i
|
120
|
+
else
|
121
|
+
string
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Cheeba
|
2
|
+
module Reader
|
3
|
+
module Node
|
4
|
+
#
|
5
|
+
# hashpair
|
6
|
+
#
|
7
|
+
def self.hashpair(hsh, sym, spc)
|
8
|
+
/^(#{spc}*)(\S*)#{hsh}#{spc}*(.*)#{spc}*$/
|
9
|
+
end
|
10
|
+
|
11
|
+
#
|
12
|
+
# hashpair, both key and val are symbol
|
13
|
+
#
|
14
|
+
def self.hashpair_sym_all(hsh, sym, spc)
|
15
|
+
/^(#{spc}*)(#{sym}\S*)#{hsh}#{spc}*(#{sym}.*)#{spc}*$/
|
16
|
+
end
|
17
|
+
|
18
|
+
#
|
19
|
+
# hashpair, key is symbol
|
20
|
+
#
|
21
|
+
def self.hashpair_sym_key(hsh, sym, spc)
|
22
|
+
/^(#{spc}*)(#{sym}\S*)#{hsh}#{spc}*(.*)#{spc}*$/
|
23
|
+
end
|
24
|
+
|
25
|
+
#
|
26
|
+
# hashpair, value is symbol
|
27
|
+
#
|
28
|
+
def self.hashpair_sym_val(hsh, sym, spc)
|
29
|
+
/^(#{spc}*)(\S*)#{hsh}#{spc}*(#{sym}.*)#{spc}*$/
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
# hashkey is symbol
|
34
|
+
#
|
35
|
+
def self.hashkey_sym(hsh, sym, spc)
|
36
|
+
/^(#{spc}*)(#{sym}\S*)#{hsh}#{spc}*$/
|
37
|
+
end
|
38
|
+
|
39
|
+
#
|
40
|
+
# hashkey is awesome
|
41
|
+
#
|
42
|
+
def self.hashkey(hsh, spc)
|
43
|
+
/^(#{spc}*)(\S*)#{hsh}#{spc}*$/
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# docstart and docterm
|
48
|
+
#
|
49
|
+
def self.document(idc, spc)
|
50
|
+
/^#{spc}*#{idc}#{spc}*$/
|
51
|
+
end
|
52
|
+
|
53
|
+
#
|
54
|
+
# comments and arrays
|
55
|
+
#
|
56
|
+
def self.left_match(idc, spc)
|
57
|
+
/^(#{spc}*)#{idc}(#{spc}*)(.*)#{spc}*$/
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|