Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer
 
PerlMonks  

comment on

( [id://3333]=superdoc: print w/replies, xml ) Need Help??
All:
I was inspired by this node to write my first serious module. There were two solutions already provided that didn't require wheel inventing, but I figured getting shredded by you fellow monks would be the best way to get better. I have no intentions of posting to CPAN even though this module does have some functionality the others do not. I would however like comments as though it were being posted. Please be brutal. I have already placed things down at the __end__ that I know are lacking and would love pointers on them as well.
package Seconds2English; use strict; use Carp; our $VERSION = '0.01'; sub new { my $class = shift; croak "Improper option use" if (@_ % 2); my %opts = @_; my $self = bless {}, $class; $self->_build(\%opts); return $self; } sub _build { my ($self, $opt_ref) = @_; if (exists $opt_ref->{start}) { my $secs = $opt_ref->{start}; $self->{time} = int(abs($secs)) if _is_number($secs); delete $opt_ref->{start}; } $self->{_croak} = sub {croak "$_[0] is not a valid option"}; $self->{time} = 0 unless (exists $self->{time}); @{$self->{_list}} = qw(years months weeks days hours minutes secon +ds); @{$self->{_table}}{@{$self->{_list}}} = (31536000, 2592000, 604800 +, 86400, 3600, 60, 1); $self->_update_vals; $self->_update_table($opt_ref) if (%{$opt_ref}); } sub _is_number { # From Scalar::Util local $_ = shift; return 0 unless defined; return 1 if (/^[+-]?\d+$/); return 1 if (/^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/); 0; } sub _update_vals { my ($self, $max) = @_; my $seconds = exists $self->{time} ? $self->{time} : 0; $self->{units} = (); my ($key, @units) = $max ? ('_english', @{$max}) : ('english', @{$ +self->{_list}}); for my $unit (@units) { if ($self->{_table}{$unit} > $seconds ) { $self->{$unit} = 0; next; } my $key = $max ? "_$unit" : $unit; $self->{$key} = int($seconds / $self->{_table}{$unit}); push @{$self->{units}}, "$self->{$key} $unit"; chop ${$self->{units}}[-1] if ($self->{$key} < 2); $seconds %= $self->{_table}{$unit}; } unless (defined $self->{units}) { $self->{$key} = ''; return; } if (@{$self->{units}} > 2) { my $last = pop @{$self->{units}}; $self->{$key} = join ', ', @{$self->{units}}, "and $last"; } elsif (@{$self->{units}} > 1) { $self->{$key} = join ' and ' , @{$self->{units}}; } elsif (@{$self->{units}} > 0) { $self->{$key} = "${$self->{units}}[0]"; } } sub _update_table { my ($self, $opt_ref) = @_; while (my ($key, $val) = each %{$opt_ref}) { unless ($key eq 'months' || $key eq 'years') { $self->{_croak}($key); } if (_is_number($val)) { $val = int(abs($val)); $self->{_table}{$key} = $val if ($val > 0); } } $self->_update_vals; } sub seconds {return $_[0]->{seconds}} sub minutes {return $_[0]->{minutes}} sub hours {return $_[0]->{hours}} sub days {return $_[0]->{days}} sub weeks {return $_[0]->{weeks}} sub months {return $_[0]->{months}} sub years {return $_[0]->{years}} sub english {return $_[0]->{english}} sub time {return $_[0]->{time}} sub in_seconds {return $_[0]->{time}} sub in_minutes {return $_[0]->{time} / $_[0]->{_table}{minutes}} sub in_hours {return $_[0]->{time} / $_[0]->{_table}{hours}} sub in_days {return $_[0]->{time} / $_[0]->{_table}{days}} sub in_weeks {return $_[0]->{time} / $_[0]->{_table}{weeks}} sub in_months {return $_[0]->{time} / $_[0]->{_table}{months}} sub in_years {return $_[0]->{time} / $_[0]->{_table}{years}} sub english_max { my ($self, $val) = @_; $self->_custom_english('max', $val); } sub english_min { my ($self, $val) = @_; $self->_custom_english('min', $val); } sub _custom_english { my ($self, $type, $val) = @_; return $self->english unless $val; for my $index (0 .. $#{$self->{_list}}) { if ($val eq ${$self->{_list}}[$index]) { my ($low, $high) = $type eq 'max' ? ($index, $#{$self->{_l +ist}}): (0, $index); $self->_update_vals([@{$self->{_list}}[$low .. $high]]); return $self->{_english}; } } $self->{_croak}($val); } sub get { my ($self, @subs) = @_; my @values; for my $sub (@subs) { $self->{_croak}($sub) if ($sub =~ /^(_|get|new|set|modify)/); eval {push @values, $self->$sub}; $self->{_croak}($sub) if $@; } return wantarray ? @values : \@values; } sub set { my $self = shift; croak "Improper option use" if (@_ % 2); my %mods = @_; while (my ($key, $val) = each %mods) { if ($val =~ /^([-+])(.*)/) { my ($op, $amount) = ($1, $2); $self->{_croak}($amount) unless _is_number($amount); unless (grep /\b$key\b/ , (@{$self->{_list}})) { $self->{_croak}($key); } $amount *= $self->{_table}{$key}; $op eq '+' ? ($self->{time} += $amount) : ($self->{time} - += $amount); } else { $self->{_croak}("$key => $val"); } } $self->{time} = int(abs($self->{time})); $self->_update_vals; } sub modify_interval { my $self = shift; croak "Improper option use" if (@_ % 2); my %opts = @_; $self->_update_table(\%opts) if %opts; } 1; __END__ To Do 1. POD - it can't be that hard, see [id://252477] or [id://107642] 2. Tests - not sure what to test, you tried to break it as you wrote +it 3. Comments! Stop thinking it is obvious, remember looking at it agai +n 10 minutes later 4. Code a way to allow more user friendly options instead of just raw + seconds 5. Consider other time intervals - decades, centuries, etc? Notes to self for later use in POD 1. Think up a good name 2. Caveats - seconds are truncated to nearest positive whole integer +first 3. Compare this module to others that do similar things 4. Methods A. new my $obj = Seconds2English->new('start' => time); my $obj = Seconds2English->new('start' => 144_000, 'months' => + 60 * 60 * 24 * 31); my $obj = Seconds2English->new('years' => 31556930); B. years, months, weeks, days, hours, minutes, seconds print $obj->hours, "\n"; # Returns number of hours currently +stored in object C. time print $obj->time, "\n"; # Returns total number of seconds to +create object D. english my $obj = Seconds2English->new('start' => 144_144); print $obj->english, "\n"; # Returns user-friendly english str +ing # output = "1 day, 16 hours, 2 minutes, and 24 seconds" E. in_years, in_months, in_weeks, in_days, in_hours, in_minutes, +in_seconds print $obj->in_months, "\n"; # Returns object time in just spe +cified unit (integer) F. english_max my $obj = Seconds2English->new('start' => 144_144_144); print $obj->english_max('weeks'), "\n"; # no interval above un +it in output # output = 238 weeks, 2 days, 8 hours, 2 minutes, and 24 secon +ds G. english_min my $obj = Seconds2English->new('start' => 144_144_144); print $obj->english_min('weeks'), "\n"; # no interval below un +it in output # output = 4 years, 6 months, and 4 weeks H. get my $obj = Seconds2English->new('start' => 144_144_144); my ($years, $days, $minutes) = $obj->get('years', 'days', 'min +utes'); my $array_ref = $obj->get('years', 'days', 'minutes'); I. set my $obj = Seconds2English->new('start' => 456_789); print $obj->english, "\n"; # ouput = 5 days, 6 hours, 53 minutes, and 9 seconds $obj->set('days' => '-3', 'hours' => '+4', 'seconds' => '-8'); print $obj->english, "\n"; # ouput = 2 days, 10 hours, 53 minutes, and 1 second J. modify_interval my $obj = Seconds2English->new('start' => 144_144_144); print $obj->english, "\n"; # output = 4 years, 6 months, 4 weeks, 8 hours, 2 minutes, and + 24 seconds $obj->modify_interval('years' => 31556930, 'months' => 3155693 +0 / 12); print $obj->english, "\n"; # output = 4 years, 6 months, 3 weeks, 3 days, 17 hours, 52 mi +nutes, and 40 seconds
Again, this was a toy project to improve, so feel free to beat me about the head for anything you think could/should be better.

Cheers - L~R


In reply to RFC: Seconds2English by Limbic~Region

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post; it's "PerlMonks-approved HTML":



  • Are you posting in the right place? Check out Where do I post X? to know for sure.
  • Posts may use any of the Perl Monks Approved HTML tags. Currently these include the following:
    <code> <a> <b> <big> <blockquote> <br /> <dd> <dl> <dt> <em> <font> <h1> <h2> <h3> <h4> <h5> <h6> <hr /> <i> <li> <nbsp> <ol> <p> <small> <strike> <strong> <sub> <sup> <table> <td> <th> <tr> <tt> <u> <ul>
  • Snippets of code should be wrapped in <code> tags not <pre> tags. In fact, <pre> tags should generally be avoided. If they must be used, extreme care should be taken to ensure that their contents do not have long lines (<70 chars), in order to prevent horizontal scrolling (and possible janitor intervention).
  • Want more info? How to link or How to display code and escape characters are good places to start.
Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (5)
As of 2024-04-18 02:31 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found