Indirect method calling without an helper variable - perl

Have this working short code
use 5.014;
package Module;
use warnings;
use Moose;
use Method::Signatures::Simple;
has 'commands' => (
is => 'ro',
isa => 'HashRef',
default => sub{{
open => 'my_open',
close => 'my_close',
}},
);
method run($cmd, $args) {
my $met = $self->commands->{$cmd} if exists $self->commands->{$cmd};
$self->$met($args) if $met;
#-----
#how to write the above two lines in one command?
#the next doesn't works
# $self->commands->{$cmd}($args) if exists $self->commands->{$cmd};
#-----
}
method my_open { say "module: open" }
method my_close { say "module: close" }
package main;
my $ef = Module->new();
$ef->run('open');
The main question is in the code - how to write in one line the "run" method - without the helper variable $met.
And, is here better way to do the above scenario - so calling methods based on input?

First of all, please don't do my $foo = $x if $y. You get unexpected and undefined behavior, so it is best to avoid that syntax.
The piece of code
my $met = $self->commands->{$cmd} if exists $self->commands->{$cmd};
$self->$met($args) if $met;
is equivalent to
if (my $met = $self->commands->{$cmd}) {
$self->$met($args);
}
because the exists test is superfluous here (an entry can only be true if it exists).
If we do not wish to introduce another variable, we have two options:
Trick around with $_:
$_ and $self->$_($args) for $self->commands->{$cmd};
This uses the for not as a loop, but as a topicalizer.
Trick around with scalar references:
$self->${\( $self->commands->{$cmd} )}($args) if $self->commands->{$cmd};
or
$self->${\( $self->commands->{$cmd} || "no_op" )}($args);
...
method no_op { }
Don't do something like this, because it is impossible-to-read line noise.
Neither of these is particularly elegant, and it would be better to use the cleaned-up solution I have shown above.
Just because something can be done in a single line does not mean it should be done. “This is Perl, not … oh, nevermind”.

Related

Replacing a class in Perl ("overriding"/"extending" a class with same name)?

I am trying to Iterate directories in Perl, getting introspectable objects as result, mostly so I can print fields like mtime when I'm using Dumper on the returns from IO::All.
I have discovered, that it can be done, if in the module IO::All::File (for me, /usr/local/share/perl/5.10.1/IO/All/File.pm), I add the line field mtimef => undef;, and then modify its sub file so it runs $self->mtimef($self->mtime); (note, this field cannot have the same name (mtime) as the corresponding method/property, as those are dynamically assigned in IO::All). So, in essence, I'm not interested in "overloading", as in having the same name for multiple function signatures - I'd want to "replace" or "override" a class with its extended version (not sure how this is properly called), but under the same name; so all other classes that may use it, get on to using the extended version from that point on.
The best approach for me now would be, if I could somehow "replace" the IO::All::File class, from my actual "runnable" Perl script -- if somehow possible, by using the mechanisms for inheritance, so I can just add what is "extra". To show what I mean, here is an example:
use warnings;
use strict;
use Data::Dumper;
my #targetDirsToScan = ("./");
use IO::All -utf8 ; # Turn on utf8 for all io
# try to "replace" the IO::All::File class
{ # recursive inheritance!
package IO::All::File;
use IO::All::File -base;
# hacks work if directly in /usr/local/share/perl/5.10.1/IO/All/File.pm
field mtimef => undef; # hack
sub file {
my $self = shift;
bless $self, __PACKAGE__;
$self->name(shift) if #_;
$self->mtimef($self->mtime); # hack
return $self->_init;
}
1;
}
# main script start
my $io = io(#targetDirsToScan);
my #contents = $io->all(0); # Get all contents of dir
for my $contentry ( #contents ) {
print Dumper \%{*$contentry};
}
... which fails with "Recursive inheritance detected in package 'IO::All::Filesys' at /usr/local/share/perl/5.10.1/IO/All/Base.pm line 13."; if you comment out the "recursive inheritance" section, it all works.
I'm sort of clear on why this happens with this kind of syntax - however, is there a syntax, or a way, that can be used to "replace" a class with its extended version but of the same name, similar to how I've tried it above? Obviously, I want the same name, so that I wouldn't have to change anything in IO::All (or any other files in the package). Also, I would preferably do this in the "runner" Perl script (so that I can have everything in a single script file, and I don't have to maintain multiple files) - but if the only way possible is to have a separate .pm file, I'd like to know about it as well.
So, is there a technique I could use for something like this?
Well, I honestly have no idea what is going on, but I poked around with the code above, and it seems all that is required, is to remove the -base from the use IO::All::File statement; and the code otherwise seems to work as I expect it - that is, the package does get "overriden" - if you change this snippet in the code above:
# ...
{ # no more recursive inheritance!? IO::All::File gets overriden with this?!
package IO::All::File;
use IO::All::File; # -base; # just do not use `-base` here?!
# hacks work if directly in /usr/local/share/perl/5.10.1/IO/All/File.pm
field mtimef => undef; # hack
sub file {
my $self = shift;
bless $self, __PACKAGE__;
$self->name(shift) if #_;
$self->mtimef($self->mtime); # hack
print("!! *haxx0rz'd* file() reporting in\n");
return $self->_init;
}
1;
}
# ...
I found this so unbelievable, I even added the print() there to make sure it is the "overriden" function that runs, and sure enough, it is; this is what I get in output:
...
!! *haxx0rz'd* file() reporting in
$VAR1 = {
'_utf8' => 1,
'mtimef' => 1394828707,
'constructor' => sub { "DUMMY" },
'is_open' => 0,
'io_handle' => undef,
'name' => './test.blg',
'_encoding' => 'utf8',
'package' => 'IO::All'
};
...
... and sure enough,the field is there, as expected, too...
Well - I hope someone eventually puts a more qualified answer here; for the time being, I hope this is as good as a fix to my problems :) ...

