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/

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: [])


188
189
190
191
192
193
194
195
196
197
# File 'lib/uhaul/facility.rb', line 188

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:



89
90
91
# File 'lib/uhaul/facility.rb', line 89

def address
  @address
end

#emailString

Returns:

  • (String)


85
86
87
# File 'lib/uhaul/facility.rb', line 85

def email
  @email
end

#geocodeGeocode?

Returns:



93
94
95
# File 'lib/uhaul/facility.rb', line 93

def geocode
  @geocode
end

#idString

Returns:

  • (String)


69
70
71
# File 'lib/uhaul/facility.rb', line 69

def id
  @id
end

#nameString

Returns:

  • (String)


77
78
79
# File 'lib/uhaul/facility.rb', line 77

def name
  @name
end

#phoneString

Returns:

  • (String)


81
82
83
# File 'lib/uhaul/facility.rb', line 81

def phone
  @phone
end

#pricesArray<Price>

Returns:



97
98
99
# File 'lib/uhaul/facility.rb', line 97

def prices
  @prices
end

#urlString

Returns:

  • (String)


73
74
75
# File 'lib/uhaul/facility.rb', line 73

def url
  @url
end

Class Method Details

.fetch(url:) ⇒ Facility

Parameters:

  • url (String)

Returns:



115
116
117
118
# File 'lib/uhaul/facility.rb', line 115

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

.parse(url:, document:) ⇒ Facility

Parameters:

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

Returns:



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

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

  id = parse_id!(document:)
  name = parse_name!(document:)

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

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

.parse_id!(document:) ⇒ String

Parameters:

  • document (Nokogiri::HTML::Document)

Returns:

  • (String)

Raises:



162
163
164
165
166
167
# File 'lib/uhaul/facility.rb', line 162

def self.parse_id!(document:)
  element = document.at_xpath('//link[@rel="canonical"]') || raise(ParseError, 'missing <link rel="canonical">')

  href = element['href']
  href.match(%r{(?<id>\d+)/$})[:id]
end

.parse_ld_json_script(document:) ⇒ Hash

Parameters:

  • document (Nokogiri::HTML::Document)

Returns:

  • (Hash)

Raises:



142
143
144
145
146
# File 'lib/uhaul/facility.rb', line 142

def self.parse_ld_json_script(document:)
  parse_ld_json_scripts(document:).find do |data|
    %w[SelfStorage LocalBusiness].include?(data['@type'])
  end
end

.parse_ld_json_scripts(document:) ⇒ Array<Hash>

Parameters:

  • document (Nokogiri::HTML::Document)

Returns:

  • (Array<Hash>)


151
152
153
154
155
# File 'lib/uhaul/facility.rb', line 151

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

.parse_name!(document:) ⇒ String

Parameters:

  • document (Nokogiri::HTML::Document)

Returns:

  • (String)

Raises:



174
175
176
177
178
# File 'lib/uhaul/facility.rb', line 174

def self.parse_name!(document:)
  element = document.at_xpath('//title') || raise(ParseError, 'missing <title>...</title>')

  element.text.match(/\|\s*(?<name>.*)\s*$/)[:name].strip
end

.sitemapSitemap

Returns:



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

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

.sitemapsArray<Sitemap>

Returns:



106
107
108
109
110
# File 'lib/uhaul/facility.rb', line 106

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

Instance Method Details

#inspectString

Returns:

  • (String)


200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/uhaul/facility.rb', line 200

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)


214
215
216
# File 'lib/uhaul/facility.rb', line 214

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