aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--dotfiles/repository.py92
1 files changed, 52 insertions, 40 deletions
diff --git a/dotfiles/repository.py b/dotfiles/repository.py
index a672a52..8c6b727 100644
--- a/dotfiles/repository.py
+++ b/dotfiles/repository.py
@@ -3,8 +3,8 @@ from click import echo
from operator import attrgetter
from .dotfile import Dotfile
-from .exceptions import DotfileException, TargetIgnored, IsDirectory, \
- InRepository
+from .exceptions import DotfileException, TargetIgnored
+from .exceptions import NotRootedInHome, InRepository, IsDirectory
class Repository(object):
@@ -18,79 +18,91 @@ class Repository(object):
homedir = py.path.local('~/', expanduser=True)
ignore = ['.git', '.hg']
- def __init__(self, repodir, homedir=homedir, ignore=ignore):
+ def __init__(self, repodir, homedir=homedir, ignore=ignore, dot=True):
self.repodir = repodir.ensure(dir=1)
self.homedir = homedir
self.ignore = ignore
+ self.dot = dot
def __str__(self):
"""Return human-readable repository contents."""
- return ''.join('%s\n' % item.short_name(self.homedir)
- for item in self.contents()).rstrip()
+ return ''.join('%s\n' % item for item in self.contents()).rstrip()
def __repr__(self):
return '<Repository %r>' % self.repodir
def _target_to_name(self, target):
"""Return the expected symlink for the given repository target."""
- return self.homedir.join(self.repodir.bestrelpath(target))
+ relpath = self.repodir.bestrelpath(target)
+ if self.dot:
+ return self.homedir.join(relpath)
+ else:
+ return self.homedir.join('.%s' % relpath)
def _name_to_target(self, name):
"""Return the expected repository target for the given symlink."""
- return self.repodir.join(self.homedir.bestrelpath(name))
+ relpath = self.homedir.bestrelpath(name)
+ if self.dot:
+ return self.repodir.join(relpath)
+ else:
+ return self.repodir.join(relpath[1:])
def _dotfile(self, name):
"""Return a valid dotfile for the given path."""
-
- # XXX: it must be below the home directory
- # it cannot be contained in the repository
- # it cannot be ignored
- # it must be a file
-
- # if not self.homedir.samefile(name.dirname):
- # raise NotRootedInHome(name)
- # if name.dirname != self.homedir:
- # raise IsNested(name)
- # if name.basename[0] != '.':
- # raise NotADotfile(name)
-
target = self._name_to_target(name)
+
+ if not name.fnmatch('%s/*' % self.homedir):
+ raise NotRootedInHome(name)
+ if name.fnmatch('%s/*' % self.repodir):
+ raise InRepository(name)
if target.basename in self.ignore:
raise TargetIgnored(name)
if name.check(dir=1):
raise IsDirectory(name)
- for path in name.parts():
- try:
- if self.repodir.samefile(path):
- raise InRepository(name)
- except py.error.ENOENT:
- # this occurs when the symlink does not yet exist
- continue
-
return Dotfile(name, target)
+ def _contents(self, dir):
+ def filter(node):
+ return node.check(dir=0) and node.basename not in self.ignore
+
+ def recurse(node):
+ return node.basename not in self.ignore
+
+ return dir.visit(filter, recurse)
+
def dotfiles(self, paths):
- """Return a list of dotfiles given a path."""
- dotfiles = []
- paths = map(py.path.local, paths)
+ paths = list(set(map(py.path.local, paths)))
+
for path in paths:
+ if path.check(dir=1):
+ paths.extend(self._contents(path))
+ paths.remove(path)
+
+ def construct(path):
try:
- dotfiles.append(self._dotfile(path))
+ return self._dotfile(path)
except DotfileException as err:
echo(err)
- return dotfiles
+ return None
+
+ return [d for d in map(construct, paths) if d is not None]
def contents(self):
- """Return a list of all dotfiles in the repository path."""
+ def construct(target):
+ return Dotfile(self._target_to_name(target), target)
+
+ contents = self._contents(self.repodir)
+ return sorted(map(construct, contents), key=attrgetter('name'))
+
+ def prune(self):
+ """Remove any empty directories in the repository."""
def filter(node):
- return node.check(dir=0) and node.basename not in self.ignore
+ return node.check(dir=1) and node.basename not in self.ignore
def recurse(node):
return node.basename not in self.ignore
- def construct(target):
- return Dotfile(self._target_to_name(target), target)
-
- contents = self.repodir.visit(filter, recurse)
- return sorted(map(construct, contents), key=attrgetter('name'))
+ for dir in self.repodir.visit(filter, recurse):
+ if not len(dir.listdir()):
+ dir.remove()