GitHub-RSS
view release on metacpan or search on metacpan
lib/GitHub/RSS.pm view on Meta::CPAN
=head1 NAME
GitHub::RSS - collect data from Github.com for feeding into RSS
=head1 SYNOPSIS
my $gh = GitHub::RSS->new(
dbh => {
dsn => "dbi:SQLite:dbname=$store",
},
);
my $last_updated = $gh->last_check;
$gh->fetch_and_store( $github_user => $github_repo, $last_updated );
if( $verbose ) {
print "Updated from $last_updated to " . $gh->last_check, "\n";
};
=head1 DESCRIPTION
This module provides a cache database for GitHub issues and scripts to
periodically update the database from GitHub.
This is mainly used for creating an RSS feed from the database, hence the
name.
=head1 METHODS
=head2 C<< ->new >>
my $gh = GitHub::RSS->new(
dbh => {
dsn => 'dbi:SQLite:dbname=db/issues.sqlite',
},
);
Constructs a new GitHub::RSS instance
=over 4
=item *
B<gh> - instance of L<Net::GitHub>
=cut
has 'gh' => (
is => 'ro',
default => sub( $self ) {
Net::GitHub->new(
maybe access_token => $self->token
),
},
);
=item *
B<token_file> - name and path of the JSON-format token file containing the
GitHub API token By default, that file is searched for under the name
C<github.credentials> in C<.>, C<$ENV{XDG_DATA_HOME}>, C<$ENV{USERPROFILE}>
and C<$ENV{HOME}>.
=cut
has 'token_file' => (
is => 'lazy',
default => \&_find_gh_token_file,
);
=item *
B<token> - GitHub API token. If this is missing, it will be attempted to read
it from the C<token_file>.
=cut
has 'token' => (
is => 'lazy',
default => \&_read_gh_token,
);
=item *
B<default_user> - name of the GitHub user whose repos will be read
=cut
has default_user => (
is => 'ro',
);
=item *
B<default_repo> - name of the GitHub repo whose issues will be read
=cut
has default_repo => (
is => 'ro',
);
=item *
B<dbh> - premade database handle or alternatively a hashref containing
the L<DBI> arguments
dbh => $dbh,
or alternatively
dbh => {
user => 'scott',
password => 'tiger',
dsn => 'dbi:SQLite:dbname=db/issues.sqlite',
}
=cut
has dbh => (
is => 'ro',
required => 1,
coerce => \&_build_dbh,
);
sub _build_dbh( $args ) {
return $args if ref($args) eq 'DBI::db';
ref($args) eq 'HASH' or die 'Not a DB handle nor a hashref';
return DBI->connect( @{$args}{qw/dsn user password options/} );
}
=item *
B<fetch_additional_pages> - number of additional pages to fetch from GitHub.
This is relevant when catching up a database for a repository with many issues.
=back
=cut
has fetch_additional_pages => (
is => 'ro',
default => '1',
);
sub _find_gh_token_file( $self, $env=undef ) {
$env //= \%ENV;
my $token_file;
# This should use File::User
for my $candidate_dir ('.',
$ENV{XDG_DATA_HOME},
$ENV{USERPROFILE},
$ENV{HOME}
) {
next unless defined $candidate_dir;
if( -f "$candidate_dir/github.credentials" ) {
$token_file = "$candidate_dir/github.credentials";
last
};
};
return $token_file
}
sub _read_gh_token( $self, $token_file=undef ) {
my $file = $token_file // $self->token_file;
if( $file ) {
open my $fh, '<', $file
or die "Couldn't open file '$file': $!";
binmode $fh;
local $/;
my $json = <$fh>;
my $token_json = decode_json( $json );
return $token_json->{token};
} else {
# We'll run without a known account
return
}
}
sub fetch_all_issues( $self,
$user = $self->default_user,
$repo = $self->default_repo,
$since=undef ) {
my @issues = $self->fetch_issues( $user, $repo, $since );
my $gh = $self->gh;
while ($gh->issue->has_next_page) {
push @issues, $gh->issue->next_page;
}
@issues
}
sub fetch_issues( $self,
$user = $self->default_user,
$repo = $self->default_repo,
$since=undef ) {
my $gh = $self->gh;
my @issues = $gh->issue->repos_issues($user => $repo,
{ sort => 'updated',
direction => 'asc', # so we can interrupt any time
state => 'all', # so we find issues that got closed
maybe since => $since,
}
);
};
=head2 C<< ->fetch_issue_comments >>
=cut
sub fetch_issue_comments( $self, $issue_number,
$user=$self->default_user,
$repo=$self->default_repo
) {
# Shouldn't this loop as well, just like with the issues?!
return $self->gh->issue->comments($user, $repo, $issue_number );
( run in 1.881 second using v1.01-cache-2.11-cpan-cdf2f3d4e48 )