Module: RMTools

Included in:
Object
Defined in:
lib/rmtools/fs/io.rb,
lib/rmtools/require.rb,
lib/rmtools/version.rb,
lib/rmtools/fs/tools.rb,
lib/rmtools/dev/timer.rb,
lib/rmtools/lang/ansi.rb,
lib/rmtools/rand/array.rb,
lib/rmtools/dev/logging.rb,
lib/rmtools/rand/string.rb,
lib/rmtools/dev/watching.rb,
lib/rmtools/time/helpers.rb,
lib/rmtools/dev/highlight.rb,
lib/rmtools/core/threadify.rb,
lib/rmtools/text/textilize.rb,
lib/rmtools/console/coloring.rb,
lib/rmtools/console/printing.rb,
lib/rmtools/console/printing.rb,
lib/rmtools/dev/trace_format.rb,
lib/rmtools/enumerable/set_ops.rb,
lib/rmtools/enumerable/traversal.rb

Defined Under Namespace

Modules: Cyrillic, KeyValueTraversal, SmarterSetOps, ValueTraversal Classes: FileWatcher, KeyValueTraversable, Painter, RMLogger, ScpHelper, TempPrinter, ValueTraversable

Constant Summary collapse

VERSION =
'2.5.0'
ENCODINGS_PATTERNS =
{}
ICONVS =
{}
ANSI2UTF =
Cyrillic::ANSI2UTF = lambda {|str|
  str.encode("WINDOWS-1251", :invalid => :replace, :undef => :replace, :replace => "").encode("UTF-8", :invalid => :replace, :undef => :replace, :replace => "")
}
UTF2ANSI =
Cyrillic::UTF2ANSI = lambda {|str|
  str.encode("UTF-8", :invalid => :replace, :undef => :replace, :replace => "").encode("WINDOWS-1251", :invalid => :replace, :undef => :replace, :replace => "")
}
Numbers =
'0123456789'
Chars =
'abcdefghijklmnopqrstuvwxyz'
Alphanum =
Chars + Chars.upcase + Numbers
ASCII_readable =
'!"#$%&\'()*+,-./[\]^_`{|}~:;<=>?@ ' + Alphanum
RuChars =
UTF2ANSI[Cyrillic::RU_LETTERS[0]]
RuAlphanum =
UTF2ANSI[Cyrillic::RU_LETTERS.join] + Numbers
IgnoreFiles =
%r{/irb(/|\.rb$)|/active_support/dependencies.rb$}

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.format_trace(a) ⇒ Object



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/rmtools/dev/trace_format.rb', line 30

def format_trace(a)
  return [] if a.empty?
  
  bt, steps, i = [], [], 0
  m = a[0].parse:caller
  # seems like that bug is fixed for now
  #m.line -= 1 if m and m.file =~ /\.haml$/
  
  while i < a.size
    m2 = a[i+1] && a[i+1].parse(:caller)
    #m2.line -= 1 if m2 and m2.file =~ /\.haml$/
    if m and m.path !~ IgnoreFiles
      step = a[i]
      if step["\n"] # already formatted
        bt << step
      else
        if m.block_level # > 1.9
          step = step.sub(/block (\(\d+ levels\) )?in/, '{'+m.block_level+'}')
        end
        if m and m.func and m2 and [m.path, m.line] == [m2.path, m2.line]
          steps << " -> `#{'{'+m.block_level+'} ' if m.block_level}#{m.func}'"
        elsif m and m.line != 0 and line = RMTools.highlighted_line(m.path, m.line)
          bt << "#{step}#{steps.join}\n#{line}"
          steps = []
        else
          bt << step
        end
      end
    end
    i += 1
    m = m2
  end
  # Magic numbers! If a size of a backtrace array > 16 (line count doesn't affect), IRB forgets to print newline after an exception trace! I don't even want to know, why the hell it's going this way!
  if bt.size > 16 and bt.last[-1] != "\n"
    bt.last << "\n"
  end
  bt
end

.format_trace_to_html(a) ⇒ Object

disclaimer: Firefox (at least 3.6+) on Windoze does not allow to use file:// protocol T_T



70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/rmtools/dev/trace_format.rb', line 70

