bak-shell 0.0.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 82a8960ce4c29af20821e579fd7b8c1f5acb3ea4
4
+ data.tar.gz: 60407ebba0cde615e224fa2949c1a10850052734
5
+ SHA512:
6
+ metadata.gz: 5f3a4b5759f252dbc457f5ccfd8be6704e618d309d241ee88438951c1fc6017752f100e91aea026aac37f3c1dd4cc0875be019999db82ae54563db28371fa01a
7
+ data.tar.gz: 0d444113b60224ad3b8e044f79c7da429fe1e80ed981235937588f3de5a989e659ed6bb8286ae6552e55d1151ac30743149bd24a006ff2267fc259f81ff2cf90
data/.gitignore ADDED
@@ -0,0 +1,20 @@
1
+ *.a
2
+ *.bundle
3
+ *.gem
4
+ *.o
5
+ *.rbc
6
+ *.so
7
+ .bundle
8
+ .config
9
+ /.bundle/
10
+ /.config
11
+ /InstalledFiles
12
+ /lib/bundler/man/
13
+ /pkg/
14
+ /tmp/
15
+ Gemfile.lock
16
+ InstalledFiles
17
+ lib/bundler/man
18
+ mkmf.log
19
+ pkg
20
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Robert Audi
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,53 @@
1
+ bak-shell
2
+ =========
3
+
4
+ A backup utility for the shell.
5
+
6
+ Installation
7
+ ------------
8
+
9
+ ```
10
+ > gem install bak-shell
11
+ ```
12
+
13
+ Usage
14
+ -----
15
+
16
+ ```
17
+ > bak help
18
+ ```
19
+
20
+ Contributing
21
+ ------------
22
+
23
+ 1. Fork it ( https://github.com/RobertAudi/bak-shell/fork )
24
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
25
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
26
+ 4. Push to the branch (`git push origin my-new-feature`)
27
+ 5. Create a new Pull Request
28
+
29
+ License
30
+ -------
31
+
32
+ Copyright (c) 2014 Robert Audi
33
+
34
+ MIT License
35
+
36
+ Permission is hereby granted, free of charge, to any person obtaining
37
+ a copy of this software and associated documentation files (the
38
+ "Software"), to deal in the Software without restriction, including
39
+ without limitation the rights to use, copy, modify, merge, publish,
40
+ distribute, sublicense, and/or sell copies of the Software, and to
41
+ permit persons to whom the Software is furnished to do so, subject to
42
+ the following conditions:
43
+
44
+ The above copyright notice and this permission notice shall be
45
+ included in all copies or substantial portions of the Software.
46
+
47
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
48
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
49
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
50
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
51
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
52
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
53
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require 'rake/clean'
2
+ require 'rubygems'
3
+ require 'rubygems/package_task'
4
+
5
+ spec = eval(File.read('bak-shell.gemspec'))
6
+
7
+ Gem::PackageTask.new(spec) do |pkg|
8
+ end
9
+ task :default => :package
data/bak-shell.gemspec ADDED
@@ -0,0 +1,23 @@
1
+ # Ensure we require the local version and not one we might have installed already
2
+ require File.join([File.dirname(__FILE__),"lib","bak-shell","version.rb"])
3
+ Gem::Specification.new do |s|
4
+ s.name = "bak-shell"
5
+ s.version = BakShell::VERSION
6
+ s.author = "Robert Audi"
7
+ s.email = "robert@audii.me"
8
+ s.homepage = "https://github.com/RobertAudi/bak-shell"
9
+ s.summary = "Backup utility for the shell"
10
+ s.description = "Backup utility that lets you back up files or directories, and manage those backups from the command line"
11
+ s.license = "MIT"
12
+ s.files = `git ls-files -z`.split("\x0")
13
+
14
+ s.bindir = "bin"
15
+ s.require_paths << "lib"
16
+ s.executables << "bak"
17
+
18
+ s.add_development_dependency "rake", "10.3.2"
19
+ s.add_development_dependency "bundler", "~> 1.6"
20
+
21
+ s.add_runtime_dependency "gli", "2.11.0"
22
+ s.add_runtime_dependency "rainbow", "2.0.0"
23
+ end
data/bin/bak ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative "../lib/bak-shell"
4
+
5
+ Signal.trap("SIGINT") do
6
+ puts "\nTerminating"
7
+ exit 1
8
+ end
9
+
10
+ bak = BakShell::CLI
11
+
12
+ exit bak.run(ARGV)
@@ -0,0 +1,20 @@
1
+ module BakShell
2
+ class CLI
3
+ extend GLI::App
4
+
5
+ program_desc "Backup utility for the shell"
6
+ version BakShell::VERSION
7
+
8
+ commands_from File.expand_path(File.join(File.dirname(File.realpath(__FILE__)), "commands"))
9
+
10
+ on_error do |exception|
11
+ case exception
12
+ when BakShell::BaseError
13
+ $stderr.puts exception.message.color(:red)
14
+ false
15
+ else
16
+ true
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,54 @@
1
+ module BakShell
2
+ class CLI
3
+ desc "Backup a file or directory"
4
+ arg_name "TARGET"
5
+ command :backup do |c|
6
+ c.desc "Replace latest version of backup (if present). Default: false"
7
+ c.switch :R, :replace, default_value: false, negatable: false
8
+
9
+ c.action do |global_options, options, args|
10
+ raise TargetMissingError.new("Target missing") if args.empty?
11
+ raise TooManyTargetsError.new("Only one target can be specified") if args.count > 1
12
+
13
+ target = File.expand_path(args.first)
14
+ raise InvalidTargetError.new("No such file or directory: #{target}") unless File.exists?(target)
15
+
16
+ puts "Backing up...".color(:green)
17
+
18
+ indexer = Indexer.instance
19
+ backup = indexer.backup_with_target(target) || indexer.add(target)
20
+ backup_dir = File.join(BakShell::BACKUP_DIR, backup.id)
21
+
22
+ unless backup.persistent
23
+ FileUtils.mkdir_p(backup_dir)
24
+
25
+ puts "Created new backup directory".color(:green)
26
+ end
27
+
28
+ base_target = File.basename(target)
29
+ destination = File.join(BakShell::BACKUP_DIR, backup.id, "#{base_target}.bak-latest-#{Time.now.to_f}")
30
+ previous_latest = Dir.glob(File.join(backup_dir, "*.bak-latest-*"))
31
+
32
+ if previous_latest.count == 1
33
+ previous_latest = previous_latest.first
34
+
35
+ if options[:replace]
36
+ FileUtils.rm_r(previous_latest, force: true)
37
+
38
+ puts "Removed old backup".color(:green)
39
+ else
40
+ FileUtils.mv(previous_latest, previous_latest.sub(/-latest/, ""))
41
+
42
+ puts "Versionned old backup".color(:green)
43
+ end
44
+ elsif previous_latest.count > 1
45
+ raise "Found more than one backup marked as latest"
46
+ end
47
+
48
+ FileUtils.cp_r(target, destination)
49
+
50
+ puts "Backup complete!".color(:green)
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,101 @@
1
+ module BakShell
2
+ class CLI
3
+ desc "Show backup info"
4
+ arg_name "TARGET"
5
+ command :info do |c|
6
+ c.desc "Show all backups for a target (Doesn't work if no target is specified!!)"
7
+ c.switch :a, :all, default_value: false, negatable: false
8
+
9
+ c.desc "Show the path to the backup of a target (takes the index of the backup as argument)"
10
+ c.arg_name "index"
11
+ c.flag :p, :path, must_match: /\A[1-9][0-9]*\Z/
12
+
13
+ c.desc "Show the path to the latest backup of a target (equivalent to `-p 1`)"
14
+ c.switch :P, :"latest-path", default_value: false, negatable: false
15
+
16
+ c.action do |global_options, options, args|
17
+ raise TooManyTargetsError.new("Only one target can be specified") if args.count > 1
18
+
19
+ indexer = Indexer.instance
20
+
21
+ if args.empty?
22
+ illegal_options = {
23
+ a: false,
24
+ p: nil,
25
+ P: false
26
+ }
27
+
28
+ command_switches = c.switches.map { |k, s| { s.name => [s.name, s.aliases].flatten } }.inject({}) { |s, cs| s.merge(cs) }
29
+ command_flags = c.flags.map { |k, s| { s.name => [s.name, s.aliases].flatten } }.inject({}) { |s, cs| s.merge(cs) }
30
+ command_options = command_switches.merge(command_flags)
31
+
32
+ illegal_error_message = ""
33
+ options.each do |o, v|
34
+ if illegal_options.has_key?(o) && illegal_options[o] != v
35
+ illegal_error_message << "The #{command_options[o].map { |k| "`#{k.length > 1 ? "--" : "-"}#{k}`" }.join("/")} option cannot be used if no target is specified\n".color(:red)
36
+ end
37
+ end
38
+
39
+ puts "#{illegal_error_message}\n" unless illegal_error_message.empty?
40
+
41
+ backups = Hash.new
42
+ indexer.backups.each { |b| backups[b[:id]] = Dir.glob(File.join(BakShell::BACKUP_DIR, b[:id], "*")).reverse }
43
+
44
+ puts "#{backups.values.inject(0) { |count, b| count + b.count }} backups found".color(:green)
45
+
46
+ backups.each do |id, b|
47
+ puts indexer.backup_with_id(id).target
48
+ puts "\t#{b.count} backups"
49
+
50
+ latest = Time.at(b.find { |v| v =~ /\.bak-latest-.*\Z/ }.sub(/\A.*\.bak-latest-/, "").to_f)
51
+ puts "\tLatest backup time: #{latest}"
52
+ end
53
+ else
54
+ if options[:P] && !options[:p].nil?
55
+ raise BakShell::InvalidOptionError.new("Conflicting options: `-p`/`--path` and `-P`/`--latest-path`")
56
+ end
57
+
58
+ target = File.expand_path(args.first)
59
+ backup = indexer.backup_with_target(target)
60
+
61
+ raise InvalidBackupError.new("No backup found for file or directory: #{target}") if backup.nil?
62
+
63
+ backup_dir = File.join(BakShell::BACKUP_DIR, backup.id)
64
+
65
+ if options[:P]
66
+ puts Dir.glob(File.join(backup_dir, "*.bak-latest-*")).first
67
+ else
68
+ backups = Dir.glob(File.join(backup_dir, "*")).reverse
69
+ backup_count = backups.count
70
+
71
+ if !options[:p].nil?
72
+ if options[:p].to_i > backup_count
73
+ raise BakShell::InvalidBackupError.new("Invalid backup index: #{options[:p]}")
74
+ end
75
+
76
+ puts backups[options[:p].to_i - 1]
77
+ else
78
+ backup_times = backups.map do |b|
79
+ Time.at(File.basename(b).sub(/\A.*\.bak-(latest-)?/, "").to_f).to_s
80
+ end
81
+
82
+ puts "#{backup_count} found"
83
+
84
+ if !options[:a] && backup_count > 5
85
+ last_five = backup_times[0..4]
86
+ remaining_backups = backup_times.drop(5).count
87
+
88
+ puts "Showing five latest backup times:".color(:green)
89
+ last_five.each_with_index { |b, i| puts "[#{i + 1}] #{b}" }
90
+ puts "#{remaining_backups} more..."
91
+ else
92
+ puts "Showing all backup times:".color(:green)
93
+ backup_times.each_with_index { |b, i| puts "[#{i + 1}] #{b}" }
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,60 @@
1
+ module BakShell
2
+ class CLI
3
+ desc "Remove (specific) backups"
4
+ arg_name "TARGET"
5
+ command :purge do |c|
6
+ c.desc "Number of backups to keep"
7
+ c.arg_name "count"
8
+ c.flag :k, :keep, default_value: 0, must_match: /\A[1-9][0-9]*\Z/
9
+
10
+ c.action do |global_options, options, args|
11
+ puts "Purging...".color(:green)
12
+
13
+ indexer = Indexer.instance
14
+ targets = Array.new
15
+ ids = Array.new
16
+
17
+ if args.empty?
18
+ raise InvalidBackupError.new("Nothing to purge") if indexer.backups.count == 0
19
+
20
+ targets = Dir.glob(File.join(BakShell::BACKUP_DIR, "*"))
21
+ targets.delete(indexer.index_file)
22
+
23
+
24
+ ids = indexer.ids
25
+ else
26
+ args.each do |arg|
27
+ target = File.expand_path(arg)
28
+ raise InvalidTargetError.new("No such file or directory: #{target}") unless File.exists?(target)
29
+
30
+ backup = indexer.backup_with_target(target)
31
+
32
+ if backup.nil?
33
+ puts"No backup found for file or directory: #{target}".color(:yellow)
34
+ next
35
+ end
36
+
37
+ targets << File.join(BakShell::BACKUP_DIR, backup.id)
38
+ ids << backup.id
39
+ end
40
+ end
41
+
42
+ keep_count = options[:k].to_i
43
+
44
+ if keep_count > 0
45
+ targets = targets.map { |t| Dir.glob(File.join(t, "*")).reverse.drop(keep_count) }.flatten
46
+ backup_count = targets.count
47
+ else
48
+ backup_count = targets.map { |t| Dir.glob(File.join(t, "*")).count }.inject(:+)
49
+ indexer.remove(ids)
50
+ end
51
+
52
+ if backup_count > 0
53
+ targets.each { |t| FileUtils.rm_r(t, force: true) }
54
+ end
55
+
56
+ puts "Purging complete! (#{backup_count} backups removed)".color(:green)
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,48 @@
1
+ module BakShell
2
+ class CLI
3
+ desc "Restore a file or directory"
4
+ arg_name "TARGET"
5
+ command :restore do |c|
6
+ c.desc "Index of backup to restore"
7
+ c.arg_name "index"
8
+ c.flag :i, :index, default_value: 1, must_match: /\A[1-9][0-9]*\Z/
9
+
10
+ c.action do |global_options, options, args|
11
+ raise TargetMissingError.new("Target missing") if args.empty?
12
+ raise TooManyTargetsError.new("Only one target can be specified") if args.count > 1
13
+
14
+ target = File.expand_path(args.first)
15
+
16
+ raise InvalidTargetError.new("No such file or directory: #{target}") unless File.exists?(target)
17
+
18
+ puts "Restoring backup...".color(:green)
19
+
20
+ indexer = Indexer.instance
21
+ backup = indexer.backup_with_target(target)
22
+
23
+ raise InvalidBackupError.new("No backup found for file or directory: #{target}") if backup.nil?
24
+
25
+ backup_dir = File.join(BakShell::BACKUP_DIR, backup.id)
26
+ index = options[:i].to_i
27
+
28
+ if index == 1
29
+ backup_to_restore = Dir.glob(File.join(backup_dir, "*.bak-latest-*")).first
30
+ else
31
+ backups = Dir.glob(File.join(backup_dir, "*")).reverse
32
+
33
+ raise BakShell::InvalidBackupError.new("Invalid backup index: #{index}") if index > backups.count
34
+
35
+ backup_to_restore = backups[index - 1]
36
+ end
37
+
38
+ temp_bak = "#{target}.bak-#{Time.now.to_f}"
39
+
40
+ FileUtils.mv(target, temp_bak)
41
+ FileUtils.cp_r(backup_to_restore, target)
42
+ FileUtils.rm_r(temp_bak, force: true)
43
+
44
+ puts "Backup restored!".color(:green)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,8 @@
1
+ module BakShell
2
+ class BaseError < ArgumentError; end
3
+ class TargetMissingError < BaseError; end
4
+ class TooManyTargetsError < BaseError; end
5
+ class InvalidTargetError < BaseError; end
6
+ class InvalidBackupError < BaseError; end
7
+ class InvalidOptionError < BaseError; end
8
+ end
@@ -0,0 +1,115 @@
1
+ require "singleton"
2
+ require "ostruct"
3
+ require "csv"
4
+ require "securerandom"
5
+
6
+ module BakShell
7
+ class Indexer
8
+ include Singleton
9
+
10
+ def backup_with_id(id)
11
+ return @backup if !@backup.nil? && id == @backup.id
12
+
13
+ backup = self.find_by_id(id)
14
+
15
+ if backup.nil?
16
+ @backup = nil
17
+ else
18
+ @backup = OpenStruct.new(backup)
19
+ end
20
+
21
+ @backup
22
+ end
23
+
24
+ def backup_with_target(target)
25
+ return @backup if !@backup.nil? && target == @backup[:target]
26
+
27
+ backup = self.find_by_target(target)
28
+
29
+ if backup.nil?
30
+ @backup = nil
31
+ else
32
+ @backup = OpenStruct.new(backup)
33
+ end
34
+
35
+ @backup
36
+ end
37
+
38
+ def add(target)
39
+ if self.targets.include?(target)
40
+ raise "Attempt to add an existing backup to the index"
41
+ end
42
+
43
+ begin
44
+ id = SecureRandom.hex
45
+ end while self.ids.include?(id)
46
+
47
+ CSV.open(self.index_file, "ab") { |f| f << [id, target] }
48
+
49
+ self.ids << id
50
+ self.targets << target
51
+ self.backups << { id: id, target: target, persistent: false }
52
+
53
+ @backup = OpenStruct.new(self.backups.last)
54
+ end
55
+
56
+ def remove(ids)
57
+ new_index = self.backups.select { |b| !ids.include?(b[:id]) }
58
+ new_index.map { |b| b.delete(:persistent) }
59
+ CSV.open(self.index_file, "wb") do |f|
60
+ new_index.each { |b| f << b.values }
61
+ end
62
+ end
63
+
64
+ def index_file
65
+ if @index_file.nil?
66
+ @index_file = File.join(BakShell::BACKUP_DIR, "baklist.index")
67
+ FileUtils.touch(@index_file)
68
+ end
69
+
70
+ @index_file
71
+ end
72
+
73
+ def index_file_exists?
74
+ File.file?(self.index_file)
75
+ end
76
+
77
+ def ids
78
+ self.load! if @ids.nil?
79
+
80
+ @ids
81
+ end
82
+
83
+ def targets
84
+ self.load! if @targets.nil?
85
+
86
+ @targets
87
+ end
88
+
89
+ def backups
90
+ self.load! if @backups.nil?
91
+
92
+ @backups
93
+ end
94
+
95
+ def load!
96
+ @ids = Array.new
97
+ @targets = Array.new
98
+ @backups = Array.new
99
+ CSV.foreach(self.index_file) do |row|
100
+ @ids << row.first
101
+ @targets << row.last
102
+ @backups << { id: row.first, target: row.last, persistent: true }
103
+ end
104
+ end
105
+ alias_method :reload!, :load!
106
+
107
+ def find_by_id(id)
108
+ self.backups.find { |b| id == b[:id] }
109
+ end
110
+
111
+ def find_by_target(target)
112
+ self.backups.find { |b| target == b[:target] }
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,3 @@
1
+ module BakShell
2
+ VERSION = "0.0.1"
3
+ end
data/lib/bak-shell.rb ADDED
@@ -0,0 +1,21 @@
1
+ # Dev shit
2
+ if ENV["BAK_SHELL_DEV_SHIT"] == "devshit"
3
+ require "awesome_print" rescue nil
4
+ end
5
+
6
+ # Standard lib shit
7
+ require "fileutils"
8
+
9
+ # Require gems shit
10
+ require "gli"
11
+ require "rainbow/ext/string"
12
+
13
+ module BakShell
14
+ # BACKUP_DIR = File.expand_path(File.join(ENV["HOME"], "bak"))
15
+ BACKUP_DIR = File.expand_path(File.join(ENV["HOME"], "tmp", "bak"))
16
+ end
17
+
18
+ require_relative "./bak-shell/version"
19
+ require_relative "./bak-shell/exceptions"
20
+ require_relative "./bak-shell/indexer"
21
+ require_relative "./bak-shell/cli"
metadata ADDED
@@ -0,0 +1,118 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bak-shell
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Robert Audi
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-06-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 10.3.2
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 10.3.2
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.6'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.6'
41
+ - !ruby/object:Gem::Dependency
42
+ name: gli
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '='
46
+ - !ruby/object:Gem::Version
47
+ version: 2.11.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '='
53
+ - !ruby/object:Gem::Version
54
+ version: 2.11.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: rainbow
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '='
60
+ - !ruby/object:Gem::Version
61
+ version: 2.0.0
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '='
67
+ - !ruby/object:Gem::Version
68
+ version: 2.0.0
69
+ description: Backup utility that lets you back up files or directories, and manage
70
+ those backups from the command line
71
+ email: robert@audii.me
72
+ executables:
73
+ - bak
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".gitignore"
78
+ - Gemfile
79
+ - LICENSE
80
+ - README.md
81
+ - Rakefile
82
+ - bak-shell.gemspec
83
+ - bin/bak
84
+ - lib/bak-shell.rb
85
+ - lib/bak-shell/cli.rb
86
+ - lib/bak-shell/commands/backup.rb
87
+ - lib/bak-shell/commands/info.rb
88
+ - lib/bak-shell/commands/purge.rb
89
+ - lib/bak-shell/commands/restore.rb
90
+ - lib/bak-shell/exceptions.rb
91
+ - lib/bak-shell/indexer.rb
92
+ - lib/bak-shell/version.rb
93
+ homepage: https://github.com/RobertAudi/bak-shell
94
+ licenses:
95
+ - MIT
96
+ metadata: {}
97
+ post_install_message:
98
+ rdoc_options: []
99
+ require_paths:
100
+ - lib
101
+ - lib
102
+ required_ruby_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ required_rubygems_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ requirements: []
113
+ rubyforge_project:
114
+ rubygems_version: 2.2.2
115
+ signing_key:
116
+ specification_version: 4
117
+ summary: Backup utility for the shell
118
+ test_files: []