Mail::Sendmail
1 direct reply — Read more / Contribute
|
by Corion
on Mar 22, 2001 at 11:34
|
|
|
Every program expands until it can send mail.
Mail::Sendmail tries to make this task easier by providing an interface to any SMTP server you specify. In hindsight, the name should rather have been Mail::Simple, but it's too late for that. An alternative to Mail::Sendmail is Net::SMTP, which has about the same feature set except MIME, but I have not (yet) looked closely enough to make a qualified comparision.
Why use Mail::Sendmail ?
You need to send mail and you're not sure that there will be a local mailer installed. Mail::Sendmail works under Windows and Unix (most likely, Mac too) and it's a pure Perl solution. Installation is easy even if you don't have a make utility installed, as it consists of only one file to copy. If you install MIME::QuotedPrint, you'll also be able to easily send attachments and HTML encoded Email. It only makes one connection to the SMTP server for all recipients of the email.
Why not use Mail::Sendmail ?
Mail::Sendmail needs an existing network connection to the internet, or at least an existing network connection to the SMTP server you want it to use. It does not support local queuing or anything fancy. It modifies the headers of your email to have Content-Type and Content-transfer-encoding headers, and it will automagically try to do the right thing and quote high-ASCII characters. If you have warnings on, it will warn quite a bit about stuff.
Mail::Sendmail only exports one function, &sendmail(). If you need finer grained feedback, like progress, etc. or if you don't have enough memory to keep your email twice! in it, Mail::Sendmail is not for you.
Caveats
Mail::Sendmail tries to do the right thing. This might bring surprising results if you use high-ASCII stuff. Mail::Sendmail tries to parse the email adresses given, but it isn't completely RFC compliant. This might bite if you have really fancy email addresses.
Example
The Mail::Sendmail documentation already has an exhaustive example, but I'm reposting my test script which I used to check whether Mail::Sendmail works under Win32 :
#!/usr/bin/perl -w
# Simple test script to test Mail::Sendmail under Win32
use strict;
use Mail::Sendmail;
# Set up some default configuration
unshift @{$Mail::Sendmail::mailcfg{'smtp'}} , 'smtprelay.t-online.de';
$Mail::Sendmail::mailcfg{'from'} = "Corion (script) <corion\@sends.no.
+spam>";
my %mail = (
Bcc => 'corion@wants.no.spam',
Subject => "Test 1",
'X-Mailer' => "Mail::Sendmail test script v0.01/$Mail::Sendmail::VER
+SION",
Message => "Test number 1",
);
sendmail( %mail ) or die "Error: $Mail::Sendmail::error\n";
|
Coordinate
1 direct reply — Read more / Contribute
|
by orbital
on Mar 19, 2001 at 23:14
|
|
|
I just recently started using the GPS::Garmin module by Joao Pedro B Gonçalves , joaop@iscsp.utl.pt to dump my GPS data directly into my perl scripts. This module works great but unfortantly the Module gave me Lat. and Long. in degrees.minutes and I wanted it in UTM (Universal Transverse Mercator). - UTM is the standard used by multiple organizations around the world to locate a specific point on earth. UTM is calculated by one central orgin on earth, everything thing from that point is represent in meters. The first set of numbers is refered to as the Easting (East-West postion) and the second number is refered to as the Northing (North-South position).
Advantages of having your data in UTM is that you are able to use USGS data in conjuction with your newly aquired data. You can obtain the data at EROS The DEM (Digital Elevation Models) data in my opinion the most useful, it allows you to grab a 3D snap shot of an area, in which you can then overlay your data on top of it.( here are the file specs for SDTS dem data Unfortantly there is no perl modules that will convert sdts information, but there are some great C libraries and programs already avaliable.
Coordinate Module
This little gem by Curtis Mills is not on CPAN, however you can download it from his site at: http://www.eskimo.com/~archer or ftp://ftp.eskimo.com/u/a/archer/aprs/xastir/ Curtis has converted several C GNU libraries into pure perl code with this module. However be aware that this is still a work in progress and has some small errors:
Please note that I didn't pay a lot of attention to
keeping the "double" notation in the form of higher
precision floating point routines. This means that
the Perl5 code won't be as accurate as the original
C-code. It doesn't matter for my purposes. If
anyone converts to Math::BigFloat for higher precision,
please send me the changes. As it is I did a
quick check and found a difference of only 1.4 meters
between my Perl results and the results from a web-based
datum-shift calculator on the 'net. -- Curt.
If you want to check the accuracy of the results generated by Coordinate I suggest submiting your data to this web form
What does it exactly do?
- Creating and manipulating Coordinate objects
- Translating coordinates between UTM and Latitude/Longitude
- Translating coordinates between ~231 different datums (Datums are used to "describe" the surface of Earth since its not a perfect Geometric shape, its Geodetic.
- Formatting coordinates into decimal degrees, degrees/minutes, and degrees/minutes/seconds.
Here is a chunk of sample code provided by Curtis:
use Coordinate;
my $position = Coordinate->new();
$position->latitude(48.125);
$position->longitude(-122.500);
$position->datum("NAD27 CONUS MEAN:W of Mississippi/Except Louisiana
+/Minnesota/Missouri"); # Datum
printf("Starting position(Lat, Long): %s %s\n",
$position->latitude(),
$position->longitude() );
$position->degrees_minutes_seconds(); # Convert to DD MM SS f
+ormat
printf("Starting position(Lat, Long): %s %s\n",
$position->formatted_latitude(),
$position->formatted_longitude() );
$position->lat_lon_to_utm();
printf("Calculated UTM position(Easting, Northing, Zone): %f %f
+ %s\n",
$position->easting(),
$position->northing(),
$position->zone() );
$position->utm_to_lat_lon();
printf("Calculated Lat, Long position(Lat, Long): %f %f\n",
$position->latitude(),
$position->longitude() );
print "Changing from NAD27 to WGS84 datum...\n";
$position = $position->datum_shift_to_wgs84();
printf("Calculated Lat, Long position(Lat, Long): %f %f\n",
$position->latitude(),
$position->longitude() );
$position->degrees_minutes_seconds(); # Convert to DD MM SS
printf("Calculated Lat, Long position(Lat, Long): %s %s\n",
$position->formatted_latitude(),
$position->formatted_longitude() );
print "Changing from WGS84 to NAD27 datum...\n";
$position = $position->datum_shift_from_wgs84_to( "NAD27 CONUS MEAN:
+W of Mississippi/Except Louisiana/Minnesota/Missouri" );
printf("Calculated Lat, Long position(Lat, Long): %f %f\n",
$position->latitude(),
$position->longitude() );
print "\n0\n";
my $temp = CoordinateFormat->new( "0" );
printf(" decimal_degrees: %s\n", $temp->decimal_degrees( )
+);
printf(" degrees_minutes: %s\n", $temp->degrees_minutes( )
+);
printf("degrees_minutes_seconds: %s\n\n", $temp->degrees_minutes_sec
+onds() );
print "180\n";
$temp->raw( "180" );
printf(" decimal_degrees: %s\n", $temp->decimal_degrees( "1
+80") );
printf(" degrees_minutes: %s\n", $temp->degrees_minutes( "1
+80") );
printf("degrees_minutes_seconds: %s\n\n", $temp->degrees_minutes_sec
+onds("180") );
print "180 30\n";
$temp->raw( "180 30" );
printf(" decimal_degrees: %s\n", $temp->decimal_degrees( "1
+80 30") );
printf(" degrees_minutes: %s\n", $temp->degrees_minutes( "1
+80 30") );
printf("degrees_minutes_seconds: %s\n\n", $temp->degrees_minutes_sec
+onds("180 30") );
print "180.50\n";
$temp->raw( "180.50" );
printf(" decimal_degrees: %s\n", $temp->decimal_degrees( "1
+80.50") );
printf(" degrees_minutes: %s\n", $temp->degrees_minutes( "1
+80.50") );
printf("degrees_minutes_seconds: %s\n\n", $temp->degrees_minutes_sec
+onds("180.50") );
$temp->raw( "180 30.50" );
print "180 30.50\n";
printf(" decimal_degrees: %s\n", $temp->decimal_degrees( "1
+80 30.50") );
printf(" degrees_minutes: %s\n", $temp->degrees_minutes( "1
+80 30.50") );
printf("degrees_minutes_seconds: %s\n\n", $temp->degrees_minutes_sec
+onds("180 30.50") );
$temp->raw( "180 30 30" );
print "180 30 30\n";
printf(" decimal_degrees: %s\n", $temp->decimal_degrees( "1
+80 30 30") );
printf(" degrees_minutes: %s\n", $temp->degrees_minutes( "1
+80 30 30") );
printf("degrees_minutes_seconds: %s\n\n", $temp->degrees_minutes_sec
+onds("180 30 30") );
$temp->raw( "180 30 30.5" );
print "180 30 30.5\n";
printf(" decimal_degrees: %s\n", $temp->decimal_degrees("180
+30 30.5") );
printf(" degrees_minutes: %s\n", $temp->degrees_minutes("180
+30 30.5") );
printf("degrees_minutes_seconds: %s\n\n", $temp->degrees_minutes_sec
+onds("180 30 30.5") );
$temp->raw( "-180 30 30.5" );
print "-180 30 30.5\n";
printf(" decimal_degrees: %s\n", $temp->decimal_degrees("-180
+ 30 30.5") );
printf(" degrees_minutes: %s\n", $temp->degrees_minutes("-180
+ 30 30.5") );
printf("degrees_minutes_seconds: %s\n\n", $temp->degrees_minutes_sec
+onds("-180 30 30.5") );
The output from this looks like following:
Starting position(Lat, Long): 48.125 -122.5
Starting position(Lat, Long): 48 07 30.00000000 -122 30 0.00000000
Calculated UTM position(Easting, Northing, Zone): 537208.685551 533
+0095.589079 10U
Calculated Lat, Long position(Lat, Long): 48.124997 -122.500000
Changing from NAD27 to WGS84 datum...
Calculated Lat, Long position(Lat, Long): 48.124789 -122.501238
Calculated Lat, Long position(Lat, Long): 48 07 29.23995960 -122 30
+ 4.45751911
Changing from WGS84 to NAD27 datum...
Calculated Lat, Long position(Lat, Long): 48.124997 -122.500000
0
decimal_degrees: 0
degrees_minutes: 00 0.00000000
degrees_minutes_seconds: 00 00 0.00000000
180
decimal_degrees: 180
degrees_minutes: 180 0.00000000
degrees_minutes_seconds: 180 00 0.00000000
180 30
decimal_degrees: 180.50000000
degrees_minutes: 180 30
degrees_minutes_seconds: 180 30 0.00000000
180.50
decimal_degrees: 180.50
degrees_minutes: 180 30.00000000
degrees_minutes_seconds: 180 30 0.00000000
180 30.50
decimal_degrees: 180.50833333
degrees_minutes: 180 30.50
degrees_minutes_seconds: 180 30 30.00000000
180 30 30
decimal_degrees: 180.50833333
degrees_minutes: 180 30.50000000
degrees_minutes_seconds: 180 30 30
180 30 30.5
decimal_degrees: 180.50847222
degrees_minutes: 180 30.50833333
degrees_minutes_seconds: 180 30 30.5
-180 30 30.5
decimal_degrees: -180.50847222
degrees_minutes: -180 30.50833333
degrees_minutes_seconds: -180 30 30.5
Curtis has also provide a few other methods that maybe useful,
EllipsoidTable->enumerate();
DatumTable->enumerate();
Both of these methods display the data tables that the program is basing its calculations on.
|
Storable
3 direct replies — Read more / Contribute
|
by TheoPetersen
on Feb 22, 2001 at 16:11
|
|
|
Storable is one of those modules that I use so much, I forget it's there.
My day job involves an overly
ambitious application builder. The designer (one of my co-workers
or a customer of ours) writes a text definition of an application and
runs it through our compiler (using Parse::RecDescent, which I'd
review also if it weren't being replaced), which builds the Perl object
representation of the application and stores it in a repository via DB_File.
When I first started working on the compiler, I wrote my own code
to store and reconstitute objects in the repository. As it got more
complex (and slow) I started to think this had to be a problem someone
else had already solved. I went looking for help and discovered
Storable (and CPAN along the way -- I was just a wee slip of a Perl coder then).
Storable makes this kind of thing trivial. If you have coded your
own solution as I was, don't be surprised if big stretches of perl vanish into a
few imported function calls. Here's all the code you need to turn an object into
a scalar:
use Storable qw(freeze thaw);
...
$buffer = freeze($obj);
The $buffer scalar now contains a very compact representation of the object --
whether it was an array reference, a blessed hash or whatever. Drop
that string into your favorite file, tied DBM database or SQL blob and you're done.
Retrieve that same scalar in some other stretch of code (or another program,
as long as it has loaded all the necessary modules) and you can have your
object back just as easily:
$newInstance = thaw($buffer);
If the frozen buffer was a blessed reference, then so is the new instance,
but not the same reference; Storable can be used to clone complex objects
and structures this way, and even has convenience functions for that. (But you might
want to look at Clone instead.
Storable's pod suggests that objects can inherit from it and use freeze and
thaw as methods. I don't do that; instead I store and retrieve objects
from the aforementioned tied DB_File database like so:
sub store {
my $obj = shift;
my $key = $obj->key;
$db{$key} = freeze($obj->freeze);
return $key;
}
sub fetchFromDb {
my ($key, $noWake) = @_;
if (my $buf = $db{$key}) {
my $obj = thaw($buf);
return $noWake || !$obj ? $obj : $obj->wake;
}
return undef;
}
(Code that checks if the database was opened for write and so on was omitted
for cleaner lines and that sexy soft-spoken style.)
The two functions are in a module that hides the details of the
database from the rest of the program. The store function in effect becomes
a filter that transforms an object into its retrieval key. If the object
has attributes that shouldn't be stored (run-time only information, say)
then it's special-built freeze method gets rid of it and returns $self.
The fetch function can be used to retrieve the object in its frozen state,
or (normally) will invoke a wake method to let the instance rebuild
any run-time state it needs before it faces the world.
Okay, this is rapidly turning into a review of how I use Storable
instead of what the module does, so back to the feature list.
Storable's documentation emphasizes the number of ways it will write
and retrieve objects from files and other IO entities. If you use a file
for each object (and remember that an "object" can be a simple hash or array
too, no blessings required) then Storable will do all the work including
opening and closing the files for you:
store \%table, 'file';
$hashref = retrieve('file');
To borrow more examples from the pod, you can use opened file handles too:
store_fd \@array, \*STDOUT;
nstore_fd \%table, \*STDOUT;
$aryref = fd_retrieve(\*SOCKET);
$hashref = fd_retrieve(\*SOCKET);
The "n" versions of store and store_fd use network byte ordering for
binary values, making it reasonably safe to store and retrieve objects
across architectures. The retrieval examples show fetching objects from
an open socket -- Perl-based object servers, anyone?
While feature-rich, Storable remains fast, much faster than my
original code. It is implemented in C with a close eye on Perl internals
to work swiftly and efficiently.
Storable has added quite a few features since I started using it;
for example, you can now add your own hooks to the freeze and thaw code
to implement what I did above at a lower level. In those hooks you can
use special class methods to find out more about what Storable is doing
and decide how your hook should act.
Since CPAN now (optionally) uses Storable to store metadata,
many Perl admins are aware of it, but might not be putting it to use
in their own code. Consider this module any time you find yourself
writing a loop to store a hash or array to a file. Storable "scales up"
to more complex structures seamlessly, so you can use your favorite
tricks without worrying about how you're going to write and retrieve it later.
|
HTML::TreeBuilder
No replies — Read more | Post response
|
by Nooks
on Feb 16, 2001 at 12:56
|
|
|
I've been happily using this module for a few months. If you dislike code that (ab)uses regular
expressions to parse HTML, this module could be what you're looking for!
TreeBuilder uses HTML::Parser under the hood, and at the moment is fairly tightly coupled to
HTML::Element, since it builds a tree of those objects if the parse is successful. (The author
spoke recently on the libwww mailing list about making the module capable of building a tree of,
say, subclassed HTML::Elements.)
The killer feature of this module is that it tries to parse HTML as a browser would, rather than treating
all input HTML as supposedly perfectly compliant documents---which the majority of them are not!
This is extremely useful. I have not seen a HTML parser for any other language that does anything like this.
Even though you'll use HTML::TreeBuilder, most of the functionality you'll want to use
is in HTML::Element. The look_down() method is very useful---called on an
Element, it searches down the tree looking for Elements that match a list of criteria. It's possible
to specify a code reference as an argument (other forms of arguments are supported); Elements that
pass the sub are returned (actually, in scalar context the first such Element is returned). Since
look_down (and its sister, look_up, among many others) returns an Element, it's
easy to search on successively more specific criteria for just what you want, and the code (written
correctly) will keep working even if the HTML changes (I've used this pretty successfully to deduce
the form contents required to fake
a HTTPS login to HotMail---I'd post it here but there is too much LWP clutter in the way of
what should be presented to show how this module shines).
The module also provides Tree cloning, cutting, and splicing functionality, much like you'd expect from
a Document Object Model in other languages (or even Perl!). TreeBuilder objects can be converted to and
from HTML and XML Element trees using the HTML::DOMbo module, by the same author. (I haven't
used this functionality myself...yet.)
There are a few slight downsides to the module---at the moment it can't be usefully subclassed (a very
minor problem); it's probably not as fast as searching your HTML with a regex; it may not even be as
fast as `grepping' through parsed HTML via HTML::Parser directly. However I had to work
with it quite extensively before I found any of these things even slightly problematic.
The author, Sean M. Burke <sburke@spinn.net>, maintains the code well, and is ready to answer questions
on the LWP mailing list.
An excellent module that anyone dealing with HTML should become familiar with.
|
diagnostics.pm
1 direct reply — Read more / Contribute
|
by damian1301
on Feb 07, 2001 at 23:57
|
|
|
Note: The only reason I put the title as 'use diagnostics' is because I didn't want it to be confused with the Perl documentation page diagnostics.
As many of the monks out there already know, there is a module that catches errors and can potentially help you solve them by giving you examples and explanations of what, possibly, could go wrong.
Just to explain this to you a little more clearly, here is an example that can commonly fail and cause a load of unneeded problems, which no one needs :). This is quite easy though.
#!/usr/bin/perl
use diagnostics;
my ($q,$w) = 1,2;
Obviously enough, this is a simple error to most Perl users. For those that don't know, the diagnostics module provides some quidelines for you.
Useless use of a constant in void context at c:\windows\TEMP\DzTemp.pl
+ line 3 (#1)
(W void) You did something without a side effect in a
context that does nothing with the return value, such as a
statement that doesn't return a value from a block, or the
left side of a scalar comma operator. Very often this
points not to stupidity on your part, but a failure of Perl
to parse your program the way you thought it would. For
example, you'd get this if you mixed up your C precedence
with Python precedence and said
$one, $two = 1, 2;
when you meant to say
($one, $two) = (1, 2);
Another common error is to use ordinary parentheses to construct a
list reference when you should be using square or curly brackets,
for example, if you say
$array = (1,2);
when you should have said
$array = [1,2];
The square brackets explicitly turn a list value into a
scalar value, while parentheses do not. So when a
parenthesized list is evaluated in a scalar context, the
comma is treated like C's comma operator, which
throws away the left argument, which is not what you want.
See perlref for more on this.
As you can see this gives great help and most of the time will solve the problem. There are also alot of other good warning modules that you should use to solve an unknown error in your script or just help you maintain it. Some of these are.
1. CGI::Carp
2. warnings
3. strict
In conclusion, this is a great module that should be put at the top of your script next to use strict; . Great stuff. I recommend it.
|
Getopt::Declare
3 direct replies — Read more / Contribute
|
by danger
on Feb 02, 2001 at 17:25
|
|
|
Getopt::Declare A quickie overview.
Getopt::Declare is a module for parsing command line options -- and
like many of Damian Conway's modules, this one has obviously been
eating its wheaties with large doses of steroids (and it arrives with
circa 1,500 lines of documentation to prove it). In short, this is not
your mother's command line parser.
Not satisfied with giving us yet another command line parser,
Damian has given us a declarative language to specify not only
command line options, but descriptions, parameter types, and actions
as well. But don't let the size of the docs intimidate you, it is
surprisingly simple to use.
The basic mechanics of using the module are as follows:
#!/usr/bin/perl -w
use strict;
use Getopt::Declare;
my $spec = <<'SPEC';
# put your specification here
SPEC
my $ops = Getopt::Declare->new($spec);
# rest of your program
Obviously, it is the specification we are really interested in.
So let's look a very trivial greplike script using the module:
#!/usr/bin/perl -w
use strict;
use Getopt::Declare;
use vars qw/$not $re/;
$::VERSION = 0.01;
my $spec = <<'EOS';
-p <pattern> pattern [required]
{ $re = $pattern }
-f <INFILE>... input filename(s) [required]
{ defer{process(@INFILE)} }
-not print out non-matches
{ $not = 1 }
EOS
my $opts = Getopt::Declare->new($spec);
sub process {
@ARGV = @_;
while(<>){
if($::not){
print unless /$::re/;
} else {
print if /$::re/;
}
}
}
__END__
An option declaration is comprised of a few components: the option
specification itself (followed by one or more tabs); the option
description (with optional directives); and an optional action block
to be executed if the option is found. Let's break out the -f option
above and look at each component:
-f <INFILE>...
# So we specify an option that looks like '-f' which takes one or
# more arguments (that's the ... part) that will be stored in the
# variable @INFILE for the action block
input filename(s) [required]
# This is the description of the option followed by the
# [required] directive which means this option must be present on
# the command line
{ defer{process(@INFILE)} }
# This is the action block. The defer() function is from
# Getopt::Declare and takes a code block which will not be
# executed until all the command line options have been parsed.
# Here we merely provide our own function and pass it the files
# from the -f option as arguments.
The option variable is available as a lexical variable within the
action block, and you may set or access any globals that are available
at the time of parsing. In our example above we set the global
$re and $not variables in the action blocks
so we can access those later rather than accessing those options via
the $opts object. We deferred our file processing action
because we want to ensure all options have been parsed (and all
globals set) before we start grepping our files.
You can also restrict parameter types when specifying parameter
variables using a few predefined parameter types:
-p <var:s> # accept strings
-n <var:n> # accept any real number
-i <var:i> # accept only integers
And, because this is Perl, you can also specify your own regex to
limit the types of things a parameter should accept or define new
types:
-hw <hw:/\d+x\d+$/> HeightxWidth coordinates
#or
[pvtype: coord /\d+x\d+$]
-hw <hw:coord> HeightxWidth coordinates
This module also gives us a -help option for free, and
a -v option (if you've defined a $VERSION
variable). Here's what we get with those:
$ perl dopt.pl -v
dopt.pl: version 0.01 (Fri Feb 2 09:30:34 2001)
$ perl dopt.pl -help
Usage: dopt.pl [options] -p <pattern> -f <INFILE>...
dopt.pl -help
dopt.pl -version
Options:
-p <pattern> pattern
-f <INFILE>... input filename(s)
-not print out non-matches
And, with 1,500 lines of documentation I've clearly only
scratched the surface in this little overview. Go ahead and grab it,
read the docs, and play around (and, if you're feeling brave, read the
code too).
|
HTML::Mason module review
1 direct reply — Read more / Contribute
|
by TheoPetersen
on Feb 01, 2001 at 17:24
|
|
|
In honor of Mason's 1.0 release I thought I'd submit a quick review.
This is one of my favorite Perl modules, and my tool of choice for creating Web pages.
Mason is developed by Jonathan Swartz
and a large number of other contributors. It was created originally for CMP Media,
publishers of TechWeb.com and a wide variety of other sites;
since then it has been put in use at a number of high-profile sites
such as Salon.com and AvantGo.com, not to mention hordes of lower traffic places.
Embedding Perl in HTML
Anyone who's perused the HTML:: namespace on CPAN knows that there are a number of tools
to choose from in putting Perl into Web pages. Among that list of choices, Mason is
unique, just like all the rest :).
Mason is first and foremost a Perl embedding tool; that is, it inserts Perl code
directly into pages, to be interpretted on the fly as documents are served.
This distinguishes it from the "template" camp of tools that separate documents
and code; I won't get into the argument over which is better, but if you work on both document content and
code together, you'll probably prefer an embedding tool. Other choices in this camp
include HTML::EmbPerl and Apache::ASP.
Mason adds a few new tags to tell its interpretter where to find Perl code.
To evaluate a single line of Perl, preface it with a % at the beginning of the line:
% my $date = localtime();
To interpolate a Perl variable or expression into other text, surround it with the <% % > tag:
Today's date is <% $date %>
% lines can also be used to create conditional sections, or to build text using loops. To borrow two examples from the docs:
% my $ua = $r->header_in('User-Agent');
% if ($ua =~ /msie/i) {
Welcome, Internet Explorer users
% } elsif ($ua =~ /mozilla/i) {
Welcome, Netscape users
% }
% foreach $item (@list) {
<li><% $item %>
% }
Longer pieces of code can be set off using <%perl> sections, where
all statements up to the ending </%perl> are interpretted as code.
Component architecture
Mason's code embedding is nice, but the module really shines when used to build documents from pieces.
In Mason, sections of pages are components. A top-level component corresponds to a
document, and can contain all the content and Perl code directly. More likely
though, the top-level refers to other components via the component call tag, <& &>.
For example, suppose we have a page layout consisting of a header component, a navigation bar,
a footer and what ever text the page contains. A top-level for that design would look like this:
<& header &>
<& nav-bar &>
Here is the content of this page.
<& footer &>
Passing arguments to components
But wait, there's more! The top-level component automatically receives any CGI parameters
passed along with the request as arguments; it can also pass parameters along to any invoked
component, which can in turn pass info along to any components they use.
To invoke a component with arguments, pass them along inside the tags:
<& header, date=>$date, section=>'News' &>
To receive those values, the component needs an <%args> section:
<%args>
$date
$section=>'Updates'
</%args>
In this example, the date argument is required to have a value when the component
is invoked; the section argument has a default value, and can be omitted.
Autohandlers and dhandlers
Mason provides two mechanisms for additional standardization.Autohandlers are components
that run before the top-level component; they can be used to provide a standard
set of header/footer components or enforce other site rules. Mason checks the top-level
component's directory (and all of its parents) for an autohandler and runs it before
the code in the top-level itself.
dhandlers take the place of a component that doesn't exist. This is most commonly used
to replace top-level components requested by a generated URL; if Mason doesn't find
the requested component, it checks the directory (and its parents) for a dhandler and
runs that instead. The dhandler can look at the request URL and figure out what
to do, such as retrieving the actual requested document from a database.
Caching
Behind the scenes, Mason translates a component into a Perl function and calls it;
text in the component becomes literal values stored in the function, while the embedded code
moves into the function more or less as-is. Mason caches the translated code
in a separate directory for better run-time performance. As requests occur and components
are called, Mason checks the modification times of its cache and the actual component file;
if nothing has changed, it just uses the cache.
Used in conjunction with mod_perl, this provides excellent performance, but those
stat calls for checking times still add up. You can improve Mason's speed by telling it
to check only a single file (the reload file) for updates, and then store the paths to changed
components in that file as you modify them.
And more...
Mason has far too many features to do justice in a short review. A quick list of things
to look for in the documentation:
- Filters that allow you to work on the output of a component; these can be used
to apply further rules or changes once a document has been generated.
- Support for previewing, profiling and debugging components.
- Component methods and attributes that make a rich object-oriented system.
- Though it's a separate product, the Mason Content Manager provides graphical
file management and a staging/production system that is aware of components
and works with the reload file mechanism mentioned above.
Take a look at the Mason Headquarters for more information.
2001-03-04 Edit by Corion : Changed PRE tags to CODE tags.
|
Geo::Weather
No replies — Read more | Post response
|
by damian1301
on Jan 28, 2001 at 22:14
|
|
|
I just got this module today with the advice of jcwren and it is the easiest way to get the weather from the web. All it takes is a simple snippet like this:
#!/usr/bin/perl
use Geo::Weather;
my $weather = new Geo::Weather;
$weather->get_weather('Philadelphia','PA');
print $weather->report();
And that little bit will come with a weather report that will come out with this (Today, I mean):
<font size=+4>Philadelphia, Pennsylvania</font><br>
<img src="http://image.weather.com/weather/wx_icons/PFMScurrent/28.gif
+" border=0>
<font size=+3>Mostly Cloudy</font><br>
<table border=0>
<tr><td><b>Temp</b></td><td>37° F</td>
<tr><td><b>Wind</b></td><td>From the West at 15 mph</td>
<tr><td><b>Dew Point</b></td><td>18° F</td>
<tr><td><b>Rel. Humidity</b></td><td>39%</td>
<tr><td><b>Visibility</b></td><td>unlimited</td>
<tr><td><b>Barometer</b></td><td>30.25 inches</td>
<tr><td><b>Sunrise</b></td><td>7:14 am</td>
<tr><td><b>Sunset</b></td><td>5:16 pm</td>
</table>
But if you don't want that big report you can even shorten it to make it more limited and more..fashionable:
use Geo::Weather;
my $weather = new Geo::Weather;
my $current = $weather->get_weather('19067');
print "The current temperature is $current->{temp} degrees\n";
And that would just print:
The current temperature is 35 degrees
Now, all this code presented is well documented in the module. All the keys are covered with a description next to it. So, in conclusion, this is MUCH easier than going out and writing a whole load of regular expressions to grab the weather where the results might not even be accurate! Trust me, I know. Get it here
UPDATE: In the full report, you can use the zip code in there too for convenience. If your not connected to the internet when you run it, all that will happen is a little error.
|
AppConfig
3 direct replies — Read more / Contribute
|
by BoredByPolitics
on Jan 21, 2001 at 19:01
|
|
|
++
Extremely flexible handling of both configuration files, and commandline parameters.
Four parameter types - boolean, scalar, array and hash.
As each parameter is defined it can have various properties set,
including DEFAULT setting, ACTION to take on use,
VALIDATE based on regex or sub result, various types of
variable EXPANDing, variable name ALIASes.
The properties DEFAULT, ACTION, VALIDATE, and EXPAND can be set globally as well as local to a parameter.
Can handle multiple sources of configuration file, including filehandles.
--
VALIDATEs which use a sub don't appear to trigger the return of a false value from the arg() method, although a warning to the user is printed.
ACTIONs cannot alter the value they're attached to, as this sets up a circular reference.
Overall
An extremely useful module which, although it takes a bit of time to get to know, is well worth the extra effort.
|
Text::CSV
5 direct replies — Read more / Contribute
|
by TStanley
on Jan 10, 2001 at 21:27
|
|
|
Author: Alan Citterman
I had a project where I needed to extract data from a file and send
it to a customer. The file in question was from a database, and it
had been exported to a CSV text file.
I would have tried to write my own regular expression to handle this,
but my overall knowledge of Perl isn't that good. However, after some
research, I found a reference to this module.
#!/usr/bin/perl
use strict;
use Text::CSV;
I knew that the text file had lines of data that I didn't need, and
that there was an easily recognizable pattern in those lines, so I could
use a regular expression to put those lines into a trash file.
my $input="input.csv";
my $output="output.txt";
my $trash="trashfile";
my $csv=Text::CSV->new(); #Creates a new Text::CSV object
open(INFILE,$input) || die "Can't open file $input";
open(OUTFILE,">$output") || die "Can't open file $output";
open(TRASH,">$trash") || die "Can't open file $trash";
Now to start reading the data from the file, store it in the $_ variable
and print it to the trash file if its not good, or parse the variable, and
print it to the output file if it is.
while (<INFILE>) {
if (/"X"/) { #The trash data has these 3 characters in it
print TRASH "$_\n";
}
else { #Now to deal with the data I want to keep
if($csv->parse($_)) { #checks to see if data exists in $_ and
+parses it if it does
my @fields=$csv->fields; # puts the values from each field in an
+array
my $elements=@fields; #gets the number of elements in the arra
+y
for ($x=0;$x<$elements;$x++) {
print OUTFILE "$fields[$x]\t";
}
}
}
}
Now that the files have been written to, I can close them up, and remove
the trash file
close INFILE;
close OUTFILE;
close TRASH;
unlink $trash;
All in all, a very useful module.
|
DBI vs. Oraperl
No replies — Read more | Post response
|
by wardk
on Dec 11, 2000 at 23:56
|
|
|
Module(s):
- Oraperl.pm
- DBI.pm (DBD::Oracle)
Author(s):
- DBI and Oraperl emulation using DBD::Oracle by <cite><Tim.Bunce@ig.co.uk></cite>
- Original Oraperl 2.4 code and documentation by <cite>Kevin Stock <kstock@auspex.fr></cite>.
This is a short review of the two most used Perl toolkits for retrieving
data from an Oracle database, DBI and Oraperl.
In the beginning, there was Oraperl, and if you were using Perl 4, Oraperl was
the only game in town. Then came Perl 5, then DBI. Now every RDBMS
worth using has a uniform interface for Perl.
But...what to do with all that old Oraperl code? Re-write?, /dev/null?, keep it?
......you *can* keep it!
The Oraperl module (originally written by Kevin Stock) was re-written for Perl 5
by Tim Bunce to use the DBD::Oracle interface. So you get the best of both
worlds...backward compatability for your existing
scripts, and a new API to utilize in case you desire a generic interface that
could perhaps work against another DBI supported RDBMS.
On the job, I use both. I support a web-based HR system that was written
originally in Perl 4 using Oraperl. This codebase now is on Perl 5, however the
scripts still utilize Oraperl.
So for new development I use DBD::Oracle via DBI, and when maintaining existing
scripts I keep using Oraperl.
When first taking over this position, I was convinced that I would need to eventually remove all
that damn Oraperl....then I read the perldoc, and realized that this was not the same
Oraperl I used about 4 years previous in another life, but one written and supported by the
very man who wrote and supported DBI and DBD::Oracle...Tim Bunce.
So instead of spending time re-writing boatloads of working code, I spent some time
using Benchmark and attempted to see if there was any significant performance differences
in using DBD:Oracle directly, or using the Oraperl interface to it.
My benchmarks all proved out to me that Oraperl was in fact slower....just barely.
In fact, they were so close in my tests that I just have no valid performance
reasons to rework any Oraperl into native DBI.
So which to use for accessing your Oracle database? Since Tim mentions in the
docs that Oraperl is for backward compatibility, I suggest that you do not <cite>use
Oraperl;</cite> if you are starting fresh. If you have existing code and are
curious to the benefits of porting the code to native DBI...that's your call, I
found no performance reasons for doing so, and since the same man that supports
DBI wrote Oraperl, there shouldn't be any pressing issues over support.
Bottom line is that both these modules are of high quality and production-ready,
you can be safe utilizing either.
For this review, I have decided to not supply any code samples as Tim Bunce has
provided plenty of them in the perldocs.
So if you are curious about syntactical and/or other differences....please refer to these links:
|
DBD::RAM
1 direct reply — Read more / Contribute
|
by mirod
on Nov 30, 2000 at 11:37
|
|
|
Description
DBD::RAM
is Jeff Zucker's driver for the DBI
that allows you to import files in memory and treat them as relational tables,
with SQL queries.
Several tables can be created this way and SQL joins can be simulated
through loops.
Changes to the data can be reflected to the original file (if the table is
created using the catalog function) or the table can
be export-ed to one of the formats supported by DBD::RAM.
DBD::RAM can process the following formats:
- CSV (Comma Separated Values),
- Fixed-width records, using pack to define the format,
- Perl structures (arrays of arrayrefs or hasrefs) so you can create
the data in the Perl script and then use it as a relational table,
- XML,
- User-defined, which allows the user to define a subroutine that will
parse the input and return an array with the various fields,
- Other DBI data bases, so you can load a table in memory, close the
connection and then process the data,
- MP3 headers from a group of directories.
The data in all formats can be input either locally, from strings,
files or pipes, or remotely through
LWP
Why use DBD::RAM
- you are dealing with existing data, which format you have no control over, but
you still want to access it through the DBI interface, using SQL,
- you want to use SQL without installing a relational DB,
- you want to prototype an application without a DB but think
that you might add one down the line,
- you want to convert data from a DBD::RAM supported format to an
other,
- you want to use an XML file as a table (exporting it back might
not work for you though)
Why NOT use DBD::RAM
You will not use DBD::RAM essentially if you need a real data base.
- you process huge amounts of data,
- the data is already in an existing DB.
Example
The obligatory XML example:
# connect to the DB
my $dbh= DBI->connect( "DBI:RAM:" , {RaiseError => 1} );
# create the table
$dbh->func(
{ table_name => 'projects',
data_type => 'XML',
record_tag => 'projects project',
col_names => 'pid,name,description',
file_source => 'project.xml',
}, 'import');
# prepare the SQL statement
my $sth= $dbh->prepare( "SELECT * FROM projects");
# execute the statement
$sth->execute();
# output the result of the query
while( my $hashref= $sth->fetchrow_hashref())
{ foreach my $field ( keys %$hashref)
{ print "$field: $hashref->{$field}\t"; }
print "\n";
}
# export the table back as XML
$dbh->func(
{ data_type => 'XML',
data_target => "new_projects.xml",
data_source => "select * from projects",
record_tag => 'projects project',
col_names => 'pid,name,description',
}, 'export' );
Note on XML import: make sure you include the whole hierarchy
of tags in the record_tag argument, from the document
root to the record tag itself.
Personal Notes
I really like DBD::RAM. It allows treating lots of structured data as
a relational table, including XML (mostly for extracting data from an
XML file though). It also allows quick prototyping without having to
go through the pain of yet-another-mysql install.
The good DBD::RAM is really flexible. For example in the CSV format the field and record separators
can actually be redefined so it's more like ASV (Anything Separated Values).
For most formats field definitions can also be extracted from the first line of the file
instead of being hard coded in the script.
The XML import allows you a good deal of customizing the data you want to
extract from the XML file, including having records inherit attributes from
their parents. Encoding conversions, and especially latin-1 output are
also handled.
The documentation is pretty good: it is comprehensive and includes lots of
examples that can be cut-n-paste'd.
The not-so-good
The initial debugging of an application can be quite a pain though, as error
messages on import are no too helpful, they tell you that something
is wrong but not quite where the exact error is.
Some XML data is difficult to extract (for example if several parent tags
have attributes with the same name you can't fold them properly) but this
can be fixed by a simple pre-processing of the data.
The syntax of the XML option is slightly confusing (space separated list
of tags for the record_tag argument, but comma (no space) for
the col_names argument.
This should improve with future versions of the module.
Related Modules
For XML processing you might want to have a look at
XML::RAX
or at DBIx::XML_RDB (to export XML from a RDB).
DBD::RAM should be replaced by AnyData and DBD::AnyData any time now, see
this
note for the planned architecture.
Update: after the author reviewing the... review I
have fixed a couple of (embarassing) mistakes: DBD::RAM does
file locking (through flock), but it does not do
join. I have fixed the review accordingly (plus
a couple of other points Jeff mentioned.)
Update (2): AnyData and DBD::AnyData are out and officially replace DBD::RAM.
|
Memoize
No replies — Read more | Post response
|
by ariels
on Nov 30, 2000 at 11:06
|
|
|
Description
Memoization speeds up calculation of a function by storing its previously-computed values. MJD's excellent Memoize module memoizes (almost) any function you ask it to. If you use Memoize;, applying this optimisation can be as simple as saying memoize 'slow_function';.
Additional methods exist, allowing such things as persistant storage of memoized values.
Why should I use it?
Memoization is not a technique for everyday use. Only pure functions (functions which are executed only for their return values, not for any side effects) can be memoized. It's the sort of module you should download and learn how (and when) to use.
Eventually you'll write a slow routine that gets called in a slow loop. If only a small number of different argument combinations get passed to the routine, it's an excellent candidate for memoization.
The documentation contains several examples, some of which can serve as ideas for usage. The section on persistant storage of the cache is particularly noteworthy.
Why should I read it?
The documentation, code and a Perl Journal article make excellent associated reading, sure to improve your Perl. Both explain in detail what memoization is, and how (and when!) to use it. In particular, the article explains how the module performs its magic, by giving a very short (but complete!) implementation of the principle routine, memoize.
Ways to use the module are mentioned, with examples:
- Simple memoization
- Using a batch process to "pre-memoize" some values persistantly
- Memoizing list and scalar context together (normally the module keeps the contexts separate).
- Support for Memoize::Expire.
- Memoization as a lightweight substitute for dynamic programming.
- Memoization as a clearer replacement for the Orcish maneuver.
You should also read the documentation (or the article), paying attention to the routine's limitation regarding functions taking multiple arguments which may contain the contents of $; (see "Why shouldn't I use it", below).
Finally, the author of this review is mentioned in the module's documentation. This gives at least one person an additional reason to read the documentation.
Why shouldn't I use it?
Most likely, because your problem doesn't need it.
If your problem does need memoization, you might still need to help the module a bit.
- If your routine takes multiple arguments, Memoize codes them using the single key join $; , @_; if your arguments may contain the contents of $; (in particular, if one of the arguments is binary data), you might need to do some extra work.
- If your routine takes a complex data structure as argument, you'll probably also need to do some extra work to use the module.
The documentation describes both problems and how to solve them. Neither is a showstopper, but it is probably worth your while to know about these problems before you run into them.
Probably the single biggest drawback of memoization is that it doesn't work for functions with side effects. This is nothing to do with Memoize, and everything to do with the technique. Unfortunately, there is no way the module can check this for you. Blindly applying memoize to every slow function in your program will not work, and will cause your program to fail without warnings! Make sure to understand what you are doing, before you do it.
2001-03-15 Edit by Corion : Removed spurious links
|
Spreadsheet::WriteExcel
1 direct reply — Read more / Contribute
|
by BigGuy
on Nov 03, 2000 at 20:03
|
|
|
To use this module
use Spreadsheet::WriteExcel;
As stated above this module allows you to write excel files without that annoying popup asking you how your file is delimeted when you open the file. It works very well, the only problem i had was trying to convert one of our flat files that is over 70,000 records in length. But after i checked the problem is documented, it can only produce files <~ 7.0 MB. heres on little program I have created
with it
#!/usr/bin/perl
use Spreadsheet::WriteExcel;
print "Enter the path to the file(ie /home/): ";
$path = <>;
chomp($path);
print "Enter the filename(ie data): ";
$fname = <>;
chomp($fname);
$file = "$path$fname";
my $workbook = Spreadsheet::WriteExcel->new("/tmp/$fname.xls");
$worksheet1 = $workbook->addworksheet(sheet1);
$worksheet2 = $workbook->addworksheet(sheet2);
$worksheet3 = $workbook->addworksheet(sheet3);
$format = $workbook->addformat();
$format->set_bold();
$format->set_color('black');
$format->set_align('center');
$format->set_size('10');
open input, "$file" || die "Can't open that file";
$k=0;
for (<input>){
chomp($_);
@data = split(/\t/,$_);
for ($i=0; $i<=@data; $i++){
$worksheet1->write($k, $i, $data[$i], $format);
}
$k++;
}
Good Luck
Rob
|
Image::Magick
5 direct replies — Read more / Contribute
|
by Corion
on Oct 25, 2000 at 08:20
|
|
|
Update:The Image Magick URL was updated, thanks to the good folks notifying me.
Image::Magick (also called PerlMagick) is the Perl wrapper for
Image Magick,
a feature rich command line image manipulation program.
What's good ?
If you have to manipulate images (crop, resize, convert,
montage, grayscale, paint), Image::Magick is the
thing for you. The ImageMagick suite of programs is seasoned
and converts images between a set of claimed 68 different formats.
If you're thinking about rolling your own image decoder, think
if maybe ImageMagick already does the stuff for you.
ImageMagick (and Image::Magick) is available for a lot of
platforms, ranging from Win32 to VMS and OS/2.
Image::Magick is used widely for creating images, for example the
GeoCities banner generator uses Image::Magick.
What's bad ?
ImageMagick is written in C. While there is not that
much bad about this immediately, this means, you have to find
and install a version of ImageMagick written for your platform.
The second thing is, that Image::Magick also needs
some XS compiled, which means that you will need some way
to compile the XS for your target machine. ActiveState
has a Image::Magick for Perl 5.005_03 (build 522), but
there is no Image::Magick package for Perl 5.6 (build 613+)
- a nasty thing to find out after you upgrade.
The third thing that might keep you from using Image::Magick
is, that it is a bit overkill for some jobs. If you only need
the dimensions of an image, think about using Image::Size,
and if the image types you want to accept are limited (think PPM),
a Perl decoder might be faster at load time/run time than
Image::Magick. But Image::Magick saves you the trouble
of finding a correct specification for your particular image
format and you can add new file formats easily.
Things of note
The interface documentation for the Perl interface is installed
with the main ImageMagick package, the Image::Magick package
itself comes without documentation. The documentation itself
leaves some bits wishing, it takes some experimentation until
you get the parameter names right for every function call
you can make. Other than that, the design is OK, you
instantiate an Image::Magick object, into which you can
load one or more images, you convert them and then you write
them out to disk using the methods of the object. This
is mainly efficient for converting large batches of images.
If you can't get Image::Magick to work from within Perl,
there is always the backup method of using shell commands
to convert and mogrify, which are two command
line programs that act as call-ins to the ImageMagick libraries
as well.
|
|