From: Zachary Stevens Date: Sun, 1 Nov 2009 01:30:42 +0000 (+0000) Subject: Start work splitting out Gitalist::Model::Git into smaller pieces. X-Git-Tag: 0.000000_01~100^2~15^2~13 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=catagits%2FGitalist.git;a=commitdiff_plain;h=7e7f933555dfa0aebb2f972e466a2d22aef1b86d Start work splitting out Gitalist::Model::Git into smaller pieces. --- diff --git a/.gitignore b/.gitignore index acaa20e..2c92d23 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ pm_to_blib MANIFEST MANIFEST.bak *~ +.#* +*# diff --git a/lib/Gitalist/Git/Project.pm b/lib/Gitalist/Git/Project.pm new file mode 100644 index 0000000..5465177 --- /dev/null +++ b/lib/Gitalist/Git/Project.pm @@ -0,0 +1,127 @@ +use MooseX::Declare; + +class Gitalist::Git::Project { + # FIXME, use Types::Path::Class and coerce + use MooseX::Types::Common::String qw/NonEmptySimpleStr/; + use DateTime; + use Path::Class; + + has name => ( isa => NonEmptySimpleStr, + is => 'ro' ); + has path => ( isa => "Path::Class::Dir", + is => 'ro'); + + has description => ( isa => NonEmptySimpleStr, + is => 'ro', + lazy_build => 1, + ); + has owner => ( isa => NonEmptySimpleStr, + is => 'ro', + lazy_build => 1, + ); + has last_change => ( isa => 'DateTime', + is => 'ro', + lazy_build => 1, + ); + + + method _build_description { + my $description = $self->path->file('description')->slurp; + chomp $description; + return $description; + } + + method _build_owner { + my $owner = (getpwuid $self->path->stat->uid)[6]; + $owner =~ s/,+$//; + return $owner; + } + + method _build_last_change { + my $last_change; + my $output = $self->run_cmd( + qw{ for-each-ref --format=%(committer) + --sort=-committerdate --count=1 refs/heads + }); + if (my ($epoch, $tz) = $output =~ /\s(\d+)\s+([+-]\d+)$/) { + my $dt = DateTime->from_epoch(epoch => $epoch); + $dt->set_time_zone($tz); + $last_change = $dt; + } + return $last_change; + } + + +=head2 run_cmd + +Call out to the C binary and return a string consisting of the output. + +=cut + + method run_cmd (@args) { + unshift @args, ( '--git-dir' => $self->path ); + print STDERR 'RUNNING: ', $self->_git, qq[ @args], $/; + + open my $fh, '-|', $self->_git, @args + or die "failed to run git command"; + binmode $fh, ':encoding(UTF-8)'; + + my $output = do { local $/ = undef; <$fh> }; + close $fh; + + return $output; + } + + has _git => ( isa => NonEmptySimpleStr, is => 'ro', lazy_build => 1 ); + use File::Which; + method _build__git { + my $git = File::Which::which('git'); + + if (!$git) { + die < ( isa => 'Git::PurePerl', is => 'rw', lazy_build => 1 ); + use Git::PurePerl; + method _build__gpp { + my $gpp = Git::PurePerl->new(gitdir => $self->path); + return $gpp; + } + + method project_dir (Path::Class::Dir $project) { + my $dir = $project->stringify; + $dir .= '/.git' + if -f dir($dir)->file('.git/HEAD'); + return $dir; + } + + + # Compatibility + +=head2 project_info + +Returns a hash containing properties of this project. The keys will +be: + + name + description (empty if .git/description is empty/unnamed) + owner + last_change + +=cut + + method project_info { + return { + name => $self->name, + description => $self->description, + owner => $self->owner, + last_change => $self->last_change, + }; + }; + +} # end class diff --git a/lib/Gitalist/Git/Repo.pm b/lib/Gitalist/Git/Repo.pm new file mode 100644 index 0000000..2d4c6f5 --- /dev/null +++ b/lib/Gitalist/Git/Repo.pm @@ -0,0 +1,91 @@ +use MooseX::Declare; + +class Gitalist::Git::Repo { + use MooseX::Types::Common::String qw/NonEmptySimpleStr/; + use Path::Class; + use Gitalist::Git::Project; + has repo_dir => ( isa => NonEmptySimpleStr, + is => 'ro', + lazy_build => 1 ); + + method _build_repo_dir { + return Gitalist->config->{repo_dir}; + } + +=head2 _is_git_repo + +Determine whether a given directory (as a L object) is a +C repo. + +=cut + + method _is_git_repo ($dir) { + return -f $dir->file('HEAD') || -f $dir->file('.git/HEAD'); + } + +=head2 project_dir + +The directory under which the given project will reside i.e C<.git/..> + +=cut + + method project_dir ($project) { + my $dir = blessed($project) && $project->isa('Path::Class::Dir') + ? $project->stringify + : $self->dir_from_project_name($project); + + $dir .= '/.git' + if -f dir($dir)->file('.git/HEAD'); + + return $dir; + } + +=head2 list_projects + +For the C specified in the config return an array of projects where +each item will contain the contents of L. + +=cut + + method list_projects { + my $base = dir($self->repo_dir); + my @ret; + my $dh = $base->open || die "Could not open $base"; + while (my $file = $dh->read) { + next if $file =~ /^.{1,2}$/; + + my $obj = $base->subdir($file); + next unless -d $obj; + next unless $self->_is_git_repo($obj); + + # XXX Leaky abstraction alert! + my $is_bare = !-d $obj->subdir('.git'); + + my $name = (File::Spec->splitdir($obj))[-1]; + push @ret, Gitalist::Git::Project->new( name => $name, + path => $obj, + ); + #push @ret, { + # name => ($name . ( $is_bare ? '' : '/.git' )), + # $self->get_project_properties( + # $is_bare ? $obj : $obj->subdir('.git') + # ), + #}; + } + + return [sort { $a->{name} cmp $b->{name} } @ret]; + } + +=head2 dir_from_project_name + +Get the corresponding directory of a given project. + +=cut + + method dir_from_project_name (Str $project) { + return dir($self->repo_dir)->subdir($project); + } + + + +} # end class diff --git a/lib/Gitalist/Git/Util.pm b/lib/Gitalist/Git/Util.pm new file mode 100644 index 0000000..8f7dd0f --- /dev/null +++ b/lib/Gitalist/Git/Util.pm @@ -0,0 +1,25 @@ +use MooseX::Declare; + +class Gitalist::Git::Util { + has git => ( isa => NonEmptySimpleStr, is => 'ro', lazy_build => 1 ); + sub _build_git { + my $git = File::Which::which('git'); + + if (!$git) { + die <new( + path => dir("$Bin/../lib/repositories/repo1"), + name => "repo1", +); +isa_ok($proj, 'Gitalist::Git::Project'); + +like( $proj->_git, qr#/git$#, 'git binary found'); +isa_ok($proj->_gpp, 'Git::PurePerl', 'gpp instance created'); +like($proj->path, qr#/repositories/repo1#, 'repository path is set'); +is($proj->name, qw/repo1/, 'repository name is set'); +is($proj->description, qq/some test repository/, 'repository description loaded'); +isa_ok($proj->last_change, 'DateTime', 'last_change'); + + diff --git a/t/git/repo.t b/t/git/repo.t new file mode 100644 index 0000000..155827b --- /dev/null +++ b/t/git/repo.t @@ -0,0 +1,17 @@ +use strict; +use warnings; +use FindBin qw/$Bin/; +use Test::More qw/no_plan/; + +use Data::Dumper; + +BEGIN { use_ok 'Gitalist::Git::Repo' } + +my $repo = Gitalist::Git::Repo->new( repo_dir => "$Bin/../lib/repositories" ); +isa_ok($repo, 'Gitalist::Git::Repo'); + +is($repo->repo_dir, "$Bin/../lib/repositories", "repo->repo_dir is correct" ); + +my $project_list = $repo->list_projects; +warn(Dumper($project_list)); +isa_ok(@$project_list[0], 'Gitalist::Git::Project');