alib 0.4.0 → 0.5.0

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.
@@ -5,7 +5,8 @@
5
5
  #
6
6
  module ALib::Util
7
7
  #--{{{
8
- class << self
8
+
9
+ module Exporter
9
10
  #--{{{
10
11
  def export(*syms)
11
12
  #--{{{
@@ -16,12 +17,23 @@ module ALib::Util
16
17
  end
17
18
  #--}}}
18
19
  end
20
+ def self.included other
21
+ other.extend self
22
+ end
23
+ #--}}}
24
+ end
25
+
26
+ class << self
27
+ #--{{{
28
+ include Exporter
29
+
19
30
  def append_features c
20
31
  #--{{{
21
32
  super
22
33
  c.extend self
23
34
  #--}}}
24
35
  end
36
+ alias_method "included", "append_features"
25
37
  #--}}}
26
38
  end
27
39
  #
@@ -482,9 +494,17 @@ module ALib::Util
482
494
  def tmpnam(*argv)
483
495
  #--{{{
484
496
  args, opts = argv_split argv
485
- dirname = argv.shift || getopt(%w(dir base prefix), opts, '.')
497
+ dirname = (argv.shift || getopt(%w(dir base prefix), opts, '.')).to_s
486
498
  seed = getopt 'seed', opts, prognam
487
499
  reap = getopt 'reap', opts, true
500
+ dot = getopt 'dot', opts, true
501
+ nodot = getopt 'nodot', opts, false
502
+
503
+ dot = false if nodot
504
+
505
+ #p 'nodot' => nodot
506
+ #p 'dot' => dot
507
+ #p dirname
488
508
 
489
509
  dirname = File.expand_path dirname
490
510
 
@@ -493,16 +513,26 @@ module ALib::Util
493
513
 
494
514
  if reap
495
515
  begin
496
- baseglob = "%s__*__*__*__%s" % [ host, seed ]
497
- glob = File.join(dirname, baseglob)
498
- host_re = %r/^#{ host }$/
499
- candidates = Dir[glob]
500
- candidates.each do |candidate|
516
+ baseglob =
517
+ if nodot
518
+ "%s__*__*__*__%s" % [ host, seed ]
519
+ else
520
+ ".%s__*__*__*__%s" % [ host, seed ]
521
+ end
522
+ host_re =
523
+ if nodot
524
+ %r/^#{ host }$/
525
+ else
526
+ %r/^\.#{ host }$/
527
+ end
528
+ g = File.join dirname, baseglob
529
+ Dir.glob(g).each do |candidate|
501
530
  basename = File.basename candidate
502
531
  parts = basename.split %r/__/, 5
503
532
  if parts[0] =~ host_re
504
533
  pid = Integer parts[1]
505
534
  unless alive? pid
535
+ #STDERR.puts "FileUtils.rm_rf #{ candidate }"
506
536
  FileUtils.rm_rf candidate
507
537
  end
508
538
  end
@@ -513,13 +543,23 @@ module ALib::Util
513
543
  end
514
544
 
515
545
  basename =
516
- "%s__%s__%s__%s__%s" % [
517
- host,
518
- Process::pid,
519
- timestamp('nospace' => true),
520
- rand,
521
- seed,
522
- ]
546
+ if nodot
547
+ "%s__%s__%s__%s__%s" % [
548
+ host,
549
+ Process::pid,
550
+ timestamp('nospace' => true),
551
+ rand,
552
+ seed,
553
+ ]
554
+ else
555
+ ".%s__%s__%s__%s__%s" % [
556
+ host,
557
+ Process::pid,
558
+ timestamp('nospace' => true),
559
+ rand,
560
+ seed,
561
+ ]
562
+ end
523
563
 
524
564
  File.join(dirname, basename)
525
565
  #--}}}
@@ -754,7 +794,7 @@ module ALib::Util
754
794
  #
755
795
  def splitpath path
756
796
  #--{{{
757
- path = "#{ path }"
797
+ path = path.to_s
758
798
  dirname, basename = File::split path
