uhaul 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/uhaul/address.rb +46 -7
- data/lib/uhaul/crawl.rb +1 -1
- data/lib/uhaul/dimensions.rb +0 -2
- data/lib/uhaul/facility.rb +28 -7
- data/lib/uhaul/geocode.rb +33 -10
- data/lib/uhaul/parse_error.rb +7 -0
- data/lib/uhaul/version.rb +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 22343f801475f0781ff4ec3722470098b7d05e124b56253deb9d912d1dd54fac
|
4
|
+
data.tar.gz: f49c694569358b34e8dddf4a5493a5cbbce5ec7e77a19c2c2d3c8a7515f821d4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3ad0314baee02a85ca1587c923fd6748ad6c14833ee17536efe2a4251753ac8a1afd23d917585a93c31da4033dc4c37bd827e8ea5e9dfc483a534ad6b79b48ed
|
7
|
+
data.tar.gz: 81376338d60cd9f9875f9d9dfa3c72dd880fbd9821c3d73f678f0ed8d0dc7abc189ca596c2d479d8528e55f536fdf49b7fb1060b42c625a110269e59b94eb45e
|
data/lib/uhaul/address.rb
CHANGED
@@ -3,8 +3,6 @@
|
|
3
3
|
module UHaul
|
4
4
|
# The address (street + city + state + zip) of a facility.
|
5
5
|
class Address
|
6
|
-
ADDRESS_SELECTOR = '.item-des-box .text-box .part_title_1'
|
7
|
-
ADDRESS_REGEX = /(?<street>.+),\s+(?<city>.+),\s+(?<state>.+)\s+(?<zip>\d{5})/
|
8
6
|
# @attribute [rw] street
|
9
7
|
# @return [String]
|
10
8
|
attr_accessor :street
|
@@ -48,16 +46,57 @@ module UHaul
|
|
48
46
|
"#{street}, #{city}, #{state} #{zip}"
|
49
47
|
end
|
50
48
|
|
49
|
+
# @param document [String]
|
50
|
+
# @param data [Hash] optional
|
51
|
+
#
|
52
|
+
# @return [Address]
|
53
|
+
def self.parse(document:, data:)
|
54
|
+
parse_by_data(data:) || parse_by_document(document:)
|
55
|
+
end
|
56
|
+
|
51
57
|
# @param data [Hash]
|
52
58
|
#
|
53
59
|
# @return [Address]
|
54
|
-
def self.
|
60
|
+
def self.parse_by_data(data:)
|
61
|
+
address = data&.dig('address')
|
62
|
+
return unless address
|
63
|
+
|
55
64
|
new(
|
56
|
-
street:
|
57
|
-
city:
|
58
|
-
state:
|
59
|
-
zip:
|
65
|
+
street: address['streetAddress'],
|
66
|
+
city: address['addressLocality'],
|
67
|
+
state: address['addressRegion'],
|
68
|
+
zip: address['postalCode']
|
60
69
|
)
|
61
70
|
end
|
71
|
+
|
72
|
+
# @param document [Nokogiri::HTML::Document]
|
73
|
+
#
|
74
|
+
# @return [Address]
|
75
|
+
def self.parse_by_document(document:)
|
76
|
+
element = document.at_css('address')
|
77
|
+
return unless element
|
78
|
+
|
79
|
+
element.text.match(/(?<street>.+)[\r\n,]+(?<city>.+)[\r\n,]+(?<state>.+)[\r\n\s,]+(?<zip>\d{5})/) do |match|
|
80
|
+
new(
|
81
|
+
street: strip(match[:street]),
|
82
|
+
city: strip(match[:city]),
|
83
|
+
state: strip(match[:state]),
|
84
|
+
zip: strip(match[:zip])
|
85
|
+
)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# @param text [String]
|
90
|
+
#
|
91
|
+
# @return [String]
|
92
|
+
def self.strip(text)
|
93
|
+
return unless text
|
94
|
+
|
95
|
+
text
|
96
|
+
.strip
|
97
|
+
.gsub(/^[\s\p{Space},],+/, '')
|
98
|
+
.gsub(/[\s\p{Space},]+$/, '')
|
99
|
+
.gsub(/[\s\p{Space}]+/, ' ')
|
100
|
+
end
|
62
101
|
end
|
63
102
|
end
|
data/lib/uhaul/crawl.rb
CHANGED
data/lib/uhaul/dimensions.rb
CHANGED
data/lib/uhaul/facility.rb
CHANGED
@@ -5,8 +5,6 @@ module UHaul
|
|
5
5
|
#
|
6
6
|
# e.g. https://www.uhaul.com/Locations/Self-Storage-near-Inglewood-CA-90301/712030/
|
7
7
|
class Facility
|
8
|
-
class ParseError < StandardError; end
|
9
|
-
|
10
8
|
PRICES_SELECTOR = '#roomTypes > ul:not([id*="VehicleStorage"]) > li'
|
11
9
|
|
12
10
|
SITEMAP_URLS = %w[
|
@@ -126,11 +124,11 @@ module UHaul
|
|
126
124
|
def self.parse(url:, document:)
|
127
125
|
data = parse_ld_json_script(document:)
|
128
126
|
|
129
|
-
id =
|
130
|
-
name =
|
127
|
+
id = parse_id!(document:)
|
128
|
+
name = parse_name!(document:)
|
131
129
|
|
132
|
-
geocode = Geocode.parse(data:
|
133
|
-
address = Address.parse(data:
|
130
|
+
geocode = Geocode.parse(data:, document:)
|
131
|
+
address = Address.parse(data:, document:)
|
134
132
|
prices = document.css(PRICES_SELECTOR).map { |element| Price.parse(element:) }.compact
|
135
133
|
|
136
134
|
new(id:, url:, name:, address:, geocode:, prices:)
|
@@ -144,7 +142,7 @@ module UHaul
|
|
144
142
|
def self.parse_ld_json_script(document:)
|
145
143
|
parse_ld_json_scripts(document:).find do |data|
|
146
144
|
%w[SelfStorage LocalBusiness].include?(data['@type'])
|
147
|
-
end
|
145
|
+
end
|
148
146
|
end
|
149
147
|
|
150
148
|
# @param document [Nokogiri::HTML::Document]
|
@@ -156,6 +154,29 @@ module UHaul
|
|
156
154
|
elements.map { |element| element.text.empty? ? {} : JSON.parse(element.text) }
|
157
155
|
end
|
158
156
|
|
157
|
+
# @param document [Nokogiri::HTML::Document]
|
158
|
+
#
|
159
|
+
# @raise [ParseError]
|
160
|
+
#
|
161
|
+
# @return [String]
|
162
|
+
def self.parse_id!(document:)
|
163
|
+
element = document.at_xpath('//link[@rel="canonical"]') || raise(ParseError, 'missing <link rel="canonical">')
|
164
|
+
|
165
|
+
href = element['href']
|
166
|
+
href.match(%r{(?<id>\d+)/$})[:id]
|
167
|
+
end
|
168
|
+
|
169
|
+
# @param document [Nokogiri::HTML::Document]
|
170
|
+
#
|
171
|
+
# @raise [ParseError]
|
172
|
+
#
|
173
|
+
# @return [String]
|
174
|
+
def self.parse_name!(document:)
|
175
|
+
element = document.at_xpath('//title') || raise(ParseError, 'missing <title>...</title>')
|
176
|
+
|
177
|
+
element.text.match(/\|\s*(?<name>.*)\s*$/)[:name].strip
|
178
|
+
end
|
179
|
+
|
159
180
|
# @param id [String]
|
160
181
|
# @param url [String]
|
161
182
|
# @param name [String]
|
data/lib/uhaul/geocode.rb
CHANGED
@@ -14,16 +14,6 @@ module UHaul
|
|
14
14
|
# @return [Float]
|
15
15
|
attr_accessor :longitude
|
16
16
|
|
17
|
-
# @param data [Hash]
|
18
|
-
#
|
19
|
-
# @return [Geocode]
|
20
|
-
def self.parse(data:)
|
21
|
-
latitude = Float(data['latitude'])
|
22
|
-
longitude = Float(data['longitude'])
|
23
|
-
|
24
|
-
new(latitude:, longitude:)
|
25
|
-
end
|
26
|
-
|
27
17
|
# @param latitude [Float]
|
28
18
|
# @param longitude [Float]
|
29
19
|
def initialize(latitude:, longitude:)
|
@@ -44,5 +34,38 @@ module UHaul
|
|
44
34
|
def text
|
45
35
|
"#{@latitude},#{@longitude}"
|
46
36
|
end
|
37
|
+
|
38
|
+
# @param document [String]
|
39
|
+
# @param data [Hash] optional
|
40
|
+
#
|
41
|
+
# @return [Address]
|
42
|
+
def self.parse(document:, data:)
|
43
|
+
parse_by_data(data:) || parse_by_document(document:)
|
44
|
+
end
|
45
|
+
|
46
|
+
# @param data [Hash]
|
47
|
+
#
|
48
|
+
# @return [Address]
|
49
|
+
def self.parse_by_data(data:)
|
50
|
+
coordinates = data&.dig('areaServed', 'geoMidpoint')
|
51
|
+
return unless coordinates
|
52
|
+
|
53
|
+
new(
|
54
|
+
latitude: Float(coordinates['latitude']),
|
55
|
+
longitude: Float(coordinates['longitude'])
|
56
|
+
)
|
57
|
+
end
|
58
|
+
|
59
|
+
# @param document [Nokogiri::HTML::Document]
|
60
|
+
#
|
61
|
+
# @return [Address]
|
62
|
+
def self.parse_by_document(document:)
|
63
|
+
document.text.match(/latitude:\s*(?<latitude>[\+\-\d\.]+),\s*longitude:\s*(?<longitude>[\+\-\d\.]+)/) do |match|
|
64
|
+
new(
|
65
|
+
latitude: Float(match[:latitude]),
|
66
|
+
longitude: Float(match[:longitude])
|
67
|
+
)
|
68
|
+
end
|
69
|
+
end
|
47
70
|
end
|
48
71
|
end
|
data/lib/uhaul/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: uhaul
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kevin Sylvestre
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-05-
|
11
|
+
date: 2025-05-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: http
|
@@ -105,6 +105,7 @@ files:
|
|
105
105
|
- lib/uhaul/fetch_error.rb
|
106
106
|
- lib/uhaul/geocode.rb
|
107
107
|
- lib/uhaul/link.rb
|
108
|
+
- lib/uhaul/parse_error.rb
|
108
109
|
- lib/uhaul/price.rb
|
109
110
|
- lib/uhaul/rates.rb
|
110
111
|
- lib/uhaul/sitemap.rb
|
@@ -115,8 +116,8 @@ licenses:
|
|
115
116
|
metadata:
|
116
117
|
rubygems_mfa_required: 'true'
|
117
118
|
homepage_uri: https://github.com/ksylvest/uhaul
|
118
|
-
source_code_uri: https://github.com/ksylvest/uhaul/tree/v1.
|
119
|
-
changelog_uri: https://github.com/ksylvest/uhaul/releases/tag/v1.
|
119
|
+
source_code_uri: https://github.com/ksylvest/uhaul/tree/v1.2.0
|
120
|
+
changelog_uri: https://github.com/ksylvest/uhaul/releases/tag/v1.2.0
|
120
121
|
documentation_uri: https://uhaul.ksylvest.com/
|
121
122
|
post_install_message:
|
122
123
|
rdoc_options: []
|