epitools 0.5.0 → 0.5.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/VERSION +1 -1
- data/epitools.gemspec +13 -11
- data/lib/epitools/autoloads.rb +7 -0
- data/lib/epitools/basetypes.rb +49 -2
- data/lib/epitools/iter.rb +149 -0
- data/lib/epitools/path.rb +52 -13
- data/lib/epitools/sys.rb +3 -1
- data/lib/epitools/term.rb +6 -0
- data/spec/basetypes_spec.rb +15 -1
- data/spec/iter_spec.rb +89 -0
- data/spec/path_spec.rb +31 -0
- metadata +11 -9
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.5.
|
1
|
+
0.5.1
|
data/epitools.gemspec
CHANGED
@@ -4,14 +4,14 @@
|
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
|
-
s.name =
|
8
|
-
s.version = "0.5.
|
7
|
+
s.name = "epitools"
|
8
|
+
s.version = "0.5.1"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
-
s.authors = [
|
12
|
-
s.date =
|
13
|
-
s.description =
|
14
|
-
s.email =
|
11
|
+
s.authors = ["epitron"]
|
12
|
+
s.date = "2011-11-23"
|
13
|
+
s.description = "Miscellaneous utility libraries to make my life easier."
|
14
|
+
s.email = "chris@ill-logic.com"
|
15
15
|
s.extra_rdoc_files = [
|
16
16
|
"LICENSE",
|
17
17
|
"README.rdoc",
|
@@ -35,6 +35,7 @@ Gem::Specification.new do |s|
|
|
35
35
|
"lib/epitools/colored.rb",
|
36
36
|
"lib/epitools/ezdb.rb",
|
37
37
|
"lib/epitools/hexdump.rb",
|
38
|
+
"lib/epitools/iter.rb",
|
38
39
|
"lib/epitools/its.rb",
|
39
40
|
"lib/epitools/lcs.rb",
|
40
41
|
"lib/epitools/mimemagic.rb",
|
@@ -59,6 +60,7 @@ Gem::Specification.new do |s|
|
|
59
60
|
"spec/clitools_spec.rb",
|
60
61
|
"spec/colored_spec.rb",
|
61
62
|
"spec/ezdb_spec.rb",
|
63
|
+
"spec/iter_spec.rb",
|
62
64
|
"spec/lcs_spec.rb",
|
63
65
|
"spec/numwords_spec.rb",
|
64
66
|
"spec/path_spec.rb",
|
@@ -71,11 +73,11 @@ Gem::Specification.new do |s|
|
|
71
73
|
"spec/term_spec.rb",
|
72
74
|
"spec/zopen_spec.rb"
|
73
75
|
]
|
74
|
-
s.homepage =
|
75
|
-
s.licenses = [
|
76
|
-
s.require_paths = [
|
77
|
-
s.rubygems_version =
|
78
|
-
s.summary =
|
76
|
+
s.homepage = "http://github.com/epitron/epitools"
|
77
|
+
s.licenses = ["WTFPL"]
|
78
|
+
s.require_paths = ["lib"]
|
79
|
+
s.rubygems_version = "1.8.10"
|
80
|
+
s.summary = "NOT UTILS... METILS!"
|
79
81
|
|
80
82
|
if s.respond_to? :specification_version then
|
81
83
|
s.specification_version = 3
|
data/lib/epitools/autoloads.rb
CHANGED
@@ -16,6 +16,12 @@ autoload :Date, 'date'
|
|
16
16
|
autoload :Open3, 'open3'
|
17
17
|
#autoload :DelegateClass, 'delegate'
|
18
18
|
|
19
|
+
if RUBY_VERSION["1.8.7"]
|
20
|
+
autoload :Prime, 'mathn'
|
21
|
+
else
|
22
|
+
autoload :Prime, 'prime'
|
23
|
+
end
|
24
|
+
|
19
25
|
module Digest
|
20
26
|
autoload :SHA1, 'digest/sha1'
|
21
27
|
autoload :SHA2, 'digest/sha2'
|
@@ -33,6 +39,7 @@ autoload :ProgressBar, 'epitools/progressbar'
|
|
33
39
|
autoload :Trie, 'epitools/trie'
|
34
40
|
autoload :MimeMagic, 'epitools/mimemagic'
|
35
41
|
autoload :Term, 'epitools/term'
|
42
|
+
autoload :Iter, 'epitools/iter'
|
36
43
|
|
37
44
|
## Gems
|
38
45
|
autoreq :ANSI, 'ansi'
|
data/lib/epitools/basetypes.rb
CHANGED
@@ -176,6 +176,15 @@ class Integer
|
|
176
176
|
|
177
177
|
result.map{|digit| BASE62_DIGITS[digit]}.join ''
|
178
178
|
end
|
179
|
+
|
180
|
+
#
|
181
|
+
# Returns the all the prime factors of a number.
|
182
|
+
#
|
183
|
+
def factors
|
184
|
+
Prime # autoload the prime module
|
185
|
+
prime_division.map { |n,count| [n]*count }.flatten
|
186
|
+
end
|
187
|
+
|
179
188
|
end
|
180
189
|
|
181
190
|
|
@@ -312,11 +321,10 @@ class String
|
|
312
321
|
end
|
313
322
|
|
314
323
|
|
315
|
-
|
316
324
|
#
|
317
325
|
# Cached constants for base62 decoding.
|
318
326
|
#
|
319
|
-
BASE62_DIGITS = Hash[ Integer::BASE62_DIGITS.
|
327
|
+
BASE62_DIGITS = Hash[ Integer::BASE62_DIGITS.zip((0...Integer::BASE62_DIGITS.size).to_a) ]
|
320
328
|
BASE62_BASE = Integer::BASE62_BASE
|
321
329
|
|
322
330
|
#
|
@@ -511,6 +519,9 @@ class Array
|
|
511
519
|
piece_size = (size.to_f / pieces).ceil
|
512
520
|
each_slice(piece_size).to_a
|
513
521
|
end
|
522
|
+
|
523
|
+
|
524
|
+
alias_method :unzip, :transpose
|
514
525
|
|
515
526
|
end
|
516
527
|
|
@@ -721,6 +732,42 @@ module Enumerable
|
|
721
732
|
a.select.with_index{ |e, i| bitmask[i] == 1 }
|
722
733
|
end
|
723
734
|
end
|
735
|
+
|
736
|
+
#
|
737
|
+
# Does the opposite of #zip -- converts [ [:a, 1], [:b, 2] ] to [ [:a, :b], [1, 2] ]
|
738
|
+
#
|
739
|
+
def unzip
|
740
|
+
# TODO: make it work for arrays containing uneven-length contents
|
741
|
+
to_a.transpose
|
742
|
+
end
|
743
|
+
|
744
|
+
#
|
745
|
+
# Associative grouping; groups all elements who share something in common with each other.
|
746
|
+
# You supply a block which takes two elements, and have it return true if they are "neighbours"
|
747
|
+
# (eg: belong in the same group).
|
748
|
+
#
|
749
|
+
# Example:
|
750
|
+
# [1,2,5,6].group_neighbours_by { |a,b| b-a <= 1 } #=> [ [1,2], [5,6] ]
|
751
|
+
#
|
752
|
+
# (Note: This is a very fast one-pass algorithm -- therefore, the groups must be pre-sorted.)
|
753
|
+
#
|
754
|
+
def group_neighbours_by(&block)
|
755
|
+
result = []
|
756
|
+
cluster = [first]
|
757
|
+
each_cons(2) do |a,b|
|
758
|
+
if yield(a,b)
|
759
|
+
cluster << b
|
760
|
+
else
|
761
|
+
result << cluster
|
762
|
+
cluster = [b]
|
763
|
+
end
|
764
|
+
end
|
765
|
+
|
766
|
+
result << cluster if cluster.any?
|
767
|
+
|
768
|
+
result
|
769
|
+
end
|
770
|
+
alias_method :group_neighbors_by, :group_neighbours_by
|
724
771
|
|
725
772
|
end
|
726
773
|
|
@@ -0,0 +1,149 @@
|
|
1
|
+
#
|
2
|
+
# A stable iterator class.
|
3
|
+
# (You can reorder/remove elements in the container without affecting iteration.)
|
4
|
+
#
|
5
|
+
# For example, to reverse all the elements in a list:
|
6
|
+
# >> i = Iter.new(1..10)
|
7
|
+
# >> i.each_cons(2) { |a,b| b.move_before(a) }
|
8
|
+
# >> i.to_a #=> [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
|
9
|
+
#
|
10
|
+
class Iter
|
11
|
+
|
12
|
+
attr_accessor :container
|
13
|
+
|
14
|
+
def initialize(vals)
|
15
|
+
@container = vals.map{|val| Elem.new(self, val)}
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.from_elems(elems)
|
19
|
+
new([]).tap{|i| i.container = elems}
|
20
|
+
end
|
21
|
+
|
22
|
+
def ==(other)
|
23
|
+
case other
|
24
|
+
when Iter
|
25
|
+
@container == other.container
|
26
|
+
when Array
|
27
|
+
@container == other
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def each
|
32
|
+
@container.each do |elem|
|
33
|
+
yield elem
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def each_cons(num=1)
|
38
|
+
@container.each_cons(num) do |(*elems)|
|
39
|
+
yield *elems
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
alias_method :iterate, :each_cons
|
44
|
+
alias_method :every, :each_cons
|
45
|
+
|
46
|
+
def to_a
|
47
|
+
@container.map(&:val)
|
48
|
+
end
|
49
|
+
|
50
|
+
def method_missing(name, *args)
|
51
|
+
result = @container.send(name, *args)
|
52
|
+
case result
|
53
|
+
when Array
|
54
|
+
Iter.from_elems result
|
55
|
+
else
|
56
|
+
result
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class Elem < BasicObject
|
61
|
+
|
62
|
+
attr_accessor :val
|
63
|
+
|
64
|
+
def initialize(iter, val)
|
65
|
+
@iter = iter
|
66
|
+
@val = val
|
67
|
+
end
|
68
|
+
|
69
|
+
def ==(other)
|
70
|
+
self.eql?(other)
|
71
|
+
end
|
72
|
+
|
73
|
+
def container
|
74
|
+
@iter.container
|
75
|
+
end
|
76
|
+
|
77
|
+
def current
|
78
|
+
self
|
79
|
+
end
|
80
|
+
|
81
|
+
def next
|
82
|
+
p = pos+1
|
83
|
+
if p >= container.size
|
84
|
+
nil
|
85
|
+
else
|
86
|
+
container[p]
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def prev
|
91
|
+
p = pos-1
|
92
|
+
if p < 0
|
93
|
+
nil
|
94
|
+
else
|
95
|
+
container[p]
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def remove
|
100
|
+
container.delete_at(pos)
|
101
|
+
end
|
102
|
+
alias_method :delete, :remove
|
103
|
+
|
104
|
+
def replace_with(replacement)
|
105
|
+
container[pos] = Elem.new(@iter, replacement)
|
106
|
+
end
|
107
|
+
|
108
|
+
def pos
|
109
|
+
container.index(self)
|
110
|
+
end
|
111
|
+
|
112
|
+
def move_before(other)
|
113
|
+
remove
|
114
|
+
container.insert(other.pos, self) # insert at pos and shift everything over
|
115
|
+
end
|
116
|
+
|
117
|
+
def move_after(other)
|
118
|
+
remove
|
119
|
+
container.insert(other.pos+1, self) # insert after pos
|
120
|
+
end
|
121
|
+
|
122
|
+
def move_first
|
123
|
+
remove
|
124
|
+
container.insert(0, self) # insert at beginning
|
125
|
+
end
|
126
|
+
alias_method :move_start, :move_first
|
127
|
+
|
128
|
+
def move_last
|
129
|
+
remove
|
130
|
+
container.insert(-1, self) # insert at end
|
131
|
+
end
|
132
|
+
alias_method :move_end, :move_last
|
133
|
+
|
134
|
+
def value
|
135
|
+
@val
|
136
|
+
end
|
137
|
+
|
138
|
+
def method_missing(name, *args)
|
139
|
+
@val.send(name, *args)
|
140
|
+
end
|
141
|
+
|
142
|
+
def inspect
|
143
|
+
"<Elem: #{@val.inspect}>"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
|
148
|
+
end
|
149
|
+
|
data/lib/epitools/path.rb
CHANGED
@@ -57,8 +57,8 @@ class Path
|
|
57
57
|
|
58
58
|
## initializers
|
59
59
|
|
60
|
-
def initialize(newpath)
|
61
|
-
self.path
|
60
|
+
def initialize(newpath, hints={})
|
61
|
+
self.send("path=", newpath, hints)
|
62
62
|
end
|
63
63
|
|
64
64
|
def self.glob(str)
|
@@ -96,9 +96,14 @@ class Path
|
|
96
96
|
attr_writer :base
|
97
97
|
attr_writer :dirs
|
98
98
|
|
99
|
-
|
100
|
-
|
101
|
-
|
99
|
+
#
|
100
|
+
# This is the core that initializes the whole class.
|
101
|
+
#
|
102
|
+
# Note: The `hints` parameter contains options so `path=` doesn't have to touch the filesytem as much.
|
103
|
+
#
|
104
|
+
def path=(newpath, hints={})
|
105
|
+
if hints[:type] or File.exists? newpath
|
106
|
+
if hints[:type] == :dir or File.directory? newpath
|
102
107
|
self.dir = newpath
|
103
108
|
else
|
104
109
|
self.dir, self.filename = File.split(newpath)
|
@@ -261,6 +266,14 @@ class Path
|
|
261
266
|
uri?
|
262
267
|
end
|
263
268
|
|
269
|
+
def child_of?(parent)
|
270
|
+
parent.parent_of? self
|
271
|
+
end
|
272
|
+
|
273
|
+
def parent_of?(child)
|
274
|
+
# If `self` is a parent of `child`, it's a prefix.
|
275
|
+
child.path[/^#{Regexp.escape self.path}\/.+/] != nil
|
276
|
+
end
|
264
277
|
|
265
278
|
## comparisons
|
266
279
|
|
@@ -324,7 +337,16 @@ class Path
|
|
324
337
|
def ls; Path[File.join(path, "*")]; end
|
325
338
|
|
326
339
|
def ls_r; Path[File.join(path, "**/*")]; end
|
327
|
-
|
340
|
+
|
341
|
+
def ls_dirs
|
342
|
+
ls.select(&:dir?)
|
343
|
+
#Dir.glob("#{path}*/", File::FNM_DOTMATCH).map { |s| Path.new(s, :type=>:dir) }
|
344
|
+
end
|
345
|
+
|
346
|
+
def ls_files
|
347
|
+
ls.select(&:file?)
|
348
|
+
#Dir.glob("#{path}*", File::FNM_DOTMATCH).map { |s| Path.new(s, :type=>:file) }
|
349
|
+
end
|
328
350
|
|
329
351
|
def siblings
|
330
352
|
ls - [self]
|
@@ -455,7 +477,6 @@ raise "Broken!"
|
|
455
477
|
end
|
456
478
|
end
|
457
479
|
|
458
|
-
|
459
480
|
def ln_s(dest)
|
460
481
|
dest = Path[dest]
|
461
482
|
FileUtils.ln_s self, dest
|
@@ -582,7 +603,7 @@ raise "Broken!"
|
|
582
603
|
end
|
583
604
|
|
584
605
|
def lstat
|
585
|
-
#@lstat ||= File.lstat self
|
606
|
+
#@lstat ||= File.lstat self # to cache or not to cache -- that is the question.
|
586
607
|
File.lstat self
|
587
608
|
end
|
588
609
|
|
@@ -590,6 +611,9 @@ raise "Broken!"
|
|
590
611
|
lstat.mode
|
591
612
|
end
|
592
613
|
|
614
|
+
#
|
615
|
+
# Find the parent directory. If the `Path` is a filename, it returns the containing directory.
|
616
|
+
#
|
593
617
|
def parent
|
594
618
|
if file?
|
595
619
|
with(:filename=>nil)
|
@@ -598,6 +622,21 @@ raise "Broken!"
|
|
598
622
|
end
|
599
623
|
end
|
600
624
|
|
625
|
+
#
|
626
|
+
# Follows all symlinks to give the true location of a path.
|
627
|
+
#
|
628
|
+
if File.respond_to?(:realpath)
|
629
|
+
def realpath
|
630
|
+
Path.new File.realpath(path)
|
631
|
+
end
|
632
|
+
else
|
633
|
+
def realpath
|
634
|
+
require 'pathname'
|
635
|
+
Path.new Pathname.new(path).realpath
|
636
|
+
end
|
637
|
+
end
|
638
|
+
|
639
|
+
|
601
640
|
# Mimetype finding and magic (requires 'mimemagic' gem)
|
602
641
|
|
603
642
|
#
|
@@ -701,6 +740,7 @@ raise "Broken!"
|
|
701
740
|
md5
|
702
741
|
rm
|
703
742
|
truncate
|
743
|
+
realpath
|
704
744
|
mv/1
|
705
745
|
move/1
|
706
746
|
chmod/1
|
@@ -717,9 +757,8 @@ raise "Broken!"
|
|
717
757
|
end
|
718
758
|
}
|
719
759
|
end
|
720
|
-
|
721
|
-
|
722
|
-
|
760
|
+
|
761
|
+
|
723
762
|
#
|
724
763
|
# Same as File.expand_path, except preserves the trailing '/'.
|
725
764
|
#
|
@@ -811,7 +850,7 @@ class Path::URL < Path
|
|
811
850
|
# TODO: only include certain methods from Path (delegate style)
|
812
851
|
# (eg: remove commands that write)
|
813
852
|
|
814
|
-
def initialize(uri)
|
853
|
+
def initialize(uri, hints={})
|
815
854
|
@uri = URI.parse(uri)
|
816
855
|
self.path = @uri.path
|
817
856
|
end
|
@@ -849,7 +888,7 @@ class Path::URL < Path
|
|
849
888
|
# ...and this is: 80
|
850
889
|
#
|
851
890
|
def port
|
852
|
-
uri.
|
891
|
+
uri.port
|
853
892
|
end
|
854
893
|
|
855
894
|
#
|
data/lib/epitools/sys.rb
CHANGED
@@ -224,8 +224,10 @@ module Sys
|
|
224
224
|
def self.ps(*pids)
|
225
225
|
options = PS_FIELDS.join(',')
|
226
226
|
|
227
|
+
pids = pids.map(&:to_i)
|
228
|
+
|
227
229
|
if pids.any?
|
228
|
-
command = "ps -p #{pids.
|
230
|
+
command = "ps -p #{pids.join(',')} -o #{options}"
|
229
231
|
else
|
230
232
|
command = "ps ax -o #{options}"
|
231
233
|
end
|
data/lib/epitools/term.rb
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
#require 'epitools'
|
2
2
|
|
3
|
+
#
|
4
|
+
# Example usage:
|
5
|
+
# puts Term::Table[ (1..100).to_a ].horizontally #=> prints all the numbers, ordered across rows
|
6
|
+
# puts Term::Table[ (1..100).to_a ].vertically #=> prints all the numbers, ordered across columns
|
7
|
+
# puts Term::Table[ [[1,2], [3,4]] ] #=> prints the table that was supplied
|
8
|
+
#
|
3
9
|
module Term
|
4
10
|
|
5
11
|
extend self
|
data/spec/basetypes_spec.rb
CHANGED
@@ -259,6 +259,11 @@ describe Integer do
|
|
259
259
|
sum.to_base62.from_base62.to_s(16).should == sum
|
260
260
|
end
|
261
261
|
|
262
|
+
it "factors numbers" do
|
263
|
+
10.factors.should == [2,5]
|
264
|
+
256.factors.should == [2,2,2,2,2,2,2,2]
|
265
|
+
end
|
266
|
+
|
262
267
|
end
|
263
268
|
|
264
269
|
|
@@ -358,8 +363,17 @@ describe Enumerable do
|
|
358
363
|
it "powersets" do
|
359
364
|
[1,2,3].powerset.should == [[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]
|
360
365
|
Enum.new([1,2], :each).powerset.should == [[], [1], [2], [1, 2]]
|
361
|
-
end
|
366
|
+
end
|
362
367
|
|
368
|
+
it "unzips" do
|
369
|
+
[ [:a, 1], [:b, 2] ].unzip.should == [ [:a, :b], [1, 2] ]
|
370
|
+
end
|
371
|
+
|
372
|
+
it "group_neighbours_bys" do
|
373
|
+
a = [1,2,5,6,7,10,11,13]
|
374
|
+
result = a.group_neighbours_by { |a,b| b-a <= 1 }
|
375
|
+
result.should == [[1,2],[5,6,7],[10,11],[13]]
|
376
|
+
end
|
363
377
|
end
|
364
378
|
|
365
379
|
describe Hash do
|
data/spec/iter_spec.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'epitools/iter'
|
2
|
+
|
3
|
+
describe Iter do
|
4
|
+
|
5
|
+
before :each do
|
6
|
+
@i = Iter.new([1,2,3,4,5])
|
7
|
+
end
|
8
|
+
|
9
|
+
it "iterates" do
|
10
|
+
@i.iterate(2) do |i,j|
|
11
|
+
i.should_not == j
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
it "to_a's" do
|
16
|
+
@i.to_a.should == [1,2,3,4,5]
|
17
|
+
end
|
18
|
+
|
19
|
+
it "reverses" do
|
20
|
+
@i.iterate(2) do |a, b|
|
21
|
+
b.move_before(a)
|
22
|
+
end
|
23
|
+
|
24
|
+
@i.to_a.should == [5,4,3,2,1]
|
25
|
+
end
|
26
|
+
|
27
|
+
it "next/prevs" do
|
28
|
+
@i.iterate(2) do |a,b|
|
29
|
+
a.next.should == b
|
30
|
+
b.prev.should == a
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
it "removes" do
|
35
|
+
@i.iterate {|x| x.remove if x % 2 == 1 }
|
36
|
+
@i.to_a.should == [2,4]
|
37
|
+
end
|
38
|
+
|
39
|
+
it "replaces" do
|
40
|
+
@i.first.replace_with(-1)
|
41
|
+
@i.to_a.should == [-1,2,3,4,5]
|
42
|
+
end
|
43
|
+
|
44
|
+
it "slices, values, indexes, etc." do
|
45
|
+
# todo: slice should return an iter
|
46
|
+
@i.first.should == 1
|
47
|
+
@i[0..1].should == @i.values_at(0,1)
|
48
|
+
@i[0..-1].should == @i
|
49
|
+
@i[-1].should == @i.last
|
50
|
+
@i[-2..-1].should == @i.values_at(-2,-1)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "move_first/last" do
|
54
|
+
@i.first.move_last
|
55
|
+
@i.to_a.should == [2,3,4,5,1]
|
56
|
+
|
57
|
+
@i.last.move_first
|
58
|
+
@i.should == [1,2,3,4,5]
|
59
|
+
end
|
60
|
+
|
61
|
+
it "cluters nearby elements" do
|
62
|
+
class Cluster < Array
|
63
|
+
def min_distance(other)
|
64
|
+
a, b = other.max, other.min
|
65
|
+
x, y = max, min
|
66
|
+
[a-x, a-y, b-x, b-y].map(&:abs).min
|
67
|
+
end
|
68
|
+
|
69
|
+
def absorb(other)
|
70
|
+
concat other
|
71
|
+
sort!
|
72
|
+
other.clear
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
a = [1,2,5,6,7,10,11,13].map { |e| Cluster.new [e] }
|
77
|
+
i = Iter.new(a)
|
78
|
+
|
79
|
+
i.each_cons(2) do |a,b|
|
80
|
+
if b.any? and a.any? and a.min_distance(b) <= 1
|
81
|
+
b.absorb(a)
|
82
|
+
a.remove
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
i.to_a.should == [[1,2],[5,6,7],[10,11],[13]]
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
data/spec/path_spec.rb
CHANGED
@@ -126,6 +126,7 @@ describe Path do
|
|
126
126
|
it "handles URLs" do
|
127
127
|
path = Path["http://google.com/?search=blah"]
|
128
128
|
path.host.should == "google.com"
|
129
|
+
path.port.should == 80
|
129
130
|
path.query.should == {"search" => "blah"}
|
130
131
|
path.uri?.should == true
|
131
132
|
end
|
@@ -321,4 +322,34 @@ describe Path do
|
|
321
322
|
# swap two symlinks
|
322
323
|
end
|
323
324
|
|
325
|
+
it 'realpaths' do
|
326
|
+
Path["/etc"].realpath.should == Path["/etc"]
|
327
|
+
|
328
|
+
end
|
329
|
+
|
330
|
+
it 'parents and childs properly' do
|
331
|
+
|
332
|
+
root = Path["/"]
|
333
|
+
parent = Path["/blah/stuff"]
|
334
|
+
child = Path["/blah/stuff/what"]
|
335
|
+
neither = Path["/whee/yay"]
|
336
|
+
|
337
|
+
# Table of parent=>child relationships
|
338
|
+
{
|
339
|
+
parent => child,
|
340
|
+
root => parent,
|
341
|
+
root => child,
|
342
|
+
root => neither,
|
343
|
+
}.each do |p, c|
|
344
|
+
p.parent_of?(c).should == true
|
345
|
+
c.parent_of?(p).should == false
|
346
|
+
|
347
|
+
c.child_of?(p).should == true
|
348
|
+
p.child_of?(c).should == false
|
349
|
+
end
|
350
|
+
|
351
|
+
neither.parent_of?(child).should == false
|
352
|
+
neither.parent_of?(parent).should == false
|
353
|
+
end
|
354
|
+
|
324
355
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: epitools
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-
|
12
|
+
date: 2011-11-23 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
16
|
-
requirement: &
|
16
|
+
requirement: &83289590 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 2.2.0
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *83289590
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: mechanize
|
27
|
-
requirement: &
|
27
|
+
requirement: &83289150 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: 1.0.0
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *83289150
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: sqlite3-ruby
|
38
|
-
requirement: &
|
38
|
+
requirement: &83288710 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,7 +43,7 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *83288710
|
47
47
|
description: Miscellaneous utility libraries to make my life easier.
|
48
48
|
email: chris@ill-logic.com
|
49
49
|
executables: []
|
@@ -70,6 +70,7 @@ files:
|
|
70
70
|
- lib/epitools/colored.rb
|
71
71
|
- lib/epitools/ezdb.rb
|
72
72
|
- lib/epitools/hexdump.rb
|
73
|
+
- lib/epitools/iter.rb
|
73
74
|
- lib/epitools/its.rb
|
74
75
|
- lib/epitools/lcs.rb
|
75
76
|
- lib/epitools/mimemagic.rb
|
@@ -94,6 +95,7 @@ files:
|
|
94
95
|
- spec/clitools_spec.rb
|
95
96
|
- spec/colored_spec.rb
|
96
97
|
- spec/ezdb_spec.rb
|
98
|
+
- spec/iter_spec.rb
|
97
99
|
- spec/lcs_spec.rb
|
98
100
|
- spec/numwords_spec.rb
|
99
101
|
- spec/path_spec.rb
|
@@ -126,7 +128,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
126
128
|
version: '0'
|
127
129
|
requirements: []
|
128
130
|
rubyforge_project:
|
129
|
-
rubygems_version: 1.8.
|
131
|
+
rubygems_version: 1.8.10
|
130
132
|
signing_key:
|
131
133
|
specification_version: 3
|
132
134
|
summary: NOT UTILS... METILS!
|