dotfiler 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,3 @@
1
+ module Dotfiler
2
+ VERSION = "0.1.0"
3
+ 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: []