command line option used in a module and in the main script

I have a module misc which is used by a few scripts. Each script accept two standard options (-help and -verbose) as well as a bunch of its own ones.
So, every scripts now has
my ($verbose,$quiet) = (1,0);
my $help = undef;
...
GetOptions("verbose|v+" => \$verbose, "quiet|q+" => \$quiet, "help|h" => \$help,
....)
or die "GetOptions: error (see above)\n";
if (defined $help) { usage(); exit; }
$verbose -= $quiet;
which is already boring.
Now, I want the misc functions to be aware of the value of $verbose too, so I have to move $verbose et al to misc and use $misc::verbose in scripts:
misc:
our $verbose = 1;
my $quiet = 0;
our $help = undef;
our %common_options = ("verbose|v+" => \$verbose, "quiet|q+" => \$quiet, "help|h" => \$help);
sub set_verbose () { $verbose -= $quiet; }
script:
GetOptions(\%misc::common_options,"verbose|v","quiet|q","help|h",
"count=i" => \$count, "start=i" => \$start, "mincoverage=i" => \$mincoverage,
"output=s" => \$output, "input=s" => \$input, "targets=s" => \$targets)
or die "GetOptions: error (see above)\n";
if (defined $misc::help) { usage(); exit; }
misc::set_verbose();
which does not look much better (and appears to not work anyway, at least -help is ignored).
So, what do people use for command line options shared between modules and scripts?
Personally, I do it simpler:
use a hash to store command line options
GetOptions(\%args, "xxx1","xxx2");
Pass that hash - as is - to ANY classes' constructrs, or module setters
my $object = Class1->new(%args, %constructor_args);
Module2::set_args(\%args); #
Argument setter in the module would be:
# Module2
our %args;
sub set_args { %args = %{ shift }; }
This ensures that:
I NEVER have to worry about moving parameters from scope to scope and having to modify some calls. They are ALL passed around 100% of needed places
Neat and non-messy code. Since we are broadcasting, we don't need to worry about subscribers' individual needs.
Pattern easily replicated to ALL classes you own.
As a matter of fact, if you want to be extra crafty, you can even replace Module2::set_args(\%args); calls for several classes with a smart code that:
Reads in a list of loaded modules
Checks which of those modules implements set_args() call via UNIVERSAL::can()
Calls the supporting modules' set_args() call
The latter makes the code even cleaner, in that N calls to set_args() one for each non-class module is are all replaced by one set_all_modules_args() call.
Have a module that's responsible for getting standard options.
Use this module, and everyone will be able to access the verbose() call.
package StandardOptions;
use base qw(Exporter);
our #EXPORT = qw(verbose);
use Getopt::Long;
Getopt::Long::Configure(qw(pass_through));
my $helpNeeded;
my $verbose
GetOptions ("help" => \$helpNeeded, "verbose" => $verbose);
if ($helpNeeded) {
#provide help
exit(0);
}
sub verbose {
return $verbose;
}
1;

Perl - Overloading package/class properties?

