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