Re: RFC: utils.pm

From: Tore Aursand (tore_at_aursand.no)
Date: 02/11/04


Date: Wed, 11 Feb 2004 20:36:05 +0100

On Wed, 11 Feb 2004 15:23:37 +0000, Ben Morrow wrote:
>> split_csv( $value ) - Easy CSV splitting. (*)

> As you say, there are modules to do this. Or
>
> @values = split /,/, $values;

Not quite as powerful as this one;

  my @array = ();
  push( @array, $+ ) while $string =~ m{
      "([^\"\\]*(?:\\.[^\"\\]*)*)",? # groups the phrase inside the quotes
      | ([^,]+),?
      | ,
  }gx;
  push( @array, undef ) if ( substr($string, -1, 1) eq ',' );

>> random_string( $length ) - Generates a random string $length
>> characters long.

> This is a little harder... also *much* less useful.

Maybe, but I find myself constantly using this one when I want to create
cookies. Nice to have. :)

>> as_string( $value, [$default] ) - Always returns a defined value,
>> optionally $default if $value
>> isn't defined.

> Eh what? You don't *need* to cast in Perl.
>
> $value || $default

This only returns $default if $value isn't true. So if we have this
function:

  sub as_string {
      my $value = shift;
      my $default = shift;
      return $value || $default;
  }

What happens when you pass 0 to this function?

> or defined($value) ? $value : $default

Better, but I'm tired to writing this god damn line each time I want to
make sure that a value is defined. :)

My 'as_string' function will only return the $default value if a) it is
given, and b) if the input $value is undefined.

When it comes to "casting". Maybe it's not the correct word to use, but
when dealing with ie. CGI parameters, I find my functions useful. Here's
an example;

  my $firstname = as_string( $cgi->param('firstname') );
  my $lastname = as_string( $cgi->param('lastname') );
  my $age = as_int( $cgi->param('age') );

>> as_int( $value ) - Always returns $value as an integer.

> err.. int($value)

What if $value isn't a number? Ah. A warning occurs, of course. I don't
want warnings. 'as_int' clearly says that I want _any_ value passed to it
returned as an integer. If that's not possible, return 0.

> Or POSIX::floor, or POSIX::ceil.

I know about them. I just didn't want to deal with more than one module,
so I created my own functions instead. :)

>> as_decimal( $value, [$decimals] ) - Always returns $value as a
>> decimal number with $decimals
>> numbers after the decimal point.

> sprintf

Excactly what 'as_decimal' uses, except that I prefer writing

  my $value = as_decimal( 1.23456789, 2 );

instead of

  my $value = sprintf( '%.2f', 1.23456789 );

>> as_boolean( $value ) - Always returns $value as a boolena value (ie.
>> TRUE/1 or FALSE/0).

> !!$value

Yeah, but 'as_boolean' takes care of "other" boolean values to;

  sub as_boolean {
      my $value = as_string( shift );
      return ( $value =~ m,^1|y|yes|on|true$,i ) ? 1 : 0;
  }

I needed this function when dealing with output from an application where
a true value could be almost anything.

No, I didn't write that program, and it wasn't in Perl (or any other
interpreted language), so I couldn't change the output.

>> as_date( $value ) - Always returns $value as a date (YYYY-MM-DD).
>> as_time( $value ) - Always returns $value as a time (HH:MM:SS).
>> as_datetime( $value ) - Always returns $value as a datetime (which
>> means combining as_date() and as_time()).

> POSIX::strftime

'as_date', 'as_time' and 'as_datetime' are - of course - a lot easier to
use than 'strftime'. :)

I don't use the 'strftime' much, and every time I had to use it, I had to
look it up in the documentation. I don't want to do that. I'm lazy. :)

>> VALIDATION
>> Each of the CASTING functions also have a is_* function, which returns
>> TRUE/1 or FALSE/0 depending on wether the input argument conforms to
>> the datatype.

> Ummm... everything is (can be) a string.

Yes, but not everything can be everything _else_ than a string. Let's
look at my CGI parameter example again. Instead of writing

  my $nr = (defined $cgi->param('nr')) ? $cgi->param('nr') : 0;
  unless ( $nr >= 0 && $nr <= 12 ) {
      # Error
  }