I am attempting to port some code from PHP that basically boils down to property overloading. That is, if you try to get or set a class property that is not actually defined as a part of the class, it will send that information to a function that will pretty much do anything I want with it. (In this case, I want to search an associative array within the class before giving up.)
However, Perl is... quite a bit different from PHP, given that classes are already hashes. Is there any way that I can apply some equivalent of __get() and __set() to a Perl "class" that will remain completely encapsulated in that package, transparent to anything trying to actually get or set properties?
EDIT: The best way to explain this may be to show you code, show the output, and then show what I want it to output.
package AccessTest;
my $test = new Sammich; #"improper" style, don't care, not part of the question.
say 'bacon is: ' . $test->{'bacon'};
say 'cheese is: ' . $test->{'cheese'};
for (keys $test->{'moreProperties'}) {
say "$_ => " . $test->{'moreProperties'}{$_};
}
say 'invalid is: ' . $test->{'invalid'};
say 'Setting invalid.';
$test->{'invalid'} = 'true';
say 'invalid is now: ' . $test->{'invalid'};
for (keys $test->{'moreProperties'}) {
say "$_ => " . $test->{'moreProperties'}{$_};
}
package Sammich;
sub new
{
my $className = shift;
my $this = {
'bacon' => 'yes',
'moreProperties' => {
'cheese' => 'maybe',
'ham' => 'no'
}
};
return bless($this, $className);
}
This currently outputs:
bacon is: yes
Use of uninitialized value in concatenation (.) or string at ./AccessTest.pl line 11.
cheese is:
cheese => maybe
ham => no
Use of uninitialized value in concatenation (.) or string at ./AccessTest.pl line 17.
invalid is:
Setting invalid.
invalid is now: true
cheese => maybe
ham => no
Now, I need to make modifications to Sammich only, without making any changes at all to the initial AccessTest package, that will result in this:
bacon is: yes
cheese is: maybe
cheese => maybe
ham => no
invalid is: 0
Setting invalid.
invalid is now: true
cheese => maybe
ham => no
invalid => true
As you can see, the desired effect is that the 'cheese' property, since it's not a part of the test object directly, would instead be grabbed from the 'moreProperties' hash. 'invalid' would attempt the same thing, but since it is neither a direct property nor in 'moreProperties', it would act in whatever way programmed - in this case, I would want it to simply return the value 0, without any errors or warnings. Upon attempting to set the 'invalid' property, it would not be added to the object directly, because it's not already there, but would instead be added to the 'moreProperties' hash.
I'm expecting this to take more than the six or so lines it would require in PHP, but as it is a very important concept of OOP, I fully expect Perl to handle it somehow.
As I have said in my comments, the reason this problem is hard is that you aren't following one of the golden rules of Object-Oriented Programming, namely encapsulation. How can you expect to intercept a call that isn't a method? If your exposed API consists of getter/setters then you can intercept an unknown method call with an AUTOLOAD method. If you don't you may use #pilcrow's noble suggestion of using a tied hash (Edit: or #tchrist's valiant use of the overload pragma); still this is more a tribute to Perl's flexibility than your API.
To do this more correctly (and yes I see you "require" that the API not be modified, if you choose to ignore this, then call this post a message to future readers).
#!/usr/bin/env perl
use v5.10; # say
use strict;
use warnings;
use MooseX::Declare;
use Method::Signatures::Modifiers;
class Sammich {
has 'bacon' => ( isa => 'Str', is => 'rw', default => 'yes' );
has 'more' => (
isa => 'HashRef',
is => 'rw',
default => sub{ {
cheese => 'maybe',
ham => 'no',
} },
);
our $AUTOLOAD;
method AUTOLOAD ($val?) {
# get unknown method name
my $attr = (split( /::/, $AUTOLOAD))[-1];
my $more = $self->more;
# see if that method name exists in "more"
if (exists $more->{$attr}) {
# if so, are there new values? then set
if (defined $val) {
$more->{$attr} = $val;
}
# ... and return
return $more->{$attr};
}
# attr does not exist, so set it or initialize it to 0
$more->{$attr} = defined $val ? $val : 0;
return $more->{$attr};
}
}
# I don't care that you don't care
my $test = Sammich->new();
say 'bacon is: ' . $test->bacon;
say 'cheese is: ' . $test->cheese;
for (keys %{ $test->more }) {
say "$_ => " . $test->more->{$_};
}
say 'invalid is: ' . $test->invalid;
say 'Setting invalid.';
$test->invalid( 'true' );
say 'invalid is now: ' . $test->invalid;
for (keys %{ $test->more }) {
say "$_ => " . $test->more->{$_};
}
Some may say that my wording here is harsh and perhaps it is. I try to help those who will be helped, therefore seeing a bolded message like
I need to make modifications to Sammich only, without making any changes at all to the initial AccessTest package
then demanding that Perl bow to your whim
I'm expecting this to take more than the six or so lines it would require in PHP, but as it is a very important concept of OOP, I fully expect Perl to handle it somehow.
is irksome. I hope that future readers will see this as a case example of why encapsulation helps in the long run.
Update to the update
(I am receiving anonymous downvotes, presumably for abetting your misguided approach. :) )
Just to be clear, the question you pose is an "XY Problem", specifically an artifact of the mistaken translation of OO technique from PHP to Perl. For example, as mentioned passim in this question, object properties in perl generally should not be implemented as directly accessed hash(ref) elements. That's the wrong "X".
Jumping from one language to another introduces more than merely syntactical differences.
Update
You can accomplish what you want with something like this:
package UnfortunateHack; {
use Tie::Hash;
our #ISA = qw(Tie::StdHash);
sub FETCH {
my ($self, $key) = #_;
return exists $self->{$key}
? $self->{$key}
: $self->{moreProperties}->{$key};
}
}
...
package Sammich;
sub new {
my $class = shift;
tie my %this, 'UnfortunateHack';
%this = ( bacon => 'yes',
moreProperties => { ... } );
bless \%this, $class;
}
Original Answer
If I understand your intent — to intercept $obj->property calls where TheClass::property isn't necessarily defined — then perl's AUTOLOAD in an OO context will do what you want.
From the linked docs (perlobj):
If you call a method that doesn't exist in a class, Perl will throw an error. However, if that class or any of its parent classes defines an AUTOLOAD method, that AUTOLOAD method is called instead.
You are completely violating encapsulation. To prove it to you, comment out the bless from &Sammich::new so that it returns a plain hash reference.
package Sammich;
sub new {
my $className = shift;
my $this = {
'bacon' => 'yes',
'moreProperties' => {
'cheese' => 'maybe',
'ham' => 'no'
}
};
# don't even bother blessing it
# return bless($this, $className);
}
The only way to get what you want it is to use magic.
In Perl classes are more than hashes, they are built on packages and you can define there whatever method you want and it remains encapsulated in that package/class.
You can see a code example in the Object-Oriented Programming in Perl Tutorial.

