NAME
VPNDetection::Result - what a lookup answers
SYNOPSIS
my $result = $client->lookup('45.83.91.1');
$result->ip; # '45.83.91.1'
$result->is_vpn; # 1
$result->vpn->{provider}; # 'mullvad'
$result->is_hosting; # 1, 0, or undef when your plan omits it
$result->is_hosting // 0; # read an absent field as false
$result->has('is_hosting'); # 1 if your plan carries it at all
ABSENT IS NOT FALSE
An absent field is one your plan does not include. It never means "we checked and found nothing", so undef and 0 are genuinely different answers.
This is the one place Perl makes it easy to get wrong, because undef, 0, '' and a missing hash key are all false:
if ($result->is_hosting) { ... } # WRONG: absent and false look alike
if (($result->is_hosting // 0)) { ... } # right: absent reads as false
if ($result->has('is_hosting')) { ... } # right: asks whether it was served
// (defined-or) is the reader for "treat absent as false", and has is the reader for "was this served at all". A result is also a plain hash reference, so exists $result->{is_hosting} is has spelled out and defined $result->{is_hosting} is the same test one level down.
A detail object that is present but empty ({}) means the flag above it is false. A populated one always carries every one of its keys.
METHODS
ip, is_vpn, is_bogon
Always present. is_bogon is set when the answer was computed locally rather than served.
is_hosting, is_relay, is_tor, is_cdn, is_resproxy, is_dcproxy, is_mobproxy
The tier-gated flags: 1, 0, or undef when your plan omits the field.
vpn, hosting, relay, tor, cdn, resproxy, dcproxy, mobproxy
The detail objects, as plain hash references, or undef when your plan omits them.
has($field)
Whether your plan carries $field. Croaks on a name that is not a field.
fields
The field names this answer carried, in wire order.
raw
The decoded response body, untouched. Treat it, and the result itself, as read only: the cache hands the same object to every later caller of an address.