command-set 0.8.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.
@@ -0,0 +1,300 @@
1
+ module Command
2
+ module Results
3
+ #The root class of the List class family. A compositable tree, iterated by ListIterator.
4
+ class ListItem
5
+ class Exception < ::Exception; end
6
+ class NoMatch < Exception; end
7
+
8
+ @@next_seq = 0
9
+
10
+ def initialize(value)
11
+ @sequence = (@@next_seq +=1)
12
+ @value = value
13
+ @order = nil
14
+ @parent = nil
15
+ @options = {}
16
+ @depth = nil
17
+ end
18
+
19
+ attr_reader :value, :sequence
20
+ attr_accessor :parent, :order, :options, :depth
21
+
22
+ def ==(other)
23
+ return (ListItem === other &&
24
+ value == other.value)
25
+ end
26
+
27
+ def tree_order_next
28
+ right = self.next_sibling
29
+ if right.nil?
30
+ if self.parent.nil?
31
+ return nil
32
+ else
33
+ return self.parent.list_end
34
+ end
35
+ else
36
+ return right
37
+ end
38
+ end
39
+
40
+ def to_s
41
+ return @value.to_s
42
+ end
43
+
44
+ def eql?(other)
45
+ return self == other
46
+ end
47
+
48
+ def next_sibling
49
+ return nil if self.parent.nil?
50
+ return self.parent.after(self)
51
+ end
52
+
53
+ def match_on(value)
54
+ return value.to_s == self.to_s
55
+ end
56
+
57
+ def match(key)
58
+ if key == :*
59
+ return true
60
+ elsif key == :**
61
+ return true
62
+ else
63
+ match_on(key)
64
+ end
65
+ end
66
+
67
+ def filter(path)
68
+ if path.empty? || path == [:**]
69
+ return self
70
+ else
71
+ raise NoMatch
72
+ end
73
+ end
74
+
75
+ def inspect
76
+ "<i(#{@order}) #{value.inspect}>"
77
+ end
78
+ end
79
+
80
+ #A sort of virtual list element, used to denote the end of a list in iteration.
81
+ class ListEnd < ListItem
82
+ def initialize(end_of)
83
+ @end_of = end_of
84
+ end
85
+
86
+ attr_reader :end_of
87
+
88
+ def parent
89
+ @end_of.parent
90
+ end
91
+
92
+ def order
93
+ @end_of.order
94
+ end
95
+
96
+ def next_sibling
97
+ @end_of.next_sibling
98
+ end
99
+
100
+ def to_s
101
+ @end_of.to_s
102
+ end
103
+
104
+ def inspect
105
+ "<e(#{@end_of.order}):#{@end_of.name}>"
106
+ end
107
+ end
108
+
109
+ #A List contains ListItems - this class is sorely under-documented.
110
+ #Honestly, much of it's functionality is either speculative (and it's quite
111
+ #possible that you ain't gonna need it) or else required by internal
112
+ #code and probably not needed by client code.
113
+ #
114
+ #What's most important to understand is that Lists can nest both
115
+ #ListItems and other Lists, which makes them a bit like a tree, and a
116
+ #bit like a list.
117
+ class List < ListItem
118
+ def initialize(name, values=[])
119
+ @name = name
120
+ create_order = 0
121
+ values = values.map do |item|
122
+ if ListItem === item
123
+ if item.order.nil?
124
+ item.order = (create_order += 1)
125
+ else
126
+ create_order = item.order
127
+ end
128
+ else
129
+ item = ListItem.new(item)
130
+ item.order = (create_order += 1)
131
+ end
132
+
133
+ if item.parent.nil?
134
+ item.parent = self
135
+ end
136
+
137
+ item
138
+ end
139
+ super(values)
140
+ @open = true
141
+ end
142
+
143
+ attr_reader :name
144
+ alias values value
145
+
146
+ def to_s
147
+ @name.to_s
148
+ end
149
+
150
+ def list_end
151
+ return ListEnd.new(self)
152
+ end
153
+
154
+ def tree_order_next
155
+ deeper = self.first_child
156
+ if deeper.nil?
157
+ return self.list_end
158
+ else
159
+ return deeper
160
+ end
161
+ end
162
+
163
+ #Helper for #filter
164
+ def filter_into_array(path)
165
+ next_path = path.first == :** ? path : path[1..-1]
166
+ return values.find_all do |value|
167
+ value.match(path[0])
168
+ end.map do |item|
169
+ begin
170
+ item.filter(next_path)
171
+ rescue NoMatch => nm
172
+ nm
173
+ end
174
+ end.reject do |value|
175
+ NoMatch === value
176
+ end
177
+ end
178
+
179
+ #+path+ should be an array of arguments to match (against list names
180
+ #or item values). The special path element +:**+ can be used to keep
181
+ #opening lists to find whatever. The result will be a List that
182
+ #contains only matching elements. +:*+ will match any single item at
183
+ #that level.
184
+ def filter(path)
185
+ if path == [:**]
186
+ list = List.new(@name, @value)
187
+ list.order = @order
188
+ return list
189
+ end
190
+
191
+ if path.first == :**
192
+ double_stars = filter_into_array(path)
193
+ path = path[1..-1]
194
+ else
195
+ double_stars = nil
196
+ end
197
+
198
+ list = filter_into_array(path)
199
+
200
+ if (double_stars.nil? || double_stars.empty?) && path.length > 0 && list.empty?
201
+ raise NoMatch
202
+ end
203
+
204
+ unless double_stars.nil?
205
+ list.each do |item|
206
+ unless double_stars.find {|starred| starred.order == item.order }
207
+ double_stars << item
208
+ end
209
+ end
210
+
211
+ double_stars.sort! {|left, right| left.order <=> right.order}
212
+
213
+ list = double_stars
214
+ end
215
+
216
+ list = List.new(@name, list)
217
+ list.order = @order
218
+ return list
219
+ end
220
+
221
+ def open?
222
+ @open
223
+ end
224
+
225
+ def first_child
226
+ if values.empty?
227
+ return nil
228
+ else
229
+ return values[0]
230
+ end
231
+ end
232
+
233
+ def after(item)
234
+ index = nil
235
+ values.each_with_index do |value, idx|
236
+ if value.equal?(item)
237
+ index = idx
238
+ break
239
+ end
240
+ end
241
+ if index.nil? or index >= values.length
242
+ return nil
243
+ else
244
+ return values[index + 1]
245
+ end
246
+ end
247
+
248
+ def close
249
+ return self unless @open
250
+ @open = false
251
+ values.each do |item|
252
+ next unless List === item
253
+ item.close
254
+ end
255
+ return self
256
+ end
257
+
258
+ def add(item)
259
+ unless ListItem === item
260
+ item = ListItem.new(item)
261
+ end
262
+ item.parent = self
263
+ item.order = values.length
264
+ values.push(item)
265
+ return item
266
+ end
267
+
268
+ def ==(other)
269
+ return (List === other &&
270
+ name == other.name &&
271
+ values == other.values)
272
+ end
273
+
274
+ def eql?(other)
275
+ return self == other
276
+ end
277
+
278
+ def inspect
279
+ "<#{@open ? "L":"l"}(#@order):#{name.to_s} #{values.inspect} #{name.to_s}>"
280
+ end
281
+ end
282
+
283
+ #Basically an Enumerator over a List. Give it any list element, and
284
+ ##each will take you to the end of the list.
285
+ class ListIterator
286
+ include Enumerable
287
+ def initialize(list)
288
+ @list = list
289
+ end
290
+
291
+ def each
292
+ thumb = @list
293
+ until thumb.nil?
294
+ yield thumb
295
+ thumb = thumb.tree_order_next
296
+ end
297
+ end
298
+ end
299
+ end
300
+ end