How to call a subroutine exported from a .pm file - perl

I have a script Attachments.pm which contains below
package app::Attachments;
use MIME::Lite;
BEGIN {
use Exporter();
#ISA = qw(Exporter);
#EXPORT = qw(&SendEMmsgAttachments);
}
sub SendEMmsgAttachments {
$EM_SERVER = "1234.com";
$EM_FROM = "yyy#1234.com"; #hardcoded
$EM_TIMEOUT = 120;
my $mailMessage;
my $mailToEmailAddress;
my $mailSubject;
my $mailBody;
my $mailAttachmentFileName;
my $mailAttachmentFullPath;
$mailMessage = MIME::Lite->new(
From => $EM_FROM,
To => $mailToEmailAddress,
Subject => $mailSubject,
Type => 'multipart/mixed'
) or die "Error creating multipart container: $!\n";
### Add the text message part
$mailMessage->attach(
Type => 'text/csv',
Data => $mailBody
) or die "Error adding the text message part: $!\n";
### Add the text file
$mailMessage->attach(
Encoding => 'base64',
Type => "text",
I want to use SendEMmsgAttachments in my testscript.pl file so that I can send my Excel attachment in email.
Can anyone help me out in resolving the issue?

Here is a very basic hello world, using a perl module and script, demonstrating how to use the Exporter library. See perldoc Exporter for more details.
Foo.pm
package Foo;
use strict;
use warnings FATAL => 'all';
use Exporter 'import';
our $VERSION = '0.01';
our #EXPORT_OK = qw( bar );
sub bar {
return "Hello World";
}
1; # Last statement of a .pm file must evaluate to 'true'
try_foo.pl
#!/usr/bin/env perl
use warnings;
use strict;
use Foo qw( bar );
my $msg = bar();
print $msg . "\n";
In action:
perl try_foo.pl
Hello World

In your program you have this line:
use Exporter();
This instructs Perl to load the Exporter module but to not import anything from it. The parentheses denote that you want to supply your own import list rather than accepting the default list. Since you have nothing in your import list, nothing is imported.
You get around this by inheriting from Exporter by adding it to #ISA. There's plenty of sample code out there that does this. However, you only really want the import routine rather than being a more specific version of an exporting tool. You can do that by asking for only the import routine:
use Exporter qw(import);
After that you need to specify what you want to export by default in #EXPORT. For subroutines, leave off the &. It's not a big deal but it's common to see it like this:
our #EXPORT = qw( SendEmmsgAttachments );
If you want your program to specifically ask to import a subroutine you can put that in #EXPORT_OK. These entries are not exported by default—they are allowed to be exported if you ask for them:
our #EXPORT_OK = qw( SendEmmsgAttachments );
The our is there to declare the variable as a package variable. Your program isn't bothered by that because you don't use strict (which isn't the end of the world but is a good habit).
I'd take your BEGIN block and replace it with:
use Exporter qw(import);
our #EXPORT_OK = qw( SendEmmsgAttachments );
When you run into problems like this, create the smallest example that shows the problem so you eliminate anything else that might be a problem.

Related

Perl and Catalyst: accessing maketext from a model

Edited to clarify / reflect what I've been trying:
I'm using CatalystX::I18N::* in order to be able to internationalise my site. I have that working nicely, and my site text is coming from $c->maketext().
However, I've been trying to access these codes from my database model (in order to generate, e.g., success or failure messages when checking input before creating / updating) and am struggling.
According to the CatalystX::I18N docs, CatalystX::I18N::Maketext is a 'Helpful wrapper around Locale::Maketext. Can also be used outside of Catalyst'.
I have MyApp::Maketext setup as directed:
package MyApp::Maketext;
use strict;
use warnings;
use parent qw(CatalystX::I18N::Maketext);
1;
I have a little test script running, the setup for which is this:
#!/usr/bin/perl
use strict;
use warnings;
use FindBin qw( $Bin );
use lib "$Bin/../lib";
use TopTable::Maketext;
use Path::Class::Dir;
my $dir = Path::Class::Dir->new( "$Bin/..", "root", "locale" );
TopTable::Maketext->load_lexicon(
locales => ["en_GB"], # Required
directories => [$dir], # Required
gettext_style => 0, # Optional, Default 1
);
I am then trying two different ways to get a handle to the maketext() method:
my $lang = TopTable::Maketext->get_handle;
printf "%s\n", $lang->maketext( "menu.title.news" );
Gives the following result:
Can't call method "maketext" on an undefined value at bin\maketext-demo.pl line 23.
If I swap ->get_handle to ->new:
my $lang = TopTable::Maketext->new;
printf "%s\n", $lang->maketext( "menu.title.news" );
I get the following:
maketext doesn't know how to say:
menu.title.news
as needed at bin\maketext-demo.pl line 23.
I'm at a bit of a loss as to what to try next! Thank you so much in advance for any pointers anyone can give.
Chris
I have finally got my head around this - this is the code that eventually worked:
#!/usr/bin/perl
use strict;
use warnings;
use FindBin qw( $Bin );
use lib "$Bin/../lib";
use Data::Dumper::Concise;
use TopTable::Maketext;
use Config::ZOMG;
use Path::Class::Dir;
my $tt_config = Config::ZOMG->new( name => 'TopTable' );
my $config_hash = $tt_config->load;
my (#locales, %inhertiance, $config);
$config = $config_hash->{I18N}{locales};
foreach my $locale (keys %$config) {
push(#locales, $locale);
$inhertiance{$locale} = $config->{$locale}{inherits} if defined $con
+fig->{$locale}{inherits};
}
my $dir = Path::Class::Dir->new( "$Bin/..", "root", "locale" );
TopTable::Maketext->load_lexicon(
locales => \#locales,
directories => [$dir],
gettext_style => 1,
inheritance => \%inhertiance,
);
my $lang = TopTable::Maketext->get_handle( "en_GB" );
printf "%s\n", $lang->maketext( "menu.title.league-tables", "Division Three" );
1;
This gives the correct value of:
League Tables for Division Three
Thanks for putting up with my spam!

Can't use Exporter properly

I've wasted many days on a larger block of code, containing Exporter statements, which used to work about a year ago. Several variations on this have failed, including an install of ./FOO/BAR/Foobar.pm which signaled success.
I am using Windows 10, Perl v5.26.0, built for MSWin32-x64-multi-thread
Caller.pl
#!/usr/bin/perl
use lib ".";
use lib "./FOO/BAR";
use strict;
use warnings;
use FOO::BAR::Foobar;
print "try FOO::BAR::Foobar::foo()\n";
FOO::BAR::Foobar::foo(); # perl can't find this
print "try foo()\n";
foo(); # errors out - can't find &main::foo
print "done\n";
./FOO/BAR/Foobar.pl
#!/usr/bin/perl -w
use strict;
use warnings;
package Foobar;
our($VERSION , #ISA , #EXPORT , #EXPORT_OK , %EXPORT_TAGS , $FOO);
BEGIN {
require Exporter;
#ISA = qw(Exporter);
#EXPORT_OK = qw(foo);
}
sub foo {
print "Loaded\n";
$FOO = q{some val};
}
1;
Execution dump
perl Caller.pl
try FOO::BAR::Foobar::foo()
Undefined subroutine &FOO::BAR::Foobar::foo called at Caller.pl line 12.
What's going on? Should I abandon any use of Exporter? But then how to link functions across modules?
There are three things going wrong:
With FOO::BAR::Foobar::foo(): There is no such sub, there is only Foobar::foo().
With foo(): use FOO::BAR::Foobar; is the equivalent of BEGIN { require FOO::BAR::Foobar; FOO::BAR::Foobar->import() }, but there is no method import available in the package FOO::BAR::Foobar, since you're inheriting Exporter into the package Foobar.
With foo(): You're using #EXPORT_OK instead of #EXPORT, which means that you need to explicitly import foo, but you're not doing that in use FOO::BAR::Foobar;.
So two things are needed to fix this:
As already pointed out in the comment by #HåkonHægland, change package Foobar; to package FOO::BAR::Foobar;.
Change use FOO::BAR::Foobar; to use FOO::BAR::Foobar qw/foo/;.
Then the code you've shown will work - there's no need to abandon Exporter. I'd just recommend a different style of using Exporter: Instead of inheriting via #ISA, just import import into your package. Here's how I would have written your file ./FOO/BAR/Foobar.pm:
package FOO::BAR::Foobar;
use strict;
use warnings;
use Exporter 'import';
our #EXPORT_OK = qw(foo);
our $FOO;
sub foo {
print "Loaded\n";
$FOO = q{some val};
}
1;

perl : how to share variables and subroutines across various perl files

I am trying something of this sort:
main.pl
use YAML::XS
our $yaml_input = YAML::XS::LoadFile("$input_file");
parse_yaml($yaml_input);
#this variable has to be passed to the function parse_yaml which is in other file.
parser.pl
sub parse_yaml($yaml_input)
{
#some processing
}
I have read few answers about using a package but how do we use it in this case.
Basically you need to import the parse_yaml subroutine into you current program, rather than trying to export the value of the parameter, but I'm uncertain why you have written your own parse_yaml utility when YAML::XS::LoadFile has already done it for you
This is all very clearly described in the documentation for the Exporter module
Here's a brief example
main.pl
use strict;
use warnings 'all';
use YAML::XS 'LoadFile';
use MyUtils 'parse_yaml';
my $input_file = 'data.yaml';
my $yaml_input = LoadFile($input_file);
parse_yaml($input_file);
# this variable has to be passed to the function parse_yaml which is in other file.
MyUtils.pm
package MyUtils;
use strict;
use warnings;
use Exporter 'import';
our #EXPORT_OK = 'parse_yaml';
sub parse_yaml {
my ($yaml_file) = #_;
# some processing
}
1;

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;

"require" doesn't work where "use" works

The My_Module-module (used in this package) uses Signals::XSIG and sets $XSIG{'WINCH'}[1] = sub { ... };
use warnings;
use 5.014;
package Auto_sid;
use Exporter 'import';
our #EXPORT = qw(something);
use My_Module;
no warnings qw(redefine);
sub My_Module::list_to_big {
my ( #arguments ) = #_;
require Signals::XSIG;
Signals::XSIG->import(%Signals::XSIG::XSIG);
#{$Signals::XSIG::XSIG{'WINCH'}} = ();
no Signals::XSIG;
# ...
# do something that My_Module normaly doesn't do and
# which doesn't work when modified $SIG{'WINCH'}
# ...
}
When I use this I get a lot of error-messages like:
"NUM63" is not exported by the Signals::XSIG module
"ARRAY(0xc23180)" is not exported by the Signals::XSIG module
"TRAP" is not exported by the Signals::XSIG module
"ARRAY(0xc119c8)" is not exported by the Signals::XSIG module
...
When I use use instead of require it works fine.
Why does this not work with require?
Because when you do this:
Signals::XSIG->import(%Signals::XSIG::XSIG);
you're passing the contents of that hash to the import routine. As NUM63 and TRAP are signal names, and they are not valid exports for Signals::XSIG, you're getting those errors.
You need to do this:
Signals::XSIG->import('%XSIG');
Because it recognizes Exporter recognizes the string '%XSIG' as one of the things it does export.
use Signals::XSIG qw( %XSIG );
is equivalent to
BEGIN {
require Signals::XSIG;
Signals::XSIG->import(qw( %XSIG ));
}
instead of
# Passes the string '%XSIG'
Signals::XSIG->import(qw( %XSIG ));
you do
# Passes the contents of %Signals::XSIG::XSIG
Signals::XSIG->import(%Signals::XSIG::XSIG);
import is complaining about all the incorrect values you passed to it.
(You also got rid of the BEGIN, but that's unrelated to the errors you are currently getting.)