File Coverage

blib/lib/Pinto/Role/Committable.pm
Criterion Covered Total %
statement 57 69 82.6
branch 14 20 70.0
condition 5 6 83.3
subroutine 12 13 92.3
pod 0 5 0.0
total 88 113 77.8


line stmt bran cond sub pod time code
1             # ABSTRACT: Role for actions that commit changes to the repository
2              
3             package Pinto::Role::Committable;
4              
5 38     38   17290 use Moose::Role;
  38         112  
  38         372  
6 38     38   198728 use MooseX::Types::Moose qw(Bool Str ArrayRef);
  38         119  
  38         434  
7 38     38   190486 use MooseX::MarkAsMethods ( autoclean => 1 );
  38         109  
  38         389  
8              
9 38     38   127332 use Try::Tiny;
  38         113  
  38         2684  
10 38     38   262 use List::MoreUtils qw(uniq);
  38         92  
  38         802  
11              
12 38     38   20557 use Pinto::Constants qw(:lock);
  38         110  
  38         3278  
13 38     38   4744 use Pinto::Types qw(StackName StackDefault StackObject DiffStyle);
  38         100  
  38         341  
14 38     38   246886 use Pinto::Util qw(is_interactive throw is_blank is_not_blank);
  38         96  
  38         39275  
