Perl read_config sub, oop or not? - perl

I have a package (really just one subroutine) I use frequently to parse config file etc. Basically it looks like this:
sub get_settings {
my %config;
my $config = 'path...';
unless(-r $config) {
die("Couldn't read config");
}
open CONFIG, '<', $config or die $!;
while(<CONFIG>) {
next if (($_ eq "\n") or /^\;/);
chomp;
my($setting, $value) = split(/=/, $_);
$config{$setting} = $value;
}
return %config;
}
Pretty basic, but I was wondering how (and if) this could/should be re-written to OOP? Really just for learning, never quite seen when and why to use bless. =)
Thanks!

Here is (hopefully!) a simple example of an OO based config abstraction using:
Moose
MooseX::SimpleConfig
NB. You can use other modules or even roll your own. Below serves just as a general example.
RoomConfig.pm
package RoomConfig;
use Moose;
with 'MooseX::SimpleConfig';
has doors => (is => 'rw', isa => 'Int', required => 1);
has windows => (is => 'rw', isa => 'Int', default => sub {0});
1;
So above is our OO config class. Everything is neatly declared so you clearly know that config options are available and valid, ie. its self documenting.
So to create a room from a config file would be:
use RoomConfig;
my $box_room = RoomConfig->new_with_config( configfile => 'box_room.yaml' );
Because its a class i can also instantiate a room without a config file:
my $cupboard = RoomConfig->new( doors => 1 );
my $utility_room = RoomConfig->new( doors => 2 );
my $master_bedroom = RoomConfig->new(
doors => 1,
windows => 2, # dual aspect
);
And also with these particular modules we get extra features like this:
# below throws exception because room must have a door!
my $room_with_no_door_or_window = RoomConfig->new;
Thus my configuration can easily come from a configuration file or by setting attributes.
And we can go further by extending our config for different types of rooms:
BathRoomConfig.pm
package BathRoomConfig;
use Moose;
extends 'RoomConfig';
has loos => (is => 'rw', isa => 'Int', default => sub {0});
has sinks => (is => 'rw', isa => 'Int', default => sub {0});
has baths => (is => 'rw', isa => 'Int', default => sub {1});
1;
And if we used this config (bathroom.yaml):
doors: 1
windows: 1
bath: 1
loos: 1
sinks: 2
Then you could do this:
use BathRoomConfig;
my $upstairs_bathroom = BathRoomConfig->new_with_config(
configfile => 'bathroom.yaml'
);
my $closet_room = BathRoomConfig->new_with_config(
configfile => 'bathroom.yaml',
baths => 0,
sinks => 1,
windows => 0,
);
Note that $closet_room makes use of both the config file and setting attributes.
Also note that if my config file didn't have doors (ie. required property) then it would have thrown an error on new_with_config.
And finally we may find introspecting our defined config class handy:
use RoomConfig;
say "RoomConfig provides the following options:";
for my $attr (RoomConfig->meta->get_attribute_list) {
next if $attr eq 'configfile';
say '-> ', $attr;
}
Now there is nothing stopping you implementing most of this in a standard config package so at the end of the day its just horses for courses!
However the ease of managing all this is so much easier with OO and the features that these already written modules provide is big advantage especially in bigger projects.

The answer to the question has more to do with the programs you are using the package in than with the package itself.
If this a pretty large OOP based apps/scripts then it definitely makes sense to OOP it because that is what the customer expects (the apps and the people writing these apps/scripts). Also having imperative style libraries stick out like a sore thumb and create complexity.
Conversely if the package is used in shorter imperative scripts, then an OOP interface will conflict with the customer's expectation (i.e. the scripts + people developing them).
Maybe you are migrating between approaches (e.g. the script become big and unwieldy and need to be better organised, maybe with OOP) in that case a settings/config kind of Class is a good place to start as they tend to be well separated and have clear lines of responsability.
In short : do what makes most sense where the package is used.

You can take a look at source code from modules on CPAN.
For example Config::General should answer your questions...

Related

Writing to read-only attributes inside a Perl Moose class

