Accessing class variables using a variable with the class name in perl - perl

I'm wondering how I would go about doing this:
package Something;
our $secret = "blah";
sub get_secret {
my ($class) = #_;
return; # I want to return the secret variable here
}
Now when I go
print Something->get_secret();
I want it to print blah. Now before you tell me to just use $secret, I want to make sure that if a derived class uses Something as base, and I call get_secret I should get that class' secret.
How do you reference the package variable using $class? I know I can use eval but is there more elegant solution?

Is $secret supposed to be modifiable within the package? If not, you can get rid of the variable and instead just have a class method return the value. Classes that want to have a different secret would then override the method, instead of changing the value of the secret. E.g.:
package Something;
use warnings; use strict;
use constant get_secret => 'blah';
package SomethingElse;
use warnings; use strict;
use base 'Something';
use constant get_secret => 'meh';
package SomethingOther;
use warnings; use strict;
use base 'Something';
package main;
use warnings; use strict;
print SomethingElse->get_secret, "\n";
print SomethingOther->get_secret, "\n";
Otherwise, perltooc contains useful techniques to fit a variety of scenarios. perltooc points to Class::Data::Inheritable which looks like it would fit your needs.

You can use a symbolic reference:
no strict 'refs';
return ${"${class}::secret"};

Related

Error in Perl Rose::DB : Can't use string ... as a HASH ref while "strict"

