File Coverage

blib/lib/App/Critique/Session.pm
Criterion Covered Total %
statement 30 136 22.0
branch 0 48 0.0
condition 0 18 0.0
subroutine 10 41 24.3
pod 0 26 0.0
total 40 269 14.8


line stmt bran cond sub pod time code
1             package App::Critique::Session;
2              
3 2     2   5135 use strict;
  2         6  
  2         79  
4 2     2   18 use warnings;
  2         7  
  2         137  
5              
6             our $VERSION = '0.05';
7             our $AUTHORITY = 'cpan:STEVAN';
8              
9 2     2   15 use Scalar::Util ();
  2         8  
  2         59  
10 2     2   12 use Carp ();
  2         5  
  2         51  
11              
12 2     2   12 use Path::Tiny ();
  2         6  
  2         39  
13              
14 2     2   499 use Git::Wrapper ();
  2         32356  
  2         43  
15 2     2   1184 use Perl::Critic ();
  2         1480561  
  2         46  
16 2     2   14 use Perl::Critic::Utils ();
  2         4  
  2         43  
17              
18 2     2   9 use App::Critique;
  2         4  
  2         26  
19 2     2   1302 use App::Critique::Session::File;
  2         6  
  2         2643  
20              
21             sub new {
22 0     0 0   my ($class, %args) = @_;
23              
24             Carp::confess('You must specify a git_work_tree')
25 0 0 0       unless $args{git_work_tree} && -d $args{git_work_tree};
26              
27             # setup the perl critic instance
28 0           my $critic = $class->_initialize_perl_critic( %args );
29              
30             # auto-discover the current git repo and branch
31 0           my ($git, $git_branch, $git_head_sha) = $class->_initialize_git_repo( %args );
32              
33             # initialize all the work tree related info ...
34 0           my ($git_work_tree, $git_work_tree_root) = $class->_initialize_git_work_tree( $git, %args );
35              
36             # now that we have worked out all the details,
37             # we need to determine the path to the actual
38             # critique file.
39 0           my $path = $class->_generate_critique_file_path( $git_work_tree_root, $git_branch );
40              
41             # inflate this if we have it ...
42             $args{perl_critic_profile} = Path::Tiny::path( $args{perl_critic_profile} )
43 0 0         if $args{perl_critic_profile};
44              
45             my $self = bless {
46             # user supplied ...
47             perl_critic_profile => $args{perl_critic_profile},
48             perl_critic_theme => $args{perl_critic_theme},
49             perl_critic_policy => $args{perl_critic_policy},
50 0           git_work_tree => Path::Tiny::path( $git_work_tree ),
51              
52             # auto-discovered
53             git_work_tree_root => Path::Tiny::path( $git_work_tree_root ),
54             git_branch => $git_branch,
55             git_head_sha => $git_head_sha,
56              
57             # local storage
58             current_file_idx => 0,
59             tracked_files => [],
60             file_criteria => {},
61              
62             # Do Not Serialize
63             _path => $path,
64             _critic => $critic,
65             _git => $git,
66             } => $class;
67              
68             # handle adding tracked files
69 0           $self->set_tracked_files( @{ $args{tracked_files} } )
70 0 0         if exists $args{tracked_files};
71              
72             $self->set_file_criteria( $args{file_criteria} )
73 0 0         if exists $args{file_criteria};
74              
75             $self->{current_file_idx} = $args{current_file_idx}
76 0 0         if exists $args{current_file_idx};
77              
78 0           return $self;
79             }
80              
81             sub locate_session_file {
82 0     0 0   my ($class, $git_work_tree) = @_;
83              
84 0 0         Carp::confess('Cannot call locate_session_file with an instance')
85             if Scalar::Util::blessed( $class );
86              
87 0 0 0       Carp::confess('You must specify a git-work-tree')
88             unless $git_work_tree && -d $git_work_tree;
89              
90 0           my %args = (git_work_tree => $git_work_tree);
91 0           my ($git, $git_branch) = $class->_initialize_git_repo( %args );
92 0           my (undef, $git_work_tree_root) = $class->_initialize_git_work_tree( $git, %args );
93              
94 0           my $session_file = $class->_generate_critique_file_path(
95             $git_work_tree_root,
96             $git_branch
97             );
98              
99 0           return $session_file;
100             }
101              
102             # accessors
103              
104 0     0 0   sub git_work_tree { $_[0]->{git_work_tree} }
105 0     0 0   sub git_work_tree_root { $_[0]->{git_work_tree_root} }
106 0     0 0   sub git_branch { $_[0]->{git_branch} }
107 0     0 0   sub git_head_sha { $_[0]->{git_head_sha} }
108 0     0 0   sub perl_critic_profile { $_[0]->{perl_critic_profile} }
109 0     0 0   sub perl_critic_theme { $_[0]->{perl_critic_theme} }
110 0     0 0   sub perl_critic_policy { $_[0]->{perl_critic_policy} }
111              
112 0     0 0   sub tracked_files { @{ $_[0]->{tracked_files} } }
  0            
