dotfiler 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +302 -0
- data/dotfiler.gemspec +28 -0
- data/exe/dotfiler +7 -0
- data/lib/dotfiler.rb +11 -0
- data/lib/dotfiler/cli/commands.rb +39 -0
- data/lib/dotfiler/cli/commands/add.rb +105 -0
- data/lib/dotfiler/cli/commands/backup.rb +36 -0
- data/lib/dotfiler/cli/commands/command.rb +39 -0
- data/lib/dotfiler/cli/commands/edit.rb +37 -0
- data/lib/dotfiler/cli/commands/init.rb +79 -0
- data/lib/dotfiler/cli/commands/install.rb +69 -0
- data/lib/dotfiler/cli/commands/list.rb +43 -0
- data/lib/dotfiler/cli/commands/remove.rb +39 -0
- data/lib/dotfiler/cli/commands/version.rb +13 -0
- data/lib/dotfiler/config.rb +71 -0
- data/lib/dotfiler/container.rb +58 -0
- data/lib/dotfiler/copier.rb +21 -0
- data/lib/dotfiler/dotfile.rb +18 -0
- data/lib/dotfiler/dotfiles.rb +101 -0
- data/lib/dotfiler/file_system.rb +43 -0
- data/lib/dotfiler/mover.rb +21 -0
- data/lib/dotfiler/path.rb +77 -0
- data/lib/dotfiler/remover.rb +15 -0
- data/lib/dotfiler/shell.rb +60 -0
- data/lib/dotfiler/symlinker.rb +23 -0
- data/lib/dotfiler/version.rb +3 -0
- metadata +156 -0
@@ -0,0 +1,21 @@
|
|
1
|
+
module Dotfiler
|
2
|
+
class Copier
|
3
|
+
include Dotfiler::Import["fs"]
|
4
|
+
|
5
|
+
def call(source, target, options = {})
|
6
|
+
check_paths!(source, target.parent_dir)
|
7
|
+
|
8
|
+
fs.copy(source.to_s, target.to_s, options)
|
9
|
+
|
10
|
+
target.join(source.name)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def check_paths!(*paths)
|
16
|
+
paths.each do |path|
|
17
|
+
raise Error, "Path '#{path}' does not exist" unless path.exists?
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Dotfiler
|
2
|
+
class Dotfile
|
3
|
+
attr_reader :name, :link, :path
|
4
|
+
|
5
|
+
def initialize(name:, link:, path:)
|
6
|
+
@name = name
|
7
|
+
|
8
|
+
to_path = Dotfiler.resolve["to_path"]
|
9
|
+
|
10
|
+
@link = to_path.(link)
|
11
|
+
@path = to_path.(path)
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_h
|
15
|
+
{ name: name, link: link.to_s, path: path.to_s }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require "forwardable"
|
2
|
+
|
3
|
+
require "dotfiler/dotfile"
|
4
|
+
|
5
|
+
module Dotfiler
|
6
|
+
class Dotfiles
|
7
|
+
extend Forwardable
|
8
|
+
include Enumerable
|
9
|
+
|
10
|
+
include Dotfiler::Import["fs", "config", "to_path"]
|
11
|
+
|
12
|
+
HOME_PLACEHOLDER = "%home%".freeze
|
13
|
+
DOTFILES_PLACEHOLDER = "%dotfiles%".freeze
|
14
|
+
|
15
|
+
def_delegators :config, :home_path, :relative_home_path
|
16
|
+
|
17
|
+
def initialize(args)
|
18
|
+
super(args)
|
19
|
+
load_data!
|
20
|
+
end
|
21
|
+
|
22
|
+
def find(name)
|
23
|
+
@dotfiles.find { |dotfile| dotfile.name == name }
|
24
|
+
end
|
25
|
+
|
26
|
+
def each(&block)
|
27
|
+
@dotfiles.each(&block)
|
28
|
+
end
|
29
|
+
|
30
|
+
def names
|
31
|
+
@dotfiles.map(&:name)
|
32
|
+
end
|
33
|
+
|
34
|
+
def name_taken?(name)
|
35
|
+
names.include?(name)
|
36
|
+
end
|
37
|
+
alias_method :exists?, :name_taken?
|
38
|
+
|
39
|
+
def add!(*args)
|
40
|
+
dotfile = Dotfile.new(*args)
|
41
|
+
|
42
|
+
File.open(config.dotfiles_file_path.to_s, "a") do |file|
|
43
|
+
file << dotfile_to_line(dotfile)
|
44
|
+
end
|
45
|
+
|
46
|
+
reload!
|
47
|
+
end
|
48
|
+
|
49
|
+
def remove!(name)
|
50
|
+
content = @dotfiles.each_with_object("") do |dotfile, result|
|
51
|
+
next if dotfile.name == name
|
52
|
+
|
53
|
+
result << dotfile_to_line(dotfile)
|
54
|
+
end
|
55
|
+
|
56
|
+
File.open(config.dotfiles_file_path.to_s, "w+") { |file| file << content }
|
57
|
+
|
58
|
+
reload!
|
59
|
+
end
|
60
|
+
|
61
|
+
def list
|
62
|
+
@dotfiles.map(&:to_h)
|
63
|
+
end
|
64
|
+
|
65
|
+
def load_data!
|
66
|
+
@dotfiles = if config.set?
|
67
|
+
parse(File.readlines(config.dotfiles_file_path.to_s))
|
68
|
+
else
|
69
|
+
{}
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
alias_method :reload!, :load_data!
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def parse(lines)
|
78
|
+
lines.sort.each_with_object([]) do |line, result|
|
79
|
+
sanitized_line = line.gsub("\n", "")
|
80
|
+
|
81
|
+
next if sanitized_line.empty?
|
82
|
+
|
83
|
+
name, link, path = sanitized_line.split(" :: ")
|
84
|
+
|
85
|
+
link = link.sub(HOME_PLACEHOLDER, home_path.to_s)
|
86
|
+
path = path.sub(DOTFILES_PLACEHOLDER, config[:dotfiles])
|
87
|
+
|
88
|
+
result << Dotfile.new(name: name, link: link, path: path)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def dotfile_to_line(dotfile)
|
93
|
+
name, link, path = dotfile.to_h.values_at(:name, :link, :path)
|
94
|
+
|
95
|
+
link = link.sub(home_path.to_s, HOME_PLACEHOLDER)
|
96
|
+
path = path.sub(config[:dotfiles], DOTFILES_PLACEHOLDER)
|
97
|
+
|
98
|
+
[name, link, path].join(" :: ") + "\n"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
|
3
|
+
module Dotfiler
|
4
|
+
class FileSystem
|
5
|
+
attr_reader :utils
|
6
|
+
|
7
|
+
def initialize(utils: FileUtils)
|
8
|
+
@utils = utils
|
9
|
+
end
|
10
|
+
|
11
|
+
def move(source_path, destination_path, *options)
|
12
|
+
utils.move(source_path, destination_path, *options)
|
13
|
+
end
|
14
|
+
|
15
|
+
def copy(source_path, destination_path, *options)
|
16
|
+
utils.cp_r(source_path, destination_path, *options)
|
17
|
+
end
|
18
|
+
|
19
|
+
def symlink(source, destination, *options)
|
20
|
+
utils.symlink(source, destination, *options)
|
21
|
+
end
|
22
|
+
|
23
|
+
def remove(path)
|
24
|
+
utils.remove_entry_secure(path)
|
25
|
+
end
|
26
|
+
|
27
|
+
def create_file(name, content = "")
|
28
|
+
file = File.new(name, "w+")
|
29
|
+
file.write(content)
|
30
|
+
file.close
|
31
|
+
end
|
32
|
+
|
33
|
+
def create_dir(path, *options)
|
34
|
+
utils.mkdir_p(path, *options)
|
35
|
+
end
|
36
|
+
|
37
|
+
def execute(command, *command_options, **options)
|
38
|
+
full_command = ([command] + command_options).join(" ")
|
39
|
+
|
40
|
+
options[:capture] == true ? `#{full_command}` : system(full_command)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Dotfiler
|
2
|
+
class Mover
|
3
|
+
include Dotfiler::Import["fs"]
|
4
|
+
|
5
|
+
def call(source, target, options = { secure: true })
|
6
|
+
check_paths!(source, target.parent_dir)
|
7
|
+
|
8
|
+
fs.move(source.to_s, target.to_s, options)
|
9
|
+
|
10
|
+
target.join(source.name)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def check_paths!(*paths)
|
16
|
+
paths.each do |path|
|
17
|
+
raise Error, "Path '#{path}' does not exist" unless path.exists?
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require "pathname"
|
2
|
+
|
3
|
+
module Dotfiler
|
4
|
+
class Path
|
5
|
+
include Comparable
|
6
|
+
|
7
|
+
def initialize(raw_path, expand: true)
|
8
|
+
@path = raw_path.nil? ? "" : raw_path
|
9
|
+
@full_path = Pathname.new(raw_path)
|
10
|
+
@full_path = @full_path.expand_path if expand == true
|
11
|
+
end
|
12
|
+
|
13
|
+
def <=>(other_path)
|
14
|
+
self.full <=> other_path.full
|
15
|
+
end
|
16
|
+
|
17
|
+
def full
|
18
|
+
@full_path
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
@full_path.to_s
|
23
|
+
end
|
24
|
+
|
25
|
+
def join(*paths)
|
26
|
+
new_path = @full_path
|
27
|
+
|
28
|
+
paths.each { |path| new_path = new_path.join(path) }
|
29
|
+
|
30
|
+
self.class.new(new_path.to_s)
|
31
|
+
end
|
32
|
+
|
33
|
+
def contains?(other_path)
|
34
|
+
other_path = self.class.new(other_path) if other_path.is_a?(String)
|
35
|
+
|
36
|
+
children.any? { |child| child == other_path }
|
37
|
+
end
|
38
|
+
|
39
|
+
def within?(other_path)
|
40
|
+
other_path = self.class.new(other_path) if other_path.is_a?(String)
|
41
|
+
|
42
|
+
other_path.children.any? { |child| child == self }
|
43
|
+
end
|
44
|
+
|
45
|
+
def children
|
46
|
+
@full_path.children.map { |child| self.class.new(child.to_s) }
|
47
|
+
end
|
48
|
+
|
49
|
+
def parent_dir
|
50
|
+
self.class.new(@full_path.parent.to_s)
|
51
|
+
end
|
52
|
+
|
53
|
+
def real
|
54
|
+
@full_path.realpath
|
55
|
+
end
|
56
|
+
|
57
|
+
def name
|
58
|
+
@full_path.basename
|
59
|
+
end
|
60
|
+
|
61
|
+
def exists?
|
62
|
+
File.exists?(self.to_s)
|
63
|
+
end
|
64
|
+
|
65
|
+
def file?
|
66
|
+
!dir? && File.file?(@full_path)
|
67
|
+
end
|
68
|
+
|
69
|
+
def dir?
|
70
|
+
File.directory?(@full_path)
|
71
|
+
end
|
72
|
+
|
73
|
+
def symlink?
|
74
|
+
File.symlink?(@full_path)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Dotfiler
|
2
|
+
class Remover
|
3
|
+
include Dotfiler::Import["fs"]
|
4
|
+
|
5
|
+
def call(path, only_symlinks: true)
|
6
|
+
if only_symlinks && !path.symlink?
|
7
|
+
raise(Error, "Cannot remove '#{path}' since it is not a symbolic link")
|
8
|
+
end
|
9
|
+
|
10
|
+
fs.remove(path.to_s)
|
11
|
+
|
12
|
+
path
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Dotfiler
|
2
|
+
class Shell
|
3
|
+
TERMINATION_CODES = {
|
4
|
+
clean: 0,
|
5
|
+
info: 0,
|
6
|
+
error: 1
|
7
|
+
}.freeze
|
8
|
+
|
9
|
+
def initialize(output: $stdout, input: $stdin, error: $stderr)
|
10
|
+
@output = output
|
11
|
+
@error = error
|
12
|
+
@input = input
|
13
|
+
end
|
14
|
+
|
15
|
+
def print(content, type = nil)
|
16
|
+
case type
|
17
|
+
when :error
|
18
|
+
error.puts("ERROR: #{content}")
|
19
|
+
when :info
|
20
|
+
output.puts("# #{content}")
|
21
|
+
else
|
22
|
+
output.puts("#{content}")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def prompt(text, available_answers = {})
|
27
|
+
question = text
|
28
|
+
|
29
|
+
if available_answers.any?
|
30
|
+
question += "\n\n"
|
31
|
+
available_answers.each { |key, details| question += " #{key}) #{details[:desc]}\n" }
|
32
|
+
else
|
33
|
+
available_answers = default_prompt_answer
|
34
|
+
question += " [y/N] "
|
35
|
+
end
|
36
|
+
|
37
|
+
print(question)
|
38
|
+
|
39
|
+
answer_key = input.gets.strip.downcase
|
40
|
+
|
41
|
+
return :other unless available_answers.key?(answer_key)
|
42
|
+
|
43
|
+
available_answers[answer_key].fetch(:value)
|
44
|
+
end
|
45
|
+
|
46
|
+
def terminate(type, message: nil)
|
47
|
+
print(message, type) unless message.nil?
|
48
|
+
exit(TERMINATION_CODES[type])
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
attr_reader :output, :error, :input
|
54
|
+
|
55
|
+
def default_prompt_answer
|
56
|
+
{ "y" => { value: :yes }, "n" => { value: :no } }
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Dotfiler
|
2
|
+
class Symlinker
|
3
|
+
include Dotfiler::Import["fs"]
|
4
|
+
|
5
|
+
def call(source, link_path, options = {})
|
6
|
+
check_paths!(source, link_path.parent_dir)
|
7
|
+
|
8
|
+
options[:force] = true if link_path.symlink?
|
9
|
+
|
10
|
+
fs.symlink(source.to_s, link_path.to_s, options)
|
11
|
+
|
12
|
+
link_path
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def check_paths!(*paths)
|
18
|
+
paths.each do |path|
|
19
|
+
raise Error, "Path '#{path}' does not exist" unless path.exists?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dotfiler
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Aleksandar Radunovic
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-06-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: dry-container
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.6.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.6.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: dry-auto_inject
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.4.6
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.4.6
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: hanami-cli
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.2.0
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.2.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: bundler
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.16'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.16'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '10.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '10.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '3.0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '3.0'
|
97
|
+
description: CLI gem for managing dotfiles
|
98
|
+
email:
|
99
|
+
- aleksandar@radunovic.io
|
100
|
+
executables:
|
101
|
+
- dotfiler
|
102
|
+
extensions: []
|
103
|
+
extra_rdoc_files: []
|
104
|
+
files:
|
105
|
+
- LICENSE.txt
|
106
|
+
- README.md
|
107
|
+
- dotfiler.gemspec
|
108
|
+
- exe/dotfiler
|
109
|
+
- lib/dotfiler.rb
|
110
|
+
- lib/dotfiler/cli/commands.rb
|
111
|
+
- lib/dotfiler/cli/commands/add.rb
|
112
|
+
- lib/dotfiler/cli/commands/backup.rb
|
113
|
+
- lib/dotfiler/cli/commands/command.rb
|
114
|
+
- lib/dotfiler/cli/commands/edit.rb
|
115
|
+
- lib/dotfiler/cli/commands/init.rb
|
116
|
+
- lib/dotfiler/cli/commands/install.rb
|
117
|
+
- lib/dotfiler/cli/commands/list.rb
|
118
|
+
- lib/dotfiler/cli/commands/remove.rb
|
119
|
+
- lib/dotfiler/cli/commands/version.rb
|
120
|
+
- lib/dotfiler/config.rb
|
121
|
+
- lib/dotfiler/container.rb
|
122
|
+
- lib/dotfiler/copier.rb
|
123
|
+
- lib/dotfiler/dotfile.rb
|
124
|
+
- lib/dotfiler/dotfiles.rb
|
125
|
+
- lib/dotfiler/file_system.rb
|
126
|
+
- lib/dotfiler/mover.rb
|
127
|
+
- lib/dotfiler/path.rb
|
128
|
+
- lib/dotfiler/remover.rb
|
129
|
+
- lib/dotfiler/shell.rb
|
130
|
+
- lib/dotfiler/symlinker.rb
|
131
|
+
- lib/dotfiler/version.rb
|
132
|
+
homepage: https://github.com/aradunovic/dotfiler
|
133
|
+
licenses:
|
134
|
+
- MIT
|
135
|
+
metadata: {}
|
136
|
+
post_install_message:
|
137
|
+
rdoc_options: []
|
138
|
+
require_paths:
|
139
|
+
- lib
|
140
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - ">="
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: '2.3'
|
145
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
146
|
+
requirements:
|
147
|
+
- - ">="
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
150
|
+
requirements: []
|
151
|
+
rubyforge_project:
|
152
|
+
rubygems_version: 2.6.8
|
153
|
+
signing_key:
|
154
|
+
specification_version: 4
|
155
|
+
summary: CLI gem for managing dotfiles
|
156
|
+
test_files: []
|