diff options
| author | Murilo Ijanc <[email protected]> | 2026-07-26 14:40:05 -0300 |
|---|---|---|
| committer | Murilo Ijanc <[email protected]> | 2026-07-26 14:40:05 -0300 |
| commit | 0bc578d6d334c7f1ccaf329cd61bb8bce76b7f59 (patch) | |
| tree | 86f73108f0432fd8330edcaab8b8ae7018322dee /lib/Cepis/Address.pm | |
| download | perl-6661cb35162c38099b86f248b938f5e4d4d11515.tar.gz | |
Initial importv0.1.0
Diffstat (limited to 'lib/Cepis/Address.pm')
| -rw-r--r-- | lib/Cepis/Address.pm | 213 |
1 files changed, 213 insertions, 0 deletions
diff --git a/lib/Cepis/Address.pm b/lib/Cepis/Address.pm new file mode 100644 index 0000000..a938d6f --- /dev/null +++ b/lib/Cepis/Address.pm @@ -0,0 +1,213 @@ +package Cepis::Address; + +use strict; +use warnings; + +use Cepis::Geo; + +our $VERSION = '0.1.0'; + +our @FIELDS = qw( + cep logradouro complemento unidade bairro localidade + uf estado regiao ibge gia ddd siafi +); + +{ + no strict 'refs'; + for my $name (@FIELDS) { + *{$name} = sub { return $_[0]->{$name} }; + } +} + +sub from_json { + my ( $class, $data ) = @_; + + my %raw = %{ $data || {} }; + my %self; + for my $name (@FIELDS) { + $self{$name} = _string( $raw{$name} ); + } + $self{geo} = Cepis::Geo->from_json( $raw{geo} ); + $self{raw} = \%raw; + + return bless \%self, $class; +} + +sub geo { return $_[0]->{geo} } +sub raw { return $_[0]->{raw} } + +sub field { + my ( $self, $name ) = @_; + return $self->{raw}{$name}; +} + +sub formatted_cep { + my ($self) = @_; + my $cep = $self->{cep}; + return $cep unless length($cep) == 8; + return substr( $cep, 0, 5 ) . '-' . substr( $cep, 5, 3 ); +} + +sub to_hash { + my ($self) = @_; + my %out = map { $_ => $self->{$_} } @FIELDS; + $out{geo} = $self->{geo} ? $self->{geo}->to_hash : undef; + return \%out; +} + +sub _string { + my ($value) = @_; + return '' unless defined $value; + return '' if ref $value; + return "$value"; +} + +1; + +__END__ + +=encoding UTF-8 + +=head1 NAME + +Cepis::Address - a CEP and everything the database knows about it + +=head1 SYNOPSIS + + my $a = Cepis::lookup('01310-100'); + + $a->logradouro; # "Avenida Paulista" + $a->localidade; # "São Paulo" + $a->uf; # "SP" + $a->ibge; # "3550308" + $a->cep; # "01310100" (always unmasked) + $a->formatted_cep; # "01310-100" + +=head1 DESCRIPTION + +B<Field names are the API's own, in Portuguese> — C<logradouro>, C<bairro>, +C<localidade>. Everything else in this library is English; the fields are the +one deliberate exception. + +The reason is fidelity: what you read in the JSON at +L<https://cep.is/01001000> is exactly what you read on the object. Translating +to C<street>/C<city> would mean maintaining a field-by-field mapping and forcing +anyone reading the API docs alongside this library to translate back in their +head. The values are Portuguese either way — they come from the Correios +database. + +Fields the database does not have for that CEP come back as empty strings, never +C<undef>. That is what the API returns, and changing it here would create a +difference between reading the JSON and reading this library. + +L</geo> is the exception: it is C<undef> when there is no coordinate. There the +difference is real, because the API B<omits> the whole field in that case (about +43% of CEPs) rather than sending null. + +=head1 CONSTRUCTOR + +=head2 from_json + + my $address = Cepis::Address->from_json( $hashref ); + +Builds from the decoded JSON object the API returned. + +Unknown fields are ignored during construction but preserved in L</raw>. A new +field in the API therefore cannot break this library. + +A malformed C<geo> block yields C<< geo => undef >> rather than propagating the +error. + +=head1 ACCESSORS + +=head2 cep + +The 8 digits, unmasked. + +=head2 logradouro + +Street name. + +=head2 complemento + +Complement, such as C<"lado ímpar">. + +=head2 unidade + +Unit, when the CEP belongs to a specific building or post office box range. + +=head2 bairro + +Neighbourhood. + +=head2 localidade + +Municipality name. + +=head2 uf + +Two-letter state code. + +=head2 estado + +Full state name. + +=head2 regiao + +Region of the country, such as C<"Sudeste">. + +=head2 ibge + +IBGE municipality code. + +=head2 gia + +GIA code (São Paulo state tax registry). + +=head2 ddd + +Area code for telephone dialling. + +=head2 siafi + +SIAFI municipality code. + +=head2 geo + +A L<Cepis::Geo> object, or C<undef> when the database has no coordinate for +this CEP. Addresses coming out of C<lookup_many> always have C<undef> here — see +L<Cepis::Client/lookup_many>. + +=head2 raw + +The decoded response exactly as it arrived, as a hashref, so a field the API +grows before this library is updated is not lost. + +=head2 field + + $address->field('campo_do_futuro'); + +Reads any field of the raw response by name, including one this release does not +know about. Returns C<undef> when the field is absent. + +=head2 formatted_cep + +The CEP with its mask: C<01001-000>. + +The API always returns it unmasked; formatting is presentation, and it lives +here so it does not become a repeated C<substr> in every project that uses this. + +=head2 to_hash + +Every named field plus C<geo> as a plain hashref, with C<geo> itself flattened +to a hashref or C<undef>. + +=head1 SEE ALSO + +L<Cepis>, L<Cepis::Geo> + +=head1 LICENCE + +ISC. Copyright (c) 2026 Murilo Ijanc E<lt>[email protected]<gt>. + +=cut |