epitools 0.5.0 → 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- 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!
|