awesome-cheeba 1.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/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
|