line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package TextLinkAds; |
2
|
|
|
|
|
|
|
|
3
|
2
|
|
|
2
|
|
34697
|
use strict; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
92
|
|
4
|
2
|
|
|
2
|
|
14
|
use warnings; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
84
|
|
5
|
|
|
|
|
|
|
|
6
|
2
|
|
|
2
|
|
24
|
use Carp qw( carp croak ); |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
1548
|
|
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
our $VERSION = '0.01'; |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
=head1 NAME |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
TextLinkAds - Retrieve Text Link Ads advertiser data |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
=head1 SYNOPSIS |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
use TextLinkAds; |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
my $tla = TextLinkAds->new; |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
# Fetch link information from text-link-ads.com... |
23
|
|
|
|
|
|
|
my @links = @{ $tla->fetch( $inventory_key ) }; |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
# Output the data in some meaningful way... |
26
|
|
|
|
|
|
|
print " |
27
|
|
|
|
|
|
|
foreach my $link ( @links ) { |
28
|
|
|
|
|
|
|
my $before = $link->{BeforeText} || ''; |
29
|
|
|
|
|
|
|
my $after = $link->{AfterText} || ''; |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
print <<"END_OF_HTML"; |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
$before $link->{Text} $after |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
END_OF_HTML |
36
|
|
|
|
|
|
|
} |
37
|
|
|
|
|
|
|
print ''; |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
=head1 DESCRIPTION |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
This module fetches advertiser information for a given Text Link Ads publisher |
43
|
|
|
|
|
|
|
account. |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
See L. |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
=head1 METHODS |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
=head2 ->new( \%options ) |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
Instantiate a new TextLinkAds object. |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
=head3 %options |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
=over |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
=item cache |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
Optional. By default this module will try to use L to store |
61
|
|
|
|
|
|
|
data retrieved from the text-link-ads.com site for one hour. You may use the |
62
|
|
|
|
|
|
|
C parameter to provide an alternative object that implements the |
63
|
|
|
|
|
|
|
L interface. To disable caching set C to a scalar value |
64
|
|
|
|
|
|
|
that resolves to C. |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
=item tmpdir |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
Optional. A temporary directory to use when caching data. The default |
69
|
|
|
|
|
|
|
behaviour is to use the directory determined by |
70
|
|
|
|
|
|
|
Ltmpdir|File::Spec/tmpdir>. |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
=back |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
=cut |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
sub new { |
78
|
1
|
|
|
1
|
1
|
613
|
my ( $class, $args ) = @_; |
79
|
|
|
|
|
|
|
|
80
|
1
|
|
|
|
|
4
|
my $self = bless {}, $class; |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
# Where the tmpdir isn't defined or valid, use File::Spec to determine an |
83
|
|
|
|
|
|
|
# appropriate directory... |
84
|
1
|
|
|
|
|
4
|
my $tmpdir = $args->{tmpdir}; |
85
|
1
|
0
|
33
|
|
|
6
|
unless ( defined $tmpdir && -d $tmpdir && -w $tmpdir ) { |
|
|
|
33
|
|
|
|
|
86
|
1
|
|
|
|
|
8
|
require File::Spec; |
87
|
1
|
|
|
|
|
121
|
$tmpdir = File::Spec->tmpdir; |
88
|
|
|
|
|
|
|
} |
89
|
1
|
|
|
|
|
8
|
$self->{tmpdir} = $tmpdir; |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
# Where cache is not defined, or is a scalar with a true value, fall back |
93
|
|
|
|
|
|
|
# to using Cache::FileCache (providing it is installed)... |
94
|
1
|
|
|
|
|
3
|
my $cache = $args->{cache}; |
95
|
1
|
50
|
0
|
|
|
5
|
if ( !defined $cache || ( !ref $cache && $cache ) ) { |
|
|
|
33
|
|
|
|
|
96
|
1
|
|
|
|
|
3
|
eval { require Cache::FileCache; }; |
|
1
|
|
|
|
|
1077
|
|
97
|
1
|
50
|
|
|
|
95419
|
unless ( $@ ) { |
98
|
1
|
|
|
|
|
14
|
$cache = Cache::FileCache->new({ |
99
|
|
|
|
|
|
|
cache_root => $tmpdir, |
100
|
|
|
|
|
|
|
default_expires_in => '1 hour', |
101
|
|
|
|
|
|
|
}); |
102
|
|
|
|
|
|
|
|
103
|
1
|
|
|
|
|
350
|
$self->{cache} = $cache; |
104
|
|
|
|
|
|
|
} |
105
|
|
|
|
|
|
|
} |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
|
108
|
1
|
|
|
|
|
8
|
return $self; |
109
|
|
|
|
|
|
|
} |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
=head2 ->fetch( $inventory_key, \%options ) |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
Fetch advertiser information for the given key. It will first attempt to get |
115
|
|
|
|
|
|
|
the data from the cache where available, and failing that will send a request |
116
|
|
|
|
|
|
|
to text-link-ads.com, using the *_proxy environment variables and the |
117
|
|
|
|
|
|
|
If-Modified_Since request header. |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
=head3 $inventory_key |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
Required. The XML Key for the desired site as provided by Text Link Ads. |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
=head3 %options |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
=over |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
=item user_agent |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
Optional. In the vanilla code examples provided by Text Link Ads, both the |
131
|
|
|
|
|
|
|
user agent and referer CGI environment variables are included in the URI used |
132
|
|
|
|
|
|
|
to retrieve the XML data. While the link appears to function without them, it |
133
|
|
|
|
|
|
|
would probably be polite to include them where possible. |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
=item referer |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
See above. |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
=back |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
=cut |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
sub fetch { |
146
|
0
|
|
|
0
|
1
|
|
my ( $self, $inventory_key, $args ) = @_; |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
# First, attempt to retrieve the data from the cache where available... |
149
|
0
|
|
|
|
|
|
my $links; |
150
|
0
|
0
|
|
|
|
|
if ( defined $self->{cache} ) { |
151
|
0
|
|
|
|
|
|
$links = $self->{cache}->get( "tla_$inventory_key" ); |
152
|
|
|
|
|
|
|
|
153
|
0
|
0
|
|
|
|
|
return $links if defined $links; |
154
|
|
|
|
|
|
|
} |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
# Otherwise, we'll need to retrieve the data from text-link-ads.com, so |
157
|
|
|
|
|
|
|
# create a new user agent object... |
158
|
0
|
|
|
|
|
|
require LWP::UserAgent; |
159
|
0
|
|
|
|
|
|
my $ua = LWP::UserAgent->new( |
160
|
|
|
|
|
|
|
agent => "TextLinkAds.pm/$VERSION " . LWP::UserAgent->_agent, |
161
|
|
|
|
|
|
|
); |
162
|
0
|
|
|
|
|
|
$ua->env_proxy; # obey the *_proxy environment variables |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
# Determine the URI to use when requesting the advertiser data... |
166
|
0
|
|
|
|
|
|
my $referer = $self->{referer}; |
167
|
0
|
|
|
|
|
|
my $user_agent = $self->{user_agent}; |
168
|
|
|
|
|
|
|
|
169
|
0
|
0
|
|
|
|
|
my $uri = 'http://www.text-link-ads.com/xml.php' |
|
|
0
|
|
|
|
|
|
170
|
|
|
|
|
|
|
. "?inventory_key=$inventory_key" |
171
|
|
|
|
|
|
|
. ( defined $referer ? "&referer=$referer" : '' ) |
172
|
|
|
|
|
|
|
. ( defined $user_agent ? "&user_agent=$user_agent" : '' ); |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
# Request the advertiser data, using "If-Modified-Since" where possible... |
176
|
0
|
|
|
|
|
|
my $temp_file = $self->{tmpdir} . "/tla_$inventory_key"; |
177
|
0
|
|
|
|
|
|
my $response = $ua->mirror( $uri, $temp_file ); |
178
|
|
|
|
|
|
|
|
179
|
0
|
0
|
|
|
|
|
if ( !$response->is_success ) { |
180
|
0
|
|
|
|
|
|
croak $response->status_line; |
181
|
|
|
|
|
|
|
} |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
# The resulting file was empty. This may mean there were no advertisers, |
184
|
|
|
|
|
|
|
# though it's also possible that an incorrect $inventory_key was given... |
185
|
0
|
0
|
|
|
|
|
if ( -z $temp_file ) { |
186
|
0
|
|
|
|
|
|
carp "No advertisers found for '$inventory_key'"; |
187
|
0
|
|
|
|
|
|
return []; |
188
|
|
|
|
|
|
|
} |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
# Parse the XML... |
191
|
0
|
|
|
|
|
|
require XML::Simple; |
192
|
0
|
|
|
|
|
|
$links = XML::Simple::XMLin($temp_file)->{Link}; |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
# Remove empty BeforeText/AfterText attributes... |
196
|
0
|
|
|
|
|
|
foreach my $link ( @$links ) { |
197
|
0
|
0
|
|
|
|
|
delete $link->{BeforeText} if ref $link->{BeforeText}; |
198
|
0
|
0
|
|
|
|
|
delete $link->{AfterText} if ref $link->{AfterText}; |
199
|
|
|
|
|
|
|
} |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
# Store the new data if caching is enabled... |
203
|
0
|
0
|
|
|
|
|
$self->{cache}->set( "tla_$inventory_key", $links ) |
204
|
|
|
|
|
|
|
if defined $self->{cache}; |
205
|
|
|
|
|
|
|
|
206
|
0
|
|
|
|
|
|
return $links; |
207
|
|
|
|
|
|
|
} |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
1; # End of the module code; everything from here is documentation... |
212
|
|
|
|
|
|
|
__END__ |