tms 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -4,7 +4,7 @@ Time Machine Status
4
4
 
5
5
  View avaliable Time Machine backups and show changes
6
6
 
7
- Name and idea from [fernlightning.com](http://www.fernlightning.com/doku.php?id=software:misc:tms)
7
+ Name from [fernlightning.com](http://www.fernlightning.com/doku.php?id=software:misc:tms)
8
8
 
9
9
  ## Copyright
10
10
 
data/Rakefile CHANGED
@@ -1,4 +1,4 @@
1
- require 'rubygems'
1
+ # require 'rubygems'
2
2
  require 'rake'
3
3
  require 'jeweler'
4
4
  require 'rake/gem_ghost_task'
@@ -9,7 +9,7 @@ name = 'tms'
9
9
  Jeweler::Tasks.new do |gem|
10
10
  gem.name = name
11
11
  gem.summary = %Q{Time Machine Status}
12
- gem.description = %Q{View avaliable Time Machine backups and show diff}
12
+ gem.description = %Q{View avaliable Time Machine backups and show their diff}
13
13
  gem.homepage = "http://github.com/toy/#{name}"
14
14
  gem.license = 'MIT'
15
15
  gem.authors = ['Boba Fat']
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.2.0
1
+ 1.3.0
data/bin/tms CHANGED
@@ -1,46 +1,68 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- begin
4
- require 'tms'
5
- rescue LoadError
6
- require 'rubygems'
7
- require 'tms'
8
- end
9
-
3
+ require 'tms'
10
4
  require 'optparse'
11
5
 
12
6
  def option_parser
13
- OptionParser.new do |op|
7
+ @option_parser ||= OptionParser.new do |op|
14
8
  op.banner = <<-BANNER
9
+ #{op.program_name}, version #{Tms.version}
10
+
15
11
  Usege:
16
- List: #{op.program_name} [options]
17
- Diff: #{op.program_name} [options] id|nxxx id|nxxx
18
- Diff with previous: #{op.program_name} [options] id|nxxx
12
+ List: #{op.program_name} [options]
13
+ Diff: #{op.program_name} [options] id|nXXX [id|nXXX]
19
14
  BANNER
20
15
 
21
- op.on('-d', '--directory DIRECTORY', 'Use backup directory') do |dir|
22
- Tms::Backup.backups_dir = dir
16
+ op.on('-d', '--directory DIRECTORY', 'Use backup directory') do |backups_dir|
17
+ Tms::Backup.backups_dir = backups_dir
23
18
  end
24
19
 
25
- op.on('-f', '--filter DIRECTORY', 'Show diff starting from directory') do |dir|
26
- Tms::Backup.filter_dir = dir
20
+ op.on('-f', '--filter DIRECTORY', 'Show diff starting from directory', ' (can be used multiple times)') do |filter_dir|
21
+ Tms::Backup.add_filter_dir(filter_dir)
27
22
  end
28
23
 
29
- op.on('-p', '--[no-]progress', 'Show backups in progress') do |show_in_progress|
24
+ op.on('-i', '--[no-]in-progress', 'Show backups in progress',
25
+ ' (note: some directories will be',
26
+ ' empty in unfinished backups)') do |show_in_progress|
30
27
  Tms::Backup.show_in_progress = show_in_progress
31
28
  end
32
29
 
33
30
  op.on('-l', '--[no-]long', 'Show more info about backup in list') do |show_all_columns|
34
31
  Tms::Backup.show_all_columns = show_all_columns
35
32
  end
33
+
34
+ op.on('--[no-]color', 'Use color', ' (true by default if stdout is a tty)') do |colorize|
35
+ Tms::Backup.colorize = colorize
36
+ end
37
+
38
+ op.on('--[no-]progress', 'Show progress when counting folder size', ' (true by default if stderr is a tty)') do |show_progress|
39
+ Tms::Backup.show_progress = show_progress
40
+ end
41
+
42
+ op.on('--[no-]decimal', 'Use base 10 size') do |use_decimal|
43
+ Tms::Space.base10 = use_decimal
44
+ end
45
+
46
+ op.on_tail('-h', '--help', 'Show this message') do
47
+ puts op.help
48
+ exit
49
+ end
50
+
51
+ op.on_tail('--version', 'Show version') do
52
+ puts Tms.version
53
+ exit
54
+ end
36
55
  end
37
56
  end
38
57
 
39
- options, ids = ARGV.partition{ |arg| arg =~ /^--?\D/ }
58
+ ids, options = ARGV.partition{ |arg| arg =~ /^[\-n]?\d+$/ }
40
59
  begin
41
60
  option_parser.parse!(options)
42
- rescue OptionParser::InvalidOption => e
43
- abort e
61
+ rescue OptionParser::ParseError => e
62
+ abort "#{e.to_s}\n#{option_parser.help}"
63
+ end
64
+ unless options.empty?
65
+ abort "Unknown arguments: #{options.join(' ')}"
44
66
  end
45
67
 
46
68
  case ids.length
@@ -1,10 +1,11 @@
1
1
  #include "ruby.h"
2
2
  #include <CoreServices/CoreServices.h>
3
+ #include <SystemConfiguration/SystemConfiguration.h>>
3
4
 
4
5
  static VALUE backup_volume(VALUE self){
5
6
  OSStatus status = pathTooLongErr;
6
7
  char *path;
7
- size_t pathLength = 256;
8
+ size_t pathLength;
8
9
 
9
10
  CFDataRef aliasData;
10
11
  AliasHandle alias;
@@ -15,7 +16,7 @@ static VALUE backup_volume(VALUE self){
15
16
  if (aliasData) {
16
17
  if (noErr == PtrToHand(CFDataGetBytePtr(aliasData), (Handle *)&alias, CFDataGetLength(aliasData))) {
17
18
  if (noErr == FSResolveAlias(NULL, alias, &fs, &wasChanged)) {
18
- path = malloc(pathLength);
19
+ path = malloc(pathLength = 256);
19
20
  while (noErr != (status = FSRefMakePath(&fs, (UInt8*)path, pathLength))) {
20
21
  if (pathTooLongErr == status) {
21
22
  pathLength += 256;
@@ -35,7 +36,30 @@ static VALUE backup_volume(VALUE self){
35
36
  }
36
37
  }
37
38
 
39
+ static VALUE computer_name(VALUE self){
40
+ char *name;
41
+ size_t nameLength;
42
+
43
+ CFStringEncoding encoding;
44
+ CFStringRef cfName;
45
+
46
+ if (cfName = SCDynamicStoreCopyComputerName(NULL, &encoding)) {
47
+ name = malloc(nameLength = 256);
48
+ while (!CFStringGetCString(cfName, name, nameLength, encoding)) {
49
+ nameLength += 256;
50
+ name = reallocf(name, nameLength);
51
+ }
52
+
53
+ CFRelease(cfName);
54
+
55
+ return rb_str_new2(name);
56
+ } else {
57
+ return Qnil;
58
+ }
59
+ }
60
+
38
61
  void Init_tms() {
39
62
  VALUE cTms = rb_define_module("Tms");
40
63
  rb_define_singleton_method(cTms, "backup_volume", backup_volume, 0);
64
+ rb_define_singleton_method(cTms, "computer_name", computer_name, 0);
41
65
  }
data/lib/tms.rb CHANGED
@@ -1,10 +1,18 @@
1
+ require 'tms/path'
2
+ require 'tms/backup'
3
+ require 'tms/table'
4
+
1
5
  module Tms
2
6
  class << self
7
+ def version
8
+ File.read(File.join(File.dirname(__FILE__), '../VERSION')).strip
9
+ end
10
+
3
11
  def list
4
12
  backups = Backup.list
5
13
  Table.new do |t|
6
- t.col '', :red
7
- t.col '', :blue
14
+ t.col '', Backup.colorize? && :red
15
+ t.col '', Backup.colorize? && :blue
8
16
  t.col 'num'
9
17
  t.col 'name'
10
18
  if Backup.show_all_columns
@@ -53,7 +61,7 @@ module Tms
53
61
  end
54
62
  backup_a = Backup.list[a_id] or abort("No backup #{a}")
55
63
  backup_b = Backup.list[b_id] or abort("No backup #{b}")
56
- Backup.diff(backup_a, backup_b)
64
+ Comparison.new(backup_a, backup_b).run
57
65
  end
58
66
 
59
67
  private
@@ -88,9 +96,3 @@ module Tms
88
96
  end
89
97
  end
90
98
  end
91
-
92
- require 'tms.so'
93
- require 'tms/backup'
94
- require 'tms/space'
95
- require 'tms/table'
96
- require 'tms/pathname'
@@ -1,135 +1,116 @@
1
- require 'colored'
2
1
  require 'xattr'
2
+ require 'tms.so'
3
+ require 'tms/comparison'
4
+ require 'tms/better_attr_accessor'
3
5
 
4
- class Tms::Backup
5
- class << self
6
- def backups_dir
7
- @backups_dir ||= begin
8
- backup_volume = Tms.backup_volume
9
- abort 'backup volume not avaliable' if backup_volume.nil?
6
+ module Tms
7
+ class Backup
8
+ class << self
9
+ extend BetterAttrAccessor
10
10
 
11
- computer_name = `scutil --get ComputerName`.strip
12
- abort 'can\'t get computer name' unless $?.success?
11
+ def backup_volume
12
+ Tms.backup_volume or abort('backup volume not avaliable')
13
+ end
13
14
 
14
- backups_dir = Pathname(backup_volume) + 'Backups.backupdb' + computer_name
15
- abort "ops! backups dir is not a dir" unless backups_dir.directory?
15
+ def computer_name
16
+ Tms.computer_name or abort('can\'t get computer name')
17
+ end
16
18
 
17
- backups_dir
19
+ def backups_dir
20
+ unless @backups_dir
21
+ self.backups_dir = Path.new(backup_volume) / 'Backups.backupdb' / computer_name
22
+ end
23
+ @backups_dir
24
+ end
25
+ def backups_dir=(backups_dir)
26
+ backups_dir = Path.new(backups_dir)
27
+ abort %{backups dir «#{backups_dir}» is not a dir} unless backups_dir.directory?
28
+ @backups_dir = backups_dir
18
29
  end
19
- end
20
- def backups_dir=(dir)
21
- @backups_dir = Pathname(dir)
22
- end
23
30
 
24
- attr_accessor :filter_dir
25
- attr_accessor :show_in_progress
26
- attr_accessor :show_all_columns
27
-
28
- def list
29
- @list ||= begin
30
- backups_dir.children.map do |path|
31
- case path.basename.to_s
32
- when /^\d{4}-\d{2}-\d{2}-\d{6}$/
33
- new(path)
34
- when /^\d{4}-\d{2}-\d{2}-\d{6}\.inProgress$/
35
- if show_in_progress
36
- path.children.select(&:directory?).each do |path_in_progress|
37
- new(path_in_progress, true)
38
- end
39
- end
40
- end
41
- end.flatten.compact.sort
31
+ def filter_dirs
32
+ @filter_dirs ||= []
33
+ end
34
+ def filter_dirs?
35
+ !filter_dirs.empty?
36
+ end
37
+ def add_filter_dir(filter_dir)
38
+ filter_dirs << File.expand_path(filter_dir)
42
39
  end
43
- end
44
40
 
45
- def diff(a, b)
46
- total = 0
47
- if filter_dir
48
- root_dir = (a.path.children + b.path.children).select(&:directory?).select{ |child| Pathname(File.join(child, filter_dir)).exist? }.map(&:basename).uniq
49
- root_dir = File.join(root_dir, filter_dir)
50
- total += compare(a.path + root_dir, b.path + root_dir, Pathname('/') + root_dir)
51
- else
52
- (a.path.children(false) | b.path.children(false)).reject{ |child| child.to_s[0, 1] == '.' }.sort.each do |path|
53
- total += compare(a.path + path, b.path + path, Pathname('/') + path)
54
- end
41
+ better_attr_accessor :show_in_progress
42
+ better_attr_accessor :show_all_columns
43
+ better_attr_accessor :colorize
44
+ better_attr_accessor :show_progress
45
+
46
+ def colorize?
47
+ !colorize.nil? ? colorize : $stdout.tty?
55
48
  end
56
- puts "#{'Total:'.bold} #{Tms::Space.space(total, :color => true)}"
57
- end
58
49
 
59
- private
50
+ def show_progress?
51
+ !show_progress.nil? ? show_progress : $stderr.tty?
52
+ end
60
53
 
61
- def compare(a, b, path)
62
- case
63
- when !a.exist?
64
- puts "#{' █'.green} #{b.colored_size(:recursive => true)} #{path}#{b.postfix}"
65
- b.count_size || 0
66
- when !b.exist?
67
- puts "#{'█ '.blue} #{a.colored_size(:recursive => true)} #{path}#{a.postfix}"
68
- 0
69
- when a.ftype != b.ftype
70
- puts "#{'!!!'.red.bold} #{a.colored_size(:recursive => true)} #{path} (#{a.ftype}=>#{b.ftype})"
71
- b.count_size || 0
72
- when a.lino != b.lino
73
- if a.readable? && b.readable?
74
- puts "#{'█≠█'.yellow} #{a.colored_size} #{path}#{a.postfix}" unless a.symlink? && a.readlink == b.readlink
75
- if a.real_directory?
76
- total = 0
77
- (a.children(false) | b.children(false)).sort.each do |child|
78
- total += compare(a + child, b + child, path + child)
54
+ def list
55
+ @list ||= begin
56
+ backups_dir.children.map do |path|
57
+ case path.basename.to_s
58
+ when /^\d{4}-\d{2}-\d{2}-\d{6}$/
59
+ new(path)
60
+ when /^\d{4}-\d{2}-\d{2}-\d{6}\.inProgress$/
61
+ if show_in_progress?
62
+ path.children.select(&:directory?).map do |path_in_progress|
63
+ new(path_in_progress, true)
64
+ end
65
+ end
79
66
  end
80
- total
81
- else
82
- b.size
83
- end
84
- else
85
- puts "??? #{path}#{a.postfix}".red.bold
86
- 0
67
+ end.flatten.compact.sort
87
68
  end
88
- else
89
- 0
90
69
  end
91
70
  end
92
- end
93
71
 
94
- attr_reader :path, :in_progress
95
- def initialize(path, in_progress = false)
96
- @path = path
97
- @in_progress = in_progress
98
- end
72
+ extend BetterAttrAccessor
99
73
 
100
- def name
101
- @name ||= in_progress ? "#{path.dirname.basename}/#{path.basename}" : path.basename.to_s
102
- end
74
+ better_attr_reader :path, :in_progress
75
+ def initialize(path, in_progress = false)
76
+ @path = path
77
+ @in_progress = in_progress
78
+ end
103
79
 
104
- def started_at
105
- @start_date ||= Time.at(xattr.get('com.apple.backupd.SnapshotStartDate').to_i / 1_000_000.0)
106
- end
107
- def finished_at
108
- @finished_at ||= Time.at(xattr.get('com.apple.backupd.SnapshotCompletionDate').to_i / 1_000_000.0)
109
- end
110
- def completed_in
111
- finished_at - started_at
112
- end
113
- {
114
- :state => 'com.apple.backupd.SnapshotState',
115
- :type => 'com.apple.backupd.SnapshotType',
116
- :version => 'com.apple.backup.SnapshotVersion',
117
- :number => 'com.apple.backup.SnapshotNumber',
118
- }.each do |name, attr|
119
- class_eval <<-src
120
- def #{name}
121
- @#{name} ||= xattr.get('#{attr}').to_i rescue '-'
122
- end
123
- src
124
- end
80
+ def name
81
+ @name ||= in_progress? ? "#{path.dirname.basename}/#{path.basename}" : path.basename.to_s
82
+ end
125
83
 
126
- def <=>(other)
127
- name <=> other.name
128
- end
84
+ def started_at
85
+ @start_date ||= Time.at(xattr.get('com.apple.backupd.SnapshotStartDate').to_i / 1_000_000.0)
86
+ end
87
+ def finished_at
88
+ @finished_at ||= Time.at(xattr.get('com.apple.backupd.SnapshotCompletionDate').to_i / 1_000_000.0)
89
+ end
90
+ def completed_in
91
+ finished_at - started_at
92
+ end
93
+ {
94
+ :state => 'com.apple.backupd.SnapshotState',
95
+ :type => 'com.apple.backupd.SnapshotType',
96
+ :version => 'com.apple.backup.SnapshotVersion',
97
+ :number => 'com.apple.backup.SnapshotNumber',
98
+ }.each do |name, attr|
99
+ class_eval <<-src
100
+ def #{name}
101
+ @#{name} ||= xattr.get('#{attr}').to_i rescue '-'
102
+ end
103
+ src
104
+ end
129
105
 
130
- private
106
+ def <=>(other)
107
+ name <=> other.name
108
+ end
109
+
110
+ private
131
111
 
132
- def xattr
133
- @xattr ||= Xattr.new(path)
112
+ def xattr
113
+ @xattr ||= Xattr.new(path)
114
+ end
134
115
  end
135
116
  end
@@ -0,0 +1,20 @@
1
+ module BetterAttrAccessor
2
+ def better_attr_reader(*names)
3
+ names.each do |name|
4
+ attr_reader name
5
+ # leaves nil and false as is, returns true for everything else
6
+ class_eval <<-RUBY
7
+ def #{name}?
8
+ @#{name} && true
9
+ end
10
+ RUBY
11
+ end
12
+ end
13
+
14
+ def better_attr_accessor(*names)
15
+ better_attr_reader *names
16
+ names.each do |name|
17
+ attr_writer name
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,110 @@
1
+ require 'tms/space'
2
+
3
+ module Tms
4
+ class Comparison
5
+ attr_reader :backup_a, :backup_b, :total
6
+ def initialize(backup_a, backup_b)
7
+ @backup_a, @backup_b = backup_a, backup_b
8
+ end
9
+
10
+ def run
11
+ @total = 0
12
+ begin
13
+ root_dirs = (backup_a.path.children(false) | backup_b.path.children(false)).sort
14
+ root_dirs.reject!{ |root_dir| root_dir.path[0, 1] == '.' }
15
+ root_dirs.each do |root_dir|
16
+ dirs = Backup.filter_dirs? ? Backup.filter_dirs.map{ |filter_dir| root_dir / filter_dir } : [root_dir]
17
+ dirs.each do |dir|
18
+ compare(backup_a.path / dir, backup_b.path / dir, Path.new('/', dir))
19
+ end
20
+ end
21
+ rescue Interrupt
22
+ puts
23
+ puts 'Interrupted'
24
+ end
25
+ line "#{colorize 'Total:', :total} #{space(total)}"
26
+ end
27
+
28
+ private
29
+
30
+ def compare(a, b, path)
31
+ case
32
+ when !a.exist?
33
+ count_space b, path, colorize(' █', :right), "#{path}#{b.postfix}", :recursive => true
34
+ when !b.exist?
35
+ count_space a, path, colorize('█ ', :left), "#{path}#{a.postfix}", :recursive => true, :no_total => true
36
+ when a.ftype != b.ftype
37
+ count_space b, path, colorize('█≠█', :diff_type), "#{path}#{b.postfix} (#{a.ftype}=>#{b.ftype})", :recursive => true
38
+ when a.lstat.ino != b.lstat.ino
39
+ if a.readable_real? && b.readable_real?
40
+ count_space b, path, colorize('█≠█', :diff), "#{path}#{b.postfix}" unless b.symlink? && a.readlink == b.readlink
41
+ if b.directory? && !b.symlink?
42
+ (a.children(false) | b.children(false)).sort.each do |child|
43
+ compare(a / child, b / child, path / child)
44
+ end
45
+ end
46
+ else
47
+ line colorize("??? #{Space::NOT_COUNTED_SPACE} #{path}#{a.postfix}", :unreadable)
48
+ end
49
+ else
50
+ progress do
51
+ path
52
+ end
53
+ end
54
+ end
55
+
56
+ COLORS = {
57
+ :total => {:extra => :bold},
58
+ :right => {:foreground => :green},
59
+ :left => {:foreground => :blue},
60
+ :diff_type => {:foreground => :red, :extra => :bold},
61
+ :diff => {:foreground => :yellow},
62
+ :unreadable => {:foreground => :red, :extra => :bold},
63
+ }
64
+ CLEAR_LINE = "\e[K"
65
+
66
+ def line(s)
67
+ $stdout.puts "#{s}#{CLEAR_LINE}"
68
+ end
69
+
70
+ def progress
71
+ if Tms::Backup.show_progress?
72
+ @last_progress ||= Time.now
73
+ if (now = Time.now) > @last_progress + 0.1
74
+ $stderr.print "#{yield}#{CLEAR_LINE}\r"
75
+ @last_progress = now
76
+ end
77
+ end
78
+ end
79
+
80
+ def space(size)
81
+ Tms::Space.space(size, :color => Tms::Backup.colorize?)
82
+ end
83
+
84
+ def colorize(s, type)
85
+ if Tms::Backup.colorize?
86
+ Colored.colorize(s, COLORS[type])
87
+ else
88
+ s
89
+ end
90
+ end
91
+
92
+ def count_space(backup_path, path, prefix, postfix, options = {})
93
+ sub_total = 0
94
+ if options[:recursive]
95
+ backup_path.find do |sub_path|
96
+ sub_total += sub_path.size_if_real_file
97
+ progress do
98
+ "#{prefix} #{space sub_total} #{path / sub_path.to_s[backup_path.to_s.length..-1].to_s}"
99
+ end
100
+ end
101
+ else
102
+ sub_total = backup_path.size_if_real_file
103
+ end
104
+ line "#{prefix} #{space sub_total} #{postfix}"
105
+ unless options[:no_total]
106
+ @total += sub_total
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,118 @@
1
+ require 'find'
2
+
3
+ module Tms
4
+ # cleaned up Pathname
5
+ class Path
6
+ attr_reader :path
7
+ def initialize(*parts)
8
+ @path = File.join(*parts)
9
+ end
10
+
11
+ def /(other)
12
+ self.class.new(@path, other)
13
+ end
14
+
15
+ def hash
16
+ @path.hash
17
+ end
18
+
19
+ def ==(other)
20
+ return false unless Path === other
21
+ other.to_s == @path
22
+ end
23
+ alias_method :===, :==
24
+ alias_method :eql?, :==
25
+
26
+ def <=>(other)
27
+ return nil unless Path === other
28
+ @path <=> other.to_s
29
+ end
30
+
31
+ def basename(*args)
32
+ self.class.new(File.basename(@path, *args))
33
+ end
34
+
35
+ def dirname(*args)
36
+ self.class.new(File.dirname(@path))
37
+ end
38
+
39
+ def readlink
40
+ self.class.new(File.readlink(@path))
41
+ end
42
+
43
+ def ftype
44
+ File.ftype(@path)
45
+ end
46
+
47
+ def lstat
48
+ File.lstat(@path)
49
+ end
50
+
51
+ def size
52
+ File.size(@path)
53
+ end
54
+
55
+ def size_if_real_file
56
+ file? && !symlink? ? File.size(@path) : 0
57
+ end
58
+
59
+ def exist?
60
+ File.exist?(@path)
61
+ end
62
+
63
+ def file?
64
+ File.file?(@path)
65
+ end
66
+
67
+ def directory?
68
+ File.directory?(@path)
69
+ end
70
+
71
+ def symlink?
72
+ File.symlink?(@path)
73
+ end
74
+
75
+ def readable_real?
76
+ File.readable_real?(@path)
77
+ end
78
+
79
+ def children(with_directory = true)
80
+ with_directory = false if @path == '.'
81
+ result = []
82
+ Dir.foreach(@path) do |e|
83
+ next if e == '.' || e == '..'
84
+ if with_directory
85
+ result << self.class.new(File.join(@path, e))
86
+ else
87
+ result << self.class.new(e)
88
+ end
89
+ end
90
+ result
91
+ end
92
+
93
+ def find(&block)
94
+ if @path == '.'
95
+ Find.find(@path){ |f| yield self.class.new(f.sub(/^.\//, '')) }
96
+ else
97
+ Find.find(@path){ |f| yield self.class.new(f) }
98
+ end
99
+ end
100
+
101
+ def to_s
102
+ @path.dup
103
+ end
104
+ alias_method :to_str, :to_s
105
+ alias_method :to_path, :to_s
106
+
107
+ def postfix
108
+ case
109
+ when symlink?
110
+ '@'
111
+ when directory?
112
+ '/'
113
+ else
114
+ ''
115
+ end
116
+ end
117
+ end
118
+ end
@@ -1,41 +1,50 @@
1
1
  require 'colored'
2
2
 
3
- module Tms::Space
4
- SIZE_SYMBOLS = %w[B K M G T P E Z Y].freeze
5
- COLORS = [].tap do |colors|
6
- [:white, :black, :yellow, :red].each do |color|
7
- colors << {:foreground => color}
8
- colors << {:foreground => color, :extra => :bold}
9
- end
10
- colors << {:foreground => :yellow, :extra => :reversed}
11
- colors << {:foreground => :red, :extra => :reversed}
12
- end.freeze
3
+ module Tms
4
+ module Space
5
+ SIZE_SYMBOLS = %w[B K M G T P E Z Y].freeze
6
+ COLORS = [].tap do |colors|
7
+ [:white, :black, :yellow, :red].each do |color|
8
+ colors << {:foreground => color}
9
+ colors << {:foreground => color, :extra => :bold}
10
+ end
11
+ colors << {:foreground => :yellow, :extra => :reversed}
12
+ colors << {:foreground => :red, :extra => :reversed}
13
+ end.freeze
14
+ PRECISION = 1
15
+ LENGTH = 4 + PRECISION + 1
16
+ COEF = 1 / Math.log(10)
13
17
 
14
- def space(size, options = {})
15
- precision = [options[:precision].to_i, 1].max || 2
16
- length = 4 + precision + (options[:can_be_negative] ? 1 : 0)
18
+ EMPTY_SPACE = ' ' * LENGTH
19
+ NOT_COUNTED_SPACE = '!' * LENGTH
17
20
 
18
- number = size.to_i
19
- degree = 0
20
- while number.abs >= 1000 && degree < SIZE_SYMBOLS.length - 1
21
- degree += 1
22
- number /= 1024.0
23
- end
21
+ class << self
22
+ attr_writer :base10
23
+ def denominator
24
+ @denominator ||= @base10 ? 1000.0 : 1024.0
25
+ end
26
+
27
+ def space(size, options = {})
28
+ case size
29
+ when false
30
+ NOT_COUNTED_SPACE.bold.red
31
+ when 0
32
+ EMPTY_SPACE
33
+ else
34
+ number, degree = size, 0
35
+ while number.abs >= 1000 && degree < SIZE_SYMBOLS.length - 1
36
+ number /= denominator
37
+ degree += 1
38
+ end
24
39
 
25
- space = "#{(degree == 0 ? number.to_s : "%.#{precision}f" % number).rjust(length)}#{number == 0 ? ' ' : SIZE_SYMBOLS[degree]}"
26
- if options[:color]
27
- unless ''.respond_to?(:red)
28
- require 'toy/fast_gem'
29
- fast_gem 'colored'
40
+ space = "#{degree == 0 ? number.to_s : "%.#{PRECISION}f" % number}#{SIZE_SYMBOLS[degree]}".rjust(LENGTH)
41
+ if options[:color]
42
+ color = [[Math.log(size) * COEF, 1].max.to_i, COLORS.length].min - 1
43
+ space = Colored.colorize(space, COLORS[color])
44
+ end
45
+ space
46
+ end
30
47
  end
31
- step = options[:color].is_a?(Hash) && options[:color][:step] || 10
32
- start = options[:color].is_a?(Hash) && options[:color][:start] || 1
33
- coef = 10.0 / (step * Math.log(10))
34
- color = [[Math.log(size) * coef - start, 0].max.to_i, COLORS.length - 1].min rescue 0
35
- Colored.colorize(space, COLORS[color])
36
- else
37
- space
38
48
  end
39
49
  end
40
- self.extend self
41
50
  end
@@ -1,39 +1,41 @@
1
1
  require 'colored'
2
2
 
3
- class Tms::Table
4
- attr_accessor :cols, :rows
3
+ module Tms
4
+ class Table
5
+ attr_accessor :cols, :rows
5
6
 
6
- def initialize(&block)
7
- @cols, @rows = [], []
8
- yield self if block_given?
9
- end
10
-
11
- ADJUST = {:left => :ljust, :right => :rjust, :center => :center}
12
- def col(name, color = nil, adjust = nil)
13
- @cols << {:name => name, :color => color, :adjust => ADJUST[adjust]}
14
- end
7
+ def initialize(&block)
8
+ @cols, @rows = [], []
9
+ yield self if block_given?
10
+ end
15
11
 
16
- def <<(row)
17
- @rows << row
18
- end
12
+ ADJUST = {:left => :ljust, :right => :rjust, :center => :center}
13
+ def col(name, color = nil, adjust = nil)
14
+ @cols << {:name => name, :color => color, :adjust => ADJUST[adjust]}
15
+ end
19
16
 
20
- def lines
21
- @cols.each_with_index do |col, i|
22
- col[:width] = ([col[:name]] + @rows.map{ |row| row[i] }).map(&:to_s).map(&:length).max
17
+ def <<(row)
18
+ @rows << row
23
19
  end
24
20
 
25
- ([@cols.map{ |col| col[:name] }] + @rows).each_with_index.map do |line, i|
26
- line.zip(@cols).map do |val, col|
27
- width, color, adjust = col.values_at(:width, :color, :adjust)
28
- adjust ||= val.is_a?(Integer) ? :rjust : :ljust
29
- val_s = val.to_s.send(adjust, width)
30
- val_s = Colored.colorize(val_s, :foreground => color) if color
31
- val_s
32
- end.join(' ')
21
+ def lines
22
+ @cols.each_with_index do |col, i|
23
+ col[:width] = ([col[:name]] + @rows.map{ |row| row[i] }).map(&:to_s).map(&:length).max
24
+ end
25
+
26
+ ([@cols.map{ |col| col[:name] }] + @rows).each_with_index.map do |line, i|
27
+ line.zip(@cols).map do |val, col|
28
+ width, color, adjust = col.values_at(:width, :color, :adjust)
29
+ adjust ||= val.is_a?(Integer) ? :rjust : :ljust
30
+ val_s = val.to_s.send(adjust, width)
31
+ val_s = Colored.colorize(val_s, :foreground => color) if color
32
+ val_s
33
+ end.join(' ')
34
+ end
33
35
  end
34
- end
35
36
 
36
- def print
37
- puts lines
37
+ def print
38
+ puts lines
39
+ end
38
40
  end
39
41
  end
@@ -5,13 +5,13 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{tms}
8
- s.version = "1.2.0"
8
+ s.version = "1.3.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Boba Fat"]
12
- s.date = %q{2010-12-06}
12
+ s.date = %q{2010-12-11}
13
13
  s.default_executable = %q{tms}
14
- s.description = %q{View avaliable Time Machine backups and show diff}
14
+ s.description = %q{View avaliable Time Machine backups and show their diff}
15
15
  s.executables = ["tms"]
16
16
  s.extensions = ["ext/tms/extconf.rb"]
17
17
  s.extra_rdoc_files = [
@@ -28,7 +28,9 @@ Gem::Specification.new do |s|
28
28
  "ext/tms/tms.c",
29
29
  "lib/tms.rb",
30
30
  "lib/tms/backup.rb",
31
- "lib/tms/pathname.rb",
31
+ "lib/tms/better_attr_accessor.rb",
32
+ "lib/tms/comparison.rb",
33
+ "lib/tms/path.rb",
32
34
  "lib/tms/space.rb",
33
35
  "lib/tms/table.rb",
34
36
  "tms.gemspec"
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tms
3
3
  version: !ruby/object:Gem::Version
4
- hash: 31
4
+ hash: 27
5
5
  prerelease: false
6
6
  segments:
7
7
  - 1
8
- - 2
8
+ - 3
9
9
  - 0
10
- version: 1.2.0
10
+ version: 1.3.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Boba Fat
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-12-06 00:00:00 +03:00
18
+ date: 2010-12-11 00:00:00 +03:00
19
19
  default_executable: tms
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -90,7 +90,7 @@ dependencies:
90
90
  version: "0"
91
91
  type: :development
92
92
  version_requirements: *id005
93
- description: View avaliable Time Machine backups and show diff
93
+ description: View avaliable Time Machine backups and show their diff
94
94
  email:
95
95
  executables:
96
96
  - tms
@@ -109,7 +109,9 @@ files:
109
109
  - ext/tms/tms.c
110
110
  - lib/tms.rb
111
111
  - lib/tms/backup.rb
112
- - lib/tms/pathname.rb
112
+ - lib/tms/better_attr_accessor.rb
113
+ - lib/tms/comparison.rb
114
+ - lib/tms/path.rb
113
115
  - lib/tms/space.rb
114
116
  - lib/tms/table.rb
115
117
  - tms.gemspec
@@ -1,49 +0,0 @@
1
- require 'pathname'
2
-
3
- class Pathname
4
- def real_directory?
5
- directory? && !symlink?
6
- end
7
-
8
- def lino
9
- @ino ||= lstat.ino
10
- end
11
-
12
- def postfix
13
- case
14
- when symlink?
15
- '@'
16
- when directory?
17
- '/'
18
- else
19
- ''
20
- end
21
- end
22
-
23
- def count_size(options = {})
24
- if @count_size.nil?
25
- if exist?
26
- if directory?
27
- @counted_size = 0
28
- find{ |path| @counted_size += path.size rescue nil } if options[:recursive]
29
- else
30
- @counted_size = size
31
- end
32
- else
33
- @counted_size = false
34
- end
35
- end
36
- @counted_size
37
- end
38
-
39
- def colored_size(options = {})
40
- case size = count_size(options)
41
- when false
42
- '!!!!!!'
43
- when 0
44
- ' '
45
- else
46
- Tms::Space.space(size, :color => true)
47
- end
48
- end
49
- end