Using Perl and Moose, object data can be accessed in 2 ways.
$self->{attribute} or $self->attribute()
Here is a simple example demonstrating both:
# Person.pm
package Person;
use strict;
use warnings;
use Moose;
has 'name' => (is => 'rw', isa => 'Str');
has 'age' => (is => 'ro', isa => 'Int');
sub HAPPY_BIRTHDAY {
my $self = shift;
$self->{age}++; # Age is accessed through method 1
}
sub HAPPY_BIRTHDAY2 {
my $self = shift;
my $age = $self->age();
$self->age($age + 1); # Age is accessed through method 2 (this will fail)
}
1;
# test.pl
#!/usr/bin/perl
use strict;
use warnings;
use Person;
my $person = Person->new(
name => 'Joe',
age => 23,
);
print $person->age()."\n";
$person->HAPPY_BIRTHDAY();
print $person->age()."\n";
$person->HAPPY_BIRTHDAY2();
print $person->age()."\n";
I know that when you are outside of the Person.pm file it is better to use the $person->age() version since it prevents you from making dumb mistakes and will stop you from overwriting a read only value, but my question is...
Inside of Person.pm is it best to use $self->{age} or $self->age()? Is it considered bad practice to overwrite a read-only attribute within the module itself?
Should this attribute be changed to a read/write attribute if its value is ever expected to change, or is it considered acceptable to override the read-only aspect of the attribute by using $self->{age} within the HAPPY_BIRTHDAY function?
When using Moose, the best practice is to always use the generated accessor methods, even when inside the object's own class. Here are a few reasons:
The accessor methods may be over-ridden by a child class that does something special. Calling $self->age() assures that the correct method will be called.
There may be method modifiers, such as before or after, attached to the attribute. Accessing the hash value directly will skip these.
There may be a predicate or clearer method attached to the attribute (e.g. has_age). Messing with the hash value directly will confuse them.
Hash keys are subject to typos. If you accidentally say $self->{aeg} the bug will not be caught right away. But $self->aeg will die since the method does not exist.
Consistency is good. There's no reason to use one style in one place and another style elsewhere. It makes the code easier to understand for newbs as well.
In the specific case of a read-only attribute, here are some strategies:
Make your objects truly immutable. If you need to change a value, construct a new object which is a clone of the old one with the new value.
Use a read-only attribute to store the real age, and specify a private writer method
For example:
package Person;
use Moose;
has age => ( is => 'ro', isa => 'Int', writer => '_set_age' );
sub HAPPY_BIRTHDAY {
my $self = shift;
$self->_set_age( $self->age + 1 );
}
Update
Here's an example of how you might use a lazy builder to set one attribute based on another.
package Person;
use Moose;
has age => ( is => 'rw', isa => 'Int', lazy => 1, builder => '_build_age' );
has is_baby => ( is => 'rw', isa => 'Bool', required => 1 );
sub _build_age {
my $self = shift;
return $self->is_baby ? 1 : 52
}
The lazy builder is not called until age is accessed, so you can be sure that is_baby will be there.
Setting the hash element directly will of course skip the builder method.
I don't think $self->{age} is a documented interface, so it's not even guaranteed to work.
In this case I'd use a private writer as described in https://metacpan.org/pod/Moose::Manual::Attributes#Accessor-methods:
has 'weight' => (
is => 'ro',
writer => '_set_weight',
);
You could even automate this using 'rwp' from https://metacpan.org/pod/MooseX::AttributeShortcuts#is-rwp:
use MooseX::AttributeShortcuts;
has 'weight' => (
is => 'rwp',
);
Out-of-the-box perl isn't type safe and doesn't have much in the way of encapsulation, so it's easy to do reckless things. Moose imposes some civilization on your perl object, exchanging security and stability for some liberty. If Moose gets too stifling, the underlying Perl is still there so there are ways to work around any laws the iron fist of Moose tries to lay down.
Once you have wrapped your head around the fact that you have declared an attribute read-only, but you want to change it, even though you also said you wanted it to be read-only, and in most universes you declare something read only because you don't want to change it, then by all means go ahead and update $person->{age}. After all, you know what you are doing.

How can I change an overloaded operator on a Moose class at runtime?

