lib/gitdocs/repository/path.rb
# -*- encoding : utf-8 -*-
# Class for executing File and Git operations on a specific path in the
# repository.
module Gitdocs
class Repository
class Path
attr_reader :relative_path
# @param [Gitdocs::Repository] repository
# @param [String] relative_path
def initialize(repository, relative_path)
@repository = repository
@relative_path = relative_path.gsub(%r{^/}, '')
@absolute_path = File.join(
File.absolute_path(@repository.root), @relative_path
)
end
# @return [String]
def relative_dirname
result = File.dirname(@relative_path)
return '' if result == '.'
result
end
def join(path_fragment)
@relative_path = File.join(@relative_path, path_fragment)
@absolute_path = File.join(@absolute_path, path_fragment)
end
# Write the content to the path and create any necessary directories.
#
# @param [String] content
# @return [void]
def write(content)
FileUtils.mkdir_p(File.dirname(@absolute_path))
File.open(@absolute_path, 'w') { |f| f.puts(content) }
end
# Touch and path and create any necessary directories.
def touch
FileUtils.mkdir_p(File.dirname(@absolute_path))
FileUtils.touch(@absolute_path)
end
# Create the path as a directory.
def mkdir
FileUtils.mkdir_p(@absolute_path)
end
# Move file to the repository path
# @param [String] filename
def mv(filename)
FileUtils.mkdir_p(File.dirname(@absolute_path))
FileUtils.mv(filename, @absolute_path)
end
# Remove the path, but only if it is a file.
def remove
return nil unless File.file?(@absolute_path)
FileUtils.rm(@absolute_path)
end
# @return [Boolean]
def text?
return false unless File.file?(@absolute_path)
mime_type = File.mime_type?(File.open(@absolute_path))
!!(mime_type =~ %r{text/|x-empty}) # rubocop:disable DoubleNegation
end
# Returns file meta data based on relative file path
#
# @example
# meta
# => { :author => "Nick", :size => 1000, :modified => ... }
#
# @raise [RuntimeError] if the file is not found in any commits
#
# @return [Hash<:author => String, :size => Integer, modified => Time>]
def meta
commit = @repository.last_commit_for(@relative_path)
# FIXME: This should actually just return an empty hash
fail("File #{@relative_path} not found") unless commit
{
author: commit.author[:name],
size: total_size,
modified: commit.author[:time]
}
end
def exist?
File.exist?(@absolute_path)
end
def directory?
File.directory?(@absolute_path)
end
def absolute_path(ref = nil)
return @absolute_path unless ref
blob = @repository.blob_at(@relative_path, ref)
content = blob ? blob.text : ''
tmp_path = File.expand_path(File.basename(@relative_path), Dir.tmpdir)
File.open(tmp_path, 'w') { |f| f.puts content }
tmp_path
end
def readme_path
return nil unless directory?
Dir.glob(File.join(@absolute_path, 'README.{md}')).first
end
DirEntry = Struct.new(:name, :is_directory)
# @return [Array<DirEntry>] entries in the directory
# * excluding any git related directories
# * sorted by filename, ignoring any leading '.'s
def file_listing
return nil unless directory?
Dir
.glob(File.join(@absolute_path, '{*,.*}'))
.reject { |x| x.match(%r{/\.(\.|git|gitignore|gitmessage~)?$}) }
.sort_by { |x| File.basename(x).sub(/^\./, '') }
.map { |x| DirEntry.new(File.basename(x), File.directory?(x)) }
end
def content
return nil unless File.file?(@absolute_path)
File.read(@absolute_path)
end
# Returns the revisions available for a particular file
#
# @param [String] file
#
# @return [Array<Hash>]
def revisions
@repository.commits_for(@relative_path, 100).map do |commit|
{
commit: commit.oid[0, 7],
subject: commit.message.split("\n")[0],
author: commit.author[:name],
date: commit.author[:time]
}
end
end
# Revert file to the specified ref
#
# @param [String] ref
def revert(ref)
return unless ref
blob = @repository.blob_at(@relative_path, ref)
# Silently fail if the file/ref do not existing in the repository.
# Which is consistent with the original behaviour.
# TODO: should consider throwing an exception on this condition
return unless blob
write(blob.text)
end
##########################################################################
private
def total_size
result =
if File.directory?(@absolute_path)
Dir[File.join(@absolute_path, '**', '*')].reduce(0) do |size, filename|
File.symlink?(filename) ? size : size + File.size(filename)
end
else
File.symlink?(@absolute_path) ? 0 : File.size(@absolute_path)
end
# HACK: A value of 0 breaks the table sort for some reason
return -1 if result == 0
result
end
end
end
end