def format_trace_to_html(a)
  a.map! do |lines|
    caller_string, snippet = lines/"\n"
    caler = caller_string.parse(:caller)
    if caler
      path = caler.path
      lines = ["<a href='#{CGI.escape 'file://'+path}'>#{path}</a>:#{caler.line} in #{caler.func}"]
      lines << RMTools::Painter.clean(snippet) if snippet
      lines * "\n"
    else
      lines
    end
  end
end

.highlighted_line(file, line) ⇒ Object



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# File 'lib/rmtools/dev/highlight.rb', line 6

def highlighted_line(file, line)
  if defined? SCRIPT_LINES__ and SCRIPT_LINES__[file]
    "   >>   #{Painter.green((SCRIPT_LINES__[file][line.to_i - 1] || "<line #{line} is not found> ").chop)}"
  else
    file = Readline::TEMPLOG if file == '(irb)' and defined? Readline::TEMPLOG
    if File.file? file
      line_read = read_lines(file, line.to_i) || "<line #{line} is not found> "
      if defined? SCRIPT_LINES__
        SCRIPT_LINES__[file] = IO.readlines(file)
        highlighted_line file, line
      else
        "   #{Painter.cyan '>>'}   #{Painter.green line_read.chop}" 
      end
    end
  end
end

.prepare_write(df, value) {|df, value| ... } ⇒ Object

Yields:

  • (df, value)


16
17
18
19
20
21
22
23
24
25
# File 'lib/rmtools/fs/io.rb', line 16

def self.prepare_write(df, value)
  return false if value.nil?
  value = value.inspect unless value.class == String
  value = value.force_encoding('UTF-8') if RUBY_VERSION > '1.9'
  df = df.tr '\\', '/'
  path = File.dirname(df)
  FileUtils.mkpath(path) if !File.directory?(path)
  yield df, value
  value.size
end

.putdateObject



13
14
15
# File 'lib/rmtools/time/helpers.rb', line 13

def putdate
  Time.now.strftime("%d.%m.%y")
end

.puttime(ms = nil) ⇒ Object



4
5
6
7
8
9
10
11
# File 'lib/rmtools/time/helpers.rb', line 4

def puttime(ms=nil)
  t = Time.now
  if ms
    t.strftime("%H:%M:%S")+sprintf(".%03d ", t.usec/1000)
  else
    t.strftime("%d.%m.%y %H:%M:%S ")
  end
end

.randarr(len, &b) ⇒ Object



6
7
8
9
# File 'lib/rmtools/rand/array.rb', line 6

def randarr(len, &b)
  a = (0...len).to_a.shuffle
  block_given? ? a.map!(&b) : a
end

.randstr(len = 8, what = :alphanum) ⇒ Object



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/rmtools/rand/string.rb', line 19

def randstr(len=8, what=:alphanum)
  s = ''
  res = case what
    when :bytes, :binary
      if Urandom
        SecureRandom.random_bytes len
      else
        len.times {s.concat rand 256}; s
      end
    when :ascii
      if Urandom
        SecureRandom.random_bytes(len*3).tr("^ -~", '')[0...len]
      else
        len.times {s.concat ASCII_readable[rand 95].ord}; s
      end
    when :char
      if Urandom
        res = SecureRandom.base64(len*2).tr("^a-z", '')[0...len]
      else
        len.times {s.concat Chars[rand 26].ord}; s
      end
    when :alphanum
      if Urandom
        res = SecureRandom.base64(len).tr("+/=", '')[0...len]
      else
        len.times {s.concat Alphanum[rand 62].ord}; s
      end
    when :num, :digit
      if Urandom
        res = SecureRandom.hex(len).tr("^0-9", '')[0...len]
      else
        len.times {s.concat Numbers[rand 10].ord}; s
      end
    when :hex
      if Urandom
        res = SecureRandom.hex len/2
      else
        len.times {s.concat Numbers[rand 10].ord}; s
      end
    when :cyr, :cyrilic
      if Urandom
        res = ANSI2UTF[SecureRandom.random_bytes(len*8).tr("^\270\340-\377", '')[0...len]]
      else
        len.times {s.concat RuChars[rand 33].ord}; ANSI2UTF[s]
      end
    when :cyr_full, :cyr_alphanum
      if Urandom
        res = ANSI2UTF[SecureRandom.random_bytes(len*3.5).tr("^0-9\250\270\340-\377\300-\337", '')[0...len]]
      else
        len.times {s.concat RuAlphanum[rand 76].ord}; ANSI2UTF[s]
      end
      
    when Symbol then raise ArgumentError, "invalid symbol :#{what}, valid symbols are
      :bytes, :binary, :char, :alphanum, :num, :digit, :hex, :cyr, :cyrilic, :cyr_full, :cyr_alphanum"
    when String then res = randstr(len*10, :bytes).tr("^#{what}", '')
    else raise ArgumentError, "invalid argument #{what}, class #{(what.class)}"
  end
  res << randstr((len-res.size)*2, what) while res.size < len
  res[0...len]
