From 4e9a9c902ea77719d24b393579364262e2e9cf98 Mon Sep 17 00:00:00 2001 From: stdweird Date: Sat, 19 Sep 2015 18:02:41 +0200 Subject: [PATCH 01/15] ObjectText: refactor text related methods from TextRender in new module (unmodified TextRender unittests) --- src/main/perl/ObjectText.pm | 309 ++++++++++++++++++++++++++ src/main/perl/TextRender.pm | 185 ++------------- src/test/perl/modules/myobjecttext.pm | 39 ++++ src/test/perl/objecttext.t | 171 ++++++++++++++ src/test/perl/textrender.t | 4 +- 5 files changed, 538 insertions(+), 170 deletions(-) create mode 100644 src/main/perl/ObjectText.pm create mode 100644 src/test/perl/modules/myobjecttext.pm create mode 100644 src/test/perl/objecttext.t diff --git a/src/main/perl/ObjectText.pm b/src/main/perl/ObjectText.pm new file mode 100644 index 00000000..d13de3bc --- /dev/null +++ b/src/main/perl/ObjectText.pm @@ -0,0 +1,309 @@ +# ${license-info} +# ${developer-info} +# ${author-info} +# ${build-info} + +package CAF::ObjectText; + +use strict; +use warnings; + +use parent qw(CAF::Object); +use LC::Exception qw (SUCCESS throw_error); + +use overload ('""' => '_stringify'); + +use Readonly; +Readonly::Scalar my $DEFAULT_USECACHE => 1; + +=pod + +=head1 NAME + +CAF::ObjectText - Base class for handling text + +=head1 SYNOPSIS + +Define subclass via + package SubClass; + use parent qw(CAF::ObjectText); + + sub _get_text + { + my ($self) = @_; + return "actual text"; + } + +And use it via + my $sc = SubClass->new(log => $self); + print "$sc"; # stringification + + $sc = SubClass->new(log => $self); + # return CAF::FileWriter instance (text already added) + my $fh = $sc->filewriter('/some/path'); + die "Problem rendering the text" if (!defined($fh)); + $fh->close(); + +=head1 DESCRIPTION + +This class simplyfies text handling via stringification and producing +a C instance. + +=head2 Methods + +=over + +=item _initialize_textopts + +Handle some common options in the subclass C<_initialize> method. + +=over + +=item C + +A C object to log to. + +=item C + +If C is true, the produced text will be verified that it ends with +an end-of-line, and if missing, a newline character will be added. +By default, C is true. + +C set to false will not strip trailing newlines (use C +or something similar for that). + +=item C + +If C is false, the text is always re-produced. +Default is to cache the produced text (C is true). + +=back + +=cut + +sub _initialize_textopts +{ + my ($self, %opts) = @_; + + %opts = () if !%opts; + + $self->{log} = $opts{log} if $opts{log}; + + if (exists($opts{eol})) { + $self->{eol} = $opts{eol}; + $self->verbose("Set eol to $self->{eol}"); + } else { + # Default to true + $self->{eol} = 1; + }; + + if(exists($opts{usecache})) { + $self->{usecache} = $opts{usecache}; + } else { + $self->{usecache} = $DEFAULT_USECACHE; + } + $self->verbose("No caching") if (! $self->{usecache}); + +} + +=pod + +=item fail + +Handle failures. Stores the error message in the C attribute, +logs it with C and returns undef. +All failures should use Cfail("message");>. +No error logging should occur in the subclass. + +=cut + +sub fail +{ + my ($self, @messages) = @_; + $self->{fail} = join('', @messages); + $self->verbose("FAIL: ", $self->{fail}); + return; +} + +=pod + +=item C<_get_text_test> + +Run additional tests before the actual text is produced via C. +Returns undef in case of failure, SUCCESS otherwise. + +The method is called in C before the caching is checked. + +Default implementation does not test anything, always returns SUCCESS. +This method should be redefined in the subclass. + +=cut + +sub _get_text_test +{ + return SUCCESS; +} + +=pod + +=item C<_get_text> + +Produce the actual text in C +(or call another method that does so). + +Returns 2 element tuple with first element the resulting text +(or undef in case of failure). The second element is an error message +prefix (ideally, real error message is set via the C attribute). + +This method needs to be defined in the subclass. + +=cut + +sub _get_text +{ + my ($self) = @_; + my $msg = "no _get_text implemented for ".ref($self); + throw_error($msg); + $self->fail($msg); + return (undef, $msg); +} + + +=pod + +=item C + +C produces and returns the text. + +In case of an error, C returns C +(no error is logged). +This is the main difference from the auto-stringification that +returns an empty string in case of a rendering error. + +By default, the result is cached. To force re-producing the text, +clear the current cache by passing C<1> as first argument +(or disable caching completely with the option C +set to false during the initialisation). + +=cut + +sub get_text +{ + my ($self, $clearcache) = @_; + + return if (!$self->_get_text_test()); + + if ($clearcache) { + $self->verbose("get_text clearing cache"); + delete $self->{_cache}; + }; + + if (exists($self->{_cache})) { + $self->debug(1, "Returning the cached value"); + return $self->{_cache} + }; + + my ($res, $errmsg) = $self->_get_text(); + + if (defined($res)) { + if($self->{eol} && $res !~ m/\n$/) { + $self->verbose("eol set, and rendered text was missing final newline. adding newline."); + $res .= "\n"; + } + if($self->{usecache}) { + $self->{_cache} = $res; + }; + return $res; + } else { + $errmsg = "No valid text produced from ".ref($self) if (! $errmsg); + $errmsg .= ": $self->{fail}" if ($self->{fail}); + return $self->fail($errmsg); + } +} + + +# Handle possible undef from get_text to avoid 'Use of uninitialized value' warnings +sub _stringify +{ + my ($self) = @_; + # Always default cache behaviour + my $text = $self->get_text(); + if(defined($text)) { + return $text; + } else { + return ""; + } +} + +=pod + +=item C + +Create and return an open C instance with +first argument as the filename. If the C method fails +(i.e. returns undef), C is returned. + +The text is added to the filehandle. +It's up to the consumer to cancel +and/or close the instance. + +All C initialisation options are supported +and passed on. (If no C option is provided, + the one from the current instance is passed). + +Two new options C
and C