Velocity Reviews

Velocity Reviews (http://www.velocityreviews.com/forums/index.php)
-   Perl Misc (http://www.velocityreviews.com/forums/f67-perl-misc.html)
-   -   I am needing a gentle introduction to accessing a perl array from areference (http://www.velocityreviews.com/forums/t909387-i-am-needing-a-gentle-introduction-to-accessing-a-perl-array-from-areference.html)

Larry W. Virden 01-08-2009 08:37 PM

I am needing a gentle introduction to accessing a perl array from areference
 
I have some legacy code, the author of whom is no longer available for
consultation.

Here is a piece of the code in question:

:
up here, the DBI connection to the database, the select statement,
etc. occurs
:

# put results of query into single array and return
while (@next_row= $query->fetchrow_array()){

push(@array_result,@next_row);
}

$query->finish;
$dbconn->disconnect();
return \@array_result;

I am a perl novice. I am trying to figure out a readable approach to
dealing with the value that is returned.

One piece of code that I've bubble-gummed and bailing wired together
basically does this:

$super_list = thefunction($argument);
my $tot = scalar(@$super_list);

if ( $tot_supers >= 1 ) {
for (my $staff=0; $staff < $tot_supers; $staff++ ) {
if ( defined( $super_list->[$staff]{'User_Name'} ) ) {
$super_login = $super_list->[$staff]
{'User_Name'};
chomp($super_login);
print $super_login . "\n" ;
} else {
print "Unable to identify user $staff \n";
}
}
}

The main concern I have here is that if I want to write a "print all
columns of data" type routine, I would have to hand code a series of
if (defined...) type statements, differing depending on what table is
being dumped.

Is there another approach that would allow me to dynamically generate
the columns from the hash and then loop through them?

I apologize from the vague description - I'm still working on
understanding the data structure , etc.

Jim Gibson 01-08-2009 09:11 PM

Re: I am needing a gentle introduction to accessing a perl array from a reference
 
In article
<5ac3764f-75b2-4a7d-96ed-6e36e22484c9@a12g2000pro.googlegroups.com>,
Larry W. Virden <lvirden@gmail.com> wrote:

> I have some legacy code, the author of whom is no longer available for
> consultation.
>
> Here is a piece of the code in question:
>
> :
> up here, the DBI connection to the database, the select statement,
> etc. occurs
> :
>
> # put results of query into single array and return
> while (@next_row= $query->fetchrow_array()){
>
> push(@array_result,@next_row);
> }


Are you sure this is your code? The above statements will have the
unfortunate effect of concatenating all of the rows returned into a
single array. Perhaps this is what you actually have:
while ( my @next_row= $query->fetchrow_array()){

push(@array_result,\@next_row);
}

Notice the difference. This loop will produce an array of references to
arrays, one for each row. This is called an "array-of-arrays".

>
> $query->finish;
> $dbconn->disconnect();
> return \@array_result;
>
> I am a perl novice. I am trying to figure out a readable approach to
> dealing with the value that is returned.
>
> One piece of code that I've bubble-gummed and bailing wired together
> basically does this:
>
> $super_list = thefunction($argument);


What is the relationship between thefuncion() and the code you have
shown above?

> my $tot = scalar(@$super_list);
>
> if ( $tot_supers >= 1 ) {


What is the value of $tot_supers? Do you mean $tot?

> for (my $staff=0; $staff < $tot_supers; $staff++ ) {
> if ( defined( $super_list->[$staff]{'User_Name'} ) ) {


Where did the hash %{$super_list->[$staff]} come from?

> $super_login = $super_list->[$staff]
> {'User_Name'};
> chomp($super_login);
> print $super_login . "\n" ;
> } else {
> print "Unable to identify user $staff \n";
> }
> }
> }
>
> The main concern I have here is that if I want to write a "print all
> columns of data" type routine, I would have to hand code a series of
> if (defined...) type statements, differing depending on what table is
> being dumped.


Why? Why not use the elements that went into the select statement that
generated the returned data? You should know what you have asked for.

>
> Is there another approach that would allow me to dynamically generate
> the columns from the hash and then loop through them?
>
> I apologize from the vague description - I'm still working on
> understanding the data structure , etc.


Please explain in more detail your data structures so that we may
understand them. Your descriptions are incomplete.

--
Jim Gibson

Tad J McClellan 01-08-2009 09:56 PM

Re: I am needing a gentle introduction to accessing a perl array from a reference
 
Larry W. Virden <lvirden@gmail.com> wrote:
> I have some legacy code, the author of whom is no longer available for
> consultation.
>
> Here is a piece of the code in question:



Is the code copy/pasted, or re-typed?


> up here, the DBI connection to the database, the select statement,
> etc. occurs
>:
>
> # put results of query into single array and return
> while (@next_row= $query->fetchrow_array()){
>
> push(@array_result,@next_row);
> }
>
> $query->finish;
> $dbconn->disconnect();
> return \@array_result;



That is an awfully strange way to do it, unless your query is selecting
only a single column.

Is this your real code?

The data structure here is nothing more than (a reference to) a flat array.


> I am trying to figure out a readable approach to
> dealing with the value that is returned.



To help you with that, we will need rather precise information on what
that structure is...


> One piece of code that I've bubble-gummed and bailing wired together
> basically does this:
>
> $super_list = thefunction($argument);
> my $tot = scalar(@$super_list);
> if ( $tot_supers >= 1 ) {
> for (my $staff=0; $staff < $tot_supers; $staff++ ) {



The Perlish way would be to replace the 3 lines above with:

if ( @$super_list ) {
foreach my $staff ( 0 .. $#$super_list ) {

I'm guessing you would benefit by spending some quality time with:

perldoc perlreftut

and

perldoc Data::Dumper


> if ( defined( $super_list->[$staff]{'User_Name'} ) ) {

^ ^
^ ^
Eh? Where did the hash come from?

There is no hash anywhere in your code up at the top...


> $super_login = $super_list->[$staff]
> {'User_Name'};
> chomp($super_login);
> print $super_login . "\n" ;
> } else {
> print "Unable to identify user $staff \n";
> }
> }
> }
>
> The main concern I have here is that if I want to write a "print all
> columns of data" type routine, I would have to hand code a series of
> if (defined...) type statements, differing depending on what table is
> being dumped.
>
> Is there another approach that would allow me to dynamically generate
> the columns from the hash and then loop through them?



perldoc -f keys

Returns a list consisting of all the keys of the named hash.


> I apologize from the vague description - I'm still working on
> understanding the data structure , etc.



We need an understanding of the (actual) data structure if we are
to help you suss out how to access it.

Try posting a bit of output from:

use Data::Dumper;
print Dumper $super_list;


--
Tad McClellan
email: perl -le "print scalar reverse qq/moc.noitatibaher\100cmdat/"

Tim Greer 01-08-2009 10:25 PM

Re: I am needing a gentle introduction to accessing a perl array from a reference
 
Larry W. Virden wrote:

> I have some legacy code, the author of whom is no longer available for
> consultation.
>
> Here is a piece of the code in question:
>
> :
> up here, the DBI connection to the database, the select statement,
> etc. occurs


Can you either post the fully relevant portions of your code, or
elaborate on some aspects? Some parts of the code you've posted don't
seem to relate to the other, so either relevant code is missing, or...?
--
Tim Greer, CEO/Founder/CTO, BurlyHost.com, Inc.
Shared Hosting, Reseller Hosting, Dedicated & Semi-Dedicated servers
and Custom Hosting. 24/7 support, 30 day guarantee, secure servers.
Industry's most experienced staff! -- Web Hosting With Muscle!

Larry W. Virden 01-09-2009 12:13 PM

Re: I am needing a gentle introduction to accessing a perl array froma reference
 
On Jan 8, 4:11*pm, Jim Gibson <jimsgib...@gmail.com> wrote:
> In article
> <5ac3764f-75b2-4a7d-96ed-6e36e2248...@a12g2000pro.googlegroups.com>,
> Larry W. Virden <lvir...@gmail.com> wrote:
>



>
> > # put results of query into single array and return
> > * while (@next_row= $query->fetchrow_array()){

>
> > * * push(@array_result,@next_row);
> > * }

>
> Are you sure this is your code? The above statements will have the
> unfortunate effect of concatenating all of the rows returned into a
> single array. Perhaps this is what you actually have:
> * *while ( my @next_row= $query->fetchrow_array()){
>
> * * *push(@array_result,\@next_row);
> * *}


That was a literal copy and paste from the code. The only thing I can
figure, looking at the code, is that in the most cases, I think there
is only one record satisfying the select, so there's perhaps never
been a time when more than one item was pushed. I just inherited this
system, so I'm still learning about it.

>
> Notice the difference. This loop will produce an array of references to
> arrays, one for each row. This is called an "array-of-arrays".
>
>
>
> > * $query->finish;
> > * $dbconn->disconnect();
> > * return \@array_result;

>
> > I am a perl novice. I am trying to figure out a readable approach to
> > dealing with the value that is returned.

>
> > One piece of code that I've bubble-gummed and bailing wired together
> > basically does this:

>
> > $super_list = thefunction($argument);

>
> What is the relationship between thefuncion() and the code you have
> shown above?


The initial code I showed is a small part of a web service "method".
the $super_list line is the call to the web service.

>
> > my $tot = scalar(@$super_list);

>
> > if ( $tot_supers >= 1 ) {

>
> What is the value of $tot_supers? Do you mean $tot?


yes, that was a mistake in my attempt to translate things into a
simpler form for the posting. I apologize.


>
> > * * * * for (my $staff=0; $staff < $tot_supers; $staff++ ) {
> > * * * * * * * * if ( defined( $super_list->[$staff]{'User_Name'} ) ) {

>
> Where did the hash %{$super_list->[$staff]} come from?


The web service returns an array reference, assigned to $super_list.
$staff is the attempt to increment through the rows of the array.

>
> > * * * * * * * * * * * *$super_login = $super_list->[$staff]
> > {'User_Name'};
> > * * * * * * * * * * * *chomp($super_login);
> > * * * * * * * * * * * *print $super_login . "\n" ;
> > * * * * * * * * } else {
> > * * * * * * * * * * * * print "Unable to identify user $staff \n";
> > * * * * * * * * }
> > * * * * }
> > * * }

>
> > The main concern I have here is that if I want to write a "print all
> > columns of data" type routine, I would have to hand code a series of
> > if (defined...) type statements, differing depending on what table is
> > being dumped.

>
> Why? Why not use the elements that went into the select statement that
> generated the returned data? You should know what you have asked for.


I was wanting to write some code that would , upon receiving one of
these references to an array, just loop through the array and print
out the column names and values. The web service module that I'm
interacting with has a dozen or more methods. Rather than trying to
figure out what columns are returned by each method, I was hoping a
more generic print function could be written that would just iterate
through the array.

>
>
>
> > Is there another approach that would allow me to dynamically generate
> > the columns from the hash and then loop through them?

>
> > I apologize from the vague description - I'm still working on
> > understanding the data structure , etc.

>
> Please explain in more detail your data structures so that we may
> understand them. Your descriptions are incomplete.


The reference to the array is supposed to be results from select
statements, some of which are "select * from ...." . Certainly I can
do a describe of each table, and then hard code a series of print
statements. I was just hoping there was a streamlined best practice
for that sort of access that would generically apply.

Thank you so much for your time.

Larry W. Virden 01-09-2009 12:19 PM

Re: I am needing a gentle introduction to accessing a perl array froma reference
 
On Jan 8, 5:25*pm, Tim Greer <t...@burlyhost.com> wrote:
> Larry W. Virden wrote:
> > I have some legacy code, the author of whom is no longer available for
> > consultation.

>
> > Here is a piece of the code in question:

>
> > :
> > up here, the DBI connection to the database, the select statement,
> > etc. occurs

>
> Can you either post the fully relevant portions of your code, or
> elaborate on some aspects? *Some parts of the code you've posted don't
> seem to relate to the other, so either relevant code is missing, or...?


Thank you for your question. The bit of code was from 1600+ lines of
code in a web services program, as well as parts from a 50+ line
program which invokes one method from the web service, interacts with
other programs, to generate some internal information. I don't have
rights to distribute the whole program, so I was trying to (and
obviously failed at) picking just a small portion to get at a better
understanding at how one would generically access the elements of a
hash given a reference. I need to do more reading, and have some
references elsewhere in this thread I will use to try and learn more.

Thank you all so much for your help.

Larry W. Virden 01-09-2009 12:35 PM

Re: I am needing a gentle introduction to accessing a perl array froma reference
 
On Jan 8, 4:56*pm, Tad J McClellan <ta...@seesig.invalid> wrote:
> Larry W. Virden <lvir...@gmail.com> wrote:
>
> > I have some legacy code, the author of whom is no longer available for
> > consultation.

>
> > Here is a piece of the code in question:

>
> Is the code copy/pasted, or re-typed?


The code dealing with the fetchrow was copy/pasted. The remaining code
was typed with me trying to include just relevant details (without all
the debugging statements, etc.). I failed at some of the retyping. I
apologize.

>
> > up here, the DBI connection to the database, the select statement,
> > etc. occurs
> >:

>
> > # put results of query into single array and return
> > * while (@next_row= $query->fetchrow_array()){

>
> > * * push(@array_result,@next_row);
> > * }

>
> > * $query->finish;
> > * $dbconn->disconnect();
> > * return \@array_result;

>
> That is an awfully strange way to do it, unless your query is selecting
> only a single column.


I didn't write the code, and haven't used arrays, references, and DBI
enough to know about it. In the particularly select I was looking at,
I think the select might very well be one that only returns a single
row. I'm still trying to learn about the database and its
characteristics.

>
> Is this your real code?
>
> The data structure here is nothing more than (a reference to) a flat array.


That's the real code from the program I'm trying to understand. So,
when the fetchrow_array returns @next_row, this is a "flat array".
It's still a hash, right? With the hash entries (sorry I don't know
the technical term - the values by which one accesses information in
the hash) being the names of the columns from the select and the
information stored in the hash being the values from the columns. And
fetchrow_array returns ONE row worth of data.

That's what I understood.

One point that confused me though was the push(@array_result,
@next_row); statement. From other statements mentioned here, it sounds
to me as if, in cases of multiple rows returned, @array_result becomes
a "mish mash" of information, overwriting previously existing
values.

Am I understanding that correctly?

>
> > I am trying to figure out a readable approach to
> > dealing with the value that is returned.

>
> To help you with that, we will need rather precise information on what
> that structure is...


I'm trying to understand the code to the point that I can figure the
structure out. I apologize for my limited vocabulary and
understanding.

>
> > One piece of code that I've bubble-gummed and bailing wired together
> > basically does this:

>
> > $super_list = thefunction($argument);
> > my $tot = scalar(@$super_list);
> > if ( $tot_supers >= 1 ) {
> > * * * * for (my $staff=0; $staff < $tot_supers; $staff++ ) {

>
> The Perlish way would be to replace the 3 lines above with:
>
> * *if ( @$super_list ) {
> * * * *foreach my $staff ( 0 .. $#$super_list ) {
>
> I'm guessing you would benefit by spending some quality time with:
>
> * *perldoc perlreftut
>
> and
>
> * *perldoc Data::Dumper


Thank you so much for the suggestion. I'll read those and hopefully
have a better comprehension of what is going on.

>
> > * * * * * * * * if ( defined( $super_list->[$staff]{'User_Name'} ) ) {

>
> * * * * * * * * * * * * * * * * * * * * * * * * * * *^ * * * * * ^
> * * * * * * * * * * * * * * * * * * * * * * * * * * *^ * * * * * ^
> Eh? Where did the hash come from?


The original documentation for this web services program was spotty. I
took what was provided, tried to get something to work, talked to some
people who knew more perl than I did, and that's where I ended up
getting data back that was expected. The data that was returned to
$super_list was an array generated by the push
(@array_result,@next_row) statement after a DBI fetchrow_array. The
hash index is one of the column names of data returned by the fetch
from the select handle.

>
> There is no hash anywhere in your code up at the top...
>
>


The fetchrow_array was from DBI, returning sql rows from an oracle
database.

>
>
>
> > * * * * * * * * * * * *$super_login = $super_list->[$staff]
> > {'User_Name'};
> > * * * * * * * * * * * *chomp($super_login);
> > * * * * * * * * * * * *print $super_login . "\n" ;
> > * * * * * * * * } else {
> > * * * * * * * * * * * * print "Unable to identify user $staff \n";
> > * * * * * * * * }
> > * * * * }
> > * * }

>
> > The main concern I have here is that if I want to write a "print all
> > columns of data" type routine, I would have to hand code a series of
> > if (defined...) type statements, differing depending on what table is
> > being dumped.

>
> > Is there another approach that would allow me to dynamically generate
> > the columns from the hash and then loop through them?

>
> * * perldoc -f keys
>
> * * * * Returns a list consisting of all the keys of the named hash.


Ah! That's the concept I was seeking.

>
> > I apologize from the vague description - I'm still working on
> > understanding the data structure , etc.

>
> We need an understanding of the (actual) data structure if we are
> to help you suss out how to access it.
>
> Try posting a bit of output from:
>
> * *use Data::Dumper;
> * *print Dumper $super_list;
>


Thanks - the code diving I am doing is to develop a better
understanding of what this web service is doing, so that I can figure
out how things could be done better. This is, alas, not one of the
primary tasks I have, so I have to eek out time every day or so to dig
in. The feedback here has been quite valuable, and I believe I have
several valuable avenues for learning.

I appreciate everyone's time and patience.

Tad J McClellan 01-09-2009 02:50 PM

Re: I am needing a gentle introduction to accessing a perl array from a reference
 
Larry W. Virden <lvirden@gmail.com> wrote:
> On Jan 8, 4:56*pm, Tad J McClellan <ta...@seesig.invalid> wrote:
>> Larry W. Virden <lvir...@gmail.com> wrote:



>> > # put results of query into single array and return
>> > * while (@next_row= $query->fetchrow_array()){

>>
>> > * * push(@array_result,@next_row);
>> > * }

>>
>> > * $query->finish;
>> > * $dbconn->disconnect();
>> > * return \@array_result;

>>
>> That is an awfully strange way to do it, unless your query is selecting
>> only a single column.


> I think the select might very well be one that only returns a single
> row.



Oh. That makes it less strange.

But coding up a loop where you depend on always having a single
interation is kind of silly. It is a non-looping loop!


>> The data structure here is nothing more than (a reference to) a flat array.

>
> That's the real code from the program I'm trying to understand. So,
> when the fetchrow_array returns @next_row, this is a "flat array".
> It's still a hash, right?



No. If the array contained references to hashes (which it does not)
then it would not be "flat", it would be "hierarchical".

So, we still do not know where the hashes are coming from...

.... they are certainly not coming from any of the code we've seen thus far.


> With the hash entries (sorry I don't know
> the technical term - the values by which one accesses information in
> the hash)


keys (indexes into a hash)

values (the values associated with particular keys)


> being the names of the columns from the select and the
> information stored in the hash being the values from the columns. And
> fetchrow_array returns ONE row worth of data.
>
> That's what I understood.



That part is mostly correct, though there is nothing creating any
hashes that we can see...


> One point that confused me though was the push(@array_result,
> @next_row); statement. From other statements mentioned here, it sounds
> to me as if, in cases of multiple rows returned, @array_result becomes
> a "mish mash" of information, overwriting previously existing
> values.
>
> Am I understanding that correctly?



mish mash, yes.

overwriting, no.

push() adds to what is already there.

If you had "select name, address from...", then all of the even indexes
in @array_result would be names and all of the odd indexes would be addresses.

That is, alternating names and addresses.


>> > I am trying to figure out a readable approach to
>> > dealing with the value that is returned.

>>
>> To help you with that, we will need rather precise information on what
>> that structure is...

>
> I'm trying to understand the code to the point that I can figure the
> structure out.



Just ask Data::Dumper to show you the data structure, then figure it
out from the output of Data::Dumper.


>> > * * * * * * * * if ( defined( $super_list->[$staff]{'User_Name'} ) ) {

>>
>> * * * * * * * * * * * * * * * * * * * * * * * * * * *^ * * * * * ^
>> * * * * * * * * * * * * * * * * * * * * * * * * * * *^ * * * * * ^
>> Eh? Where did the hash come from?

>
> The original documentation for this web services program was spotty. I
> took what was provided, tried to get something to work, talked to some
> people who knew more perl than I did, and that's where I ended up
> getting data back that was expected. The data that was returned to
> $super_list was an array generated by the push
> (@array_result,@next_row) statement after a DBI fetchrow_array. The
> hash index is one of the column names of data returned by the fetch
> from the select handle.



fetchrow_array does not do hashes or include column names, so its
return value must be getting modified somewhere else in the code...

However, you/we probably don't need to find it if only we knew for
certain what data structure you are ending up with.


>> > * * * * * * * * * * * *$super_login = $super_list->[$staff]
>> > {'User_Name'};


>> > The main concern I have here is that if I want to write a "print all
>> > columns of data" type routine, I would have to hand code a series of
>> > if (defined...) type statements, differing depending on what table is
>> > being dumped.

>>
>> > Is there another approach that would allow me to dynamically generate
>> > the columns from the hash and then loop through them?

>>
>> * * perldoc -f keys
>>
>> * * * * Returns a list consisting of all the keys of the named hash.

>
> Ah! That's the concept I was seeking.



Then you can apply "Use Rule 1" from perlreftut.pod to get a list
of keys. I like to apply it in 3 steps:

my @keys = keys %hash; # pretend it is an ordinary hash

my @keys = keys %{ }; # replace the _name_ with a block

# fillin the block with something that returns the right kind of reference
my @keys = keys %{ $super_list->[$staff] };


--
Tad McClellan
email: perl -le "print scalar reverse qq/moc.noitatibaher\100cmdat/"

RedGrittyBrick 01-09-2009 02:52 PM

Re: I am needing a gentle introduction to accessing a perl arrayfrom a reference
 

Larry W. Virden wrote:
> On Jan 8, 5:25 pm, Tim Greer <t...@burlyhost.com> wrote:
>> Larry W. Virden wrote:
>>> I have some legacy code, the author of whom is no longer available for
>>> consultation.
>>> Here is a piece of the code in question:
>>> :
>>> up here, the DBI connection to the database, the select statement,
>>> etc. occurs

>> Can you either post the fully relevant portions of your code, or
>> elaborate on some aspects? Some parts of the code you've posted don't
>> seem to relate to the other, so either relevant code is missing, or...?

>
> Thank you for your question. The bit of code was from 1600+ lines of
> code in a web services program, as well as parts from a 50+ line
> program which invokes one method from the web service, interacts with
> other programs, to generate some internal information. I don't have
> rights to distribute the whole program, so I was trying to (and
> obviously failed at) picking just a small portion to get at a better
> understanding at how one would generically access the elements of a
> hash given a reference. I need to do more reading, and have some
> references elsewhere in this thread I will use to try and learn more.
>
> Thank you all so much for your help.


Just a suggestion:

Use Data::Dumper to find out what you have in various variables in the
actual program being worked on.

The output of Data::Dumper can be used to initialise a variable. You can
therefore include that output as code in new tiny throwaway Perl
programs to test out various ways to further manipulate or process those
data structures.

I find debugging tiny programs is easier than debugging big/complex
poorly understood ones. You also end up with small programs you can post
here for further help.

--
RGB

Peter J. Holzer 01-10-2009 09:17 AM

Re: I am needing a gentle introduction to accessing a perl arrayfrom a reference
 
On 2009-01-09 12:35, Larry W. Virden <lvirden@gmail.com> wrote:
> On Jan 8, 4:56*pm, Tad J McClellan <ta...@seesig.invalid> wrote:
>> Larry W. Virden <lvir...@gmail.com> wrote:
>> > # put results of query into single array and return
>> > * while (@next_row= $query->fetchrow_array()){

>>
>> > * * push(@array_result,@next_row);
>> > * }

>>
>> > * $query->finish;
>> > * $dbconn->disconnect();
>> > * return \@array_result;

>>
>> That is an awfully strange way to do it, unless your query is selecting
>> only a single column.

>
> I didn't write the code, and haven't used arrays, references, and DBI
> enough to know about it. In the particularly select I was looking at,
> I think the select might very well be one that only returns a single
> row. I'm still trying to learn about the database and its
> characteristics.
>
>>
>> Is this your real code?
>>
>> The data structure here is nothing more than (a reference to) a flat array.

>
> That's the real code from the program I'm trying to understand. So,
> when the fetchrow_array returns @next_row, this is a "flat array".
> It's still a hash, right?


No, fetchrow_array returns an array, not a hash. If you want a hash(ref),
use fetchrow_hashref. Since the rest of the code looks like it expects
an array of hashrefs I have to ask whether you are sure that this is
really the code which is called. Or is there a very similar method which
differs only in calling fetchrow_hashref instead of fetchrow_array, and
you are calling that instead?

hp


All times are GMT. The time now is 11:22 PM.

Powered by vBulletin®. Copyright ©2000 - 2014, vBulletin Solutions, Inc.
SEO by vBSEO ©2010, Crawlability, Inc.