15              
16             #------------------------------------------------------------------------------
17              
18             our $VERSION = '0.13'; # VERSION
19              
20             #------------------------------------------------------------------------------
21              
22             with qw(Pinto::Role::Plated);
23              
24             #------------------------------------------------------------------------------
25              
26             has stack => (
27             is => 'ro',
28             isa => StackName | StackDefault | StackObject,
29             writer => '_set_stack',
30             default => undef,
31             );
32              
33             has dry_run => (
34             is => 'ro',
35             isa => Bool,
36             default => 0,
37             );
38              
39             has message => (
40             is => 'ro',
41             isa => Str,
42             predicate => 'has_message',
43             );
44              
45             has use_default_message => (
46             is => 'ro',
47             isa => Bool,
48             default => 0,
49             );
50              
51             has diff_style => (
52             is => 'ro',
53             isa => DiffStyle,
54             predicate => 'has_diff_style',
55             );
56              
57             has lock_type => (
58             is => 'ro',
59             isa => Str,
60             default => $PINTO_LOCK_TYPE_EXCLUSIVE,
61             init_arg => undef,
62             );
63              
64             has affected => (
65             is => 'ro',
66             isa => ArrayRef,
67             default => sub { [] },
68             init_arg => undef,
69             );
70              
71             #------------------------------------------------------------------------------
72              
73             requires qw( execute repo );
74              
75             #------------------------------------------------------------------------------
76              
77             around BUILD => sub {
78             my ( $orig, $self ) = @_;
79              
80             # Inflate the stack into a real object. As a side
81             # effect, this also verifies that the stack exists.
82              
83             my $stack = $self->repo->get_stack( $self->stack );
84             $self->_set_stack($stack);
85              
86             # Make sure we aren't locked if we intend to commit
87             $self->stack->assert_not_locked unless $self->dry_run;
88              
89             return $self->$orig;
90             };
91              
92             #------------------------------------------------------------------------------
93              
94             around execute => sub {
95             my ( $orig, $self, @args ) = @_;
96              
97             try {
98             $self->repo->txn_begin;
99             $self->before_execute;
100             $self->$orig(@args);
101             $self->after_execute;
102             }
103             catch {
104             $self->repo->txn_rollback;
105             $self->repo->clean_files;
106             throw $_;
107             };
108              
109             return $self->result;
110             };
111              
112             #------------------------------------------------------------------------------
113              
114             sub before_execute {
115 183     183 0 662 my ($self) = @_;
116              
117 183         5743 $self->stack->start_revision;
118              
119 183         30531 return $self;
120             }
121              
122             #------------------------------------------------------------------------------
123              
124             sub after_execute {
125 166     166 0 561 my ($self, @dists) = @_;
126              
127 166 50       5454 local $ENV{PINTO_DIFF_STYLE} = $self->diff_style
128             if $self->has_diff_style;
129              
130 166         4350 my $stack = $self->stack;
131 166 100       4648 if ( $self->dry_run ) {
    100          
132              
133 4 50       48 $stack->refresh->has_changed
134             ? $self->show($stack->diff, {no_newline => 1})
135             : $self->notice('No changes were made');
136              
137 4         201 $self->repo->txn_rollback;
138 4         132 $self->repo->clean_files;
139             }
140             elsif ( $stack->refresh->has_not_changed ) {
141              
142 11         308 $self->diag('No changes were made');
143 11         794 $self->repo->txn_rollback;
144             }
145             else {
146              
147 151         4179 my $msg = $self->compose_message;
148 151         1409 $stack->commit_revision( message => $msg );
149              
150 148         6255 $self->result->changed;
151 148         3576 $self->repo->txn_commit;
152             }
153              
154             # Release the exclusive lock and just use a shared lock, since
155             # we won't be writing to the repository at this point.
156 163         14362 $self->repo->unlock; $self->repo->lock($PINTO_LOCK_TYPE_SHARED);
  163         5087  
157              
158 163         1277 return $self;
159             }
160              
161             #------------------------------------------------------------------------------
162              
163             sub compose_message {
164 151     151 0 533 my ($self) = @_;
165              
166 151         4327 my $stack = $self->stack;
167 151         928 my $title = $self->generate_message_title;
168              
169 151 100 100     6581 return $self->message
170             if $self->has_message and is_not_blank( $self->message );
171              
172 79 100 66     2345 return $title
173             if $self->has_message and is_blank( $self->message );
174              
175 78 100       2628 return $title
176             if $self->use_default_message;
177              
178 77 50       658 return $title
179             if not is_interactive;
180              
181 0         0 my $template = $self->generate_message_template($title);
182 0         0 my $message = $self->chrome->edit( $template );
183 0         0 $message =~ s/^ [#] .* $//gmsx; # Strip comments
184              
185 0 0       0 throw 'Aborting due to empty commit message' if is_blank($message);
186              
187 0         0 return $message;
188             }
189              
190             #------------------------------------------------------------------------------
191              
192             sub generate_message_title {
193 147     147 0 587 my ( $self, $extra ) = @_;
194              
195 147         498 my $class = ref $self;
196 147         1861 my ($action) = $class =~ m/ ( [^:]* ) $/x;
197 147         463 my @dists = uniq( sort @{$self->affected} );
  147         4039  
198 147 50       7970 my $title = "$action " . join( ', ', @dists ) . ( $extra ? " $extra" : '' );
199              
200 147         6182 return $title;
201             }
202              
203             #------------------------------------------------------------------------------
204              
205             sub generate_message_template {
206 0     0 0   my ( $self, $title ) = @_;
207              
208 0           my $stack = $self->stack;
209 0           my $diff = $stack->diff;
210              
211             # Prepend "#" to each line of the diff,
212             # so they are treated as comments.
213 0           $diff =~ s/^/# /gm;
214              
215 0           my $msg = <<"END_MESSAGE";
216             $title
217              
218              
219             #-------------------------------------------------------------------------------
220             # Please edit or amend the message above as you see fit. The first line of the
221             # message will be used as the title. Any line that starts with a "#" will be
222             # ignored. To abort the commit, delete the entire message above, save the file,
223             # and close the editor.
224             #
225             # Changes to be committed to stack $stack:
226             #
227             $diff
228             END_MESSAGE
229              
230 0           chomp $msg;
231 0           return $msg;
232             }
233              
234             #------------------------------------------------------------------------------
235             1;
236              
237             __END__
238              
239             =pod
240              
241             =encoding UTF-8
242              
243             =for :stopwords Jeffrey Ryan Thalhammer
244              
245             =head1 NAME
246              
247             Pinto::Role::Committable - Role for actions that commit changes to the repository
248              
249             =head1 VERSION
250              
251             version 0.13
252              
253             =head1 AUTHOR
254              
255             Jeffrey Ryan Thalhammer <jeff@stratopan.com>
256              
257             =head1 COPYRIGHT AND LICENSE
258              
259             This software is copyright (c) 2015 by Jeffrey Ryan Thalhammer.
260              
261             This is free software; you can redistribute it and/or modify it under
262             the same terms as the Perl 5 programming language system itself.
263              
264             =cut