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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f59b5b62bfba503dc018fd54d0862d83c39a28c42c111cb65f7c715ec148dac6
4
- data.tar.gz: 9a39b0bbd31c21f7434e8230521f3ad69a7013915dc52eb8a917840a56e208d5
3
+ metadata.gz: c905d2edd9ff4e5738a2a14992e64168ab876049a481a57f03bfec551aa65bbc
4
+ data.tar.gz: 81a3dabab5584a115d2a2826e34d681c7a910201b1a13ee58b5b3a9f2129b728
5
5
  SHA512:
6
- metadata.gz: 9032e99a44dcc83f6277722d79384cdb5fefbebd4324d34c049b51e62dbc09fca49a16450863c1b607c6dc6075445dc744fab9bf717fe43599223b358e6dd191
7
- data.tar.gz: 8abee532a4ecc30fe0ea6c9c365fe8c137e7190425b398f54cdc3dadeee4ec00431b3b55d25b91b4cc3f36918626c0d7404a370388f0622792d4f859919cbf09
6
+ metadata.gz: 6daaa56aeba42a400b2a6e0387fc329e7b95b88a16bae5b704efc2ec956f59d0e0c1580816ea4378441bee7755fc3d1bacb2f7e48f3ac5553b45dcc97330c112
7
+ data.tar.gz: 05b6d0bd082e135f2274d3e896c2baab6da7d4ec02bebdb05c3f4a8a05b5cfe342f020985c29efabd4b421968c803afb02ba5349ad23d28ee6ea7acce22a5c0e
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.116
1
+ 0.5.118
@@ -16,7 +16,6 @@ end
16
16
  #
17
17
  %w[
18
18
  core_ext
19
- zopen
20
19
  colored
21
20
  clitools
22
21
  numwords
@@ -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
- # Sum the elements
212
- #
213
- def sum(&block)
214
- if block_given?
215
- map(&block).reduce(:+)
216
- else
217
- reduce(:+)
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 base62 encoding
269
+ # Cached constants for encoding numbers into bases up to 64
270
270
  #
271
- BASE62_DIGITS = ['0'..'9', 'A'..'Z', 'a'..'z'].map(&:to_a).flatten
272
- BASE62_BASE = BASE62_DIGITS.size
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 to a string representation (in "base62" encoding).
275
+ # Convert a number into a string representation (encoded in a base <= 64).
276
276
  #
277
- # Base62 encoding represents the number using the characters: 0..9, A..Z, a..z
277
+ # The number is represented usiing the characters: 0..9, A..Z, a..z, _, -
278
278
  #
279
- # It's the same scheme that url shorteners and YouTube uses for their
280
- # ID strings. (eg: http://www.youtube.com/watch?v=dQw4w9WgXcQ)
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 to_base62
283
- result = []
284
- remainder = self
285
- max_power = ( Math.log(self) / Math.log(BASE62_BASE) ).floor
286
-
287
- max_power.downto(0) do |power|
288
- divisor = BASE62_BASE**power
289
- #p [:div, divisor, :rem, remainder]
290
- digit, remainder = remainder.divmod(divisor)
291
- result << digit
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
- result << remainder if remainder > 0
309
+ digits.reverse.map { |d| alphabet[d] }.join
310
+ end
295
311
 
296
- result.map{|digit| BASE62_DIGITS[digit]}.join ''
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 base62 decoding.
314
+ # Cached constants for base conversion.
315
315
  #
316
- BASE62_DIGITS = Hash[ Integer::BASE62_DIGITS.zip((0...Integer::BASE62_DIGITS.size).to_a) ]
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 (encoded in base16 "hex" -- for example, an MD5 or SHA1 hash)
321
- # into "base62" format. (See Integer#to_base62 for more info.)
319
+ # Convert a string encoded in some base <= 64 into an integer.
320
+ # (See Integer#to_base for more info.)
322
321
  #
323
- def to_base62
324
- to_i(16).to_base62
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 base62 into an integer.
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 from_base62
332
- accumulator = 0
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
  #
@@ -241,8 +241,8 @@ module Kernel
241
241
  #
242
242
  # Print "self" with a linefeed at the end
243
243
  #
244
- def displayln
245
- puts self
244
+ def displayln(out=$stdout)
245
+ out.puts self
246
246
  end
247
247
 
248
248
  end
@@ -596,7 +596,11 @@ class Path
596
596
  when String, Numeric, true, false, nil
597
597
  self[key] = new
598
598
  else
599
- raise "Error: Can't use a #{new.class} as an xattr value. Try passing a String."
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 =~ pat
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 Tempfile.new(prefix).path, unlink_when_garbage_collected: true
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&param2=value2&...
1538
1602
  #
1539
- def to_s; uri.to_s; end
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
- # ...this is: 'http'
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[/^#{s}$/i] }
1617
+ define_method("#{s}?") { scheme.downcase == s }
1550
1618
  end
1551
1619
 
1552
- def http?; super or https?; end
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
- # Note: open is already aliased to io in parent class.
1664
+ alias_method :io, :open
1665
+
1592
1666
  def read(*args)
1593
- require 'open-uri'
1594
- case scheme
1595
- when /https?/i
1596
- io.read(*args)
1597
- else
1598
- raise "No connector for #{scheme} yet. Please fix!"
1599
- end
1600
- end
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
 
@@ -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
@@ -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 base62" do
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
 
@@ -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.tmpfile
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"
@@ -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.116
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-09-04 00:00:00.000000000 Z
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
@@ -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