I have a Moose class with an overloaded stringification operator which I would actually like to change at runtime, because I need text output in different formats.
In other words, I would like to be able to do something like this:
$obj = Class->new("a'");
$obj->formatter("A::Formatter");
print "$obj";
# prints "a'"
$obj->formatter("Another::Formatter");
print "$obj";
# prints a1
I would also like the formatters to be modular, so that I can encapsulate and plug in different ones without creating a giant spaghetti mess.
How should I best do this? are roles a good solution for this kind of problem? I have checked MooseX::Object::Pluggable, MooseX::Traits::Pluggable and MooseX::Traits to start but need advice as this is uncharted territory for me.
You don't need to change the overloading at runtime, just how it's implemented. Remember, overloading can be done with a method name as opposed to a subref:
package Foo;
use Moose;
use overload q{""} => '_stringify';
has id => (
is => 'ro',
isa => 'Str',
required => 1,
);
has formatter_class => (
is => 'rw',
isa => 'ClassName',
required => 1,
);
sub _stringify {
my $self = shift;
return $self->formatter_class()->new()->format($self);
}

Perl share variables with subclasses

I know this may be a very simple topic but I am trying to get the best logic since I am still new to Perl.
If I do not use OO and just split the code into files, all global variables are accessed among all files.
I am trying to do the same but using OO style. Example is I want a base class say called "BaseSub" that has a hash containing the configuration for the application say called %Config. Now I have a sub class called "DB" for the database connection and I want to access the settings from %Config which lives in "BaseSub" package. How do I do that.
If you're writing OO perl in this day and age, you really should be using Moose. It makes OO code much easier, cleaner and smaller.
The proper way to inherit variables is to make object attributes. Here's a quick example:
package MyBaseClass;
use Moose;
has config => (
is => 'ro',
default => sub { {
who => 'World',
} }
);
package MyClass;
use Moose;
extends qw(MyBaseClass);
sub greet
{
my $self = shift;
printf("Hello %s!\n", $self->config->{who});
}
package main;
my $object = MyClass->new();
$object->greet();
A great starting point for learning about Moose is the Moose::Manual.
Edit:
If you want be able to modify the config, you can either just poke the hashref returned from the config accessor directly:
$object->config->{who} = 'Friends';
But a better approach might be to make a config class and make the config attribute hold an instance of that:
package Myconfig;
use Moose;
has who => (is => 'rw', default => 'World');
package MyBaseClass;
use Moose;
has config => (
is => 'ro',
isa => 'MyConfig',
default => sub { MyConfig->new },
);
# inherit, instantiate, etc as before...
$object->config->who('Friends');
Another approach could be Moose::Meta::Attribute::Native::Trait::Hash which makes it easy to setup helper methods to work with native Perl datatypes.
Use its full name.
for (keys(%BaseSub::Config)) {
print("$_: $BaseSub::Config{$_}\n");
}
You could also import it.
our %Config; *Config = \%BaseSub::Config;
for (keys(%Config)) {
print("$_: $Config{$_}\n");
}

Setting Up Perl Module Structure