759
799
  [ dirname, (%r/^([^\.]*)(.*)$/).match(basename)[1,2] ].flatten
760
800
  #--}}}
@@ -765,6 +805,7 @@ module ALib::Util
765
805
  #
766
806
  def unzipped path, z_pat = %r/\.(?:z|gz)$/io
767
807
  #--{{{
808
+ path = path.to_s
768
809
  zipped = zipped?(path, z_pat)
769
810
  unless zipped
770
811
  yield path
@@ -784,6 +825,7 @@ module ALib::Util
784
825
  #
785
826
  def zip path, z_ext = nil
786
827
  #--{{{
828
+ path = path.to_s
787
829
  z_ext ||= '.gz'
788
830
  z_ext.gsub! %r/^\s*\.+/, '.'
789
831
  spawn "gzip --suffix #{ z_ext } --force #{ path }"
@@ -804,6 +846,7 @@ module ALib::Util
804
846
  #
805
847
  def zipped_name path, z_ext = nil
806
848
  #--{{{
849
+ path = path.to_s
807
850
  z_ext ||= '.gz'
808
851
  z_ext.gsub! %r/^\s*\.+/, '.'
809
852
  "#{ path }#{ z_ext }"
@@ -815,6 +858,7 @@ module ALib::Util
815
858
  #
816
859
  def unzip path, z_pat = nil
817
860
  #--{{{
861
+ path = path.to_s
818
862
  z_pat ||= %r/\.(?:z|gz)$/io
819
863
  spawn "gzip --force --decompress #{ path }" if zipped?(path, z_pat)
820
864
  #spawn "gzip --force --decompress #{ path }"
@@ -834,6 +878,7 @@ module ALib::Util
834
878
  #
835
879
  def unzipped_name path, z_pat = nil
836
880
  #--{{{
881
+ path = path.to_s
837
882
  z_pat ||= %r/\.(?:z|gz)$/io
838
883
  path.gsub z_pat, ''
839
884
  #--}}}
@@ -844,6 +889,7 @@ module ALib::Util
844
889
  #
845
890
  def zipped? path, z_pat = %r/\.(?:z|gz)$/io
846
891
  #--{{{
892
+ path = path.to_s
847
893
  path =~ z_pat
848
894
  #--}}}
849
895
  end
@@ -938,12 +984,13 @@ module ALib::Util
938
984
  loop do
939
985
  changed = false
940
986
  vars.each do |var, value|
987
+ var = var.to_s
941
988
  var.gsub! %r/[^a-zA-Z0-9_]/, ''
