Class: Address::NormalizedAddress

Inherits:
Object
  • Object
show all
Defined in:
app/services/address/normalized_address.rb

Overview

Constant Summary collapse

ACCEPTABLE_TYPES =

Geocoder::Configuration.lookup = :google

Set.new(%w[street_address premise subpremise establishment])
Attrs =

Attribute keys exposed by a normalized address (also used as the
attr_reader list and the JSON shape returned to callers).

%i[address_line1 address_line2 city state_province state_code
postal_code country country_iso latitude longitude formatted_address].freeze

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(data) ⇒ NormalizedAddress

Returns a new instance of NormalizedAddress.



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'app/services/address/normalized_address.rb', line 16

def initialize(data)
  @address_line1  = nil
  @address_line2  = nil
  @city           = nil
  @postal_code    = nil
  @state_province = nil
  @country        = nil
  @country_iso    = nil
  @latitude       = nil
  @longitude      = nil
  @state_code     = nil
  # The Google result consists of:
  #
  # - An array ("address_components") of hashes consisting of
  #   {"long_name" => "...", "short_name" => "...", "types" => [...]}
  # - A "geometry" hash, with the latitude and longitude
  # - A "partial_match" indicator (which we're ignoring)
  # - A "types" array (which we're also ignoring)

  data["address_components"].each do |hash|
    types = hash["types"]
    value = hash["long_name"]
    next if value.blank?

    short_value = hash["short_name"]
    if types.include? "subpremise"
      @address_line2 = value
      @address_line2.insert(0, '#') unless @address_line2.starts_with?('#')
    elsif types.include? "street_number"
      @house_number = value
    elsif types.include? "sublocality"
      @city = value
    elsif types.include? "locality"
      @city = value if @city.nil?
    elsif types.include? "country"
      @country = value
      @country_iso = short_value
    elsif types.include? "postal_code"
      @postal_code = value
    elsif types.include? "route"
      @street = value
    elsif types.include? "administrative_area_level_1"
      @state_province = value
      @state_code = short_value
    elsif types.include? "establishment"
      @establishment = value
    end
  end

  @address_line1 = "#{@house_number} #{@street}".squish.presence || @establishment

  @formatted_address = data["formatted_address"] || [
    @address_line1, @address_line2, @city, @state_province, @postal_code
  ].select(&:present?).join(' ')

  # Latitude and longitude

  return unless data["geometry"] && data["geometry"]["location"]

  loc = data["geometry"]["location"]
  @latitude = BigDecimal(loc["lat"].to_s)
  @longitude = BigDecimal(loc["lng"].to_s)
end

Class Method Details

.from_string(raw_address_string) ⇒ Object



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'app/services/address/normalized_address.rb', line 88

def self.from_string(raw_address_string)
  normalized_address = nil

  # Geocoder.search() returns an array of results. Take the first one.
  geocoded = Geocoder.search(raw_address_string)
  if geocoded.present?
    # Geocoder returns data that may or may not be granular enough. For
    # instance, attempting to retrieve information about nonexistent
    # address '100 Main St, XYZ, PA, US' still returns a value, but the
    # value's type is "administrative_area_level_1", which means the data
    # is granular to a (U.S.) state. If it's a valid address, we should
    # get data that's more granular than that. Of the codes listed at
    # http://code.google.com/apis/maps/documentation/geocoding/#Types
    # we're interested in "street_address", "premise" and
    # "subpremise".
    data = geocoded[0].data
    types = Set.new(data["types"])
    normalized_address = new(data) if types.intersect?(ACCEPTABLE_TYPES)
  end

  normalized_address
end

Instance Method Details

#inspectObject



84
85
86
# File 'app/services/address/normalized_address.rb', line 84

def inspect
  Attrs.index_with { |field| send(field) }
end

#to_sObject



80
81
82
# File 'app/services/address/normalized_address.rb', line 80

def to_s
  @formatted_address
end