I'm having trouble figuring out how to structure Perl modules in an object oriented way so I can have one parent module with a number of submodules and only the specific submodules that are needed would be loaded by a calling script. For example I want to be able to make method calls like so:
use Example::API;
my $api = Example::API->new();
my $user = {};
$user->{'id'} = '12345';
$api->Authenticate();
$user->{'info'} = $api->Users->Get($user->{'id'});
$user->{'friends'} = $api->Friends->Get($user->{'id'});
In terms of file structure I'd like to have the modules setup as follows or in whatever structure is required to make everything work correctly:
api.pm
users.pm
friends.pm
...
The reason I want to do this in the first place is so that if someone just wants to authenticate against the API they don't have to load all the other modules. Similarly, if someone just wants to get a user's information, they wouldn't have to load the friends.pm module, just the users.pm. I'd appreciate it if you could provide the necessary example Perl code for setting up each module as well as explain how the file structure should be setup. If I'm going about this all wrong to accomplish what I'm try to accomplish I'd appreciate an explanation of the best way to do this and some example code on how it should be setup.
From your example, in your main module I assume you will be providing accessor methods to get at the subclasses. So all you have to do is include require Sub::Module; at the top of that method. Nothing will happen at compile time, but the first time that code is run, perl will load the module. After the first load, the line require Sub::Module; will become a no-op.
If all of your code is object oriented, you won't need to worry about importing functions. But if you do, the statement use Module qw(a b c); is interpreted as:
BEGIN {
require Module;
Module->import(qw(a b c));
}
BEGIN makes it happen at compile time, but there is nothing stopping you from using the internals at run time. Any subroutines you import at runtime must be called with parenthesis, and prototypes will not work, so unless you know what you are doing, runtime imports are probably a bad idea. Runtime requires and access via package methods are completely safe though.
So your $api->Users method might work something like this:
# in package 'Example::API' in the file 'Example/API.pm'
sub Users {
require Example::API::Users; # loads the file 'Example/API/Users.pm'
return Example::API::Users->new( #_ ); # or any other arguments
}
In my examples above, I showed two translations between package names and the files they were in. In general, all :: are changed to / and .pm is added to the end. Then perl will search for that file in all of the directories in the global variable #INC. You can look at the documentation for require for all of the details.
Update:
One way to cache this method would be to replace it at runtime with a function that simply returns the value:
sub Users {
require Example::API::Users;
my $users = Example::API::Users->new;
no warnings 'redefine';
*Users = sub {$users};
$users
}
Here's a big ugly Moose example that selectively applies roles to an API driver instance.
script.pl
use Example::User;
# User object creates and authenticates a default API object.
my $user = Example::User->new( id => '12345' );
# When user metadata is accessed, we automatically
# * Load the API driver code.
# * Get the data and make it available.
print "User phone number is: ", $user->phone_number, "\n";
# Same thing with Friends.
print "User has ", $user->count_friends, " friends\n";
print "User never logged in\n" unless $user->has_logged_in;
Example/API.pm - the basic protocol driver class:
package Example::API;
use Moose;
has 'host' => (
is => 'ro',
default => '127.0.0.1',
);
sub Authenticate {
return 1;
}
# Load the user metadata API driver if needed.
# Load user metadata
sub GetUserInfo {
my $self = shift;
require Example::API::Role::UserInfo;
Example::API::Role::UserInfo->meta->apply($self)
unless $self->does('Example::API::Role::UserInfo');
$self->_Get_UserInfo(#_);
}
# Load the friends API driver if needed.
# Load friends data and return an array ref of Friend objects
sub GetFriends {
my $self = shift;
#require Example::API::Role::Friends;
Example::API::Role::Friends->meta->apply($self)
unless $self->does('Example::API::Role::Friends');
$self->_Get_Friends(#_);
}
The user metadata and friends data drivers are built as 'roles' which are dynamically applied to an API driver instance as needed.
Example/API/Role/UserInfo.pm:
package Example::API::Role::UserInfo;
use Moose::Role;
sub _Get_UserInfo {
my $self = shift;
my $id = shift;
my $ui = Example::API::User::MetaData->new(
name => 'Joe-' . int rand 100,
phone_number => int rand 999999,
);
return $ui;
}
Example/API/Role/Friends.pm:
use Moose::Role;
sub _Get_Friends {
my $self = shift;
my $id = shift;
my #friends = map {
Example::API::Friend->new(
friend_id => "$id-$_",
name => 'John Smith'
);
} 1 .. (1 + int rand(5));
return \#friends;
}
A friend object:
Example/API/Friend.pm
package Example::API::Friend;
use Moose;
has 'friend_id' => (
is => 'ro',
isa => 'Str',
required => 1,
);
has 'name' => ( isa => 'Str', is => 'ro', required => 1 );
And a user metadata object.
Example/API/User/MetaData.pm
package Example::API::User::MetaData;
use Moose;
has 'name' => (
is => 'ro',
isa => 'Str',
);
has 'phone_number' => (
is => 'ro',
isa => 'Str',
);
has 'last_login' => (
is => 'ro',
isa => 'DateTime',
predicate => 'has_logged_in',
);
And finally a user object. I've used many Moose features to make this a very capable object with only a small amount of imperative code.
package Example::User;
use Moose;
has 'id' => (
is => 'ro',
isa => 'Int',
required => 1,
);
has 'server_connection' => (
is => 'ro',
isa => 'Example::API',
builder => '_build_server_connection',
);
# Work with a collection of friend objects.
has 'friends' => (
is => 'ro',
isa => 'ArrayRef[Example::API::Friend]',
traits => ['Array'],
handles => {
all_friends => 'elements',
map_friends => 'map',
filter_friends => 'grep',
find_option => 'first',
get_option => 'get',
join_friends => 'join',
count_friends => 'count',
has_no_friends => 'is_empty',
sorted_friends => 'sort',
},
lazy_build => 1,
);
has 'user_info' => (
is => 'ro',
isa => 'Example::API::User::MetaData',
handles => {
name => 'name',
last_login => 'last_login',
phone_number => 'phone_number',
has_logged_in => 'has_logged_in',
},
lazy_build => 1,
);
sub _build_server_connection {
my $api = Example::API->new();
$api->Authenticate();
return $api;
}
sub _build_friends {
my $self = shift;
$self->server_connection->GetFriends( $self->id );
}
sub _build_user_info {
my $self = shift;
$self->server_connection->GetUserInfo( $self->id );
}
This example makes use of a lot of Moose magic, but you wind up with a very simple interface for those using the objects. While this is close to 200 lines of formatted code, we get a huge amount done.
Adding type coercion would give an even easier interface. Raw string dates can be automatically parsed into DateTime objects. Raw IP addresses and server names can be converted into API servers.
I hope this inspires you to take a look at Moose. The documentation is excellect, check out the Manual and the Cookbooks, in particular.
Managing the exports is tricky, but you could use an AUTOLOAD solution to this problem. If perl doesn't recognize the subroutine name you are trying to call, it can pass it to a sub called AUTOLOAD. Suppose we did this:
use Example::API;
sub AUTOLOAD {
my $api = shift;
eval "require $AUTOLOAD"; # $api->Foo->... sets $AUTOLOAD to "Example::API::Foo"
die $# if $#; # fail if no Example::API::Foo package
$api;
}
Then this code:
$api = new Example::API;
$api->Foo->bar(#args);
will (assuming we haven't imported Example::API::Foo first) call our AUTOLOAD method, attempt to load the Example::API::Foo module, and then try to call the method Example::API::Foo::bar with the $api object and the other arguments you provide.
Or in the worst case,
$api->Foo->bar(#args)
causes this code to be invoked
eval "require Example::API::Foo";
die $# if $#;
&Example::API::Foo::bar($api,#args);
Depending on how you use this feature, it might be a lot more overhead than just importing everything you need.
There are a number of tools that can be used to quickly build an skeletal structure for your new module development.
h2xs comes with the standard Perl distribution. Its primary focus is on building XS code for interfacing with C libraries. However, it does provide basic support for laying out pure Perl projects: h2xs -AX --skip-exporter -n Example::API
I use Module::Starter to build a beginning layout for my module development. It does a lot that h2xs doesn't do. module-starter --module=Example::API,Example::Friends,Example::Users --author="Russel C" --email=russel#example.com
Dist::Zilla is a new tool that handles many tasks related to maintaining a Perl module distribution. It is amazingly powerful and flexible. But it is new and the docs are a bit rough. The unavoidable complexity that comes with all that power and flexibility means that learning to use it is a project. It looks very interesting, but I haven't taken the time to dive in, yet.
If you need to limit the number of methods loaded, you can use AutoLoader or SelfLoader to load subroutines as they are called. This will lead to a slight overhead when a method is called for the first time. In my experience, this approach is rarely needed.
The best thing is to keep your objects small and strictly defined so that they embody a simple concept. Do not allow ambiguity or half-way concepts into your objects, instead consider using composition and delegation to handle areas of potential confusion. For example, instead of adding date formatting methods to handle a user's last login, assign DateTime objects to the last_login attribute.
In the interest of making composition and delegation easy, consider using Moose to build your objects. It removes much of the drudgery involved in Perl OOP and object composition and delegation in specific.

What's the difference between an object and a class in Perl?

I'm having a little trouble getting my head around the conceptual difference between an object and a class. I don't really understand the distinction between the two in any programming language, but currently I'm working with Perl, and Moose, so I'd prefer an explanation using those things.
Cheers
There are lots of "a class is a blueprint, an object is something built from that blueprint", but since you've asked for a specific example using Moose and Perl, I thought I'd provide one.
In this following example, we're going have a class named 'Hacker'. The class (like a blueprint) describes what hackers are (their attributes) and what they can do (their methods):
package Hacker; # Perl 5 spells 'class' as 'package'
use Moose; # Also enables strict and warnings;
# Attributes in Moose are declared with 'has'. So a hacker
# 'has' a given_name, a surname, a login name (which they can't change)
# and a list of languages they know.
has 'given_name' => (is => 'rw', isa => 'Str');
has 'surname' => (is => 'rw', isa => 'Str');
has 'login' => (is => 'ro', isa => 'Str');
has 'languages' => (is => 'rw', isa => 'ArrayRef[Str]');
# Methods are what a hacker can *do*, and are declared in basic Moose
# with subroutine declarations.
# As a simple method, hackers can return their full name when asked.
sub full_name {
my ($self) = #_; # $self is my specific hacker.
# Attributes in Moose are automatically given 'accessor' methods, so
# it's easy to query what they are for a specific ($self) hacker.
return join(" ", $self->given_name, $self->surname);
}
# Hackers can also say hello.
sub say_hello {
my ($self) = #_;
print "Hello, my name is ", $self->full_name, "\n";
return;
}
# Hackers can say which languages they like best.
sub praise_languages {
my ($self) = #_;
my $languages = $self->languages;
print "I enjoy programming in: #$languages\n";
return;
}
1; # Perl likes files to end in a true value for historical reasons.
Now that we've got our Hacker class, we can start making Hacker objects:
#!/usr/bin/perl
use strict;
use warnings;
use autodie;
use Hacker; # Assuming the above is in Hacker.pm
# $pjf is a Hacker object
my $pjf = Hacker->new(
given_name => "Paul",
surname => "Fenwick",
login => "pjf",
languages => [ qw( Perl C JavaScript) ],
);
# So is $jarich
my $jarich = Hacker->new(
given_name => "Jacinta",
surname => "Richardson",
login => "jarich",
languages => [ qw( Perl C Haskell ) ],
);
# $pjf can introduce themselves.
$pjf->say_hello;
$pjf->praise_languages;
print "\n----\n\n";
# So can $jarich
$jarich->say_hello;
$jarich->praise_languages;
This results in the following output:
Hello, my name is Paul Fenwick
I enjoy programming in: Perl C JavaScript
----
Hello, my name is Jacinta Richardson
I enjoy programming in: Perl C Haskell
If I want I can have as many Hacker objects as I like, but there's still only one Hacker class that describes how all of these work.
All the best,
Paul
A class is a type (like "SUV"). An object is an instance of a class ("David's SUV").
Perl-wise:
A class is a package--a specification. A set of behaviors and data mainly to aid those behaviors.
An object is typically a "hashref", that is a collection of specific data allowed by the behavior specification in the package (and inherited behaviors).
Now, a hashref might hold a code reference. In most cases, that's behavior. But the only way the object could use that specific behavior is for that to be specified by some class behavior inherited (or mixed in) that expects that there might be a coderef sitting at that location and invoke it.
Another way to think of it is a class is a blueprint for how an object will be built.
Objects are single instances of a Class.
You are an object of class Human
(Classes in Perl are modules with some special qualities, you should better first understand only the general case).
In perl class is nothing but it is a package name.
It has a common code for the objects.
object is a instance that has access the class's properties
and methods.
package vehicle;
sub vehicle_detail
{
($number,$model,$num_of_wheel)=#_;
print "My car Details:\n#_";
}
The above class vehicle can be used by any vehicle such as bike,car,van..etc.
The object is created by the operator bless.
$bike_name='honda';
$ref_bike=\$bike_name;
bless $ref_bike,'vehicle';
Now the bless creates the object honda for the class vehicle.
I don't see people using the terms the same way in other languages. That may be one reason for the question. I think maybe PHP users say "class" when they should say "object", a lot of the time?
Anyway, what about this example -- imagine you had to create two different database connections for two different databases:
my $oracle_database_handle = DBI->connect( <oracle connection details here> );
my $mysql_database_handle = DBI->connect( <mysql connection details here> );
you would have created two objects for doing two different things, but they're both the same kind of thing -- DBI database connections.