Class: UHaul::Facility

Inherits:
Object
  • Object
show all
Defined in:
lib/uhaul/facility.rb

Overview

A facility (address + geocode + prices) on uhaul.com.

e.g. www.uhaul.com/Locations/Self-Storage-near-Inglewood-CA-90301/712030/

Defined Under Namespace

Classes: ParseError

Constant Summary collapse

PRICES_SELECTOR =
'#roomTypes > ul:not([id*="VehicleStorage"]) > li'
SITEMAP_URLS =
%w[
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-AL.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-AK.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-AZ.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-AR.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-CA.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-CO.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-CT.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-DC.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-DE.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-FL.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-GA.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-HI.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-ID.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-IL.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-IN.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-IA.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-KS.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-KY.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-LA.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-ME.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-MD.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-MA.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-MI.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-MN.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-MS.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-MO.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-MT.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-NE.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-NV.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-NH.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-NJ.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-NM.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-NY.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-NC.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-ND.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-OH.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-OK.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-OR.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-PA.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-RI.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-SC.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-SD.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-TN.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-TX.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-UT.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-VT.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-VA.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-WA.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-WV.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-WI.ashx
  https://www.uhaul.com/Locations/Sitemaps/Sitemap-for-Storage-in-WY.ashx
].freeze
DEFAULT_EMAIL =
'service@uhaul.com'
DEFAULT_PHONE =
'+1-800-468-4285'

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(id:, url:, name:, address:, geocode:, phone: DEFAULT_PHONE, email: DEFAULT_EMAIL, prices: []) ⇒ Facility

Returns a new instance of Facility.

Parameters:

  • id (String)
  • url (String)
  • name (String)
  • address (Address)
  • geocode (Geocode)
  • phone (String) (defaults to: DEFAULT_PHONE)
  • email (String) (defaults to: DEFAULT_EMAIL)
  • prices (Array<Price>) (defaults to: [])


167
168
169
170
171
172
173
174
175
176
# File 'lib/uhaul/facility.rb', line 167

def initialize(id:, url:, name:, address:, geocode:, phone: DEFAULT_PHONE, email: DEFAULT_EMAIL, prices: [])
  @id = id
  @url = url
  @name = name
  @address = address
  @geocode = geocode
  @phone = phone
  @email = email
  @prices = prices
end

Instance Attribute Details

#addressAddress

Returns:



91
92
93
# File 'lib/uhaul/facility.rb', line 91

def address
  @address
end

#emailString

Returns:

  • (String)


87
88
89
# File 'lib/uhaul/facility.rb', line 87

def email
  @email
end

#geocodeGeocode?

Returns:



95
96
97
# File 'lib/uhaul/facility.rb', line 95

def geocode
  @geocode
end

#idString

Returns:

  • (String)


71
72
73
# File 'lib/uhaul/facility.rb', line 71

def id
  @id
end

#nameString

Returns:

  • (String)


79
80
81
# File 'lib/uhaul/facility.rb', line 79

def name
  @name
end

#phoneString

Returns:

  • (String)


83
84
85
# File 'lib/uhaul/facility.rb', line 83

def phone
  @phone
end

#pricesArray<Price>

Returns:



99
100
101
# File 'lib/uhaul/facility.rb', line 99

def prices
  @prices
end

#urlString

Returns:

  • (String)


75
76
77
# File 'lib/uhaul/facility.rb', line 75

def url
  @url
end

Class Method Details

.fetch(url:) ⇒ Facility

Parameters:

  • url (String)

Returns:



117
118
119
120
# File 'lib/uhaul/facility.rb', line 117

def self.fetch(url:)
  document = Crawler.html(url:)
  parse(url:, document:)
end

.parse(url:, document:) ⇒ Facility

Parameters:

  • url (String)
  • document (Nokogiri::HTML::Document)

Returns:



126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/uhaul/facility.rb', line 126

def self.parse(url:, document:)
  data = parse_ld_json_script(document:)

  id = data['@id'].match(%r{(?<id>\d+)/#schema$})[:id]
  name = data['name']

  geocode = Geocode.parse(data: data['geo'])
  address = Address.parse(data: data['address'])
  prices = document.css(PRICES_SELECTOR).map { |element| Price.parse(element:) }.compact

  new(id:, url:, name:, address:, geocode:, prices:)
end

.parse_ld_json_script(document:) ⇒ Hash

Parameters:

  • document (Nokogiri::HTML::Document)

Returns:

  • (Hash)

Raises:



144
145
146
147
148
# File 'lib/uhaul/facility.rb', line 144

def self.parse_ld_json_script(document:)
  parse_ld_json_scripts(document:).find do |data|
    data['@type'] == 'SelfStorage'
  end || raise(ParseError, 'missing ld+json')
end

.parse_ld_json_scripts(document:) ⇒ Array<Hash>

Parameters:

  • document (Nokogiri::HTML::Document)

Returns:

  • (Array<Hash>)


153
154
155
156
157
# File 'lib/uhaul/facility.rb', line 153

def self.parse_ld_json_scripts(document:)
  elements = document.xpath('//script[@type="application/ld+json"]')

  elements.map { |element| element.text.empty? ? {} : JSON.parse(element.text) }
end

.sitemapSitemap

Returns:



102
103
104
105
# File 'lib/uhaul/facility.rb', line 102

def self.sitemap
  links = sitemaps.map(&:links).reduce(&:+)
  Sitemap.new(links:)
end

.sitemapsArray<Sitemap>

Returns:



108
109
110
111
112
# File 'lib/uhaul/facility.rb', line 108

def self.sitemaps
  SITEMAP_URLS.map do |url|
    Sitemap.fetch(url:)
  end
end

Instance Method Details

#inspectString

Returns:

  • (String)


179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/uhaul/facility.rb', line 179

def inspect
  props = [
    "id=#{@id.inspect}",
    "url=#{@url.inspect}",
    "address=#{@address.inspect}",
    "geocode=#{@geocode.inspect}",
    "phone=#{@phone.inspect}",
    "email=#{@email.inspect}",
    "prices=#{@prices.inspect}"
  ]
  "#<#{self.class.name} #{props.join(' ')}>"
end

#textString

Returns:

  • (String)


193
194
195
# File 'lib/uhaul/facility.rb', line 193

def text
  "#{@id} | #{@name} | #{@phone} | #{@email} | #{@address.text} | #{@geocode ? @geocode.text : 'N/A'}"
end