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   17054 use Moose::Role;
  38         103  
  38         315  
6 38     38   187560 use MooseX::Types::Moose qw(Bool Str ArrayRef);
  38         103  
  38         484  
7 38     38   180013 use MooseX::MarkAsMethods ( autoclean => 1 );
  38         115  
  38         347  
8              
9 38     38   120468 use Try::Tiny;
  38         104  
  38         2981  
10 38     38   247 use List::MoreUtils qw(uniq);
  38         89  
  38         869  
11              
12 38     38   19829 use Pinto::Constants qw(:lock);
  38         97  
  38         3679  
13 38     38   4553 use Pinto::Types qw(StackName StackDefault StackObject DiffStyle);
  38         104  
  38         334  
14 38     38   232061 use Pinto::Util qw(is_interactive throw is_blank is_not_blank);
  38         102  
  38         36070  
15              
16             #------------------------------------------------------------------------------
17              
18             our $VERSION = '0.14'; # 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 608 my ($self) = @_;
116              
117 183         5572 $self->stack->start_revision;
118              
119 183         27146 return $self;
120             }
121              
122             #------------------------------------------------------------------------------
123              
124             sub after_execute {
125 166     166 0 561 my ($self, @dists) = @_;
126              
127 166 50       5181 local $ENV{PINTO_DIFF_STYLE} = $self->diff_style
128             if $self->has_diff_style;
129              
130 166         4098 my $stack = $self->stack;
131 166 100       4439 if ( $self->dry_run ) {
    100          
132              
133 4 50       40 $stack->refresh->has_changed
134             ? $self->show($stack->diff, {no_newline => 1})
135             : $self->notice('No changes were made');
136              
137 4         117 $self->repo->txn_rollback;
138 4         113 $self->repo->clean_files;
139             }
140             elsif ( $stack->refresh->has_not_changed ) {
141              
142 11         237 $self->diag('No changes were made');
143 11         640 $self->repo->txn_rollback;
144             }
145             else {
146              
147 151         3780 my $msg = $self->compose_message;
148 151         1314 $stack->commit_revision( message => $msg );
149              
150 148         5824 $self->result->changed;
151 148         3399 $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         12614 $self->repo->unlock; $self->repo->lock($PINTO_LOCK_TYPE_SHARED);
  163         4548  
157              
158 163         1129 return $self;
159             }
160              
161             #------------------------------------------------------------------------------
162              
163             sub compose_message {
164 151     151 0 477 my ($self) = @_;
165              
166 151         4087 my $stack = $self->stack;
167 151         891 my $title = $self->generate_message_title;
168              
169 151 100 100     6169 return $self->message
170             if $self->has_message and is_not_blank( $self->message );
171              
172 79 100 66     2087 return $title
173             if $self->has_message and is_blank( $self->message );
174              
175 78 100       2195 return $title
176             if $self->use_default_message;
177              
178 77 50       617 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 602 my ( $self, $extra ) = @_;
194              
195 147         521 my $class = ref $self;
196 147         1833 my ($action) = $class =~ m/ ( [^:]* ) $/x;
197 147         446 my @dists = uniq( sort @{$self->affected} );
  147         4007  
198 147 50       7649 my $title = "$action " . join( ', ', @dists ) . ( $extra ? " $extra" : '' );
199              
200 147         5931 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.14
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