File Coverage

blib/lib/GitHub/MergeVelocity.pm
Criterion Covered Total %
statement 77 90 85.5
branch 7 14 50.0
condition 1 3 33.3
subroutine 21 22 95.4
pod 0 2 0.0
total 106 131 80.9


line stmt bran cond sub pod time code
1             package GitHub::MergeVelocity;
2             $GitHub::MergeVelocity::VERSION = '0.000007';
3 1     1   59903 use strict;
  1         2  
  1         36  
4 1     1   5 use warnings;
  1         1  
  1         30  
5              
6 1     1   466 use CLDR::Number::Format::Percent ();
  1         180252  
  1         27  
7 1     1   491 use File::HomeDir ();
  1         4339  
  1         19  
8 1     1   374 use GitHub::MergeVelocity::Repository ();
  1         3  
  1         37  
9 1     1   7 use Module::Runtime qw( require_module use_module );
  1         2  
  1         8  
10 1     1   60 use Moo 1.007000;
  1         29  
  1         7  
11 1     1   850 use MooX::HandlesVia;
  1         573  
  1         5  
12 1     1   496 use MooX::Options;
  1         929  
  1         10  
13 1     1   244682 use MooX::StrictConstructor;
  1         2  
  1         8  
14 1     1   1255 use Path::Tiny qw( path );
  1         10452  
  1         66  
15 1     1   436 use Pithub::PullRequests ();
  1         72858  
  1         30  
16 1     1   482 use Text::SimpleTable::AutoWidth;
  1         4836  
  1         32  
17 1     1   6 use Types::Standard qw( ArrayRef Bool HashRef InstanceOf Int Str );
  1         2  
  1         17  
18 1     1   1425 use WWW::Mechanize::GZip ();
  1         68959  
  1         773  