end

.read(*dests) ⇒ Object

read(‘filename’)

> ‘text from filename’

read('nonexistent_filename')

couldn’t read from “nonexistent_filename” (called from (irb):9001)

> nil

read(['file1', 'file2', 'file3'])

> ‘text from first of file1, file2, file3 that exists’

read(['file1', 'file2'], ['nonexistent_file1', 'nonexistent_file2'])

coludn’t read from neither “nonexistent_file1”, nor “nonexistent_file2” (called from (irb):9003)

> [‘text from first of file1, file2 that exists’, nil]

read('file1', 'file2')

> [‘text from file1’, ‘text from file2]

read('file1', ['file2', 'file3'])

> [‘text from file1’, ‘text from first of file2 and file3 that exists’]



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/rmtools/fs/io.rb', line 65

def read(*dests)
  texts = dests.map {|dest|
    dest = dest[0] if dest.size == 1
    if dest.is Array
      if file = dest.find {|f| File.file?(f.tr '\\', '/')}
        File.open(file.tr('\\', '/'), File::RDONLY) {|f| f.read}
      else
        warn "couldn't read from neither #{dest[0].inspect} nor #{dest[1..-1].inspects*' nor '}; files missed (called from #{caller[2]})"
      end
    else
      if File.file? dest.tr('\\', '/')
        File.open(dest.tr('\\', '/'), File::RDONLY) {|f| f.read}
      else
        warn "couldn't read from #{dest.inspect}; file missed (called from #{caller[2]})"
      end
    end
  }
  texts = texts[0] if texts.size == 1
  texts
end

.read_lines(df, *lines) ⇒ Object



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/rmtools/fs/tools.rb', line 32

def read_lines(df, *lines)
  return if !lines or lines.empty?
  str = ""
  last = lines.max
  lines_found = false
  if File.file?(df)
    File.open(df, 'r') {|f|
      f.each {|line|
          no = f.lineno
          if no.in lines
            str << line 
            lines_found = true
          end
          break if no == last
    }}
    lines_found && str
  else
    STDERR.puts "#{df} is missed!"
  end
end

.require(location, mask = nil) ⇒ Object

‘ RMTools::require __FILE__, “*” ’ requires all ruby files from dir named as file under ‘rmtools-gem/lib/rmtools’ ‘ RMTools::require “folder”, “mask” ’ requires all files come within ‘mask’ from dir ‘folder’ under ‘rmtools-gem/lib/rmtools’ ‘RMTools::require “file” ’ requires ‘file.rb’ under ‘rmtools-gem/lib/rmtools’



6
7
8
9
10
11
12
13
# File 'lib/rmtools/require.rb', line 6

def self.require(location, mask=nil)
  if !mask
    location, mask = File.dirname(__FILE__), location # /path/to/gems/rmtools
  end
  mask += '.rb' unless mask['.']
  location = File.expand_path(location).chomp('.rb')
  Dir.glob(File.join location, mask) {|file| Kernel.require file}
end

.rw(df, value = nil) ⇒ Object



27
28
29
30
31
# File 'lib/rmtools/fs/io.rb', line 27

def rw(df, value=nil)
  RMTools::prepare_write df, value do
    File.open(df, File::CREAT|File::WRONLY|File::TRUNC) {|f| f << value}
  end
end

.tail(file, bytes = 1000) ⇒ Object



6
7
8
9
10
11
12
# File 'lib/rmtools/fs/tools.rb', line 6

def tail(file, bytes=1000)
  if File.file?(file)
    IO.read(file, bytes, File.size(file)-bytes)
  else
    STDERR.puts "#{file} is missed!"
  end
end

.tail_n(file, qty = 10) ⇒ Object



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/rmtools/fs/tools.rb', line 14

