I'm struggling with a bug, that I can't nail down.
I have a function that takes a postcode, does a lookup, and returns a latitude, longitude and area name.
for example, pass it AD300 it returns (something like) 42.6, 1.55, ordino - it works very well.
The function is called like this:
my ($lat, $lng, $area) = $object->release();
The return values are fine, and I can print them in perl with a warn
warn "Area $area, $rellat, $rellng";
This works fine. "Area Ordino, 42.6, 1.55"
I then take one of these values, say $area, add it to a hash of data, and pass it to a web page where it is preprocessed via TT (as I do successfully with a load of other variables).
I'm assigning the value to the hash in the normal way. e.g. $hash->{'area'} = $area;
Here is where the fun begins. When I try to reference the value in TT e.g. [% hash.area %] I don't get "Ordino" printed on the web page, I'm told I've passed an Array reference to TT.
After a little debugging, I've found that my hash variable hash.area, is somehow referencing an array (according to TT) holding the three values that I've returned from the subroutine "release". I.e. hash.area = [42.6, 1.55, ordino] according to TT.
That is, to get the value "Ordino" within the web page, I have to access [% hash.area.2 %].
Further, I can set $hash->{'area'} to equal any of the variables, $lat, $lng, or $area and get the same behavior. TT believes all three variables reference the same array. that is
$lat = $lng = $area = [42.6, 1.55, ordino] according to TT
This is bizare, I can happily print the variables in perl and they appears as normal - not an array. I've tried dumping the hash with dumper, no array, everything is fine. Yet somehow, TT is finding an array. It's doing my head in.
The site is quite large, with a lot of pages and I happily pass variables and hashes via TT to web pages all the time, and have been for 4 years now. I've never seen this. On other pages, I even pass exactly the same output from the "release" method and it is processed correctly.
I don't think my TT processing code is the problem, however the following is relevant.
my $tt = Template->new({
INCLUDE_PATH => [ @$template_directories ],
COMPILE_EXT => '.ttc',
COMPILE_DIR => '/tmp/ttc',
FILTERS => YMGN::View->filters,
PLUGIN_BASE => [ 'YMGN::V::TT::Plugins' ],
EVAL_PERL => 1
});
$self->{tt} = $tt;
$self->{template_directories} = $template_directories;
$self->{output} = $params->{output} || undef;
$self->{data} = $params->{data} || [];
The above creates a new tt object and is part of the "new" function (refed below). "data" contains the hash. "output" holds the processed template ready to send to users browser. We call new (above), process the data and create the output with the code below.
sub process {
my $self = shift;
my $params = shift;
if (!ref $self || !exists $self->{tt}) {
my $class = $self;
$self = $class->new($params);
}
if (!$self->{output}) {
die "You need to specify output";
}
delete $self->{error};
$self->y->utils->untaint(\$self->{template});
my $rv = $self->{tt}->process(
$self->{template},
$self->{data},
$self->{output},
binmode => ':utf8',
);
if (!$rv) {
warn $self->{tt}->error();
return {
error => $self->{tt}->error(),
};
}
return 0;
}
All of the above is sanitised because there is a lot of other stuff going on. I believe what's important is that the data going in looks correct, here is a full dump of the complete data that is being processed by tt (at the point of processing). The thing that is causing the problem is bubbles->[*]->{'release'} (note, that release == area in the data. The name was changed for unrelated reasons). As you can see, dumper thinks it's a string. TT deals with everything else fine.
data $VAR1 = {
'system' => {
system stuff
},
'features' => {
site feature config
},
'message_count' => '0',
'bubbles' => [
bless( {
'history' => [
{
'creator' => '73',
'points' => '10',
'screenname' => 'sarah10',
'classname' => 'Flootit::M::Bubbles',
'id' => '1378',
'updated' => '1352050471',
'type' => 'teleport',
'label' => 'teleport',
'class' => 'Flootit::M::Bubbles'
}
],
'creator' => '6',
'release' => 'Escaldes-Engordany',
'image' => 'http://six.flooting.com/files/833/7888.png',
'pop_time' => '1352050644',
'y' => $VAR1->{'y'},
'taken_by' => '0',
'city' => '3',
'title' => 'hey a new bubble',
'id' => '566',
'class' => 'Flootit::M::Bubbles',
'prize' => 'go for it kids'
}, 'Flootit::M::Bubbles' ),
bless( {
'history' => [
{
'creator' => '6',
'points' => '10',
'screenname' => 'sarah20',
'classname' => 'Flootit::M::Bubbles',
'id' => '1723',
'updated' => '1349548017',
'type' => 'teleport',
'label' => 'teleport',
'class' => 'Flootit::M::Bubbles'
},
{
'creator' => '6',
'points' => '5',
'screenname' => 'sarah20',
'classname' => 'Flootit::M::Bubbles',
'id' => '1732',
'updated' => '1349547952',
'type' => 'blow',
'label' => 'blow',
'class' => 'Flootit::M::Bubbles'
}
],
'creator' => '89',
'release' => 'Ordino',
'image' => 'http://six.flooting.com/files/1651/8035.png',
'pop_time' => '1351203843',
'y' => $VAR1->{'y'},
'taken_by' => '0',
'city' => '3',
'title' => 'test4',
'id' => '1780',
'class' => 'Flootit::M::Bubbles',
'prize' => 'asdfasdf dsadsasdfasdfasdf'
}, 'Flootit::M::Bubbles' ),
bless( {
'history' => [],
'creator' => '6',
'release' => 'Andorra la Vella',
'image' => 'http://six.flooting.com/files/1671/8042.png',
'pop_time' => '0',
'y' => $VAR1->{'y'},
'taken_by' => '0',
'city' => '3',
'title' => 'Pretty flowers, tres joli',
'id' => '1797',
'class' => 'Flootit::M::Bubbles',
'prize' => 'With lots of pretty pictures'
}, 'Flootit::M::Bubbles' ),
bless( {
'history' => [],
'creator' => '6',
'release' => 'Hillrise Ward',
'image' => 'http://six.flooting.com/files/1509/8003.png',
'pop_time' => '0',
'y' => $VAR1->{'y'},
'taken_by' => '0',
'city' => '3',
'title' => 'Test beats',
'id' => '1546',
'class' => 'Flootit::M::Bubbles',
'prize' => 'Sound great'
}, 'Flootit::M::Bubbles' )
]
};
What comes out after processing is this (in $output) There is a [% FOREACH floot IN bubbles %]
Floating around ARRAY(0xfaf5d448).
from [% floot.release %]if we make this [% floot.release.2 %] it gives the correct value.
All the other fields can be referenced correctly - go figure.
The code that puts "bubbles" together is;
my $bubbles = $y->model('Bubbles')->search(['type' => 'golden', 'image' => '!NULL',
'bubble_prizes' => ['p', { 'p.bubble' => 'self.id'}], ], {
order_by => '(created>CURRENT_DATE() AND thumbsup+thumbsdown<10) DESC, COALESCE(thumbsup,0)-COALESCE(thumbsdown,0) DESC, pop_time DESC',
count => 10,
fields => ['p.title as title', 'p.prize as prize', 'city', 'taken_by', 'pop_time', 'id', 'creator'],
});
for (my $i=0; $i<@$bubbles; $i++) {
# Find specified bubbles (see below for when not found here)
my ($rellat, $rellng, $area) = $bubbles->[$i]->release() ;
$bubbles->[$i]->{'release'} = $area;
}
}
The controller then takes $bubble, bundles it up with session / site data, puts it inside an anonymous hash (as you can see in the data above) and passes it to view for processing.
The code for release is :
sub release {
my $self = shift;
my $postcode = $self->y->model('Prizes')->find({bubble => $self->id})->postcode;
my ( $user_lat, $user_long, $region_name );
if($postcode)
{
( $user_lat, $user_long, $region_name ) = $self->y->api('Location')->from_postcode($postcode);
return ( $user_lat, $user_long, $region_name );
}
}
API::Location is quite large, however the relevant lines are;
$postcode_record = $self->y->model('GeoData')->find( {
source => "ALL_COUNTRIES_POSTCODES",
country => $country_code,
sourceid => $postcode, } );
return ( $postcode_record->latitude, $postcode_record->longitude, $postcode_record->town );
The data dumps I've shown you are taken from inside TT.pm (part of view).
So, any ideas what might be going on or where to start? What can I do to try and debug this further? I'm out of ideas.
I found that the problem went away on other development servers and the production server.
I therefore tried uninstalling and reinstalling TT, however that didn't help.
As it appears it's an environment issue on my dev server, so I am retiring the box and starting a new one.