patrun 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/patrun.rb +406 -0
  3. metadata +44 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 26be7b15694fcdb3af30b2cf505e1f5446e75094
4
+ data.tar.gz: 8d6c257f618b6c0acdbfa7ff47a2cb02c0225014
5
+ SHA512:
6
+ metadata.gz: e08f11b282afa711d2b25cff516780804d1647cb0b51960edcca2768ddefc289647f401b36839f17e9b0c4ad9c5e116219beb2eaacf3c8627e1bae572fa2b2e4
7
+ data.tar.gz: f4903d54e56315f32e43964d746d07a1b5c7ce45f2954286f1f285128268369dfeb0770ec22530a6256c35d8531551a1b09406a345ff257e3c6b7d5f3589dd2b
data/lib/patrun.rb ADDED
@@ -0,0 +1,406 @@
1
+ class Patrun
2
+ def initialize(custom = nil)
3
+ @top = {}
4
+ @custom = custom
5
+ end
6
+
7
+
8
+ def add(pat, data)
9
+
10
+ customizer = nil
11
+ if @custom
12
+ customizer = @custom.call(self, pat, data)
13
+ end
14
+
15
+ pat = pat.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
16
+
17
+ keys = pat.keys.sort()
18
+
19
+ keymap = @top
20
+ valmap = nil
21
+
22
+ i = 0
23
+ while i < keys.length
24
+ key = keys[i]
25
+ val = pat[keys[i]]
26
+
27
+ if nil == val
28
+ next
29
+ end
30
+
31
+ valmap = keymap[:v]
32
+ if valmap && key == keymap[:k]
33
+ keymap = valmap[val] || (valmap[val] = {})
34
+
35
+ elsif !keymap[:k]
36
+ keymap[:k] = key
37
+
38
+ keymap[:v] = {}
39
+
40
+ keymap = keymap[:v][val] = {}
41
+
42
+ else
43
+ if key < keymap[:k]
44
+ curv = keymap[:v]
45
+ curs = keymap[:s]
46
+ keymap[:v] = {}
47
+ keymap[:s] = {:k => keymap[:k], :v => curv, :s => curs}
48
+
49
+ keymap[:k] = key
50
+ keymap = keymap[:v][val] = {}
51
+ else
52
+ valmap = keymap[:v]
53
+ keymap = keymap[:s] || (keymap[:s] = {})
54
+ i-= 1
55
+ end
56
+ end
57
+ i += 1
58
+ end
59
+
60
+ if data != nil && keymap
61
+ keymap[:d] = data
62
+ if customizer
63
+ keymap[:f] = isFunction(customizer) ? customizer : (customizer.class == Hash ? customizer[:find] : false)
64
+ if (customizer.class == Hash)
65
+ keymap[:r] = isFunction(customizer[:remove]) ? customizer[:remove] : nil
66
+ end
67
+ end
68
+ end
69
+
70
+ self
71
+ end
72
+
73
+
74
+
75
+ def findexact(pat)
76
+ find(pat, true)
77
+ end
78
+
79
+ def find(pat, exact = false)
80
+
81
+ pat = pat.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
82
+
83
+ if nil == pat
84
+ return nil
85
+ end
86
+
87
+ keymap = @top
88
+ data = @top[:d] || nil
89
+ finalfind = @top[:f]
90
+ key = nil
91
+ stars = []
92
+ foundkeys = {}
93
+ patlen = pat.keys.length
94
+
95
+
96
+ loop do
97
+ key = keymap[:k]
98
+
99
+ if keymap[:v]
100
+ nextkeymap = keymap[:v][pat[key]]
101
+ if nextkeymap
102
+ foundkeys[key] = true
103
+
104
+ if keymap[:s]
105
+ stars.push(keymap[:s])
106
+ end
107
+
108
+ data = nil == nextkeymap[:d] ? nil : nextkeymap[:d]
109
+ finalfind = nextkeymap[:f]
110
+ keymap = nextkeymap
111
+
112
+ else
113
+ keymap = keymap[:s]
114
+ end
115
+
116
+ else
117
+ keymap = nil
118
+ end
119
+
120
+ if nil == keymap && nil == data && 0 < stars.length
121
+ keymap = stars.pop()
122
+ end
123
+
124
+ break if !keymap
125
+ end
126
+
127
+ # special case for default with no properties
128
+ if nil == data && 0 == patlen && nil != top[:d]
129
+ data = top[:d]
130
+ finalfind = top[:f]
131
+ end
132
+
133
+ if exact && foundkeys.keys.length != patlen
134
+ data = nil
135
+ end
136
+
137
+ if finalfind
138
+ data = finalfind.call(self, pat, data)
139
+ end
140
+
141
+ data
142
+ end
143
+
144
+ def remove(pat)
145
+ keymap = @top
146
+ data = nil
147
+ key = nil
148
+ path = []
149
+
150
+ loop do
151
+ key = keymap[:k]
152
+
153
+ if keymap[:v]
154
+ nextkeymap = keymap[:v][pat[key]]
155
+ if nextkeymap
156
+ path.push({:km => keymap,:v => pat[key]})
157
+ data = nextkeymap[:d]
158
+ keymap = nextkeymap
159
+
160
+ else
161
+ keymap = keymap[:s]
162
+ end
163
+
164
+ else
165
+ keymap = nil
166
+ end
167
+
168
+ break if !keymap
169
+ end
170
+
171
+ if nil != data
172
+ part = path[path.length-1]
173
+ if part && part[:km] && part[:km][:v]
174
+ point = part[:km][:v][part[:v]]
175
+ if !point[:r] || point[:r].call(self, pat, point[:d])
176
+ point.delete(:d)
177
+ end
178
+ end
179
+ end
180
+ end
181
+
182
+
183
+
184
+
185
+ # values can be verbatim, glob, or array of globs
186
+ def list(pat = nil, exact = false)
187
+
188
+ acc = []
189
+
190
+ if @top[:d]
191
+
192
+ acc.push({:match => {},
193
+ :data => @top[:d],
194
+ :find => top[:f]
195
+ })
196
+ end
197
+
198
+ if pat != nil
199
+ pat = pat.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
200
+ end
201
+ descend(pat, exact, @top, {}, merge({}, pat), acc)
202
+
203
+
204
+ acc
205
+ end
206
+
207
+
208
+ def toString(dstr = nil, tree = false)
209
+ defaultFormat = Proc.new { | d |
210
+ isFunction(d) ? "<#{d}>" : "<#{d}>"
211
+ }
212
+
213
+ tree = isBoolean(dstr) ? dstr : tree
214
+ tree = nil == tree ? false : tree
215
+
216
+ dstr = isFunction(dstr) ? dstr : defaultFormat
217
+
218
+ str = []
219
+ o = []
220
+
221
+ walk(@top,o,0,[], str, dstr)
222
+
223
+ return tree ? o.join('') : str.join("\n")
224
+ end
225
+
226
+
227
+ def inspect
228
+ this.toString()
229
+ end
230
+
231
+
232
+ def toJSON(indent)
233
+
234
+ @top.to_json()
235
+
236
+ end
237
+
238
+ private
239
+
240
+
241
+
242
+ def indent(o,d)
243
+ for i in 0..d
244
+ o.push(' ')
245
+ end
246
+ end
247
+
248
+
249
+ def descend(pat, exact, keymap, match, missing, acc)
250
+
251
+ if keymap[:v]
252
+ key = keymap[:k]
253
+ patVal = pat ? (nil == pat[key] ? ( exact ? nil : '*' ) : pat[key]) : '*'
254
+
255
+ itermatch = merge({}, match)
256
+ itermissing = merge({}, missing)
257
+ nextkeymap = nil
258
+
259
+
260
+ for val in keymap[:v].keys
261
+
262
+ if gexval(patVal, val)
263
+
264
+ valitermatch = deepCopy(itermatch)
265
+ valitermatch[key] = val
266
+
267
+ valitermissing = merge({}, itermissing)
268
+ valitermissing.delete(key)
269
+
270
+ nextkeymap = keymap[:v][val]
271
+
272
+ if 0 == valitermissing.keys.length &&
273
+ nextkeymap &&
274
+ nextkeymap[:d]
275
+
276
+ acc.push({
277
+ :match => valitermatch,
278
+ :data => nextkeymap[:d],
279
+ :find => nextkeymap[:f]
280
+ })
281
+ end
282
+
283
+ if nextkeymap && nextkeymap[:v]
284
+
285
+ descend(pat, exact,
286
+ nextkeymap,
287
+ merge({}, valitermatch),
288
+ merge({}, valitermissing),
289
+ acc)
290
+ end
291
+ end
292
+ end
293
+
294
+ nextkeymap = keymap[:s]
295
+ if nextkeymap
296
+ descend(pat, exact,
297
+ nextkeymap,
298
+ merge({}, itermatch),
299
+ merge({}, itermissing),
300
+ acc)
301
+ end
302
+ end
303
+ end
304
+
305
+ def walk(n,o,counter,vs,str, dstr)
306
+
307
+ if nil != n[:d]
308
+ indent(o,counter)
309
+ o.push(dstr.call(n[:d]))
310
+
311
+ str.push("#{vs.join(', ')} -> #{dstr.call(n[:d])}")
312
+ end
313
+
314
+ if n[:k]
315
+ o.push("\n")
316
+ indent(o,counter)
317
+ o.push("#{n[:k]}:")
318
+ end
319
+
320
+ if n[:v]
321
+ counter =+ 1
322
+ for p in n[:v].keys
323
+ o.push("\n")
324
+ indent(o,counter)
325
+ o.push("#{p} ->")
326
+
327
+ vsc = deepCopy(vs)
328
+ vsc.push("#{n[:k]}=#{p}")
329
+
330
+ walk(n[:v][p],o,counter + 1,vsc,str,dstr)
331
+ end
332
+
333
+ if n[:s]
334
+ o.push("\n")
335
+ indent(o,counter)
336
+ o.push('* ->')
337
+
338
+ vsc = deepCopy(vs)
339
+ walk(n[:s],o,counter + 1,vsc,str,dstr)
340
+ end
341
+ end
342
+ end
343
+
344
+
345
+ def isBoolean(obj)
346
+
347
+ if obj != nil && obj.class == TrueClass || obj.class == FalseClass
348
+
349
+ true
350
+ else
351
+ false
352
+ end
353
+ end
354
+
355
+ def deepCopy(o)
356
+ Marshal.load(Marshal.dump(o))
357
+ end
358
+
359
+ def isFunction(param)
360
+ if param != nil && param.class == Proc
361
+ true
362
+ else
363
+ false
364
+ end
365
+ end
366
+
367
+ def merge(source, destination)
368
+ if destination != nil
369
+ source.merge(destination)
370
+ else
371
+ source
372
+ end
373
+ end
374
+
375
+ def gexval(pattern, value)
376
+
377
+
378
+ pattern = escregexp(pattern)
379
+
380
+ # use [\s\S] instead of . to match newlines
381
+ pattern = pattern.gsub(/\\\*/,'[\\s\\S]*')
382
+ pattern = pattern.gsub(/\\\?/,'[\\s\\S]')
383
+
384
+ # escapes ** and *?
385
+ pattern = pattern.gsub(/\[\\s\\S\]\*\[\\s\\S\]\*/,'\\\*')
386
+ pattern = pattern.gsub(/\[\\s\\S\]\*\[\\s\\S\]/,'\\\?')
387
+
388
+ pattern = "^#{pattern}$"
389
+
390
+ if value.to_s.match(pattern) != nil
391
+ true
392
+ else
393
+ false
394
+ end
395
+
396
+ end
397
+
398
+ def escregexp(restr)
399
+ if restr
400
+ restr.to_s.gsub(/[-\[\]{}()*+?.,\\^$|#\s]/) { "\\#{$&}" }
401
+ else
402
+ ""
403
+ end
404
+ end
405
+
406
+ end
metadata ADDED
@@ -0,0 +1,44 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: patrun
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Colm Harte
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-06-17 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: A fast pattern matcher on Ruby object properties.
14
+ email: colm.harte@nearform.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - lib/patrun.rb
20
+ homepage: http://rubygems.org/gems/patrun
21
+ licenses:
22
+ - MIT
23
+ metadata: {}
24
+ post_install_message:
25
+ rdoc_options: []
26
+ require_paths:
27
+ - lib
28
+ required_ruby_version: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ required_rubygems_version: !ruby/object:Gem::Requirement
34
+ requirements:
35
+ - - '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ requirements: []
39
+ rubyforge_project:
40
+ rubygems_version: 2.0.14
41
+ signing_key:
42
+ specification_version: 4
43
+ summary: Fast pattern matcher
44
+ test_files: []