def tail_n(file, qty=10)
  if !File.file?(file)
    return STDERR.puts "#{file} is missed!"
  end
  size = File.size(file)
  lines = []
  strlen = 0
  step = qty*100
  while qty > 0 and (offset = size-strlen-step) >= 0 and !(str = IO.read(file, step, offset)).empty?
    i = str.index("\n") || str.size
    strlen += step - i
    new_lines = str[i+1..-1]/"\n"
    qty -= new_lines.size
    lines = new_lines.concat(lines)
  end
  lines[-qty..-1]
end

.threadify(ary, max_threads = 4) ⇒ Object



3
4
5
6
7
8
9
10
11
12
13
14
# File 'lib/rmtools/core/threadify.rb', line 3

def threadify(ary, max_threads=4)
  forks = []
  ary.each do |e|
    if max_threads > forks.size
      forks << fork { yield e }
    end
    if max_threads == forks.size
      forks.delete Process.wait
    end
  end
  Process.waitall
end

.tick!Object



53
54
55
# File 'lib/rmtools/console/printing.rb', line 53

def tick!
  print %W{|\b /\b -\b \\\b +\b X\b}.rand
end

.tick_whileObject



57
58
59
60
61
62
# File 'lib/rmtools/console/printing.rb', line 57

def tick_while
  ticker = thread {loop {RMTools::tick!}}
  res = yield
  ticker.kill
  res
end

.timer(ts = 1, output = true) ⇒ Object



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# File 'lib/rmtools/dev/timer.rb', line 5

def timer(ts=1, output=true)
  timez = ts - 1
  quiet, mute_warn = $quiet, $log.mute_warn
  $quiet = $log.mute_warn = true
  t1 = Time.now
  begin
    timez.times {yield} if timez > 0
  rescue
    $quiet, $log.mute_warn = quiet, mute_warn
    raise $!
  end
  res = yield
  t2 = Time.now
  ts.times {}
  t3 = Time.now.to_f*1000
  t2 = t2.to_f*1000
  t1 = t1.to_f*1000
  delta = (t2 - t1 - (t3 - t2)).round.to_f
  $quiet, $log.mute_warn = quiet, mute_warn
  res = res.inspect
  puts "#{output ? "res: #{res.size > 1000 ? res[0...999]+"" : res}\n" : "size of res string: #{res.to_s.size}, "}one: #{Painter.gray '%0.4fms'%[delta/ts]}, total: #{Painter.gray "#{delta}ms"}"
end

.write(df, value = '', pos = 0) ⇒ Object



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/rmtools/fs/io.rb', line 33

def write(df, value='', pos=0)
  RMTools::prepare_write df, value do
    if pos == 0
      File.open(df, File::CREAT|File::WRONLY|File::APPEND) {|f| f << value}
    else
      if pos < 0
        if !File.file?(df)  
          raise IndexError, "file #{df} does not exist, can't write from position #{pos}" 
        elsif (size = File.size(df)) < -pos 
          raise IndexError, "file #{df} is shorter than #{(-pos).bytes}, can't write from position #{pos}"
        end
        pos = size - pos
      end
      File.open(df, File::CREAT|File::WRONLY) {|f| f.pos = pos; f << value}
    end
  end
end

Instance Method Details

#dump_recurse(obj, depth, maxdepth) ⇒ Object



4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/rmtools/text/textilize.rb', line 4

def dump_recurse(obj, depth, maxdepth)
  res = ''
  case obj
    when Hash
      if depth <= maxdepth
        res = "{\n"
        obj.each { |i, j|
          i = i.inspect unless i.is_a? String
          childinfo = dump_recurse(j,depth+1,maxdepth)
          res << "%s  %s => %s、\n"%[("  "*depth), i, childinfo]
        }
        res << "  "*depth+"  }"
      else
        res = obj.inspect
      end
      res
    when Array
      if depth <= maxdepth
        res = "[\n"
        obj.each_with_index { |j, i|
          childinfo = dump_recurse(j,depth+1,maxdepth)
          res << "%s  %0*d: %s、\n"%[("  "*depth), (obj.size-1).to_s.size, i, childinfo]
        }
        res << "  "*depth+"  ]"
      else
        res = obj.inspect
      end
      res
    when String then obj
    else obj.inspect
  end
end