tms 1.2.0 → 1.3.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.
@@ -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