113 0     0 0   sub file_criteria { $_[0]->{file_criteria} }
114              
115 0     0 0   sub current_file_idx { $_[0]->{current_file_idx} }
116 0     0 0   sub inc_file_idx { $_[0]->{current_file_idx}++ }
117 0     0 0   sub dec_file_idx { $_[0]->{current_file_idx}-- }
118 0     0 0   sub reset_file_idx { $_[0]->{current_file_idx}=0 }
119 0     0 0   sub set_file_idx { $_[0]->{current_file_idx}=$_[1] }
120              
121 0     0 0   sub session_file_path { $_[0]->{_path} }
122 0     0 0   sub git_wrapper { $_[0]->{_git} }
123 0     0 0   sub perl_critic { $_[0]->{_critic} }
124              
125             # Instance Methods
126              
127             sub session_file_exists {
128 0     0 0   my ($self) = @_;
129 0           return !! -e $self->{_path};
130             }
131              
132             sub set_tracked_files {
133 0     0 0   my ($self, @files) = @_;
134 0           @{ $self->{tracked_files} } = map {
135 0 0 0       (Scalar::Util::blessed($_) && $_->isa('App::Critique::Session::File')
  0 0          
136             ? $_
137             : ((ref $_ eq 'HASH')
138             ? App::Critique::Session::File->new( %$_ )
139             : App::Critique::Session::File->new( path => $_ )))
140             } @files;
141             }
142              
143             sub set_file_criteria {
144 0     0 0   my ($self, $filters_used) = @_;
145             $self->{file_criteria}->{ $_ } = $filters_used->{ $_ }
146 0           foreach keys %$filters_used;
147             }
148              
149             # ...
150              
151             sub pack {
152 0     0 0   my ($self) = @_;
153             return +{
154             perl_critic_profile => ($self->{perl_critic_profile} ? $self->{perl_critic_profile}->stringify : undef),
155             perl_critic_theme => $self->{perl_critic_theme},
156             perl_critic_policy => $self->{perl_critic_policy},
157              
158             git_work_tree => ($self->{git_work_tree} ? $self->{git_work_tree}->stringify : undef),
159             git_branch => $self->{git_branch},
160             git_head_sha => $self->{git_head_sha},
161              
162             current_file_idx => $self->{current_file_idx},
163 0           tracked_files => [ map $_->pack, @{ $self->{tracked_files} } ],
164             file_criteria => $self->{file_criteria}
165 0 0         };
    0          
166             }
167              
168             sub unpack {
169 0     0 0   my ($class, $data) = @_;
170 0           return $class->new( %$data );
171             }
172              
173             # ...
174              
175             sub load {
176 0     0 0   my ($class, $path) = @_;
177              
178 0 0 0       Carp::confess('Invalid path: ' . $path)
179             unless $path->exists && $path->is_file;
180              
181 0           my $file = Path::Tiny::path( $path );
182 0           my $json = $file->slurp;
183 0           my $data = $App::Critique::JSON->decode( $json );
184              
185 0           return $class->unpack( $data );
186             }
187              
188             sub store {
189 0     0 0   my ($self) = @_;
190              
191 0           my $file = $self->{_path};
192 0           my $data = $self->pack;
193              
194             eval {
195             # JSON might die here ...
196 0           my $json = $App::Critique::JSON->encode( $data );
197              
198             # if the file does not exist
199             # then we should try and make
200             # the path, just in case ...
201 0 0         $file->parent->mkpath unless -e $file;
202              
203             # now try and write out the JSON
204 0           my $fh = $file->openw;
205 0           $fh->print( $json );
206 0           $fh->close;
207              
208 0           1;
209 0 0         } or do {
210 0           Carp::confess('Unable to store critique session file because: ' . $@);
211             };
212             }
213              
214             # ...
215              
216             sub _generate_critique_dir_path {
217 0     0     my ($class, $git_work_tree, $git_branch) = @_;
218              
219 0           my $root = Path::Tiny::path( $App::Critique::CONFIG{'HOME'} );
220 0           my $git = Path::Tiny::path( $git_work_tree );
221              
222             # ~/.critique/<git-repo-name>/<git-branch-name>/session.json
223              
224 0           $root->child( $App::Critique::CONFIG{'DATA_DIR'} )
225             ->child( $git->basename )
226             ->child( $git_branch );
227             }
228              
229             sub _generate_critique_file_path {
230 0     0     my ($class, $git_work_tree, $git_branch) = @_;
231             $class->_generate_critique_dir_path(
232             $git_work_tree,
233             $git_branch
234             )->child(
235 0           $App::Critique::CONFIG{'DATA_FILE'}
236             );
237             }
238              
239             ## ...
240              
241             sub _initialize_git_repo {
242 0     0     my ($class, %args) = @_;
243              
244 0           my $git = Git::Wrapper->new( $args{git_work_tree} );
245              
246             # auto-discover/validate the current git branch
247 0           my ($git_branch) = map /^\*\s(.*)$/, grep /^\*/, $git->branch;
248              
249 0 0         Carp::confess('Unable to determine git branch, looks like your repository is bare')
250             unless $git_branch;
251              
252             # make sure the branch we are on is the
253             # same one we are being asked to load,
254             # this error condition is very unlikely
255             # to occur since the session file path
256             # is based on branch, which is dynamically
257             # determined on load. The only way this
258             # could happen is if you manually loaded
259             # the session file for one branch while
260             # intentionally on another branch. So while
261             # this is unlikely, it is probably something
262             # we should die about none the less since
263             # it might be a real pain to debug.
264             Carp::confess('Attempting to inflate session for branch ('.$args{git_branch}.') but branch ('.$git_branch.') is currently active')
265 0 0 0       if exists $args{git_branch} && $args{git_branch} ne $git_branch;
266              
267             # auto-discover/validate the git HEAD sha
268 0           my $git_head_sha = $args{git_head_sha};
269              
270             # if we have it already, ...
271 0 0         if ( $git_head_sha ) {
272             # test to make sure the SHA is an ancestor
273              
274 0           my ($possible_branch) = map /^\*\s(.*)$/, grep /^\*/, $git->branch({
275             contains => $git_head_sha
276             });
277              
278 0 0 0       Carp::confess('The git HEAD sha ('.$git_head_sha.') is not contained within this git branch('.$git_branch.'), something has gone wrong')
279             if defined $possible_branch && $possible_branch ne $git_branch;
280             }
281             else {
282             # auto-discover the git SHA
283 0           ($git_head_sha) = $git->rev_parse('HEAD');
284              
285 0 0         Carp::confess('Unable to determine the SHA of the HEAD, either your repository has no commits or perhaps is bare, either way, we can not work with it')
286             unless $git_head_sha;
287             }
288              
289             # if all is well, return ...
290 0           return ($git, $git_branch, $git_head_sha);
291             }
292              
293             sub _initialize_git_work_tree {
294 0     0     my ($class, $git, %args) = @_;
295              
296 0           my $git_work_tree = Path::Tiny::path( $args{git_work_tree} );
297 0           my $git_work_tree_root = $git_work_tree; # assume this is correct for now ...
298              
299             # then get the absolute root of the git work tree
300             # instead of just using what was passsed into us
301 0           my ($git_work_tree_updir) = $git->RUN('rev-parse', '--show-cdup');
302 0 0         if ( $git_work_tree_updir ) {
303 0           my $num_updirs = scalar grep $_, map { chomp; $_; } split /\// => $git_work_tree_updir;
  0            
  0            
304 0           while ( $num_updirs ) {
305 0           $git_work_tree_root = $git_work_tree_root->parent;
306 0           $num_updirs--;
307             }
308             }
309              
310 0           return ($git_work_tree, $git_work_tree_root);
311             }
312              
313             sub _initialize_perl_critic {
314 0     0     my ($class, %args) = @_;
315              
316 0           my $critic;
317 0 0         if ( $args{perl_critic_policy} ) {
318 0           $critic = Perl::Critic->new( '-single-policy' => $args{perl_critic_policy} );
319             }
320             else {
321             $critic = Perl::Critic->new(
322             ($args{perl_critic_profile} ? ('-profile' => $args{perl_critic_profile}) : ()),
323 0 0         ($args{perl_critic_theme} ? ('-theme' => $args{perl_critic_theme}) : ()),
    0          
324             );
325              
326             # inflate this as needed
327             $args{perl_critic_profile} = Path::Tiny::path( $args{perl_critic_profile} )
328 0 0         if $args{perl_critic_profile};
329             }
330              
331 0           return $critic;
332             }
333              
334             1;
335              
336             =pod
337              
338             =head1 NAME
339              
340             App::Critique::Session - Session interface for App::Critique
341              
342             =head1 VERSION
343              
344             version 0.05
345              
346             =head1 DESCRIPTION
347              
348             This is the main interace to the L<App::Critique> session file
349             and contains no real user serviceable parts.
350              
351             =head1 AUTHOR
352              
353             Stevan Little <stevan@cpan.org>
354              
355             =head1 COPYRIGHT AND LICENSE
356              
357             This software is copyright (c) 2016 by Stevan Little.
358              
359             This is free software; you can redistribute it and/or modify it under
360             the same terms as the Perl 5 programming language system itself.
361              
362             =cut
363              
364             __END__
365              
366             # ABSTRACT: Session interface for App::Critique
367