942
989
  [
943
990
  %r/\$#{ var }\b/,
944
991
  %r/\@#{ var }\b/,
945
- %r/\${\s*#{ var }\s*}/,
946
- %r/\@{\s*#{ var }\s*}/
992
+ %r/\$\{\s*#{ var }\s*\}/,
993
+ %r/\@\{\s*#{ var }\s*\}/
947
994
  ].each do |pat|
948
995
  changed = string.gsub! pat, "#{ value }"
949
996
  end
@@ -1008,30 +1055,46 @@ module ALib::Util
1008
1055
  def parse_timespec spec
1009
1056
  #--{{{
1010
1057
  ret = nil
1011
- pat = %r/(\d+(?:\.\d+)?)\s*([sSmMhHdD][^\d]*)?/
1012
- begin
1013
- "#{ spec }".scan(pat) do |m|
1014
- n = Float m[0]
1015
- unit = m[1]
1016
- if unit
1017
- factor =
1018
- case unit
1019
- when %r/^m/i
1020
- 60
1021
- when %r/^h/i
1022
- 60 * 60
1023
- when %r/^d/i
1024
- 60 * 60 * 24
1025
- else
1026
- 1
1027
- end
1028
- n *= factor
1058
+ if((m = %r/^ (\d+(?:\.\d+)?) : (\d+(?:\.\d+)?) : (\d+(?:\.\d+)?) $/iox.match(spec.to_s)))
1059
+ m, h, m, s, ignored = m.to_a
1060
+ h, m, s = Float(h), Float(m), Float(s)
1061
+ #ret = h.hours + m.minutes + s.seconds
1062
+ ret = (h * 60 * 60) + (m * 60) + (s)
1063
+ else
1064
+ pat = %r/(\d+(?:\.\d+)?)\s*([sSmMhHdDwWyY][^\d]*)?/
1065
+ begin
1066
+ "#{ spec }".scan(pat) do |m|
1067
+ n = Float m[0]
1068
+ unit = m[1]
1069
+ if unit
1070
+ factor =
1071
+ case unit
1072
+ when %r/^m/i
1073
+ case unit
1074
+ when %r/^mo/i
1075
+ 7 * (60 * 60 * 24)
1076
+ else
1077
+ 60
1078
+ end
1079
+ when %r/^h/i
1080
+ 60 * 60
1081
+ when %r/^d/i
1082
+ 60 * 60 * 24
1083
+ when %r/^w/i
1084
+ 7 * (60 * 60 * 24)
1085
+ when %r/^y/i
1086
+ 365 * 7 * (60 * 60 * 24)
1087
+ else
1088
+ 1
1089
+ end
1090
+ n *= factor
1091
+ end
1092
+ ret ||= 0.0
1093
+ ret += n
1029
1094
  end
1030
- ret ||= 0.0
1031
- ret += n
1095
+ rescue
1096
+ raise "bad time spec <#{ spec }>"
1032
1097
  end
1033
- rescue
1034
- raise "bad time spec <#{ spec }>"
1035
1098
  end
1036
1099
  ret
1037
1100
  #--}}}
@@ -1056,36 +1119,104 @@ module ALib::Util
1056
1119
  end
1057
1120
  export 'camel_case'
1058
1121
 
1059
- def atomic_copy src, dst
1122
+ def __atomic_op op, src, dst, opts = {}
1060
1123
  #--{{{
1061
1124
  f, fu = File, FileUtils
1062
1125
  src_dirname, src_basename = f.split src
1126
+ src_stat = File.stat src
1063
1127
 
1064
1128
  dst = f.join dst, src_basename if test ?d, dst
1065
-
1066
1129
  dst_dirname, dst_basename = f.split dst
1067
1130
 
1068
- tmp = f.join dst_dirname, ".#{ src_basename }.#{ hostname }.tmp"
1131
+ pid = Process.pid
1132
+ tmp = f.join dst_dirname, ".#{ dst_basename }.#{ hostname }.#{ pid }.#{ rand(4242) }.alib.tmp"
1133
+
1134
+ timeout = getopt 'timeout', opts, 42
1135
+ utime = getopt 'utime', opts
1136
+ mtime = getopt 'mtime', opts
1137
+ atime = getopt 'atime', opts
1138
+
1139
+ safe_cp = lambda do |a, b|
1140
+ 4.times do
1141
+ begin
1142
+ break(fu.cp_r(a, b, :preserve => true))
1143
+ rescue => e
1144
+ STDERR.puts(errmsg(e))
1145
+ uncache a rescue nil
1146
+ uncache b rescue nil
1147
+ sleep timeout
1148
+ end
1149
+ end
1150
+ end
1151
+
1152
+ safe_mv = lambda do |a, b|
1153
+ 4.times do
1154
+ begin
1155
+ break(fu.mv(a, b))
1156
+ rescue => e
1157
+ STDERR.puts(errmsg(e))
1158
+ uncache a rescue nil
1159
+ uncache b rescue nil
1160
+ sleep timeout
1161
+ end
1162
+ end
1163
+ end
1164
+
1165
+ safe_rm = lambda do |a|
1166
+ 4.times do
1167
+ begin
1168
+ break(fu.rm_rf(a))
1169
+ rescue => e
1170
+ STDERR.puts(errmsg(e))
1171
+ uncache a rescue nil
1172
+ sleep timeout
1173
+ end
1174
+ end
1175
+ end
1069
1176
 
1070
1177
  begin
1071
- fu.cp_r src, tmp, :preserve => true
1072
- begin
1073
- fu.mv tmp, dst
1074
- rescue
1075
- uncache tmp rescue nil
1076
- uncache dst rescue nil
1077
- sleep 42
1078
- fu.mv tmp, dst
1178
+ case op
1179
+ when 'cp'
1180
+ safe_cp[src, tmp]
1181
+ safe_mv[tmp, dst]
1182
+ when 'mv'
1183
+ safe_cp[src, tmp]
1184
+ safe_mv[tmp, dst]
1185
+ safe_rm[src]
1186
+ else
1187
+ raise ArgumentError, op.to_s
1079
1188
  end
1080
1189
  ensure
1081
- fu.rm_f tmp
1190
+ safe_rm[tmp]
1191
+ end
1192
+
1193
+ if utime or mtime or atime
1194
+ a = (atime or utime or src_stat.atime)
1195
+ m = (mtime or utime or src_stat.mtime)
1196
+ a = src_stat.atime unless Time === a
1197
+ m = src_stat.mtime unless Time === m
1198
+ ALib::Util::find(dst){|e| File.utime a, m, e}
1082
1199
  end
1083
1200
 
1084
1201
  dst
1085
1202
  #--}}}
1086
1203
  end
1204
+ export '__atomic_op'
1205
+
1206
+ def atomic_copy src, dst, opts = {}
1207
+ __atomic_op 'cp', src, dst, opts
1208
+ end
1209
+ alias_method "atomic_cp", "atomic_copy"
1210
+ export 'atomic_cp'
1087
1211
  export 'atomic_copy'
1088
1212
 
1213
+ def atomic_move src, dst, opts = {}
1214
+ __atomic_op 'mv', src, dst, opts
1215
+ end
1216
+ alias_method "atomic_mv", "atomic_move"
1217
+ export 'atomic_mv'
1218
+ export 'atomic_move'
1219
+
1089
1220
  def child_object
1090
1221
  #--{{{
1091
1222
  r, w = IO.pipe
@@ -1163,5 +1294,275 @@ module ALib::Util
1163
1294
  export '#{ m }'
1164
1295
  code
1165
1296
  end
1297
+
1298
+ #
1299
+ # setup lspawn method/state
1300
+ #
1301
+ #--{{{
1302
+ __cmdno = 0 # static var
1303
+
1304
+ __c = Class.new{
1305
+ def initialize io, prefix
1306
+ @io, @prefix = io, prefix
1307
+ end
1308
+ def << buf
1309
+ @io << "#{ @prefix }__ "
1310
+ @io << buf
1311
+ end
1312
+ }
1313
+
1314
+ __logger = lambda{|io, prefix| __c.new io, prefix}
1315
+
1316
+ __lspawn = lambda do |cmd, *a|
1317
+ begin
1318
+ Thread.critical = true
1319
+ log = a.shift
1320
+ log = (logger rescue (@logger || Logger.new(STDERR)))
1321
+
1322
+ stdout = __logger[STDOUT, :o]
1323
+ stderr = __logger[STDERR, :e]
1324
+
1325
+ log.info{ "cmd[#{ __cmdno }] <#{ cmd }>" }
1326
+
1327
+ status = Alib::Util::spawn(cmd, 'quiet'=>true, 'raise'=>false, 'stdout'=>stdout, 'stderr'=>stderr)
1328
+
1329
+ if status and status == 0
1330
+ log.info{ "status[#{ __cmdno }] <#{ status }>" }
1331
+ else
1332
+ log.warn{ "status[#{ __cmdno }] <#{ status || 127 }>" }
1333
+ end
1334
+
1335
+ if status and status.signaled?
1336
+ if status.termsig
1337
+ log.warn{ "termsig[#{ __cmdno }] <#{ status.termsig }>" }
1338
+ end
1339
+ if status.stopsig
1340
+ log.warn{ "stopsig[#{ __cmdno }] <#{ status.stopsig }>" }
1341
+ end
1342
+ end
1343
+
1344
+ status ? status.exitstatus : 127
1345
+ ensure
1346
+ __cmdno += 1
1347
+ Thread.critical = false
1348
+ end
1349
+ end
1350
+
1351
+ define_method 'lspawn', &__lspawn
1352
+
1353
+ LSPAWN_CMDNO = [0]
1354
+ LSPAWN_LOG = Hash.new{|h,k| h[k] = alib.logging.default_logger}
1355
+ LSPAWN_IO = Class.new do
1356
+ def initialize log, prefix, io
1357
+ @log, @prefix, @io = log, prefix, io
1358
+ end
1359
+ def << buf
1360
+ @log << " #{ @prefix }__ #{ buf.chomp }\n"
1361
+ #@log << "#{ buf.chomp }\n"
1362
+ @io << buf if @io
1363
+ end
1364
+ end
1365
+ #--}}}
1366
+
1367
+ def lspawn cmd, opts = {}
1368
+ #--{{{
1369
+ begin
1370
+ Thread.critical = true
1371
+
1372
+ stdin = ALib::Util::getopt ['stdin', 'i', 'in', '0', 0], opts
1373
+ stdout = ALib::Util::getopt ['stdout', 'o', 'out', '1', 1], opts
1374
+ stderr = ALib::Util::getopt ['stderr', 'e', 'err', '2', 2], opts
1375
+ expect = ALib::Util::getopt ['expect', 'status', 'success'], opts, 0
1376
+ log = Alib::Util::getopt ['logger', 'log'], opts
1377
+
1378
+ log ||= (self.logger rescue (defined?(@logger) ? @logger : LSPAWN_LOG[0]))
1379
+
1380
+ stdout = LSPAWN_IO.new log, :o, stdout
1381
+ stderr = LSPAWN_IO.new log, :e, stderr
1382
+
1383
+ cmdno = LSPAWN_CMDNO[0]
1384
+
1385
+ log.info{ "cmd[#{ cmdno }] <#{ cmd }>" }
1386
+
1387
+ status = Alib::Util::spawn(cmd, 'quiet'=>true, 'raise'=>false, 'stdin'=>stdin, 'stdout'=>stdout, 'stderr'=>stderr)
1388
+
1389
+ exitstatus = ( (status ? (status.exitstatus || 127) : 127) rescue 127 )
1390
+
1391
+ log.send(exitstatus == 0 ? 'info' : 'warn'){ "status[#{ cmdno }] <#{ exitstatus }>" }
1392
+
1393
+ if expect
1394
+ codes = [*expect].flatten.compact.map{|code| Integer code}
1395
+ success = codes.detect{|code| exitstatus == code}
1396
+ unless success
1397
+ log.fatal{ "cmd[#{ cmdno }] failure" }
1398
+ exit exitstatus
1399
+ end
1400
+ end
1401
+
1402
+ if status and status.signaled?
1403
+ if status.termsig
1404
+ log.warn{ "termsig[#{ cmdno }] <#{ status.termsig }>" }
1405
+ end
1406
+ if status.stopsig
1407
+ log.warn{ "stopsig[#{ cmdno }] <#{ status.stopsig }>" }
1408
+ end
1409
+ end
1410
+
1411
+ exitstatus
1412
+ ensure
1413
+ LSPAWN_CMDNO[0] = LSPAWN_CMDNO[0] + 1
1414
+ Thread.critical = false
1415
+ end
1416
+ #--}}}
1417
+ end
1418
+ export 'lspawn'
1419
+
1420
+ def sweep hash
1421
+ #--{{{
1422
+ srcdir, dstdir, ignored = hash.to_a.first
1423
+ Dir.glob(File.join(srcdir, '*')) do |src|
1424
+ dirname, basename = File.split src
1425
+ dst = File.join dstdir, basename
1426
+ FileUtils.mv src, dst
1427
+ end
1428
+ #--}}}
1429
+ end
1430
+ export 'sweep'
1431
+
1432
+
1433
+ def threadify list, n = 4, &b
1434
+ #--{{{
1435
+ n = Integer n
1436
+ done = Object.new.freeze
1437
+ jobs = Queue.new
1438
+
1439
+ list.each_with_index{|elem, i| jobs.push [elem, i]}
1440
+ n.times{ jobs.push done} # mark the end
1441
+
1442
+ consumers = Array.new n
1443
+
1444
+ n.times do |i|
1445
+ consumers[i] = Thread.new do
1446
+ this = Thread.current
1447
+ this.abort_on_exception = true
1448
+
1449
+ loop{
1450
+ job = jobs.pop
1451
+ this.exit if job == done
1452
+ jobs << (job << bcall(b, job))
1453
+ }
1454
+ end
1455
+ end
1456
+
1457
+ consumers.map{|t| t.join}
1458
+ jobs.push done
1459
+
1460
+ #sleep 5
1461
+
1462
+ ret = Array.new list.size
1463
+ while((job = jobs.pop) != done)
1464
+ elem, i, value = job
1465
+ ret[i] = value
1466
+ end
1467
+ ret
1468
+ #--}}}
1469
+ end
1470
+ export 'threadify'
1471
+
1472
+ def block_argv b, argv
1473
+ #--{{{
1474
+ arity = b.arity
1475
+ if arity >= 0
1476
+ argv[0,arity]
1477
+ else
1478
+ head = []
1479
+ tail = []
1480
+ n = arity.abs - 1
1481
+ head = argv[0...n]
1482
+ tail = argv[n..-1]
1483
+ [*(head + tail)]
1484
+ end
1485
+ #--}}}
1486
+ end
1487
+ export 'block_argv'
1488
+
1489
+ def bcall b, argv
1490
+ #--{{{
1491
+ a = block_argv b, argv
1492
+ b[*a]
1493
+ #--}}}
1494
+ end
1495
+ export 'bcall'
1496
+
1497
+ #
1498
+ # casting methods
1499
+ #
1500
+ module Casting
1501
+ #--{{{
1502
+ include Exporter
1503
+
1504
+ def int_list *list
1505
+ #--{{{
1506
+ list.flatten.compact.map{|i| Util.atoi i}
1507
+ #--}}}
1508
+ end
1509
+ def float_list *list
1510
+ #--{{{
1511
+ list.flatten.compact.map{|f| Float f}
1512
+ #--}}}
1513
+ end
1514
+ def string_list *list
1515
+ #--{{{
1516
+ list.flatten.compact.map{|s| String s}
1517
+ #--}}}
1518
+ end
1519
+ def string_cast arg
1520
+ arg.to_s
1521
+ end
1522
+ def int_cast arg
1523
+ Integer arg.to_s
1524
+ end
1525
+ def float_cast arg
1526
+ Float arg
1527
+ end
1528
+ def bool_cast arg
1529
+ arg ? true : false
1530
+ end
1531
+ def bool_cast arg
1532
+ case arg
1533
+ when Numeric
1534
+ not arg.zero?
1535
+ when String
1536
+ case arg
1537
+ when %r/^(1|t|true)$/i
1538
+ true
1539
+ when %r/^(0|f|false)$/i
1540
+ false
1541
+ else
1542
+ raise ArgumentError, arg.inspect
1543
+ end
1544
+ else
1545
+ arg ? true : false
1546
+ end
1547
+ end
1548
+ def re_cast arg
1549
+ Regexp === arg ? arg : %r/#{ arg }/
1550
+ end
1551
+ def pathname_cast arg, opts = {}
1552
+ expand = opts['expand'] || opts[:expand]
1553
+ pn = (Pathname === arg ? arg : Pathname.new(arg.to_s))
1554
+ expand ? pn.expand_path : pn
1555
+ end
1556
+
1557
+ instance_methods.each{|m| export m}
1558
+ #--}}}
1559
+ end
1560
+
1561
+ # dump methods into Util
1562
+ include Casting
1563
+ Casting.instance_methods.each{|m| export m}
1564
+
1565
+ def self.casting() Casting end
1566
+
1166
1567
  #--}}}
1167
1568
  end # module Util