Start work splitting out Gitalist::Model::Git into smaller pieces.
Zachary Stevens [Sun, 1 Nov 2009 01:30:42 +0000 (01:30 +0000)]
.gitignore
lib/Gitalist/Git/Project.pm [new file with mode: 0644]
lib/Gitalist/Git/Repo.pm [new file with mode: 0644]
lib/Gitalist/Git/Util.pm [new file with mode: 0644]
t/git/project.t [new file with mode: 0644]
t/git/repo.t [new file with mode: 0644]

index acaa20e..2c92d23 100644 (file)
@@ -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 (file)
index 0000000..5465177
--- /dev/null
@@ -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<git> 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 <<EOR;
+Could not find a git executable.
+Please specify the which git executable to use in gitweb.yml
+EOR
+        }
+
+        return $git;
+    }
+    has _gpp      => ( 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 (file)
index 0000000..2d4c6f5
--- /dev/null
@@ -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<Path::Class::Dir> object) is a
+C<git> 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<repo_dir> specified in the config return an array of projects where
+each item will contain the contents of L</project_info>.
+
+=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 (file)
index 0000000..8f7dd0f
--- /dev/null
@@ -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 <<EOR;
+Could not find a git executable.
+Please specify the which git executable to use in gitweb.yml
+EOR
+        }
+
+        return $git;
+    }
+
+    
+
+
+
+
+
+#
+} # end class
diff --git a/t/git/project.t b/t/git/project.t
new file mode 100644 (file)
index 0000000..ccb0913
--- /dev/null
@@ -0,0 +1,24 @@
+use strict;
+use warnings;
+use FindBin qw/$Bin/;
+use Test::More qw/no_plan/;
+
+use Data::Dumper;
+
+BEGIN { use_ok 'Gitalist::Git::Project' }
+
+use Path::Class;
+my $proj = Gitalist::Git::Project->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 (file)
index 0000000..155827b
--- /dev/null
@@ -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');