I am getting an error when using Rose::DB.
#MyApp/DB.pm
package MyIMDB::DB;
use strict; use warnings;
use base qw(Rose::DB);
__PACKAGE__->use_private_registry;
__PACKAGE__->register_db (
driver => 'SQLite',
....
);
1;
# MyApp/DB/Object.pm
package MyApp::DB::Object;
use strict; use warnings;
use MyApp::DB;
use base qw(Rose::DB::Object);
sub init_db { MyIMDB::DB->new }
1;
#
package MyApp::Users; #controller
use strict; use warnings;
use base 'Mojolicious::Controller';
use Mojo::ByteStream 'b';
use MyApp::Models::User;
use Data::Dumper;
sub my_action {
my $uc = shift;
my $err = MyApp::Models::User::->validate(...); #extra ::
# http://perldoc.perl.org/perlobj.html#Invoking-Class-Methods
}
# MyApp/Models/User.pm # 2 packages in this file
package MyApp::Models::User::Manager;
use base qw(Rose::DB::Object::Manager);
use MyApp::Models::User;
sub object_class { 'MyApp::Models::User'}
__PACKAGE__->make_manager_methods('users');
# class methods get_x, get_x_iterator, get_x_count, delete_x, update_x
1;
MyApp::Models::User
use strict; use warnings;
use base qw(MyApp::DB::Object);
__PACKAGE__->meta->setup(
#setup tables, columns....
);
sub validate {
my $u = shift;
my $n = MyApp::Models::User::Manager::->get_users_count(query => [user_name => $user]);
}
1;
The error I get is:
"Can't use string ("MyApp::Models::User") as a HASH ref while "strict refs"
in use at /usr/local/share/perl/5.18.2/Rose/DB/Object.pm line 91, <DATA> line 2231."
The entry point is my_action() method of MyApp:Users class.
I tried alternative setups of creating class MyApp::Models::User::Manager : separate .pm file, make_manager_class(), but to no avail.
(I found this discussion from 2007 with the same error message, but it does not help me out http://comments.gmane.org/gmane.comp.lang.perl.modules.dbi.rose-db-object/1537).
This may indicate I am trying to call an object method as if it were a class method. I tried the tricks listed here http://perldoc.perl.org/perlobj.html#Invoking-Class-Methods, but no success.
I now I can examine the contents of variables with Data::Dumper, but I have no clue what to dump as there are very little data structures used.
While use strict is a good idea when writing Perl code, you may want to relax the strict-ness by adding
no strict `refs`;
to get past the current error. As #ikegami pointed out another way to fix this is to get rid of the bad reference, but if you don't want to rewrite the module working around it with relaxing strict-ness is your best bet.

How to declare an array variable to be global in module scope

I have some code I decided to pull into a module in perl. I admit that I am doing a bit of monkey-see monkey-do stuff following documentation I've found online.
There is only one pubicly visible subroutine, which sets 2 variables * would like to use in other subroutines in the module, without passing them explicitly as parameters -- $fname, and #lines. Looking online I came up with the "our" keyword, but when I try to declare them at global level (see code snippet below), I get the following error:
mig_tools.pm did not return a true value at
I have worked around the issue by declaring "our $fname" and "our #lines" in every subroutine they are used, but I would prefer to declare them once at global scope. Is that possible?
Here's what I take to be the relevant part of the code.
package mig_tools;
require Exporter;
use strict;
use warnings;
our #ISA = qw(Exporter);
our #EXPORT = qw( ensure_included);
our $fname;
our #lines;
// definitions of already_has_include find_include_insert_point and ensure_included.
All-lowercase variables are reserved for local identifiers and pragma names. Your module should be in MigTools.pm and you should use it with use MigTools
The did not return a true value error is just because your module file didn't return a true value when it was executed. It is usual to add a line containing just 1; to the end of all modules
Your MigTools.pm should look like this. It is best to use the import directly from Exporter instead of subclassing it, and our doesn't help you to create a global variable, but I am a little worried about why you are structuring your module this way
package MigTools;
use strict;
use warnings;
use Exporter qw/ import /;
our #EXPORT = qw/ ensure_included /;
my ($fname, #lines)
sub ensure_included {
}
sub already_has_include {
}
sub find_include_insert_point {
}
sub ensure_included {
}
1;

Perl : Like in java we can access public member (variables) in other class is there any same concept in perl

I have 2 perl file and i want to use value of one variable in another perl file as input so how i can do it is there any concept like java we can declare it as public and use it.
any help appreciated thank you!
In this answer, I'll skip the discussion about whether it is the right decision to use OOP or not and just assume you want to do it the OOP-way.
In short, all variables of an object in Perl can be considered public. In fact, the problem is often the opposite - to make some of them private. Anyway, if you have a file Obj.pm which defines an object with a field foo which looks like this:
package Obj;
sub new {
my $class = shift;
my $self = {foo => "bar"};
bless $self, $class;
return $self;
}
you can access the foo variable as if it were public:
use Obj;
my $obj = Obj->new();
print $obj->{foo};
For perhaps a more pleasant OOP in Perl, look at the Moose package which gives you more flexibility.
As #user2864740 pointed you don't need "OO" in perl to share variables.It is one way, Let's say you have two files
Foo.pm(package):
#!/usr/bin/perl
use strict;
use warnings;
use Exporter;
package Foo;
our #ISA = qw(Exporter);
our #EXPORT = qw( blat); #exported by default
our #EXPORT_OK = qw(bar );#not exported by default
our $x="42";#variable to be shared should be "our" not "my"
sub bar {
print "Hello $_[0]\n"
}
sub blat {
print "World $_[0]\n"
}
1;
Access that variable from other file as
bar.pl :
#!/usr/bin/perl
use strict;
use warnings;
use Foo;
print "$Foo::x";#imported variable
blat("hello");#imported subroutine
If you want to import listed functions then:
#!/usr/bin/perl
use strict;
use warnings;
use Foo qw(bar blat);# import listed subs
print "$Foo::x";#imported variable
blat("hello ");#imported subroutine
bar("hi");#this also get imported

How can I call methods on Perl scalars?

I saw some code that called methods on scalars (numbers), something like:
print 42->is_odd
What do you have to overload so that you can achieve this sort of "functionality" in your code?
Are you referring to autobox? See also Should I use autobox in Perl?.
This is an example using the autobox feature.
#!/usr/bin/perl
use strict;
use warnings;
package MyInt;
sub is_odd {
my $int = shift;
return ($int%2);
}
package main;
use autobox INTEGER => 'MyInt';
print "42: ".42->is_odd."\n";
print "43: ".43->is_odd."\n";
print "44: ".44->is_odd."\n";

Why does my Perl program complain about needing explicit package names?

I have a module Routines.pm:
package Routines;
use strict;
use Exporter;
sub load_shortest_path_matrices {
my %predecessor_matrix = shift;
my %shortestpath_matrix = shift;
...
}
From another script I call the sub in the module, passing in arguments which happen to have the same name:
use Routines;
use strict;
my %predecessor_matrix = ();
my %shortestpath_matrix =();
&Routines::load_shortest_path_matrices($predecessor_matrix, $shortestpath_matrix);
However, this doesn't compile and I get
Global symbol "$predecessor_matrix" requires explicit package name
type of errors. Is it not possible to give the same name to variables in different scopes like this in Perl? (I'm from a C background)
$predecessor_matrix is a scalar and %predecessor_matrix is a hash. Different types in Perl (scalar, array, hash, function, and filehandle) have different entries in the symbol table, and, therefore, can have the same name.
Also, you have a problem in your function. It expects to be able to get two hashes from #_, but a hash in list context (such as in the argument list of a function) yields a list of key value pairs. So, both %predecessor_matrix and %shortestpath_matrix will wind up in the %predecessor_matrix of the function. What you need to do here is to use references:
package Routines;
use strict;
use Exporter;
sub load_shortest_path_matrices {
my $predecessor_matrix = shift;
my $shortestpath_matrix = shift;
$predecessor_matrix->{key} = "value";
...
}
and
use Routines;
use strict;
my %predecessor_matrix;
my %shortestpath_matrix;
Routines::load_shortest_path_matrices(
\%predecessor_matrix,
\%shortestpath_matrix
);
However, passing in structures to load as arguments is more C-like than Perl-like. Perl can return more than one value, so it is more common to see code like:
package Routines;
use strict;
use Exporter;
sub load_shortest_path_matrices {
my %predecessor_matrix;
my %shortestpath_matrix;
...
return \%predecessor_matrix, \%shortestpath_matrix;
}
and
use Routines;
use strict;
my ($predecessor_matrix, $shortestpath_matrix) =
Routines::load_shortest_path_matrices();
for my $key (keys %$predecessor_matrix) {
print "$key => $predecessor_matrix->{$key}\n";
}
you are declaring the hash %predecessor_matrix but are trying to pass the scalar $predecessor_matrix. The hash exists, the scalar doesn't.
Maybe you want to pass references to the hashes?
Routines::load_shortest_path_matrices(\%predecessor_matrix, \%shortestpath_matrix);
Here's another way to code it:
use strict;
use warnings;
use Routines;
my $predecessor_matrix = {};
my $shortestpath_matrix ={};
Routines::load_shortest_path_matrices( $predecessor_matrix
, $shortestpath_matrix
);
package Routines;
use strict;
use Exporter;
sub load_shortest_path_matrices {
my $predecessor_matrix = shift;
my $shortestpath_matrix = shift;
...
}
you can access the contents of the hashes like this
my $foobar=$shortestpath_matrix->{FOOBAR};