summaryrefslogtreecommitdiffstats
path: root/lib/cepis/models.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/cepis/models.rb')
-rw-r--r--lib/cepis/models.rb85
1 files changed, 85 insertions, 0 deletions
diff --git a/lib/cepis/models.rb b/lib/cepis/models.rb
index a0181ff..a80fe37 100644
--- a/lib/cepis/models.rb
+++ b/lib/cepis/models.rb
@@ -145,6 +145,91 @@ module Cepis
end
end
+ # The municipality that owns the band a **nonexistent** CEP falls into.
+ #
+ # It is context for an error, never an address: it arrives on the 404 of
+ # {Client#lookup}, hanging off {CEPNotFound#faixa}, and says "this CEP does
+ # not exist, but the numbers around it belong to Areal/RJ". The lookup still
+ # failed — nothing here turns it into a result.
+ #
+ # It is worth having because a CEP that fails is rarely a random number: it
+ # is usually someone who knows the city and guessed the digits. The classic
+ # guess is the `-000`, which 957 municipalities never received.
+ #
+ # **{#cep_ini} and {#cep_fim} are the smallest and the largest CEP that
+ # actually exist in the municipality, not the bounds the Correios publish.**
+ # The DNE's own start for Areal is `25845000` — exactly the CEP that just
+ # failed, which is why the published bounds would be useless here.
+ #
+ # **The band is sparse.** Areal's 118 CEPs are scattered across the ~4,900
+ # numbers between `25845001` and `25849899`. {#ceps} is there to say so. Do
+ # not present the pair as a range of valid CEPs, and do not iterate it: all
+ # but a fiftieth of it does not exist.
+ class Faixa
+ attr_reader :localidade, :uf, :estado, :regiao, :ibge, :cep_ini, :cep_fim, :ceps
+
+ def initialize(localidade:, uf:, estado:, regiao:, ibge:, cep_ini:, cep_fim:, ceps:)
+ @localidade = localidade
+ @uf = uf
+ @estado = estado
+ @regiao = regiao
+ @ibge = ibge
+ @cep_ini = cep_ini
+ @cep_fim = cep_fim
+ @ceps = ceps
+ freeze
+ end
+
+ # Build from the hash the API returned, or `nil` when the block is
+ # missing, empty or malformed.
+ #
+ # The API **omits** `faixa` — it never sends null — for a CEP falling in a
+ # band no municipality owns, so absence is the ordinary case and not an
+ # anomaly. Anything unreadable is treated as absence too: this is a bonus
+ # on top of a failure that has already happened, and it must never be the
+ # reason a clean not-found becomes something else.
+ def self.from_json(data)
+ return nil unless data.is_a?(Hash)
+
+ block = data.transform_keys(&:to_s)
+ localidade = String(block["localidade"] || "").strip
+ return nil if localidade.empty?
+
+ new(
+ localidade: localidade,
+ uf: String(block["uf"] || ""),
+ estado: String(block["estado"] || ""),
+ regiao: String(block["regiao"] || ""),
+ ibge: String(block["ibge"] || ""),
+ cep_ini: String(block["cep_ini"] || ""),
+ cep_fim: String(block["cep_fim"] || ""),
+ ceps: Integer(block["ceps"] || 0, exception: false) || 0
+ )
+ rescue TypeError, ArgumentError
+ nil
+ end
+
+ def to_h
+ {
+ localidade: @localidade, uf: @uf, estado: @estado, regiao: @regiao,
+ ibge: @ibge, cep_ini: @cep_ini, cep_fim: @cep_fim, ceps: @ceps
+ }
+ end
+
+ def ==(other)
+ other.is_a?(Faixa) && to_h == other.to_h
+ end
+ alias eql? ==
+
+ def hash
+ to_h.hash
+ end
+
+ def to_s
+ "#<Cepis::Faixa #{@localidade}/#{@uf} #{@cep_ini}-#{@cep_fim} #{@ceps} CEPs>"
+ end
+ end
+
# The response of `GET /health`.
#
# Good for two things: checking that the API answers, and knowing **which