I want to write

  my $nr = as_int( $cgi->param('nr') );
  unless ( is_int($nr, 0, 12) ) {
      # Error
  }

> is_int is simply $value =~ /^\d+$/.

No. Take a look at this:

  my @values = ( 2, -2, +2, '2', '-2', '+2' );
  foreach ( @values ) {
      print "Is $_ an integer? ";
      ( /^\d+$/ ) ? print 'Yes' : print 'No';
      print "\n";
  }

Of course, one could argue about wether a stringified '-2' (or '+2')
really is an integer, but functions like these are nice to have if you
want to _avoid_ errors:

  # $value appears from somewhere, possibly from a stupid user :)
  if ( is_int($value) ) {
      # Ok, we can use this value
      $value = as_int( $value ); # 'Cast' it to integer
  }
  else {
      # Sorry, but $value isn't even close to being an integer
  }

> Dates can and should be handled by your favourite date-time parsing
> module: the code is non-trivial, so should be reused.

I guess you're right. I created many of them (the functions) "by hand",
though, and tested them agains Date::Calc and Date::Manip. Man, that was
one useful learning session. :)

> I can't think offhand how I'd do is_decimal, probably because I've
> never had occasion to.

I once (...) encountered a problem with a web form where the user had to
register some data, and he/she _had_ to enter a number as a decimal. That
was to make sure that the data entered was as accurate as possible, or
something like that;

  sub is_decimal {
      my $value = as_string( shift );
      return ( $value =~ m,^[-+]?(\d+)?\.\d+$, ) ? 1 : 0;
  }

>> round( $value ) - Rounds a number to the nearest integer.

> int($value + 0.5) is usually good enough.

Yeah, excapt when do want to round negative numbers as well;

  return ( $value > 0 ) ? int($value + 0.5) : int($value - 0.5);

>> random_number( $min, $max ) - Returns a random number in the range
>> $min to $max.

> (rand * ($max - $min)) + $min

Right, but what if only one of $min or $max is known?

>> format_number( $value, $separator ) - Formats a number with a given
>> separator; 1234 becomes 1,234.

> Less trivial... is probably better done by something locale-aware.

Who says that a 'Utils' module can't be locale-aware? :)

>> unique( $arrayref ) - Returns only the unique elements in $array.
>> intersection( $arrayref1, $arrayref2 ) - Computes the intersection
>> of two array references.
>> union( $arrayref1, $arrayref2 ) - Computes the union of two array
>> references.

> These should all be in a module called Set::Util... feel free to write
> it.

It isn't in a module already? Doesn't it fit into List::Util?

-- 
Tore Aursand <tore@aursand.no>
"Scientists are complaining that the new "Dinosaur" movie shows
 dinosaurs with lemurs, who didn't evolve for another million years.
 They're afraid the movie will give kids a mistaken impression. What
 about the fact that the dinosaurs are singing and dancing?" -- Jay
 Leno


Relevant Pages

  • Re: Function versus Procedure
    ... of a sub). ... If you're not writing bad code, i.e., you're properly ... just more consistent coding and less problematic in the way it's ... The recommendation violates every canon of good coding I can think ...
    (microsoft.public.access.tablesdbdesign)
  • Re: VBA dates versus Excel dates
    ... I used the .Value2 property when writing the dates to the worksheet. ... End Sub ... Jon Peltier, Microsoft Excel MVP ... This post is a suggestion for Microsoft, ...
    (microsoft.public.excel.programming)
  • Re: Event Handlers: Getting a handle to the calling object
    ... The thing is that writing a case or if block to test which combo box ... What I'd ideally lie is to write is something like: ... Sub ComboBox1_Click ... enough with VBA to know if VBA classes have a "this" pointer or ...
    (microsoft.public.excel.programming)
  • Sending XML Data to a Webserver with ASP.NET
    ... Private Sub SendToWebserver_Click ... Dim strFormData As String ... Since I'm writing an ASP.NET page to accomplish a similar function, ... that extra baggage and stick to a pure .NET Framework implementation. ...
    (microsoft.public.vb.enterprise)
  • Re: A lisp newbie example app.
    ... writing a trivial little game. ... for newbies to learn from each other than from us dinosaurs. ...
    (comp.lang.lisp)