epitools 0.5.116 → 0.5.118
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.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/lib/epitools.rb +0 -1
- data/lib/epitools/autoloads.rb +5 -0
- data/lib/epitools/core_ext/array.rb +21 -0
- data/lib/epitools/core_ext/enumerable.rb +11 -8
- data/lib/epitools/core_ext/matrix.rb +30 -2
- data/lib/epitools/core_ext/numbers.rb +36 -19
- data/lib/epitools/core_ext/string.rb +19 -16
- data/lib/epitools/minimal.rb +2 -2
- data/lib/epitools/path.rb +116 -38
- data/spec/btree_spec.rb +25 -0
- data/spec/core_ext_spec.rb +46 -3
- data/spec/path_spec.rb +33 -1
- data/spec/typed_struct_spec.rb +3 -3
- metadata +3 -3
- data/lib/epitools/zopen.rb +0 -50
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c905d2edd9ff4e5738a2a14992e64168ab876049a481a57f03bfec551aa65bbc
|
4
|
+
data.tar.gz: 81a3dabab5584a115d2a2826e34d681c7a910201b1a13ee58b5b3a9f2129b728
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6daaa56aeba42a400b2a6e0387fc329e7b95b88a16bae5b704efc2ec956f59d0e0c1580816ea4378441bee7755fc3d1bacb2f7e48f3ac5553b45dcc97330c112
|
7
|
+
data.tar.gz: 05b6d0bd082e135f2274d3e896c2baab6da7d4ec02bebdb05c3f4a8a05b5cfe342f020985c29efabd4b421968c803afb02ba5349ad23d28ee6ea7acce22a5c0e
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.5.
|
1
|
+
0.5.118
|
data/lib/epitools.rb
CHANGED
data/lib/epitools/autoloads.rb
CHANGED
@@ -79,6 +79,7 @@ autoload :Matrix, 'epitools/core_ext/matrix'
|
|
79
79
|
autoload :SemanticVersion, 'epitools/semantic_version'
|
80
80
|
|
81
81
|
|
82
|
+
|
82
83
|
## Gems (common)
|
83
84
|
|
84
85
|
autoreq :Nokogiri, 'nokogiri'
|
@@ -91,6 +92,10 @@ autoreq :RBTree, 'rbtree'
|
|
91
92
|
autoreq :MultiRBTree, 'rbtree'
|
92
93
|
autoreq :ID3Tag, 'id3tag'
|
93
94
|
|
95
|
+
autoreq :Numo do
|
96
|
+
require 'numo/narray'
|
97
|
+
end
|
98
|
+
|
94
99
|
autoreq :AwesomePrint do
|
95
100
|
require 'awesome_print'
|
96
101
|
|
@@ -190,6 +190,27 @@ class Array
|
|
190
190
|
end
|
191
191
|
alias_method :col, :column
|
192
192
|
|
193
|
+
#
|
194
|
+
# Create a 2D matrix out of arrays
|
195
|
+
#
|
196
|
+
def self.matrix(height, width, initial_value=nil)
|
197
|
+
if block_given?
|
198
|
+
height.times.map do |row|
|
199
|
+
width.times.map do |col|
|
200
|
+
yield(row, col)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
else
|
204
|
+
height.times.map do
|
205
|
+
[initial_value] * width
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
alias_class_method :rect, :matrix
|
211
|
+
alias_class_method :of_arrays, :matrix
|
212
|
+
|
213
|
+
|
193
214
|
####################################################################
|
194
215
|
|
195
216
|
#
|
@@ -207,16 +207,19 @@ module Enumerable
|
|
207
207
|
end
|
208
208
|
|
209
209
|
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
210
|
+
unless defined? sum
|
211
|
+
#
|
212
|
+
# Sum the elements
|
213
|
+
#
|
214
|
+
def sum(&block)
|
215
|
+
if block_given?
|
216
|
+
map(&block).reduce(:+)
|
217
|
+
else
|
218
|
+
reduce(:+)
|
219
|
+
end
|
218
220
|
end
|
219
221
|
end
|
222
|
+
|
220
223
|
alias_method :sum_by, :sum
|
221
224
|
|
222
225
|
#
|
@@ -19,6 +19,13 @@ class Matrix
|
|
19
19
|
alias_class_method :zeros, :zero
|
20
20
|
alias_class_method :zeroes, :zero
|
21
21
|
|
22
|
+
#
|
23
|
+
# Build an array of 1's
|
24
|
+
#
|
25
|
+
def self.ones(*dims)
|
26
|
+
build(*dims) { 1 }
|
27
|
+
end
|
28
|
+
|
22
29
|
#
|
23
30
|
# Create a matrix of the specified size full of random numbers.
|
24
31
|
#
|
@@ -26,7 +33,6 @@ class Matrix
|
|
26
33
|
build(*dims) { rand }
|
27
34
|
end
|
28
35
|
|
29
|
-
|
30
36
|
#
|
31
37
|
# The size of the matrix, returned as `[rows, columns]`.
|
32
38
|
#
|
@@ -34,6 +40,17 @@ class Matrix
|
|
34
40
|
[row_size, column_size]
|
35
41
|
end
|
36
42
|
|
43
|
+
#
|
44
|
+
# Iterate over rows (takes a block or returns an Enumerator)
|
45
|
+
#
|
46
|
+
def each_row
|
47
|
+
return to_enum(:each_row) unless block_given?
|
48
|
+
|
49
|
+
(0...row_count).each do |num|
|
50
|
+
yield row(num)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
37
54
|
#
|
38
55
|
# Print the matrix to the STDOUT.
|
39
56
|
#
|
@@ -58,9 +75,20 @@ class Matrix
|
|
58
75
|
|
59
76
|
puts
|
60
77
|
end
|
61
|
-
|
62
78
|
alias_method :draw, :print
|
63
79
|
|
80
|
+
#
|
81
|
+
# Overlay one matrix onto another
|
82
|
+
#
|
83
|
+
def blit!(submatrix, top, left)
|
84
|
+
submatrix.each_row.with_index do |row, y|
|
85
|
+
row.each.with_index do |elem, x|
|
86
|
+
self[top+y,left+x] = elem
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
alias_method :overlay!, :blit!
|
91
|
+
|
64
92
|
#
|
65
93
|
# Allow mathematical operations (*, /, +, -) with a scalar (integer or float) on the right side.
|
66
94
|
#
|
@@ -266,34 +266,51 @@ class Integer
|
|
266
266
|
alias_method :bits, :to_bits
|
267
267
|
|
268
268
|
#
|
269
|
-
# Cached constants for
|
269
|
+
# Cached constants for encoding numbers into bases up to 64
|
270
270
|
#
|
271
|
-
|
272
|
-
|
271
|
+
BASE_DIGITS = [*'0'..'9', *'A'..'Z', *'a'..'z', '_', '-']
|
272
|
+
SMALL_POWERS_OF_2 = {2=>1, 4=>2, 8=>3, 16=>4, 32=>5, 64=>6}
|
273
273
|
|
274
274
|
#
|
275
|
-
# Convert a number
|
275
|
+
# Convert a number into a string representation (encoded in a base <= 64).
|
276
276
|
#
|
277
|
-
#
|
277
|
+
# The number is represented usiing the characters: 0..9, A..Z, a..z, _, -
|
278
278
|
#
|
279
|
-
#
|
280
|
-
# ID strings
|
279
|
+
# (Setting base to 64 results in the encoding scheme that YouTube and url shorteners
|
280
|
+
# use for their ID strings, eg: http://www.youtube.com/watch?v=dQw4w9WgXcQ)
|
281
281
|
#
|
282
|
-
def
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
282
|
+
def to_base(base=10)
|
283
|
+
raise "Error: Can't handle bases greater than 64" if base > 64
|
284
|
+
|
285
|
+
n = self
|
286
|
+
digits = []
|
287
|
+
alphabet = BASE_DIGITS[0...base]
|
288
|
+
|
289
|
+
if bits = SMALL_POWERS_OF_2[base]
|
290
|
+
# a slightly accelerated version for powers of 2
|
291
|
+
mask = (2**bits)-1
|
292
|
+
|
293
|
+
loop do
|
294
|
+
digits << (n & mask)
|
295
|
+
n = n >> bits
|
296
|
+
|
297
|
+
break if n == 0
|
298
|
+
end
|
299
|
+
else
|
300
|
+
# generic base conversion
|
301
|
+
loop do
|
302
|
+
n, digit = n.divmod(base)
|
303
|
+
digits << digit
|
304
|
+
|
305
|
+
break if n == 0
|
306
|
+
end
|
292
307
|
end
|
293
308
|
|
294
|
-
|
309
|
+
digits.reverse.map { |d| alphabet[d] }.join
|
310
|
+
end
|
295
311
|
|
296
|
-
|
312
|
+
def to_base62
|
313
|
+
to_base(62)
|
297
314
|
end
|
298
315
|
|
299
316
|
#
|
@@ -311,30 +311,33 @@ class String
|
|
311
311
|
end
|
312
312
|
|
313
313
|
#
|
314
|
-
# Cached constants for
|
314
|
+
# Cached constants for base conversion.
|
315
315
|
#
|
316
|
-
|
317
|
-
BASE62_BASE = Integer::BASE62_BASE
|
316
|
+
BASE_DIGITS = Integer::BASE_DIGITS.map.with_index.to_h
|
318
317
|
|
319
318
|
#
|
320
|
-
# Convert a string
|
321
|
-
#
|
319
|
+
# Convert a string encoded in some base <= 64 into an integer.
|
320
|
+
# (See Integer#to_base for more info.)
|
322
321
|
#
|
323
|
-
def
|
324
|
-
|
322
|
+
def from_base(base=10)
|
323
|
+
n = 0
|
324
|
+
chars.reverse_each.with_index do |c, power|
|
325
|
+
value = BASE_DIGITS[c]
|
326
|
+
n += (base**power) * value
|
327
|
+
end
|
328
|
+
n
|
329
|
+
end
|
330
|
+
|
331
|
+
def from_base62
|
332
|
+
from_base(62)
|
325
333
|
end
|
326
334
|
|
327
335
|
#
|
328
|
-
# Convert a string encoded in
|
329
|
-
# (See Integer#to_base62 for more info.)
|
336
|
+
# Convert a string (encoded in base16 "hex" -- for example, an MD5 or SHA1 hash)
|
337
|
+
# into "base62" format. (See Integer#to_base62 for more info.)
|
330
338
|
#
|
331
|
-
def
|
332
|
-
|
333
|
-
digits = chars.map { |c| BASE62_DIGITS[c] }.reverse
|
334
|
-
digits.each_with_index do |digit, power|
|
335
|
-
accumulator += (BASE62_BASE**power) * digit if digit > 0
|
336
|
-
end
|
337
|
-
accumulator
|
339
|
+
def to_base62
|
340
|
+
to_i(16).to_base62
|
338
341
|
end
|
339
342
|
|
340
343
|
#
|
data/lib/epitools/minimal.rb
CHANGED
data/lib/epitools/path.rb
CHANGED
@@ -596,7 +596,11 @@ class Path
|
|
596
596
|
when String, Numeric, true, false, nil
|
597
597
|
self[key] = new
|
598
598
|
else
|
599
|
-
|
599
|
+
if new.respond_to? :to_str
|
600
|
+
self[key] = new.to_str
|
601
|
+
else
|
602
|
+
raise "Error: Can't use a #{new.class} as an xattr value. Try passing a String."
|
603
|
+
end
|
600
604
|
end
|
601
605
|
end
|
602
606
|
end
|
@@ -670,7 +674,7 @@ class Path
|
|
670
674
|
return to_enum(:grep, pat).to_a unless block_given?
|
671
675
|
|
672
676
|
each_line do |line|
|
673
|
-
yield line if line
|
677
|
+
yield line if line[pat]
|
674
678
|
end
|
675
679
|
end
|
676
680
|
|
@@ -779,6 +783,62 @@ class Path
|
|
779
783
|
end
|
780
784
|
end
|
781
785
|
|
786
|
+
###############################################################################
|
787
|
+
# zopening files
|
788
|
+
###############################################################################
|
789
|
+
|
790
|
+
COMPRESSORS = {
|
791
|
+
"gz" => "gzip",
|
792
|
+
"xz" => "xz",
|
793
|
+
"bz2" => "bzip2"
|
794
|
+
}
|
795
|
+
|
796
|
+
#
|
797
|
+
# A mutation of "open" that lets you read/write gzip files, as well as
|
798
|
+
# regular files.
|
799
|
+
#
|
800
|
+
# (NOTE: gzip detection is based on the filename, not the contents.)
|
801
|
+
#
|
802
|
+
# It accepts a block just like open()!
|
803
|
+
#
|
804
|
+
# Example:
|
805
|
+
# zopen("test.txt") #=> #<File:test.txt>
|
806
|
+
# zopen("test.txt.gz") #=> #<Zlib::GzipReader:0xb6c79424>
|
807
|
+
# zopen("otherfile.gz", "w") #=> #<Zlib::GzipWriter:0x7fe30448>>
|
808
|
+
# zopen("test.txt.gz") { |f| f.read } # read the contents of the .gz file, then close the file handle automatically.
|
809
|
+
#
|
810
|
+
def zopen(mode="rb")
|
811
|
+
# if ext == "gz"
|
812
|
+
# io = open(mode)
|
813
|
+
# case mode
|
814
|
+
# when "r", "rb"
|
815
|
+
# io = Zlib::GzipReader.new(io)
|
816
|
+
# def io.to_str; read; end
|
817
|
+
# when "w", "wb"
|
818
|
+
# io = Zlib::GzipWriter.new(io)
|
819
|
+
# else
|
820
|
+
# raise "Unknown mode: #{mode.inspect}. zopen only supports 'r' and 'w'."
|
821
|
+
# end
|
822
|
+
# elsif bin = COMPRESSORS[ext]
|
823
|
+
if bin = COMPRESSORS[ext]
|
824
|
+
if which(bin)
|
825
|
+
io = IO.popen([bin, "-d" ,"-c", path])
|
826
|
+
else
|
827
|
+
raise "Error: couln't find #{bin.inspect} in the path"
|
828
|
+
end
|
829
|
+
else
|
830
|
+
io = open(path)
|
831
|
+
end
|
832
|
+
|
833
|
+
if block_given?
|
834
|
+
result = yield(io)
|
835
|
+
io.close
|
836
|
+
result
|
837
|
+
else
|
838
|
+
io
|
839
|
+
end
|
840
|
+
|
841
|
+
end
|
782
842
|
|
783
843
|
###############################################################################
|
784
844
|
# Parsing files
|
@@ -788,22 +848,24 @@ class Path
|
|
788
848
|
# Parse the file based on the file extension.
|
789
849
|
# (Handles json, html, yaml, xml, csv, marshal, and bson.)
|
790
850
|
#
|
791
|
-
def parse
|
792
|
-
case ext.downcase
|
851
|
+
def parse(io=self.io, forced_ext=nil)
|
852
|
+
case (forced_ext or ext.downcase)
|
853
|
+
when 'gz', 'bz2', 'xz'
|
854
|
+
parse(zopen, exts[-2])
|
793
855
|
when 'json'
|
794
|
-
read_json
|
856
|
+
read_json(io)
|
795
857
|
when 'html', 'htm'
|
796
|
-
read_html
|
858
|
+
read_html(io)
|
797
859
|
when 'yaml', 'yml'
|
798
|
-
read_yaml
|
860
|
+
read_yaml(io)
|
799
861
|
when 'xml', 'rdf', 'rss'
|
800
|
-
read_xml
|
862
|
+
read_xml(io)
|
801
863
|
when 'csv'
|
802
|
-
read_csv
|
864
|
+
read_csv(io)
|
803
865
|
when 'marshal'
|
804
|
-
read_marshal
|
866
|
+
read_marshal(io)
|
805
867
|
when 'bson'
|
806
|
-
read_bson
|
868
|
+
read_bson(io)
|
807
869
|
else
|
808
870
|
raise "Unrecognized format: #{ext}"
|
809
871
|
end
|
@@ -816,8 +878,9 @@ class Path
|
|
816
878
|
each_line.map { |line| JSON.parse line }
|
817
879
|
end
|
818
880
|
|
881
|
+
|
819
882
|
# Parse the file as JSON
|
820
|
-
def read_json
|
883
|
+
def read_json(io=self.io)
|
821
884
|
JSON.load(io)
|
822
885
|
end
|
823
886
|
alias_method :from_json, :read_json
|
@@ -828,7 +891,7 @@ class Path
|
|
828
891
|
end
|
829
892
|
|
830
893
|
|
831
|
-
def read_html
|
894
|
+
def read_html(io=self.io)
|
832
895
|
Nokogiri::HTML(io)
|
833
896
|
end
|
834
897
|
alias_method :from_html, :read_html
|
@@ -840,11 +903,12 @@ class Path
|
|
840
903
|
end
|
841
904
|
|
842
905
|
# Parse the file as YAML
|
843
|
-
def read_yaml
|
906
|
+
def read_yaml(io=self.io)
|
844
907
|
YAML.load(io)
|
845
908
|
end
|
846
909
|
alias_method :from_yaml, :read_yaml
|
847
910
|
|
911
|
+
|
848
912
|
# Parse the file as CSV
|
849
913
|
def read_csv(opts={})
|
850
914
|
CSV.open(io, opts).each
|
@@ -852,12 +916,12 @@ class Path
|
|
852
916
|
alias_method :from_csv, :read_csv
|
853
917
|
|
854
918
|
# Parse the file as XML
|
855
|
-
def read_xml
|
919
|
+
def read_xml(io=self.io)
|
856
920
|
Nokogiri::XML(io)
|
857
921
|
end
|
858
922
|
|
859
923
|
# Parse the file as a Ruby Marshal dump
|
860
|
-
def read_marshal
|
924
|
+
def read_marshal(io=self.io)
|
861
925
|
Marshal.load(io)
|
862
926
|
end
|
863
927
|
|
@@ -867,7 +931,7 @@ class Path
|
|
867
931
|
end
|
868
932
|
|
869
933
|
# Parse the file as BSON
|
870
|
-
def read_bson
|
934
|
+
def read_bson(io=self.io)
|
871
935
|
BSON.deserialize(read)
|
872
936
|
end
|
873
937
|
|
@@ -880,7 +944,7 @@ class Path
|
|
880
944
|
# Read ID3 tags (requires 'id3tag' gem)
|
881
945
|
#
|
882
946
|
# Available fields:
|
883
|
-
# tag.artist, tag.title, tag.album, tag.year, tag.track_nr, tag.genre, tag.get_frame(:TIT2)&.content,
|
947
|
+
# tag.artist, tag.title, tag.album, tag.year, tag.track_nr, tag.genre, tag.get_frame(:TIT2)&.content,
|
884
948
|
# tag.get_frames(:COMM).first&.content, tag.get_frames(:COMM).last&.language
|
885
949
|
#
|
886
950
|
def id3
|
@@ -1124,7 +1188,7 @@ class Path
|
|
1124
1188
|
# Remove a file or directory
|
1125
1189
|
#
|
1126
1190
|
def rm
|
1127
|
-
raise "Error: #{self} does not exist" unless symlink? or exists?
|
1191
|
+
raise "Error: #{self} does not exist" unless symlink? or exists?
|
1128
1192
|
|
1129
1193
|
if directory? and not symlink?
|
1130
1194
|
Dir.rmdir(self) == 0
|
@@ -1391,7 +1455,7 @@ class Path
|
|
1391
1455
|
# TODO: Remove the tempfile when the Path object is garbage collected or freed.
|
1392
1456
|
#
|
1393
1457
|
def self.tmpfile(prefix="tmp")
|
1394
|
-
path = Path.new
|
1458
|
+
path = Path.new(Tempfile.new(prefix).path, unlink_when_garbage_collected: true)
|
1395
1459
|
yield path if block_given?
|
1396
1460
|
path
|
1397
1461
|
end
|
@@ -1536,20 +1600,29 @@ class Path::URI < Path
|
|
1536
1600
|
#
|
1537
1601
|
# When this is: http://host.com:port/path/filename.ext?param1=value1¶m2=value2&...
|
1538
1602
|
#
|
1539
|
-
def to_s
|
1603
|
+
def to_s
|
1604
|
+
uri.to_s
|
1605
|
+
end
|
1540
1606
|
|
1607
|
+
def inspect
|
1608
|
+
"#<Path::URI:#{to_s}>"
|
1609
|
+
end
|
1541
1610
|
|
1542
|
-
|
1543
|
-
|
1544
|
-
|
1545
|
-
def scheme; uri.scheme; end
|
1611
|
+
def scheme
|
1612
|
+
uri.scheme
|
1613
|
+
end
|
1546
1614
|
alias_method :protocol, :scheme
|
1547
1615
|
|
1548
1616
|
%w[http https ftp magnet].each do |s|
|
1549
|
-
define_method("#{s}?") { scheme
|
1617
|
+
define_method("#{s}?") { scheme.downcase == s }
|
1550
1618
|
end
|
1551
1619
|
|
1552
|
-
|
1620
|
+
#
|
1621
|
+
# `http?` checks for 'http' OR 'https' schemes
|
1622
|
+
#
|
1623
|
+
def http?
|
1624
|
+
super or https?
|
1625
|
+
end
|
1553
1626
|
|
1554
1627
|
#
|
1555
1628
|
# ...and this is: 'host.com'
|
@@ -1582,22 +1655,27 @@ class Path::URI < Path
|
|
1582
1655
|
def open(mode="r", &block)
|
1583
1656
|
require 'open-uri'
|
1584
1657
|
if block_given?
|
1585
|
-
open(to_s, mode, &block)
|
1658
|
+
Kernel.open(to_s, mode, &block)
|
1586
1659
|
else
|
1587
|
-
open(to_s, mode)
|
1660
|
+
Kernel.open(to_s, mode)
|
1588
1661
|
end
|
1589
1662
|
end
|
1590
1663
|
|
1591
|
-
|
1664
|
+
alias_method :io, :open
|
1665
|
+
|
1592
1666
|
def read(*args)
|
1593
|
-
|
1594
|
-
|
1595
|
-
|
1596
|
-
|
1597
|
-
|
1598
|
-
|
1599
|
-
|
1600
|
-
|
1667
|
+
open { |io| io.read(*args) }
|
1668
|
+
end
|
1669
|
+
|
1670
|
+
# def read(*args)
|
1671
|
+
# require 'open-uri'
|
1672
|
+
# case scheme
|
1673
|
+
# when /https?/i
|
1674
|
+
# io.read(*args)
|
1675
|
+
# else
|
1676
|
+
# raise "No connector for #{scheme} yet. Please fix!"
|
1677
|
+
# end
|
1678
|
+
# end
|
1601
1679
|
|
1602
1680
|
end
|
1603
1681
|
|
data/spec/btree_spec.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'rbtree'
|
2
|
+
|
3
|
+
describe RBTree do
|
4
|
+
|
5
|
+
before :each do
|
6
|
+
@t = RBTree.new
|
7
|
+
@alphabet = [*'a'..'z']
|
8
|
+
@alphabet.shuffle.each { |c| @t[c] = c.ord }
|
9
|
+
end
|
10
|
+
|
11
|
+
it "sizes" do
|
12
|
+
@t.size.should == 26
|
13
|
+
end
|
14
|
+
|
15
|
+
it "eaches" do
|
16
|
+
visited_keys = []
|
17
|
+
|
18
|
+
@t.each do |k,v|
|
19
|
+
k.ord.should == v
|
20
|
+
visited_keys << k
|
21
|
+
end
|
22
|
+
|
23
|
+
visited_keys.should == @alphabet
|
24
|
+
end
|
25
|
+
end
|
data/spec/core_ext_spec.rb
CHANGED
@@ -423,13 +423,15 @@ describe Integer do
|
|
423
423
|
i[0..-1].should == [1,1,0,1,1,1]
|
424
424
|
end
|
425
425
|
|
426
|
-
it "converts to/from
|
427
|
-
Integer::BASE62_BASE.should == 62
|
428
|
-
|
426
|
+
it "converts to/from bases" do
|
429
427
|
[1,20,500,501,34191923].each do |n|
|
430
428
|
n.to_base62.from_base62.should == n
|
431
429
|
end
|
432
430
|
|
431
|
+
"777".from_base(16).should == 1911
|
432
|
+
500.to_base(10).should == "500"
|
433
|
+
500.to_base(10).from_base(10).should == 500
|
434
|
+
|
433
435
|
sum = "asdf".md5
|
434
436
|
sum.to_base62.from_base62.to_s(16).should == sum
|
435
437
|
end
|
@@ -584,6 +586,18 @@ describe Array do
|
|
584
586
|
a.cols.map(&:size).should == [3,3,2,2,2]
|
585
587
|
end
|
586
588
|
|
589
|
+
it "matrixes" do
|
590
|
+
m = Array.matrix(2,2,0)
|
591
|
+
m.should == [[0,0],[0,0]]
|
592
|
+
|
593
|
+
m = Array.rect(2,2)
|
594
|
+
m.should == [[nil,nil],[nil,nil]]
|
595
|
+
|
596
|
+
m = Array.rect(2,2) {|x,y| rand }
|
597
|
+
pp m
|
598
|
+
m.should_not == [[0,0],[0,0]]
|
599
|
+
end
|
600
|
+
|
587
601
|
end
|
588
602
|
|
589
603
|
|
@@ -1011,6 +1025,35 @@ describe Matrix do
|
|
1011
1025
|
m.to_a.should == [[0,0,5]]
|
1012
1026
|
end
|
1013
1027
|
|
1028
|
+
it "'each_row's" do
|
1029
|
+
m = Matrix.zeros(2,2)
|
1030
|
+
m.each_row do |row|
|
1031
|
+
row.size.should == 2
|
1032
|
+
row.to_a.should == [0,0]
|
1033
|
+
end
|
1034
|
+
|
1035
|
+
Enumerator.should === m.each_row
|
1036
|
+
end
|
1037
|
+
|
1038
|
+
it "'ones'es" do
|
1039
|
+
Matrix.ones(2).to_a == [1,1]
|
1040
|
+
Matrix.ones(2,2).to_a.should == [[1,1],[1,1]]
|
1041
|
+
proc { Matrix.ones(1,1,1) }.should raise_error(ArgumentError)
|
1042
|
+
end
|
1043
|
+
|
1044
|
+
it "blits" do
|
1045
|
+
m = Matrix.zeros(5,5)
|
1046
|
+
s = Matrix.ones(2,2)
|
1047
|
+
m.blit!(s, 2, 1)
|
1048
|
+
m.to_a.should == [
|
1049
|
+
[0,0,0,0,0],
|
1050
|
+
[0,0,0,0,0],
|
1051
|
+
[0,1,1,0,0],
|
1052
|
+
[0,1,1,0,0],
|
1053
|
+
[0,0,0,0,0],
|
1054
|
+
]
|
1055
|
+
end
|
1056
|
+
|
1014
1057
|
end
|
1015
1058
|
|
1016
1059
|
|
data/spec/path_spec.rb
CHANGED
@@ -135,8 +135,9 @@ describe Path do
|
|
135
135
|
it "reads/writes various formats (json, yaml, etc.)" do
|
136
136
|
data = { "hello" => "there", "amazing" => [1,2,3,4] }
|
137
137
|
|
138
|
-
yaml = Path.
|
138
|
+
yaml = Path["/tmp/test.yaml"]
|
139
139
|
yaml.write_yaml(data)
|
140
|
+
yaml.exists?.should == true
|
140
141
|
yaml.read_yaml.should == data
|
141
142
|
|
142
143
|
json = Path.tmpfile
|
@@ -147,6 +148,9 @@ describe Path do
|
|
147
148
|
html = Path.tmpfile
|
148
149
|
html.write data
|
149
150
|
html.read_html.at("h1").to_s.should == data
|
151
|
+
|
152
|
+
ensure
|
153
|
+
yaml.rm
|
150
154
|
end
|
151
155
|
|
152
156
|
it "parses files" do
|
@@ -367,6 +371,34 @@ describe Path do
|
|
367
371
|
tmp.size.should == before
|
368
372
|
end
|
369
373
|
|
374
|
+
it "zopens" do
|
375
|
+
tmpjson = Path["/tmp/test.json"]
|
376
|
+
|
377
|
+
hash = {
|
378
|
+
"json" => true,
|
379
|
+
"jsonity" => 500
|
380
|
+
}
|
381
|
+
|
382
|
+
tmpjson.write(JSON.dump(hash))
|
383
|
+
tmpjson.read.from_json["json"].should == true
|
384
|
+
|
385
|
+
parsed = tmpjson.parse
|
386
|
+
parsed.should == hash
|
387
|
+
|
388
|
+
system("gzip -c < #{tmpjson} > #{tmpjson}.gz")
|
389
|
+
|
390
|
+
tmpgzip = Path["#{tmpjson}.gz"]
|
391
|
+
tmpgzip.exists?.should == true
|
392
|
+
tmpgzip.size.should be > 0
|
393
|
+
|
394
|
+
parsed = tmpgzip.parse
|
395
|
+
parsed.should_not be_nil
|
396
|
+
parsed.should == hash
|
397
|
+
|
398
|
+
tmpjson&.rm
|
399
|
+
tmpgzip&.rm
|
400
|
+
end
|
401
|
+
|
370
402
|
it "exts" do
|
371
403
|
path = Path["file.tar.gz"]
|
372
404
|
path.ext.should == "gz"
|
data/spec/typed_struct_spec.rb
CHANGED
@@ -33,18 +33,18 @@ describe TypedStruct do
|
|
33
33
|
it "drops unknowns" do
|
34
34
|
|
35
35
|
ts = TypedStruct["a:int"]
|
36
|
-
lambda { ts.new a: 1, b: 2 }.should raise_error
|
36
|
+
lambda { ts.new a: 1, b: 2 }.should raise_error(NoMethodError)
|
37
37
|
|
38
38
|
ts = TypedStruct["a:int -"]
|
39
39
|
lambda {
|
40
40
|
t = ts.new a: 1, b: 2
|
41
41
|
t.a.should == 1
|
42
|
-
lambda { t.b }.should raise_error
|
42
|
+
lambda { t.b }.should raise_error(NoMethodError)
|
43
43
|
}.should_not raise_error
|
44
44
|
end
|
45
45
|
|
46
46
|
it "can't use wildcard and drop unknown at once" do
|
47
|
-
lambda { TypedStruct["a:int - *"].new }.should raise_error
|
47
|
+
lambda { TypedStruct["a:int - *"].new }.should raise_error(RuntimeError)
|
48
48
|
end
|
49
49
|
|
50
50
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
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.118
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- epitron
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-12-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -93,10 +93,10 @@ files:
|
|
93
93
|
- lib/epitools/trie.rb
|
94
94
|
- lib/epitools/typed_struct.rb
|
95
95
|
- lib/epitools/wm.rb
|
96
|
-
- lib/epitools/zopen.rb
|
97
96
|
- spec/.rspec
|
98
97
|
- spec/autoreq_spec.rb
|
99
98
|
- spec/browser_spec.rb
|
99
|
+
- spec/btree_spec.rb
|
100
100
|
- spec/clitools_spec.rb
|
101
101
|
- spec/colored_spec.rb
|
102
102
|
- spec/core_ext_spec.rb
|
data/lib/epitools/zopen.rb
DELETED
@@ -1,50 +0,0 @@
|
|
1
|
-
require 'zlib'
|
2
|
-
|
3
|
-
COMPRESSORS = {
|
4
|
-
".gz" => "gzip",
|
5
|
-
".xz" => "xz",
|
6
|
-
".bz2" => "bzip2"
|
7
|
-
}
|
8
|
-
|
9
|
-
#
|
10
|
-
# A mutation of "open" that lets you read/write gzip files, as well as
|
11
|
-
# regular files.
|
12
|
-
#
|
13
|
-
# (NOTE: gzip detection is based on the filename, not the contents.)
|
14
|
-
#
|
15
|
-
# It accepts a block just like open()!
|
16
|
-
#
|
17
|
-
# Example:
|
18
|
-
# zopen("test.txt") #=> #<File:test.txt>
|
19
|
-
# zopen("test.txt.gz") #=> #<Zlib::GzipReader:0xb6c79424>
|
20
|
-
# zopen("otherfile.gz", "w") #=> #<Zlib::GzipWriter:0x7fe30448>>
|
21
|
-
# zopen("test.txt.gz") { |f| f.read } # read the contents of the .gz file, then close the file handle automatically.
|
22
|
-
#
|
23
|
-
def zopen(path, mode="rb")
|
24
|
-
ext = File.extname(path).downcase
|
25
|
-
|
26
|
-
if ext == ".gz"
|
27
|
-
io = open(path, mode)
|
28
|
-
case mode
|
29
|
-
when "r", "rb"
|
30
|
-
io = Zlib::GzipReader.new(io)
|
31
|
-
when "w", "wb"
|
32
|
-
io = Zlib::GzipWriter.new(io)
|
33
|
-
else
|
34
|
-
raise "Unknown mode: #{mode.inspect}. zopen only supports 'r' and 'w'."
|
35
|
-
end
|
36
|
-
elsif bin = COMPRESSORS[ext]
|
37
|
-
io = IO.popen([bin, "-d" ,"-c", path])
|
38
|
-
else
|
39
|
-
io = open(path)
|
40
|
-
end
|
41
|
-
|
42
|
-
if block_given?
|
43
|
-
result = yield(io)
|
44
|
-
io.close
|
45
|
-
result
|
46
|
-
else
|
47
|
-
io
|
48
|
-
end
|
49
|
-
|
50
|
-
end
|