We have a few Third Party Perl libraries for transferring files to/from, and encrypting/decrypting files using PGP, among other things. Currently, these libraries use redundant code, and execute the above-mentioned methods using system() and command-line binaries.
I'm looking to re-write these libraries, using more modules and OOP where necessary. I'm currently racking my brain about how I want to setup the main library, which Perl scripts would include to use Net::SFTP to put/get files, and Crypt::PGPSimple to encrypt/decrypt files.
Should a library module be written in OOP? Does that make sense? Or should methods be imported (and designed procedurally) as needed, and create Net::SFTP and Crypt::PGPSimple objects as necessary? I'm just not sure I want to create a Lib object, and initialize SFTP, PGP, Constants, etc. all in the new sub. I figure I see this type of class more like a Java class with static methods, but there should be only one SFTP object/connection (not sure if Net::SFTP already takes care of this?), and one Crypt::PGPSimple, etc.
Also, back to the redundancy, this library should also have a parent which defines functions which many of the Third-Party libraries use (FTP, PGP, etc).
I'm not looking for a definite answer, as there probably isn't one, but maybe how others approach a design like this, and what makes the most "sense".
Thanks
Update: Added sample code of my OOP Library Module, which also utilizes other objects (PGPSimple, SFTP). Let me know if you can think of a better design/implementation. Thanks again.
Lib.pm
use Crypt::PGPSimple;
use Net::SFTP;
use File::Copy;
use Log::Log4perl qw(get_logger :levels);
use File::Basename;
my %CONS = (
RECIPIENT => "ClientName";
URL => 'ftp.host.com';
USER => 'user';
PASS => ''; # use subroutine to obfuscate the password
PORT => '22'
HOME_DIR => '/Home';
IN_DIR => '/Incoming';
OUT_DIR => '/Outgoing';
);
my %VARS;
# private member variables
my ($logger);
BEGIN {
%VARS = (
IS_PROD => $L_is_prod ? 1 : 0;
APPS => $ENV{'APPS'};
OUTDIR => $ENV{'OUTDIR'};
TIME_ZONE => $ENV{"TZ"};
);
$logger = get_logger("Lib");
}
sub new {
my ($class, $self) = #_;
$self = {
pgp => _setup_pgp();
sftp => undef; # Don't create SFTP connection until we need it
};
return bless($self, $class);
}
sub _setup_pgp {
my $pgp = Crypt::PGPSimple->new();
$pgp->PgpVersion(6.5.8);
$pgp->PgpExePath("/path/to/pgp-6.5.8");
$pgp->PgpKeyPath("/home/username/.pgp"); # Set this based on environment
$pgp->PublicKey("pubring.pkr");
$pgp->PrivateKey("secring.skr");
$pgp->Password(pp());
$pgp->UserId();
$pgp->PgpTempDir("/tmp/");
$pgp->PgpTimeZone();
$pgp->PgpVerbose(2);
return $pgp;
}
sub _setup_sftp {
# Create SFTP connection
my $sftp;
my ($host, $user, $pass);
$host = $CONS{URL};
$user = $CONS{USER};
$pass = $CONS{PASS};
$sftp = _connect_sftp($host, (user => $user, password => $pass));
return $sftp;
}
sub encrypt {
my ($self, $plain_file) = #_;
my $pgp = $self->{pgp};
$logger->info("Setting \$pgp->PlainTextFile to $plain_file");
$pgp->PlainTextFile($plain_file);
$pgp->PgpFlags("e");
my $result = $pgp->EncryptFile;
if($result != 0) {
$logger->info("Failed to successfully encrypt $plain_file. Error code: " . $pgp->ErrCode() . ", Result: " . $pgp->Result());
}
return $result;
}
sub put {
my $self = shift;
$self->{sftp} = _setup_sftp() if(!defined $self->{sftp});
my $local = $self->{pgp}->EncryptedTextFile();
my $remote = basename($local);
...
$sftp->put($local, $remote)
...
}
I tend to use OO if I need some kind of state for each instance. But I think nothing is bad about a procedural approach, where no "state" is needed.
Concerning the single connection problem: we (my company) use a "service-class" (procedural), which returns a suitable connection - that is useful if using threads / forks or if there can be multiple connections possible (e.g. with different options).
Update: If you decide to go OO, I would strongly recommend using Moose if possible. It will save you a lot of time, work and bugs... Have a look, how blessed references work, but use Moose or Mouse for your code.
If it makes sense to organize your data and subroutines that way, take a look at Moose, which adds a lot of OOP semantic glue to Perl 5, including inheritance.
Design the interface whichever way makes the most sense to you, anyone else who will be using the module, and the way that you intend to use it. You can even make it dual-interface relatively easily (at least if you don't use Moose; I've never made a dual-interface module with Moose, but I suspect that Moose would fight against you pretty hard if you tried it) by implicitly creating a private instance which procedural calls are run against.
Personally, I tend to go OOP for anything that requires reusable data (state, settings, etc.) for the code to run against and semi-dual-interface (specifically, procedural code which tolerates being called as MyMod->proc_name in addition to MyMod::proc_name) when every call will include all necessary data as parameters.
In this specific case, I think I'd go with a straight OO interface, with one instance of the class per open connection, then perhaps extend it to dual-interface if most apps using it only require a single connection (or perhaps not; I prefer calling things with the OO syntax rather than dealing with exported subs).
Related
Is there any way in perl to import variables from the main script to the module?
Here is my main.pl:
#!/usr/bin/perl -w
use DBI;
our $db = DBI->connect(...);
__END__
Now I want to use the $db variable in my modules, because I want to avoid duplicate connections and duplicate codes... It is possible?
You can do that by referring to $main::db in other packages. The main namespace always point to globals in the primary namespace if there is none other given. You should read up on package.
Note that this is not a very good idea as your modules will be dependent on main having the connection. Instead, you should construct your objects in a way that let you pass a database handle in. If you require a db connection at all cost, either let them throw an exception or create their own db handle.
If you are not using OO code, make the database handle an argument of every function call.
Also note that it's best practice to name the database handle $dbh.
Let's look at this for non-OO (Foo) and OO (Bar).
# this is package main (but you don't need to say so)
use strictures;
use DBI;
use Foo;
use Bar;
my $dbh = DBI->connect($dsn);
Foo::frobnicate($dbh, 1, 2)
my $bar = Bar->new(dbh => $dbh);
$bar->frobnicate(23);
package Foo;
use strictures;
sub frobnicate {
my ($dbh, $one, $two) = #_;
die q{No dbh given} unless $dbh; # could check ref($dbh)
$dbh->do( ... );
return;
}
package Bar;
use strictures;
sub new {
my ($class, %args) = #_;
die q{No dbh given} unless $args{dbh};
return bless \%args, $class;
}
sub frobnicate {
my ($self, $stuff) = #_;
$self->{dbh}->do(q{INSERT INTO bar SET baz=?}, undef, $stuff);
return;
}
__END__
You can always pass a db handle into a method. I'm not a fan of this approach, but we have code that functions using this approach.
The problem IMHO is in the debugging. It makes it difficult to know anything about the db handle itself from the code in your module, though that might not be an issue for you. Imagine, however going in to debug code that uses a db handle, but you have no idea where it came from. If you get your db handle from a method in your class, you can trace it to that subroutine and immediately you have some information. This is definitely my preferred way of doing things.
If you do pass in a DB handle, you should do some input validation, such as checking for $dbh->isa('DBI::db') (I think that's the class into which db handles are blessed).
My preference, however would be to have a subroutine in your class that gets the db handle, either based on information you pass in, or by information in the sub itself. One thing to consider is that if you're using DBI, the connect_cached() method is very helpful. From the DBI docs:
connect_cached is like "connect", except that the database handle returned is also stored in a hash associated with the given parameters. If another call is made to connect_cached with the same parameter values, then the corresponding cached $dbh will be returned if it is still valid. The cached database handle is replaced with a new connection if it has been disconnected or if the ping method fails.
Using db handle caching of some sort will, regardless of whether you were to have created the db handle in your script or in the class, give you the same connection.
So, I recommend creating a method in your class that takes all of the parameters required to replicate the creation of the db handle as you'd do it in your script, and consider using connect_cached, Apache2::DBI or something that will handle the db connection pooling/abstraction.
use parent qw<File::Spec::Unix File::Spec::Win32>;
And what--if anything--can I do about it?
Okay, I understand that Win32 inherits from Unix, but the dispatch is Win32 -> Unix and I want the Unix implementations as default until I override it and dispatch to the Win32 implementation my self.
I also understand that because File::Spec::x just passes around class names, that inheritance is not a huge issue, but I have to wonder what would happen if I actually needed some classes to coordinate this way.
Conceptually, this makes no sense. You don't have something that's a unix path and a Windows path.
Practically, this makes no sense either. There is no function in ::Win32 that's not in ::Unix.
File::Spec already abuses inheritance, and you're taking it a leap further. Inheritance is not what you need here!
Anyway, this would be equivalent to what you have, minus the error:
use parent qw( File::Spec::Unix );
use File::Spec::Win32 qw( );
sub isa {
my ($self, $class) = #_;
return $class eq 'File::Spec::Win32' || $self->SUPER::isa($class);
}
If what you want is to default to File::Spec::Unix in generally and specifically use File::Spec::Win32, you do not want to use multiple inheritance. File::Spec::Unix already inherits from File::Spec::Win32, so you've set up a (mostly) diamond inheritance which C3 does not want to resolve.
Unix
/ \
Win32 |
| |
YourCode
You simply want to inherit from File::Spec::Unix and then use File::Spec::Win32 as desired.
package My::FileSpec;
use parent qw(File::Spec::Unix);
require File::Spec::Win32;
sub devnull {
my $class = shift;
return $class->File::Spec::Win32(#_);
}
If you want to get clever, you can eliminate the wrapper method.
*devnull = File::Spec::Win32->can("devnull");
Finally, you can put that into a loop to avoid repeating yourself.
my #Win32_Methods = qw(devnull tmpdir);
for my $method (#Win32_Methods) {
my $code = File::Spec::Win32->can($method);
die "File::Spec::Win32->$method does not exist" unless $code;
no strict 'refs';
*{$method} = $code;
}
You can override isa to claim you're a File::Spec::Win32 as well as a File::Spec::Unix but that's not really true and would probably be more confusing than useful as your class only acts like Win32 for a handful of methods. Also, File::Spec has no object so it's practically not going to come up.
Overriding can is not necessary, it will return the correct code references.
first post from a newbie-user. Every question I google seems to bring me here and I always get a great answer to what I'm looking for; so naturally this was my first stop when I began pondering the usage of blessing in Perl.
I've just gotten into Perl's OOP and just today read the post asking what bless does. I now understand that it references a scalar/hash/array to an object, 'attaching' it, if you will.
In most of the examples of classes I see in Perl, they don't seem to have properties like I'm used to seeing in other languages...
{ package Person;
my $property = "This is what I'm talking about :)";
sub new { ... }
...
}
So, I created a stupid class with a property to see what would happen. I gave the property the value 'NIL' right off the bat and then 'Not Nil!' in the constructor. Using a method LIST, I was able to print the property, and as I expected, it printed 'Not Nil!'
My question is, if properties work the same as I expected them to work (declared in the body) then why use bless at all? What is the added benefit of having that reference when you could simply create a scalar/hash/array as a property, or create whatever references you want as a property?
I hope I explained what I'm trying to ask well enough, very green with Perl :)
Well, that is not how you create classes in Perl.
Your $property variable is defined in package scope. Therefore, there will only one copy of it per class rather than each object having its own copy.
One might implement such a class using hash based objects the long and hard way as below:
#!/usr/bin/perl
package Person;
use strict; use warnings;
sub new {
my $class = shift;
my $self = {};
bless $self => $class;
my ($arg) = #_;
for my $property ( qw( message ) ) {
if ( exists $arg->{$property} ) {
$self->$property($arg->{$property});
}
}
return $self;
}
sub message {
my $self = shift;
return $self->{message} unless #_;
my ($msg) = #_;
$self->{message} = $msg;
}
package main;
my $person = Person->new({
message => "This is what I'm talking about :)"
});
print $person->message, "\n";
Now, this gets tedious fast. So, there are modules that help you deal with this as well as helping you define your classes in way that is safe for inheritance.
Class::Accessor is one such utility module.
For programs where startup time is not an issue, you should consider Moose. With Moose, you can write the above as:
#!/usr/bin/perl
package Person;
use Moose;
has 'message' => (is => 'rw', isa => 'Str');
__PACKAGE__->meta->make_immutable;
no Moose;
package main;
my $person = Person->new({
message => "This is what I'm talking about :)"
});
print $person->message, "\n";
You should read perldoc perltoot and Moose::Manual::Unsweetened for the standard way of doing things.
What you did with $property in this case is declared a variable in the "Person" package's scope. You change that inside (or outside using $Person::property) of the package, and any object that refers to it will see the updated variable, so it acts a lot like a "static attribute (Java)" without any real "private" scope. By convention, hidden things in Perl ("private" or "protected") are prefixed with an underscore, but of course this isn't enforced.
You don't actually make a new class, as you pointed out, with the "package" keyword; you can use "package" without OOP at all. That simply creates a separate "namespace".
The advantage of "blessing" a variable, almost always a hash reference from what I've seen, is that you can have methods, just like any other OOP language. Just remember to bless whatever you return in the new {} subroutine ("new" isn't actually a reserved word; just a convention). When you call a method on the "object" (a blessed data structure like a hashref), the first argument of the method is the data structure itself. So, if you have a hashref called $myobject, which is blessed to AwesomeClass, and you define a method in AwesomeClass called doSomethingAwesome, which needs to accept one variable, you would have to "shift" #_ (which is the argument list of the subroutine, or use $_[0]) to access the $myobject hashref. Python does something similar, and all languages pass the object reference to the method somehow. ("this" keyword in many, see also "thiscall" calling convention)
NB: I've seen lots Perl bashing in my time, which has only been a few years as a programmer. Perl is an awesome language that was made by a very smart linguist (Larry Wall) and has a fanatical following -- more fanatical at one time than Ruby, perhaps, but not as much as David Koresh). Perl does things very differently than lots of languages but if you look at code golf entries on this site and others, you can clearly see that much can be accomplished with very little Perl (no guarantees about code legibility, especially for newcomers!)
The value of bless'ing an object is getting to use the methods from a particular package.
package MyClass;
sub answer { my ($self)=#_; return $self->{foo} * 42; }
package main;
my $object1 = { foo => 1, bar => "\t" };
my $object2 = bless { foo => 2, bar => "\t" }, "MyClass";
$ref1 = ref $object1; # 'HASH'
$ref2 = ref $object2; # 'MyClass'
$answer1 = $object1->answer; # run time error
$answer2 = $object2->answer; # calls MyClass::answer, returns 2 * 42 = 84
Ugh... Sinan's answer is entirely too learned for my taste, at least past 12am :)
So I'll give a shorter and somewhat less Perly one, just for variety's sake.
Your question is not really about Perl as far as I can tell, and can be just as easily ased in another form: "Why bother using C++ and OOP in it when C already has structs?"
In other words, you seem to be asking what the point of using OOP paradigm is.
The answer is of course that it helps solving certain software engineering problems easier than pure procedural programming. Emphasis on certain - OOP is not a panacea for every problem, any more than ANY technique/approach/paradigm is.
Using OOP (in a form of packages as classes and blessed hashes as objects in Perl) allows you to enjoy the benefits of inheritance, polymorphism and other OOPyish mumbo-jumbo which you are probably already fairly familiar with from you non-Perl OOP experience.
Can you do 100% of what you'd have done with a blessed object with a pure data structure? Absolutely. Would 100% of it be just as easy/short/readable/maintainable code as you can achieve using objects? Most likely not, though it depends on how well your OOP code actually leverages the benefits that OOP provides (BTW, I have encountered supposedly OOP code (Perl and not) which wasn't really taking any advantage of OOP paradigm and could have been made easier to read and understand had it been stripped of its OOP chrome).
I'm finding it very convenient to pass configuration and other data that is read or calculated once but then used many times throughout a program by using Perl's use mechanism. I'm doing this by exporting a hash into the caller's namespace. For example:
package Myconfiguration;
my %config;
sub import {
my $callpkg = caller(0);
my $expsym = $_[1];
configure() unless %config;
*{"$callpkg\::$expsym"} = \%config;
}
and then in other modules:
use MyConfiguration (loc_config_sym);
if ( $loc_config_sym{paramater} ) {
# ... do stuff ...
}
However, I'm not sure about this as a best practice. Is it better to add a method that returns a hash ref with the data? Something else?
If you only want to read the values of %config, then why not have a routine to do it for you?
my %config;
sub config_value
{
my ($value) = #_;
return $config{$value};
}
You could export this by default if you wanted to:
package Mypackage;
require Exporter;
#EXPORT = qw/config_value/;
The reason that I would not allow access to the hash all over the place in lots of different modules is that I would have a hard time mentally keeping track of all the places it was being used. I would rather make the above kind of access routine so that, if some bug happened, I could add a print statement to the routine, or something, to find out when the value was being accessed. I don't know if that is related to "best practices" or it is just because I'm stupid, but the kind of confusion created by global variables scares me.
There's no reason you can't have a set routine too:
sub set_value
{
my ($key, $value) = #_;
$config{$key} = $value;
}
I think it's better to work with a copy of the config hash. This way, if you modify some elements, this won't affect the rest of your code.
I usually use simple object (optionally Singleton) for this with a single method like get_property().
I suggest never exporting variables. Create a class that can return a reference to a private variable instead. People can then store it in a variable with whichever name they like, and only when they decide they want to use it.
In general, it's best to let the user decide whether or not to import symbols. Exporter makes this easy. Writing a custom import method to let the user decide what to name imported symbols can be useful on rare occasions but I don't think this is one of them.
package MyConfiguration;
require Exporter;
our #ISA = qw(Exporter);
our #EXPORT_OK = qw(Config);
our %Config;
And then, in your script:
use MyConfiguration;
print $MyConfiguration::Config{key};
or
use MyConfiguration qw(Config);
print $Config{key};
What's the best way to programatically discover all of the subroutines a perl module has? This could be a module, a class (no #EXPORT), or anything in-between.
Edit: All of the methods below look like they will work. I'd probably use the Class::Sniff or Class::Inspector in production. However, Leon's answer is marked as 'accepted' since it answers the question as posed, even though no strict 'refs' has to be used. :-) Class::Sniff may be a good choice as it progresses; it looks like a lot of thought has gone into it.
sub list_module {
my $module = shift;
no strict 'refs';
return grep { defined &{"$module\::$_"} } keys %{"$module\::"}
}
ETA: if you want to filter out imported subroutines, you can do this
use B qw/svref_2object/;
sub in_package {
my ($coderef, $package) = #_;
my $cv = svref_2object($coderef);
return if not $cv->isa('B::CV') or $cv->GV->isa('B::SPECIAL');
return $cv->GV->STASH->NAME eq $package;
}
sub list_module {
my $module = shift;
no strict 'refs';
return grep { defined &{"$module\::$_"} and in_package(\&{*$_}, $module) } keys %{"$module\::"}
}
Class::Inspector:
Class::Inspector allows you to get information about a loaded class. Most or all of this information can be found in other ways, but they aren't always very friendly, and usually involve a relatively high level of Perl wizardry, or strange and unusual looking code. Class::Inspector attempts to provide an easier, more friendly interface to this information...
Have a look at this:
Class::Sniff
The interface is rather ad-hoc at the moment and is likely to change. After creating a new instance, calling the report method is your best option. You can then visually examine it to look for potential problems:
my $sniff = Class::Sniff->new({class => 'Some::Class'});
print $sniff->report;
This module attempts to help programmers find 'code smells' in the object-oriented code. If it reports something, it does not mean that your code is wrong. It just means that you might want to look at your code a little bit more closely to see if you have any problems.
At the present time, we assume Perl's default left-most, depth-first search order. We may alter this in the future (and there's a work-around with the paths method. More on this later)...