I have .ini file (config file for the database operations):
[Section1]
SQL1=select * from <tablename>
SQL2=insert into table <table name>
I have written below code to read the each section of .ini file and its working perfect. I have to use below subroutine in my mail file, I want to call it and pass the each value in each section into the hash over there and do the database operations.
below is code:
sub Read_INI_files_get_initialData {
my ( %ini_file, $ini_sect );
tie %ini_file, 'IniFiles',( -file => "/home/testtool/config/InitialData.ini" );
for $ini_sect ( keys %ini_file ) {
%$ini_sect = %{ $ini_file{$ini_sect} };
}
print "$Section1{SQL1}\n"; # output prints the 1st SQL1 statement return in .ini file.
return (\%Section1);
}
When I call this subroutine from main file, I don't get any return value which I could use for further database opration.
You have a mixup with your variables. Also I'm not sure what you are trying to do. If you only want to read Section1, consider this example (which I have not tested).
use strict;
use warnings;
use feature 'say';
sub Read_INI_files_get_initialData {
tie my %ini_file, 'IniFiles',( -file => "/home/testtool/config/InitialData.ini" );
say "$ini_file{Section1}->{SQL1}";
# return a hashref
return { $ini_file{Section1} };
}
Basically what you did was the following:
for $ini_sect ( keys %ini_file ) {
%$ini_sect = %{ $ini_file{$ini_sect} };
}
print "$Section1{SQL1}\n"; # output prints the 1st SQL1 statement return in .ini file.
return (\%Section1);
The $ini_sect is declared above, but then you use it to iterate over the keys. So the first time the for is run, it will get a key of %ini_file. Now in the loop you asume it is actually a hashref, dereference it and assign another hash (which you dereferenced from a hash ref using the key). There are two issues here.
First, you are overwriting the variable that holds the key. In the next iteration, that value would be gone.
Second, and more important, you are trying to dereference a string. That won't work. If you add use strict and use warnings to you program (as I did above), it will tell you Can't use string ("Section1") as a HASH ref.... So there lies another problem.
What it will also tell you is that Global symbol "%Section1" requires explicit package name in the return, because you never declared it.
Think about what you want to do in your function. Use as many variables as you need, and give them meaningful names. Do you just want to read the first section of the file? Go ahead, reference it directly.
Do you want to make a copy of the whole thing? Maybe tie is not the best option. See Config::IniFiles how to do it with an OOp interface.
If I understand you correctly, you want to use the tied hash to access the SQL statements specified in your config file. This can be done by using the nested hash structure $ini_file{SectionName}{VariableName}:
use strict;
use warnings;
use Config::IniFiles;
my %initialData = Read_INI_files_get_initialData();
print $initialData{Section1}{SQL1} . "\n"; # Prints the Section1 SQL1 statement from .ini file.
sub Read_INI_files_get_initialData {
my %ini_file;
tie %ini_file, 'Config::IniFiles', ( -file => "InitialData.ini" );
return %ini_file;
}
Related
I have a bunch of scripts I wanted to replace some texts.
Context : We are using selenium for UI Automation. We used to store the references to the UiElements in a map. But we are now moving to use PageFactory (a class with all the UiElements declared as a string)
So when we used map, we had to call the UIelements as objectMap.getIdentifier("navigate.leftsidebar"). But now with PageFactory (its like instantiating a object, and the UIElements are declared as a string), I can access these UIelements objectPageFactory.navigate_leftsidebar (here navigate_leftsidebar is a String)
So I will need to go modify all my existing scripts. Is there a way I can write a script to get this replaced, than doing it manually ?
Below are the 3 scenarios that I will encounter :
Click(getElement(objectMap.getIdentifier("navigate.leftsidebar").replace("$Page", "Inbox")), "clicking on an element");
objectMap.getIdentifier("navigate.leftsidebar")
Click(objectMap.getIdentifier("navigate.leftsidebar"), "clicking on an element");
This is the expected output:
Click((objectPageFactory.navigate_leftsidebar("Inbox")), "clicking on an element");
objectPageFactory.navigate_leftsidebar
Click(objectPageFactory.navigate_leftsidebar, "clicking on an element");
Changes are :
"objectMap" to be renamed as "objectPageFactory"
There could be different types of map. if objectMap , it should be replaced as objectPageFactory; if loginMap, it should be changed as loginPageFactory
objectMap.getIdentifier("navigate.leftsidebar") >>>> objectFactory.navigate_leftsidebar (the String literal inside the bracket is separated by underscore instead of dots
getElement is not needed now
we used to have some dynamic UiElements (navigate.leftsidebar in this case), for which we used to call String.replace, now we are writing functions which will internally do a String.format
getElement(objectMap.getIdentifier("navigate.leftsidebar").replace("$Page", "Inbox")) >>>>> objectPageFactory.navigate_leftsidebar("Inbox")
I got a perl script from this link, which will do partial job sed command to replace dots.
I just need to add the different scenarios to this, is there a way ? the output should now have a pageFactory text too, based on which map
#! /usr/bin/perl
use strict ;
sub fix { $_ = shift ; s/"//g ; s/\./_/g ; return $_ }
while ( <> ) {
s/getElement\(objectMap\.getIdentifier\(("?[a-z.]+"?)\)/fix($1)/e ;
s/objectMap\.getIdentifier\(("?[a-z.]+"?)\)/fix($1)/e ;
print
}
This seems to provide the output you requested. I don't understand the language you're changing, so there might be corner cases it processes wrong. Make a backup before you change the files!
#!/usr/bin/perl
use warnings;
use strict;
sub fix {
my ($id) = #_;
return $id =~ s/[.]/_/gr
}
while (<>) {
s{getElement\((object|login)Map\.getIdentifier\("([^"]*)"\)\.replace\("\$Page", "([^"]*)"\)\)}
{"$1PageFactory." . fix($2) . qq(("$3"))}ge;
s{(object|login)Map\.getIdentifier\("([^"]*)"\)}
{"$1PageFactory." . fix($2)}ge;
print;
}
Running Perl 5.18 on Ubuntu 14.04 LTS, I wanted to create some pathname constants so I did
use Path::Class;
use constant FILEPATH => file('directory', 'filename');
However, when I came to use the constant in a hash aggregate ...
my $hash = { filepath => FILEPATH };
use Data::Dumper;
print Dumper $href;
... I was surprised to discover that the value of the filepath key was a blessed reference, not the string result from the function call that I was expecting.
I can work around the problem like this ...
use constant FILEPATH => file('directory', 'filename') . "";
... which forces the Perl interpreter to evaluate the blessed reference, but
(a) is there a better way, and
(b) what the heck is going on?!
I know that use constant is evaluated in a list context, but normally use constant MYCONST => mysub(arg1, arg2); does The Right Thing, evaluates the subroutine call and uses the return value. What's the cleverness with Path::Class::file that breaks this expectation?
The file function from the module Path::Class is in fact a constructor for a Path::Class object. It always returns an object. Even the SYNOPSIS says so.
use Path::Class;
my $dir = dir('foo', 'bar'); # Path::Class::Dir object
my $file = file('bob', 'file.txt'); # Path::Class::File object
If all you want is the path, call that method on the return value of file and assign that calls' return value to your constant. The object you get is a Path::Class::File. It provides various methods. To do the same as when you use the string overload, call the stringify method.
use constant FILEPATH => file('directory', 'filename')->stringify;
The relevant part of HTML::Tiny decides what to do based on the type of the inputs. It expects either undef, a string or an array ref, but you provided something that is none of these. In other words, this is a case of GIGO. You are using the value returned by file(), which is an object that overloads stringification.
Use either of the following to provide the expected input:
use constant FILEPATH => file('directory', 'filename')->stringify;
or
use constant FILEPATH => "".file('directory', 'filename');
Note that it's usually unsafe to unsafe to use objects as constants because they're not constant. However, since file() returns an immutable object, there's no harm in using it as a constant since it really is constant.
You're right about Path::Class::file returning an object. That's what all the "bless" stuff was in the Dumper output. Sorry I didn't realise that sooner.
As you'll see from my comments, I ran into the problem using HTML::Tiny, which you can see as follows:-
$ perl -MPath::Class -MHTML::Tiny -le 'print HTML::Tiny->new(mode=>"html")->form({action=>file("cgi-bin", "script")})'
<form action></form>
As you can see, the action attribute is missing the path. However,
$ perl -MPath::Class -MHTML::Tiny -le 'print HTML::Tiny->new(mode=>"html")->form({action=>file("cgi", "script").""})'
<form action="cgi/script"></form>
This works because the ."" string concatenation operator causes the Class::Path::File object's automatic stringifier to be called, so an actual string is passed in the {action=>value} hash, not an object.
If you dig into the HTML::Tiny 1.05 code, you'll find sub _tag on line 622, which goes
if ( ref $val ) {
return $attr if not $self->_xml_mode;
This test allows you to use { attr=>[] } to output an attribute name without a value, but unfortunately the test is also TRUE if $val is an object. I'll raise a bug report for this against HTML::Tiny.
I want to create a hash of optional query parameters that are sometimes passed to my subroutine. Sometimes a query parameter called welcome is passed in, and the value is either 1 or 0.
If that variable exists, I want to add it to a hash.
I've created a configuration value called OPTIONAL_URL_PARAMS which is a list of expected parameter names that can be passed in:
use constant OPTIONAL_URL_PARAMS => ("welcome")
So far I have:
my $tempParams = {};
if ( $optionalParam ) {
foreach my $param (#{&OPTIONAL_URL_PARAMS}) {
if ($optionalParam eq $self->{$param}) {
$tempParams->{$param} = $optionalParam;
$tempParams->{$param} =~ s/\s+//g; # strip whitespace
}
}
}
But this tries to use the value of $self->{$param} instead of its name. i.e. I want welcome to match welcome, but it's trying to match 1 instead.
I know when it comes to keys in a hash you can use keys %hash, but is there a way you can do this with regular variables?
Edit
My subroutine is being called indirectly:
my $url_handler = URL::Handler->new($param, $optionalParam);
sub new {
my $class = shift;
my $args = #_;
my $self = {
param => $args{param},
optionalParams => $args{optionalParam}
};
}
If $optionalParam's variable name is 'welcome', then I want to try and map it to the constant welcome.
This is not an answer any more, but I cannot remove it yet as there is still a discussion going on to clarify the question.
foreach my $param (#{&OPTIONAL_URL_PARAMS}) {
# ...
}
The return value of OPTIONAL_URL_PARAMS (you already got an error here and that's why you have the &, that should have told you something...) is simply a list, not an array ref. Actually at this point it should throw an error because you cannot use 1 as an array reference.
Edit
In Perl, when you pass arguments to a subroutine, all the values are flattened into a single list (reference). Specifically, if you are passing parameters to a sub, the sub doesn't know the names of the variables you originally used. It only knows their values. Therefore, if you want names as well as values, you have to pass them separately. An easy way is using a hash. E.g., in new():
my $class = shift;
my $param = shift; # The first, required parameter
my %therest = (#_); # Optional parameters, if any
Then you can say URL::Handler->new($param, 'welcome' => 1), and $therest{welcome} will have the value 1. You can also say
URL::Handler->new($param, 'welcome'=>1, 'another'=>42);
and %therest will have the two keys welcome and another.
See also some further discussion of passing whole hashes as parameters
Original
This also probably doesn't answer the question!
Some thoughts on the code from your comment.
my $url_handler = URL::Handler->new($param, $optionalParam);
sub new {
my $class = shift; # OK; refers to URL::Handler
my $args = #_; # Problematic: now $args is the _number_ of args passed (list #_ evaluated in scalar context).
my $self = {
# There is no %args hash, so the next two lines are not doing what you expect.
# I actually don't know enough perl to know what they do! :)
param => $args{param},
optionalParams => $args{optionalParam}
};
}
Some thoughts:
use strict; and use warnings; at the top of your source file, if you haven't yet.
I can think of no languages other than Algol 60 that support this idea. It goes against the idea of encapsulation, and prevents you from using an array or hash element, a function call, a constant, or an expression as the actual parameter to a call
Variable names are purely for the convenience of the programmer and have no part in the functionality of any running program. If you wished so, you could write your code using a single array #memory and have "variables" $memory[0], $memory[1] etc. But you would be bypassing the most useful part of compiler technology that allows us to relate readable text to memory locations. It is best to consider those names to be lost once the program is running
The called code should be interested only in the values passed, and it would be a nightmare if the name of a variable passed as an actual parameter were significant within the subroutine
If you were able to access the name of a variable passed as a parameter, what do you suppose would be provided to subroutine stats if the call looked like this
stats( ( $base[$i] + 0.6 + sum(#offset{qw/ x y /}) + sum(#aa) ) / #aa )
In summary, it cannot be done in general. If you need to associate a value with a name then you should probably be looking at hashes
Your code
my $url_handler = URL::Handler->new($param, $optionalParam);
sub new {
my $class = shift;
my $args = #_;
my $self = {
param => $args{param},
optionalParams => $args{optionalParam}
};
}
has a number of problems
You correctly shift the name of the class from parameter array #_, but then set my $args = #_, which sets $args to the number of elements remaining in #_. But the value of $args is irrelevant because you never use it again
You then set $self to a new anonymous hash, which is created with two elements, using the values from hash %args. But %args doesn't exist, so the value of both elements will be undef. Had you put use strict and use warnings 'all' in place you would have been alerted to this
The keys that you're using to access this non-existent hash are param and optionalParam, and I think it's more than a coincidence that they match the names of the actual parameters of the call to new
While Perl is unusual in that it allows programmatic access to its symbol tables, it is an arcane and unrecommended method. Those names are essentially hidden from the program and the programmer and while modules like Exporter must manipulate symbol tables to do their job, any such behaviour inside base-level software is very much to be avoided
Finally, you never use $self again after defining it. You should be blessing it into a class according to the $class variable (which contains the string URL::Handler) and returning it from the constructor
I hope this helps
I would like to use a Perl variable from my script in a Log::Log4perl config file. I read the documentation and found that I can use a subroutine, but I would like to do it a little bit simpler, if possible.
I want to set the filename for my appender:
log4perl.appender.av_std_LOGFILE.filename="whateverfilename.log"
But doing this this way, it is a fixed value.
I have the filename in a variable within my script and would like to use this at runtime:
log4perl.appender.av_std_LOGFILE.filename=\
sub { return &av_getLogfileName(); }
Where this is the subroutine:
sub av_getLogfileName
{
return $av_std_LOGFILE;
}
This works, but I would like to avoid the sub inside my script since the return value is very simple.
The documentation says:
Each value starting with the string sub {... is interpreted as Perl code to be executed at the time the application parses the configuration...
So I tried something like this, but it did not work:
log4perl.appender.av_std_LOGFILE.filename=\
sub { print "$av_std_LOGFILE"; }
Is there a way to get result of the variable without the sub inside my script?
print returns 1 on success, so
sub { print "$av_std_LOGFILE"; }
returns 1, not the value of $av_std_LOGFILE. You also have to fully qualify variable names in hooks, which means you'll have to make $av_std_LOGFILE a package global.
Change your hook to:
sub { return $main::av_std_LOGFILE; } # double quotes are unnecessary
and set $av_std_LOGFILE in your script like this (before calling Log::Log4perl::init):
our $av_std_LOGFILE = '/path/to/logfile';
Generally, you should avoid global variables, so I would prefer using a subroutine.
I am trying to use the tie function of the module Config::IniFiles but I cannot figure out how to reference the hash inside of sub routine. If I remove the tie function and related code from the sub routine it works perfectly.
This is the line I thought would work, but tells me that "$cfg" is not initialized.
use Config::IniFiles
sub config_file {
my $cfg_file = 'settings.ini';
my %cfg;
tie %cfg, 'Config::IniFiles', ( -file => "$cfg_file" );
#my $cfg = Config::IniFiles->new( -file => $cfg_file );
}
sub esx_host_check {
my $esx_host = config_file()->$cfg{ESX}{host};
}
I am sure it is something simple, but I am stumped.
First off, the tie function returns the internal hidden object that represents the tie, and not the tied variable itself. Secondly, you can not return a plural tied value (hash or array) from a subroutine and have it work the way you are expecting. You need to return a reference to the plural value, and then dereference it when you need to use it.
use Config::IniFiles;
sub config_file {
tie my %cfg, 'Config::IniFiles', -file => 'settings.ini'; # tie variable
return \%cfg; # return a reference to the tied variable
}
sub esx_host_check {
my $esx_host = config_file()->{ESX}{host}; # call sub and dereference value
}
If you are going to use the config hash more than a few times, its probably best to build it and then cache the result:
{my $cfg;
sub config_file {
tie %$cfg, 'Config::IniFiles', -file => 'settings.ini' unless $cfg;
return $cfg;
}}
This is a little different than above. First, we setup config_file to be a closure around the private variable $cfg. Note that it is a scalar and not a hash. Then in the sub, we check to see if the variable has been initialized, and if not, call tie. tie is passed a first argument of %$cfg which dereferences the undefined value as a hash, which has the effect of storing the tied hash reference into $cfg.
While a little more complicated, this technique will only need to build the config hash once, potentially saving a lot of time.
(1) always start your perl code with use strict. You should have received a warning in esx_host_check() about an unknown %cfg
(2) use use vars(...) to implement "global" identifiers:
use vars qw(%cfg);
sub one
{
tie %cfg, ....
}
sub two
{
my $value = $cfg{foo}{bar};
}
You are declaring %cfg with my (good!), so it's only visible inside the config_file sub; You then tie it, which returns the underlying Config::IniFiles object, and, as it is the last entry of the function, it returns that object... So I'm not sure why you are tie'ing in the first place, rather than just using the commented line.
In any case, config_file() returns a Config::IniFiles object. You then try to call a method, named by the contents of the variable $cfg{ESX}{host}.. A variable that doesn't exist!
If you want to use the tie interface, add a return \%cfg; to the end of config_file. If you want to use the object interface.. Well, I can only point you to the docs.