dynamic call to subroutines in perl

I'm a bit messed up with the following:
I have a function that calls subroutines in the following way:
sub someFunction {
my $self = shift;
my $type = $self->{'type'};
if($type eq 'one_subroutine') {
$self->updateOneSubroutine();
}
elsif($type eq 'another_one_sub') {
$self->updateAnotherOneSub();
}
(...)
else {
die "Unsupported '$type'";
}
I have to change this to let each subroutine be coded in its own file, include all available files, and automagically call the subroutine inside.
I did this in a test file with the following code:
# Assume a routines subdir with one_subroutine.pm file with
sub updateOneSubroutine(){
$self = shift;
$self->doSomeThings();
(...) #my code
}
1;
test.pl
# Saves in routines hash_ref a pair of file_name => subRoutineName for each file in routines subdir.
# This will be used later to call subroutine.
opendir(DIR,"lib/routines") or die "routines directory not found";
for my $filename (readdir(DIR)) {
if($filename=~m/\.pm$/){
# includes file
require "lib/routines/$filename";
# get rid of file extension
$filename=~s/(.*)\.pm/$1/g;
my $subroutine = "update_${file}";
# camelizes the subroutine name
$subroutine=~s/_([a-z0-9])/\u$1/g;
$routine->{ $filename } = $subroutine;
}
}
{
no strict "refs";
$routine->{$param}();
}
where param is something like "one_subroutine", that matches with a filename available.
Since each subroutine receives $self in the call, I should call the routine by $self->something();
I've tried $self->$routine->{$param}() , $self->${routine->${param}}() and many other things without success. I've checked chapter 9 "dynamic subroutines" of mastering perl, and a similar question to perl monks, but I can't still figure out how to reference the subroutine in a way that represents $self->updateAnotherOneSub() , or something similar that lets $self be read as a param in those subroutines.
Thanks in advance, Keber.
This seems a bit like an X/Y problem. What exactly are you trying to do? If it is to reduce loading time, then modules like AutoSplit/AutoLoader might be of interest to you.
If it is to create some sort of data structure of subroutines, you should be installing anonymous subs into a hash, rather than giving them all names.
Given a subroutine reference:
my $code = sub {...};
you would call it as:
$self->$code(...);
If instead you have a subroutine name, you can lookup the coderef:
my $code = 'Package::With::The::Subroutines'->can('method_name');
and if that succeeds (check it), then you can use $self->$code(...) to call it.
Given this code:
{
no strict "refs";
$routine->{$param}();
}
You would pass $self to the routine with:
{
no strict "refs";
$routine->{$param}($self);
}
Or you could approach it the way I did above with can:
'package'->can($routine->{$param})->($self)
if you don't want to turn off strict 'refs'
Try to extract the method name first, then it should work. I did a small test script that may do something like you want to, so:
my $method = $routine->{$param};
$self->$method->();
You can and of course should check, if the desired method exists like Eric said:
if ($self->can($method)) {
$self->$method->();
}
The important part here is, that you extract the method name so you have it in a single variable; otherwise perl won't figure that out for you - and as far as I know there is no way of setting parens or braces to do so.

Why can't my Perl subroutine see the value for the variable in the foreach loop that called it?

I hope this is something straightforward that I'm doing wrong. I saw something online about "variable suicide" that looked good, but it was for an older version and I'm on 5.10.1.
Anyway - a variable that I declared - $RootDirectory - just suddenly loses its value, and I can't figure out why.
Here's a script to reproduce the problem. When I run through the script in debug mode (perl -d) I can get it to print out the $RootDirectory at line 21 and 26. But it's gone by line 30.
use strict;
my $RootDirectory;
my #RootDirectories;
#RootDirectories = (
'c:\\P4\\EDW\\PRODEDW\\EDWDM\\main\\db\\'
,'c:\\P4\\EDW\\PRODEDW\\EDWADS\\main\\db\\'
,'c:\\P4\\EDW\\PRODEDW\\FJE\\main\\db\\'
);
foreach $RootDirectory (#RootDirectories) {
# $RootDirectory = 'c:\\P4\\EDW\\PRODEDW\\EDWDM\\main\\db\\';
# print ' In foreach ' . $RootDirectory. "\n";
RunSchema ();
}
exit(0);
sub RunSchema() {
# print ' In RunSchema ' . $RootDirectory. "\n";
CreateTables ();
}
sub CreateTables() {
# print ' In CreateTables ' . $RootDirectory. "\n";
SQLExecFolder ('tbl');
}
sub SQLExecFolder() {
print ' In SQLExecFolder ' . $RootDirectory. "\n"; # Variable $RootDirectory value is gone by now
}
EDIT
Thanks for all the comments! I think for now I'll use the "our" keyword which appears to work well - thanks Nathan. Also thanks toolic about the Use Warnings - I think I'm sold on that one!
The thing that continues to confuse me is why, when I did debug mode (perl -d), and stepped through the code, doing "p $RootDirectory" I got the expected output at lines 21 and 26, but not line 30. How is the situation different at line 30?
Also, I appreciate the comments about best practice being to pass $RootDirectory as a function parameter. I wanted to avoid that because I have so many functions following that - i.e. RunSchema calls CreateTables which calls SQLExecFolder. All of them would have to have the same parameter passed. Does it still make sense in this case, or are there any better ways to structure this?
What Nathan said is correct. That aside, why don't you pass in the value? It's better practice anyway:
foreach $RootDirectory (#RootDirectories) {
# $RootDirectory = 'c:\\P4\\EDW\\PRODEDW\\EDWDM\\main\\db\\';
# print ' In foreach ' . $RootDirectory. "\n";
RunSchema ($RootDirectory);
}
sub SQLExecFolder {
my $RootDirectory = shift;
print ' In SQLExecFolder ' . $RootDirectory. "\n";
}
You're declaring $RootDirectory as the loop variable in a foreach loop. As far as I understand, that means that its value is localized to the loop, and its value is restored to its previous value at the end of the loop.
In your case the variable was never assigned, so at the end of the loop it returns to its previous value of undef.
Edit: Actually, the problem is that $RootDirectory is declared with my, so it is undefined in other scopes. In the functions RunSchema, CreateTables and SQLExecFolder the variable is undefined, regardless of the localization of the foreach.
If you want the variable to be declared for strictness, but want it to be global, declare $RootDirectory with our:
our $RootDirectory;
Edit: That being said, it's not always a good idea to use a global variable. You're better off passing the variable as a parameter to the functions as others have suggested.
Others have answered your question correctly. I just want to emphasize that you should add use warnings; to your code. It would have given a clue to your problem, and it would alert you to another potential hazard.
foreach variable is special - it's local to the loop.
If the variable is preceded with the
keyword my, then it is lexically
scoped, and is therefore visible only
within the loop. Otherwise, the
variable is implicitly local to the
loop and regains its former value upon
exiting the loop. If the variable was
previously declared with my, it uses
that variable instead of the global
one, but it's still localized to the
loop. This implicit localisation
occurs only in a foreach loop.
Please take a look here
The iterator variable in foreach loop is always localized to the loop. See the foreach section in perlsyn. You can pass it to a subroutine as a parameter.
RE: When to use a global variable?
Global variables are risky because they can be changed at any time by any part of the code that accesses them. In addition, it is difficult to track when and where a change occurs, which makes it harder to track down unintentional consequences from modification. In short, each global variable increases coupling between the subroutines that use it.
When does it make sense to use a global? When the benefits outweigh the risks.
If you have many different values needed by most or all of your subroutines, it seems like a good time to use global variables. You can simplify every subroutine invocation, and make the code clearer, right?
WRONG. In this case the right approach is to aggregate all those distinct variables in one container data structure. So instead of foo( $frob, $grizzle, $cheese, $omg, $wtf ); you have foo( $state, $frob ); Where $state = { grizzle => $grizzle, cheese => $cheese, omg => $omg, wtf => $wtf };.
So now we have one variable to pass around. All those sub calls are much simpler. Yet, even so, this is onerous and you still want to clean up the extra argument from each routine.
At this point you have several options:
Make $state global and just access it directly.
Make $state into a configuration object and use methods to control access to attributes.
Make the whole module into a class, and store all the state information in an object.
Option 1 is acceptable for small scripts with few routines. The risk of hard to debug errors is small.
Option 2 makes sense when there is no obvious relationship between the different routines in the module. Using a global state object helps because it decreases coupling between code that accesses it. It is also easier to add logging to track changes to the global data.
Option 3 works well if you have a group of closely related functions that operate on the same data.
Your sample code seems like a good candidate for option 3. I created a class called MySchema and all the methods that operate on a specific directory are now methods. The invoking object carries the data it needs with it.
Now we have nice, clean code and no globals.
use strict;
use warnings;
my #directories = (
'c:\\P4\\EDW\\PRODEDW\\EDWDM\\main\\db\\',
'c:\\P4\\EDW\\PRODEDW\\EDWADS\\main\\db\\',
'c:\\P4\\EDW\\PRODEDW\\FJE\\main\\db\\',
);
for my $schema ( make_schemata(#directories) ) {
$schema->run;
}
sub make_schemata {
my #schemata = map { MySchema->new( directory => $_ } #_;
return #schemata;
}
BEGIN {
package MySchema;
use Moose;
has 'directory' => (
is => 'ro',
isa => 'Str',
required => 1,
);
sub run {
my $self = shift;
$self->create_tables;
}
sub create_tables {
my $self = shift;
$self->sql_exec_folder('tbl');
}
sub sql_exec_folder {
my $self = shift;
my $dir = $self->directory;
print "In SQLExecFolder $dir\n";
}
1;
}
As a bonus, the code in the BEGIN block can be removed and placed in a separate file for reuse by another script. All it needs to be a full-fledged module is its own file named MySchema.pm.
Not a bad effort. Here are a couple of small improvements, and one "fix" which is to pass the variable to the subroutines, as a function parameter because the $RootDirectory variable is scoped (i.e. restricted) to within the foreach loop. In general it is also considered good practice in order to make explicit what variables are being passed and/or accessed by various subroutines.
use strict;
use warnings;
sub RunSchema() {
my $root_dir = shift;
CreateTables($root_dir);
}
sub CreateTables() {
my $root_dir = shift;
SQLExecFolder('tbl', $root_dir);
}
sub SQLExecFolder() {
my ($name, $root_dir) = #_;
}
######################################################
my #RootDirectories = qw(
c:\\P4\\EDW\\PRODEDW\\EDWDM\\main\\db\\
c:\\P4\\EDW\\PRODEDW\\EDWADS\\main\\db\\
c:\\P4\\EDW\\PRODEDW\\FJE\\main\\db\\
);
foreach my $RootDirectory (#RootDirectories) {
# print ' In foreach ' . $RootDirectory. "\n";
RunSchema($RootDirectory);
}
exit(0);