App-Sqitch
view release on metacpan or search on metacpan
lib/App/Sqitch/Plan/Line.pm view on Meta::CPAN
use App::Sqitch::X qw(hurl);
use Locale::TextDomain qw(App-Sqitch);
our $VERSION = 'v1.6.1'; # VERSION
has name => (
is => 'ro',
isa => Str,
required => 1,
);
has operator => (
is => 'ro',
isa => Str,
default => '',
);
has lspace => (
is => 'ro',
isa => Str,
default => '',
);
has rspace => (
is => 'rwp',
isa => Str,
default => '',
);
has lopspace => (
is => 'ro',
isa => Str,
default => '',
);
has ropspace => (
is => 'ro',
isa => Str,
default => '',
);
has note => (
is => 'rw',
isa => Str,
default => '',
);
after note => sub {
my $self = shift;
$self->_set_rspace(' ') if $_[0] && !$self->rspace;
};
has plan => (
is => 'ro',
isa => Plan,
weak_ref => 1,
required => 1,
handles => [qw(sqitch project uri target)],
);
my %escape = (
"\n" => '\\n',
"\r" => '\\r',
'\\' => '\\\\',
);
my %unescape = reverse %escape;
sub BUILDARGS {
my $class = shift;
my $p = @_ == 1 && ref $_[0] ? { %{ +shift } } : { @_ };
if (my $note = $p->{note}) {
# Trim and then encode newlines.
$note =~ s/\A\s+//;
$note =~ s/\s+\z//;
$note =~ s/(\\[\\nr])/$unescape{$1}/g;
$p->{note} = $note;
$p->{rspace} //= ' ' if $note && $p->{name};
}
return $p;
}
sub request_note {
my ( $self, %p ) = @_;
my $note = $self->note // '';
return $note if $note =~ /\S/;
# Edit in a file.
require File::Temp;
my $tmp = File::Temp->new;
binmode $tmp, ':utf8_strict';
( my $prompt = $self->note_prompt(%p) ) =~ s/^/# /gms;
$tmp->print( "\n", $prompt, "\n" );
$tmp->close;
my $sqitch = $self->sqitch;
$sqitch->shell( $sqitch->editor . ' ' . $sqitch->quote_shell($tmp) );
open my $fh, '<:utf8_strict', $tmp or hurl add => __x(
'Cannot open {file}: {error}',
file => $tmp,
error => $!
);
$note = join '', grep { $_ !~ /^\s*#/ } <$fh>;
hurl {
ident => 'plan',
message => __ 'Aborting due to empty note',
exitval => 1,
} unless $note =~ /\S/;
# Trim the note.
$note =~ s/\A\v+//;
$note =~ s/\v+\z//;
# Set the note.
$self->note($note);
return $note;
}
sub note_prompt {
my ( $self, %p ) = @_;
__x(
"Write a {command} note.\nLines starting with '#' will be ignored.",
command => $p{for}
);
}
sub format_name {
shift->name;
}
sub format_operator {
my $self = shift;
join '', $self->lopspace, $self->operator, $self->ropspace;
}
sub format_content {
my $self = shift;
join '', $self->format_operator, $self->format_name;
}
sub format_note {
my $note = shift->note;
return '' unless length $note;
$note =~ s/([\r\n\\])/$escape{$1}/g;
return "# $note";
}
sub as_string {
my $self = shift;
return $self->lspace
. $self->format_content
. $self->rspace
. $self->format_note;
}
1;
__END__
=head1 Name
App::Sqitch::Plan::Line - Sqitch deployment plan line
=head1 Synopsis
my $plan = App::Sqitch::Plan->new( sqitch => $sqitch );
for my $line ($plan->lines) {
say $line->as_string;
}
=head1 Description
An App::Sqitch::Plan::Line represents a single line from a Sqitch plan file.
Each object managed by an L<App::Sqitch::Plan> object is derived from this
class. This is actually an abstract base class. See
L<App::Sqitch::Plan::Change>, L<App::Sqitch::Plan::Tag>, and
L<App::Sqitch::Plan::Blank> for concrete subclasses.
=head1 Interface
=head2 Constructors
=head3 C<new>
my $plan = App::Sqitch::Plan::Line->new(%params);
Instantiates and returns a App::Sqitch::Plan::Line object. Parameters:
=over
=item C<plan>
The L<App::Sqitch::Plan> object with which the line is associated.
=item C<name>
The name of the line. Should be empty for blank lines. Tags names should
not include the leading C<@>.
=item C<lspace>
The white space from the beginning of the line, if any.
=item C<lopspace>
( run in 1.772 second using v1.01-cache-2.11-cpan-5a3173703d6 )