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 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