module DotMe

Manage dotfiles.

Constants

HAVE_GIT
HOME
IGNORE
VERSION

Public Instance Methods

dotfiles() click to toggle source
# File Rakefile.rb, line 212
def dotfiles
  files = inclexcl(git(:tracked).split("\0") - IGNORE)
  custom = []

  files.each { |file|
    if File.readable?(mine = "#{file}.mine")
      custom << mine
    end
  }

  files + custom
end
install() click to toggle source

}}}

# File Rakefile.rb, line 107
def install
  symlinks.each { |target, symlink|
    dryrun(:symlink, target, symlink) {
      status, actual = status_for(symlink, target)

      begin
        File.symlink(target, symlink)
        puts "[ADDED] #{symlink} -> #{target}"
      rescue Errno::EEXIST => err
        warn "[ERROR=#{status}] #{err}"
      end
    }
  }
end
pristine() click to toggle source
# File Rakefile.rb, line 186
def pristine
  abort unless agreed?('This will undo all your local modifications AND remove all untracked files.')
  reset(false)

  git(:untracked).split("\0").sort.each { |file|
    dryrun(:rm_r, file) {
      FileUtils.rm_r(file)
      puts "[REMOVED] #{file}"
    }
  }
end
reset(ask = true) click to toggle source
# File Rakefile.rb, line 181
def reset(ask = true)
  abort unless agreed?('This will undo all your local modifications (untracked files are kept).') if ask
  git(:reset)
end
status() click to toggle source
# File Rakefile.rb, line 133
def status
  puts git(:status)
  puts

  symlinks.each { |target, symlink|
    status, actual = status_for(symlink, target)
    msg = "[#{status}] #{symlink}"

    case status
      when :MISMATCH
        msg << ':' << "\n  expected: #{target}" <<
                      "\n  got:      #{actual}"
      else
        msg << " -> #{target}"
    end

    puts msg
  }
end
uninstall() click to toggle source
# File Rakefile.rb, line 153
def uninstall
  symlinks.each { |target, symlink|
    dryrun(:unlink, symlink) {
      status, actual = status_for(symlink, target)

      case status
        when :UNTRACKED
          warn "[ERROR=#{status}] Not removing '#{symlink}' - NOT A SYMLINK"
        when :NOT_FOUND
          warn "[ERROR=#{status}] Not removing '#{symlink}' - NOT FOUND"
        when :MISMATCH
          warn "[ERROR=#{status}] Not removing symlink '#{symlink}' - targets don't match:"
          warn "  expected: #{target}"
          warn "  got:      #{actual}"
        when :TRACKED
          begin
            File.unlink(symlink)
            puts "[REMOVED] #{symlink} -> #{target}"
          rescue Errno::ENOENT => err
            warn "[ERROR] #{err}"
          end
        else
          warn "[ERROR=#{status}] #{symlink}"
      end
    }
  }
end
update() click to toggle source
# File Rakefile.rb, line 122
def update
  with_clean_working_directory do
    if out = git(:update) and git(:fetch)
      out.lines.grep(/^Updating\s+(\S+)/) {
        puts git(:log, '--oneline', '--reverse', $1) }
    end
  end

  install
end

Private Instance Methods

agree(msg) click to toggle source
# File Rakefile.rb, line 335
def agree(msg)
  print "#{msg} (yes/no) "

  case $stdin.gets.chomp[0].downcase
    when 'y' then true
    when 'n' then false
    else
      puts 'Please enter "[y]es" or "[n]o".'
      agree(msg)
  end
rescue Interrupt
  abort ''
end
agreed?(msg) click to toggle source
# File Rakefile.rb, line 349
def agreed?(msg)
  puts msg
  agree('Are you sure you want that?') && agree('Are you REALLY sure?')
end
dryrun(what, *args) { || ... } click to toggle source
# File Rakefile.rb, line 354
def dryrun(what, *args)
  if ENV['DRYRUN']
    _return = args.last.delete(:_return) if args.last.is_a?(Hash)
    warn ['[DRYRUN]', what, *args].join(' ')
    _return
  else
    yield
  end
end
git(cmd, *args) click to toggle source
# File Rakefile.rb, line 255
def git(cmd, *args)
  abort "Please install `git'." unless HAVE_GIT

  # aliases
  case cmd
    when :update
      cmd = 'pull'
      args << 'origin' << 'master'
    when :reset
      cmd = 'checkout'
      args << '-f'
    when :stash
      cmd = 'stash'
      args << 'save'
    when :unstash
      cmd = 'stash'
      args << 'pop'
    when :tracked
      cmd = 'ls-files'
      args << '-z'
      _return = %w[foo/x foo/y bar/y bar/z/a bar/z/b].join("\0")
    when :untracked
      cmd = 'ls-files'
      args << '-z' << '-o' << '--directory'
      _return = %w[foo/x.mine bar/y.mine blah].join("\0")
  end

  dryrun(:git, cmd, *args + [:_return => _return]) {
    %x{git #{[cmd, *args].join(' ')}}
  }
end
glob2re(pattern) click to toggle source
# File Rakefile.rb, line 323
def glob2re(pattern)
  %r{
    \A
    #{
      pattern.
        gsub(/\?/, '.').
        gsub(/\*/, '.*?')
    }
    (?:/|\z)
  }x
end
inclexcl(files) click to toggle source
# File Rakefile.rb, line 287
def inclexcl(files)
  if File.readable?(inclexcl = 'inclexcl')
    incl, excl, replace = [], [], false

    File.foreach(inclexcl) { |pattern|
      pattern.sub!(/\A\s*/, '')
      pattern.sub!(/\s*\z/, '')
      next if pattern.empty?

      pattern.sub!(/\A([#+-])/, '')
      prefix = $1 # save capture
      next if prefix == '#'

      pattern.sub!(/\/\z/, '')
      if pattern.count('/') > 1
        warn "[ERROR] Illegal pattern - #{pattern}"
        next
      end

      matches = files.grep(glob2re(pattern))

      if prefix == '-'
        excl += matches
      else
        replace = true
        incl += matches
      end
    }

    files  = incl if replace
    files -= excl
  end

  files.uniq
end
status_for(symlink, target) click to toggle source

{{{ Utility methods

# File Rakefile.rb, line 229
def status_for(symlink, target)
  if File.symlink?(symlink)
    if File.exists?(target)
      actual = Pathname.new(File.readlink(symlink)).realpath.to_s
      status = target == actual ? :TRACKED : :MISMATCH
    else
      status = :MISSING
    end
  else
    if File.exists?(symlink)
      status = File.exists?(target) ? :ALIEN : :UNTRACKED
    else
      status = File.exists?(target) ? :NOT_FOUND : :UNTRACKED
    end
  end

  [Status[status], actual]
end
with_clean_working_directory() { || ... } click to toggle source
# File Rakefile.rb, line 248
def with_clean_working_directory
  git(:stash)
  yield
ensure
  git(:unstash)
end