19              
20             option debug_useragent => (
21             is => 'ro',
22             isa => Int,
23             format => 'i',
24             default => 0,
25             documentation => 'Print a _lot_ of debugging info about LWP requests',
26             );
27              
28             my $token_help = <<'EOF';
29             https://help.github.com/articles/creating-an-access-token-for-command-line-use for instructions on how to get your own GitHub access token
30             EOF
31              
32             option cache_requests => (
33             is => 'ro',
34             isa => Bool,
35             documentation => 'Try to cache GET requests',
36             );
37              
38             option github_token => (
39             is => 'ro',
40             isa => Str,
41             required => 0,
42             format => 's',
43             documentation => $token_help,
44             );
45              
46             option github_user => (
47             is => 'ro',
48             isa => Str,
49             required => 0,
50             format => 's',
51             documentation => 'The username of your GitHub account',
52             );
53              
54             option url => (
55             is => 'ro',
56             isa => ArrayRef,
57             format => 's@',
58             required => 1,
59             documentation =>
60             'Full Github repo url or shorthand of username/repository. You can pass multiple url args.',
61             );
62              
63             has _report => (
64             is => 'ro',
65             isa => HashRef,
66             handles_via => 'Hash',
67             init_arg => undef,
68             handles => { _repository_for_url => 'get', _report_urls => 'keys', },
69             lazy => 1,
70             builder => '_build_report',
71             );
72              
73             has _github_client => (
74             is => 'ro',
75             isa => InstanceOf ['Pithub::PullRequests'],
76             lazy => 1,
77             builder => '_build_github_client'
78             );
79              
80             has _mech => (
81             is => 'ro',
82             isa => InstanceOf ['LWP::UserAgent'],
83             lazy => 1,
84             builder => '_build_mech',
85             );
86              
87             has _percent_formatter => (
88             is => 'ro',
89             isa => InstanceOf ['CLDR::Number::Format::Percent'],
90             handles => { '_format_percent' => 'format' },
91             lazy => 1,
92             default => sub { CLDR::Number::Format::Percent->new( locale => 'en' ) },
93             );
94              
95             sub _build_github_client {
96 1     1   653 my $self = shift;
97 1 50 33     35 return Pithub::PullRequests->new(
    50          
    50          
98             $self->cache_requests
99             || $self->debug_useragent ? ( ua => $self->_mech ) : (),
100             $self->github_user ? ( user => $self->github_user ) : (),
101             $self->github_token ? ( token => $self->github_token ) : (),
102             );
103             }
104              
105             sub _build_mech {
106 0     0   0 my $self = shift;
107              
108 0         0 my $mech;
109              
110 0 0       0 if ( $self->cache_requests ) {
111 0         0 my $dir = path( File::HomeDir->my_home );
112 0         0 $dir->child('.github-mergevelocity-cache')->mkpath;
113              
114 0         0 require_module('CHI');
115 0         0 $mech = use_module( 'WWW::Mechanize::Cached', 1.45 )->new(
116             cache => CHI->new(
117             driver => 'File',
118             root_dir => $dir->stringify,
119             )
120             );
121             }
122             else {
123 0         0 $mech = WWW::Mechanize::GZip->new;
124             }
125 0 0       0 if ( $self->debug_useragent ) {
126 0         0 use_module( 'LWP::ConsoleLogger::Easy', 0.000013 );
127 0         0 LWP::ConsoleLogger::Easy::debug_ua( $mech, $self->debug_useragent );
128             }
129 0         0 return $mech;
130             }
131              
132             sub _build_report {
133 1     1   782 my $self = shift;
134              
135 1         3 my %report;
136              
137 1         2 foreach my $url ( @{ $self->url } ) {
  1         8  
138 1         5 my $repo = GitHub::MergeVelocity::Repository->new(
139             github_client => $self->_github_client,
140             url => $url,
141             );
142 1         13382 $report{$url} = $repo;
143             }
144 1         21 return \%report;
145             }
146              
147             # workaround for init_arg being ignored
148             # https://rt.cpan.org/Ticket/Display.html?id=97849
149              
150             sub report {
151 1     1 0 4953 my $self = shift;
152 1         6 return $self->_report;
153             }
154              
155             sub print_report {
156 1     1 0 372 my $self = shift;
157              
158 1         11 my $table = Text::SimpleTable::AutoWidth->new;
159 1         1143 my @cols = (
160             'user', 'repo', 'velocity', 'PRs',
161             'merged', 'merge days', 'closed', 'close days',
162             'open', 'open days',
163             );
164 1         13 $table->captions( \@cols );
165              
166 1         5 my @repos = map { $self->_repository_for_url($_) } $self->_report_urls;
  1         873  
167              
168 1         488 foreach my $repository (
  0         0  
169             sort { $b->report->average_velocity <=> $a->report->average_velocity }
170             @repos
171             )
172             {
173 1         9 my $report = $repository->report;
174 3         173 $table->row(
175             $repository->user,
176             $repository->name,
177             $report->average_velocity,
178             $report->pull_request_count,
179 1         4228 map { $self->_columns_for_state( $report, $_ ) }
180             ( 'merged', 'closed', 'open' ),
181             );
182             }
183              
184 1     1   7 binmode( STDOUT, ':encoding(UTF-8)' );
  1         2  
  1         7  
  1         40  
185 1         1282 print $table->draw;
186 1         1055 return;
187             }
188              
189             sub _columns_for_state {
190 3     3   7 my $self = shift;
191 3         5 my $report = shift;
192 3         5 my $state = shift;
193 3         7 my $age = $state . '_age';
194              
195             return (
196 3 100       30 $report->$state
    100          
197             ? sprintf( '%s (%i)',
198             $self->_format_percent( $report->percentage_in_state($state) ),
199             $report->$state )
200             : 0,
201             $report->$age ? sprintf( '%s/PR (%i)',
202             $report->average_age_for_state($state),
203             $report->$age ) : 0,
204             );
205             }
206              
207             1;
208              
209             =pod
210              
211             =encoding UTF-8
212              
213             =head1 NAME
214              
215             GitHub::MergeVelocity - Determine how quickly your pull request might get merged
216              
217             =head1 VERSION
218              
219             version 0.000007
220              
221             =head1 SYNOPSIS
222              
223             use strict;
224             use warnings;
225              
226             use GitHub::MergeVelocity;
227              
228             my $velocity = GitHub::MergeVelocity->new(
229             url => [
230             'https://github.com/neilbowers/PAUSE-Permissions',
231             'https://github.com/oalders/html-restrict',
232             ]
233             );
234              
235             my $report = $velocity->report;
236              
237             $velocity->print_report; # prints a tabular report
238              
239             =head1 CAVEATS
240              
241             This module cannot (yet) distinguish between pull requests which were closed
242             because they were rejected and pull requests which were closed because the
243             patches were applied outside of GitHub's merge mechanism.
244              
245             =head1 AUTHOR
246              
247             Olaf Alders <olaf@wundercounter.com>
248              
249             =head1 COPYRIGHT AND LICENSE
250              
251             This software is copyright (c) 2015 by Olaf Alders.
252              
253             This is free software; you can redistribute it and/or modify it under
254             the same terms as the Perl 5 programming language system itself.
255              
256             =cut
257              
258             __END__
259              
260             # ABSTRACT: Determine how quickly your pull request might get merged
261