aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--dotfiles/dotfile.py70
1 files changed, 32 insertions, 38 deletions
diff --git a/dotfiles/dotfile.py b/dotfiles/dotfile.py
index 1a86f63..ddb2bb2 100644
--- a/dotfiles/dotfile.py
+++ b/dotfiles/dotfile.py
@@ -1,47 +1,63 @@
import errno
+def unique_suffix(path_a, path_b):
+ discard = len(str(path_a.common(path_b))) + 1
+ return (str(path_a)[discard:], str(path_b)[discard:])
+
+
class Dotfile(object):
"""
This class implements the 'dotfile' abstraction.
A dotfile has two primary attributes:
- name -- name of dotfile in the home directory (~/.vimrc)
- target -- target the dotfile should point to (~/Dotfiles/vimrc)
+ name -- name of symlink in the home directory (~/.vimrc)
+ target -- where the symlink should point to (~/Dotfiles/vimrc)
The above attributes are both py.path.local objects.
The goal is for there to be no special logic or stored global state. Only
the implementation of three operations made available to the caller:
- add -- moves a dotfile into the repository and replaces it with a symlink
+ add -- move a dotfile into the repository and replace it with a symlink
remove -- the opposite of add
- sync -- ensure that each repository file has a corresponding symlink
+ sync -- ensure that each repository file has a corresponding symlink
+ unsync -- remove the symlink leaving only the repository file
This is where most filesystem operations (link, delete, etc) should be
called, and not in the layers above.
"""
- states = {
- 'error': {'text': '(error)', 'color': 'red'},
- 'missing': {'text': '(missing)', 'color': 'yellow'},
- 'conflict': {'text': '(conflict)', 'color': 'yellow'},
- 'ok': {'text': '(ok)', 'color': 'green'},
- }
-
def __init__(self, name, target):
self.name = name
self.target = target
- self._set_state()
def __str__(self):
- short_name, _ = self._truncate_paths()
- return '%-18s %-s' % (short_name, self.state['text'])
+ short_name, _ = unique_suffix(self.name, self.target)
+ return '%s' % short_name
def __repr__(self):
return '<Dotfile %r>' % self.name
+ @property
+ def state(self):
+
+ # lets be optimistic
+ state = 'ok'
+
+ if self.target.check(exists=0):
+ # only for testing, cli should never reach this state
+ state = 'error'
+ elif self.name.check(exists=0):
+ # no $HOME symlink
+ state = 'missing'
+ elif self.name.check(link=0) or not self.name.samefile(self.target):
+ # if name exists but isn't a link to the target
+ state = 'conflict'
+
+ return state
+
def add(self):
if self.target.check(exists=1):
raise OSError(errno.EEXIST, self.target)
@@ -57,27 +73,5 @@ class Dotfile(object):
def sync(self):
self.name.mksymlinkto(self.target)
- def is_ok(self):
- return self.state == self.states['ok']
-
- def _set_state(self):
-
- # only for testing, cli should never reach this state
- if self.target.check(exists=0):
- self.state = self.states['error']
-
- # no $HOME symlink
- elif self.name.check(exists=0):
- self.state = self.states['missing']
-
- # if name exists but isn't a link to the target
- elif self.name.check(link=0) or not self.name.samefile(self.target):
- self.state = self.states['conflict']
-
- # all good
- else:
- self.state = self.states['ok']
-
- def _truncate_paths(self):
- discard = len(str(self.name.common(self.target))) + 1
- return (str(self.name)[discard:], str(self.target)[discard:])
+ def unsync(self):
+ self.name.remove()