publicstorage 0.1.0 → 0.1.2
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/README.md +5 -4
- data/lib/publicstorage/address.rb +9 -17
- data/lib/publicstorage/dimensions.rb +14 -14
- data/lib/publicstorage/facility.rb +10 -10
- data/lib/publicstorage/price.rb +12 -20
- data/lib/publicstorage/rates.rb +8 -14
- data/lib/publicstorage/version.rb +1 -1
- metadata +5 -6
- data/lib/publicstorage/availability.rb +0 -30
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fc55c7ceabfcca39423c75b3315ded193267c2d02da648056b472c4f6e5ca64a
|
4
|
+
data.tar.gz: ed7aaea3035fe7aaa876febfa809a90cf9559080e61aeca1fdffb8ed338bba61
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b4d628a25a0af8d0754c98868d3a7cd18c2a8b7d6d9dc6f66fbd1a1a83454aea72c108806f3e25a4311d5aa80862cb4a7798cd5645e08484b4f44179139f1cd3
|
7
|
+
data.tar.gz: 46684da48469510751828b452b5f00381caf94b460da89383675429ed8974878355e03cb5e2fdc879db59b5a8e591a83a557b77f131558a4ad91fafb84dc05d9
|
data/README.md
CHANGED
@@ -23,8 +23,7 @@ sitemap.links.each do |link|
|
|
23
23
|
|
24
24
|
facility = PublicStorage::Facility.fetch(url:)
|
25
25
|
|
26
|
-
puts "
|
27
|
-
puts "Line 2: #{facility.address.line2}"
|
26
|
+
puts "Street: #{facility.address.street}"
|
28
27
|
puts "City: #{facility.address.city}"
|
29
28
|
puts "State: #{facility.address.state}"
|
30
29
|
puts "ZIP: #{facility.address.zip}"
|
@@ -33,8 +32,10 @@ sitemap.links.each do |link|
|
|
33
32
|
puts
|
34
33
|
|
35
34
|
facility.prices.each do |price|
|
36
|
-
puts "
|
37
|
-
puts "
|
35
|
+
puts "ID: #{price.id}"
|
36
|
+
puts "Width: #{price.dimensions.width}"
|
37
|
+
puts "Depth: #{price.dimensions.depth}"
|
38
|
+
puts "SQFT: #{price.dimensions.sqft}"
|
38
39
|
puts "Rates: $#{price.rates.street} (street) / $#{price.rates.web} (web)"
|
39
40
|
puts
|
40
41
|
end
|
@@ -3,13 +3,9 @@
|
|
3
3
|
module PublicStorage
|
4
4
|
# Represents an address associated with a facility.
|
5
5
|
class Address
|
6
|
-
# @attribute [rw]
|
6
|
+
# @attribute [rw] street
|
7
7
|
# @return [String]
|
8
|
-
attr_accessor :
|
9
|
-
|
10
|
-
# @attribute [rw] line2
|
11
|
-
# @return [String]
|
12
|
-
attr_accessor :line2
|
8
|
+
attr_accessor :street
|
13
9
|
|
14
10
|
# @attribute [rw] city
|
15
11
|
# @return [String]
|
@@ -23,14 +19,12 @@ module PublicStorage
|
|
23
19
|
# @return [String]
|
24
20
|
attr_accessor :zip
|
25
21
|
|
26
|
-
# @param
|
27
|
-
# @param line2 [String]
|
22
|
+
# @param street [String]
|
28
23
|
# @param city [String]
|
29
24
|
# @param state [String]
|
30
25
|
# @param zip [String]
|
31
|
-
def initialize(
|
32
|
-
@
|
33
|
-
@line2 = line2
|
26
|
+
def initialize(street:, city:, state:, zip:)
|
27
|
+
@street = street
|
34
28
|
@city = city
|
35
29
|
@state = state
|
36
30
|
@zip = zip
|
@@ -39,8 +33,7 @@ module PublicStorage
|
|
39
33
|
# @return [String]
|
40
34
|
def inspect
|
41
35
|
props = [
|
42
|
-
"
|
43
|
-
"line2=#{@line2.inspect}",
|
36
|
+
"street=#{@street.inspect}",
|
44
37
|
"city=#{@city.inspect}",
|
45
38
|
"state=#{@state.inspect}",
|
46
39
|
"zip=#{@zip.inspect}"
|
@@ -53,10 +46,9 @@ module PublicStorage
|
|
53
46
|
# @return [Address]
|
54
47
|
def self.parse(data:)
|
55
48
|
new(
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
state: data['stateName'],
|
49
|
+
street: data['streetAddress'],
|
50
|
+
city: data['addressLocality'],
|
51
|
+
state: data['addressRegion'],
|
60
52
|
zip: data['postalCode']
|
61
53
|
)
|
62
54
|
end
|
@@ -3,6 +3,8 @@
|
|
3
3
|
module PublicStorage
|
4
4
|
# The dimensions associated with a price.
|
5
5
|
class Dimensions
|
6
|
+
SELECTOR = '.unit-size'
|
7
|
+
|
6
8
|
# @attribute [rw] depth
|
7
9
|
# @return [Integer]
|
8
10
|
attr_accessor :depth
|
@@ -15,19 +17,13 @@ module PublicStorage
|
|
15
17
|
# @return [Integer]
|
16
18
|
attr_accessor :sqft
|
17
19
|
|
18
|
-
# @
|
19
|
-
#
|
20
|
-
attr_accessor :display
|
21
|
-
|
22
|
-
# @param depth [Integer]
|
23
|
-
# @param width [Integer]
|
20
|
+
# @param depth [Float]
|
21
|
+
# @param width [Float]
|
24
22
|
# @param sqft [Integer]
|
25
|
-
|
26
|
-
def initialize(depth:, width:, sqft:, display:)
|
23
|
+
def initialize(depth:, width:, sqft:)
|
27
24
|
@depth = depth
|
28
25
|
@width = width
|
29
26
|
@sqft = sqft
|
30
|
-
@display = display
|
31
27
|
end
|
32
28
|
|
33
29
|
# @return [String]
|
@@ -35,17 +31,21 @@ module PublicStorage
|
|
35
31
|
props = [
|
36
32
|
"depth=#{@depth.inspect}",
|
37
33
|
"width=#{@width.inspect}",
|
38
|
-
"sqft=#{@sqft.inspect}"
|
39
|
-
"display=#{@display.inspect}"
|
34
|
+
"sqft=#{@sqft.inspect}"
|
40
35
|
]
|
41
36
|
"#<#{self.class.name} #{props.join(' ')}>"
|
42
37
|
end
|
43
38
|
|
44
|
-
# @param
|
39
|
+
# @param element [Nokogiri::XML::Element]
|
45
40
|
#
|
46
41
|
# @return [Dimensions]
|
47
|
-
def self.parse(
|
48
|
-
|
42
|
+
def self.parse(element:)
|
43
|
+
match = element.at(SELECTOR).text.match(/(?<depth>[\d\.]+)'x(?<width>[\d\.]+)'/)
|
44
|
+
depth = Float(match[:depth])
|
45
|
+
width = Float(match[:width])
|
46
|
+
sqft = Integer(depth * width)
|
47
|
+
|
48
|
+
new(depth:, width:, sqft:)
|
49
49
|
end
|
50
50
|
end
|
51
51
|
end
|
@@ -5,6 +5,8 @@ module PublicStorage
|
|
5
5
|
class Facility
|
6
6
|
SITEMAP_URL = 'https://www.publicstorage.com/sitemap_0-product.xml'
|
7
7
|
|
8
|
+
PRICE_SELECTOR = '.units-results-section .unit-list-group .unit-list-item'
|
9
|
+
|
8
10
|
# @attribute [rw] address
|
9
11
|
# @return [Address]
|
10
12
|
attr_accessor :address
|
@@ -46,21 +48,19 @@ module PublicStorage
|
|
46
48
|
# @return [Facility]
|
47
49
|
def self.fetch(url:)
|
48
50
|
document = Crawler.html(url:)
|
49
|
-
|
50
|
-
parse(data:)
|
51
|
+
parse(document:)
|
51
52
|
end
|
52
53
|
|
53
|
-
# @param
|
54
|
+
# @param document [NokoGiri::XML::Document]
|
54
55
|
#
|
55
56
|
# @return [Facility]
|
56
|
-
def self.parse(
|
57
|
-
|
58
|
-
|
59
|
-
|
57
|
+
def self.parse(document:)
|
58
|
+
data = JSON.parse(document.at_css('script[type="application/ld+json"]').text)
|
59
|
+
item = data.find { |entry| entry['@type'] == 'SelfStorage' }
|
60
|
+
address = Address.parse(data: item['address'])
|
61
|
+
geocode = Geocode.parse(data: item['geo'])
|
60
62
|
|
61
|
-
|
62
|
-
geocode = Geocode.parse(data: facility_data['store']['geocode'])
|
63
|
-
prices = unit_classes.map { |price_data| Price.parse(data: price_data) }
|
63
|
+
prices = document.css(PRICE_SELECTOR).map { |element| Price.parse(element:) }
|
64
64
|
|
65
65
|
new(address:, geocode:, prices:)
|
66
66
|
end
|
data/lib/publicstorage/price.rb
CHANGED
@@ -3,13 +3,9 @@
|
|
3
3
|
module PublicStorage
|
4
4
|
# A price associated with a unit.
|
5
5
|
class Price
|
6
|
-
# @attribute [rw]
|
6
|
+
# @attribute [rw] id
|
7
7
|
# @return [String]
|
8
|
-
attr_accessor :
|
9
|
-
|
10
|
-
# @attribute [rw] availability
|
11
|
-
# @return [Availability]
|
12
|
-
attr_accessor :availability
|
8
|
+
attr_accessor :id
|
13
9
|
|
14
10
|
# @attribute [rw] dimensions
|
15
11
|
# @return [Dimensions]
|
@@ -19,13 +15,11 @@ module PublicStorage
|
|
19
15
|
# @return [Rates]
|
20
16
|
attr_accessor :rates
|
21
17
|
|
22
|
-
# @param
|
23
|
-
# @param availability [Availability]
|
18
|
+
# @param id [String]
|
24
19
|
# @param dimensions [Dimensions]
|
25
20
|
# @param rates [Rates]
|
26
|
-
def initialize(
|
27
|
-
@
|
28
|
-
@availability = availability
|
21
|
+
def initialize(id:, dimensions:, rates:)
|
22
|
+
@id = id
|
29
23
|
@dimensions = dimensions
|
30
24
|
@rates = rates
|
31
25
|
end
|
@@ -33,24 +27,22 @@ module PublicStorage
|
|
33
27
|
# @return [String]
|
34
28
|
def inspect
|
35
29
|
props = [
|
36
|
-
"
|
37
|
-
"availability=#{@availability.inspect}",
|
30
|
+
"id=#{@id.inspect}",
|
38
31
|
"dimensions=#{@dimensions.inspect}",
|
39
32
|
"rates=#{@rates.inspect}"
|
40
33
|
]
|
41
34
|
"#<#{self.class.name} #{props.join(' ')}>"
|
42
35
|
end
|
43
36
|
|
44
|
-
# @param
|
37
|
+
# @param element [Nokogiri::XML::Element]
|
45
38
|
#
|
46
39
|
# @return [Price]
|
47
|
-
def self.parse(
|
48
|
-
|
49
|
-
dimensions = Dimensions.parse(
|
50
|
-
|
40
|
+
def self.parse(element:)
|
41
|
+
rates = Rates.parse(element:)
|
42
|
+
dimensions = Dimensions.parse(element:)
|
43
|
+
|
51
44
|
new(
|
52
|
-
|
53
|
-
availability: availability,
|
45
|
+
id: element.attr('data-unitid'),
|
54
46
|
dimensions: dimensions,
|
55
47
|
rates: rates
|
56
48
|
)
|
data/lib/publicstorage/rates.rb
CHANGED
@@ -3,9 +3,8 @@
|
|
3
3
|
module PublicStorage
|
4
4
|
# The rates associated with a price
|
5
5
|
class Rates
|
6
|
-
|
7
|
-
|
8
|
-
attr_accessor :nsc
|
6
|
+
STREET_SELECTOR = '.unit-prices .unit-pricing .unit-strike-through-price'
|
7
|
+
WEB_SELECTOR = '.unit-prices .unit-pricing .unit-price'
|
9
8
|
|
10
9
|
# @attribute [rw] street
|
11
10
|
# @return [Integer]
|
@@ -15,11 +14,9 @@ module PublicStorage
|
|
15
14
|
# @return [Integer]
|
16
15
|
attr_accessor :web
|
17
16
|
|
18
|
-
# @param nsc [Integer]
|
19
17
|
# @param street [Integer]
|
20
18
|
# @param web [Integer]
|
21
|
-
def initialize(
|
22
|
-
@nsc = nsc
|
19
|
+
def initialize(street:, web:)
|
23
20
|
@street = street
|
24
21
|
@web = web
|
25
22
|
end
|
@@ -27,22 +24,19 @@ module PublicStorage
|
|
27
24
|
# @return [String]
|
28
25
|
def inspect
|
29
26
|
props = [
|
30
|
-
"nsc=#{@nsc.inspect}",
|
31
27
|
"street=#{@street.inspect}",
|
32
28
|
"web=#{@web.inspect}"
|
33
29
|
]
|
34
30
|
"#<#{self.class.name} #{props.join(' ')}>"
|
35
31
|
end
|
36
32
|
|
37
|
-
# @param
|
33
|
+
# @param element [Nokogiri::XML::Element]
|
38
34
|
#
|
39
35
|
# @return [Rates]
|
40
|
-
def self.parse(
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
web: data['web']
|
45
|
-
)
|
36
|
+
def self.parse(element:)
|
37
|
+
street = Integer(element.at(STREET_SELECTOR).text.match(/(?<value>\d+)/)[:value])
|
38
|
+
web = Integer(element.at(WEB_SELECTOR).text.match(/(?<value>\d+)/)[:value])
|
39
|
+
new(street:, web:)
|
46
40
|
end
|
47
41
|
end
|
48
42
|
end
|
metadata
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: publicstorage
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kevin Sylvestre
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
11
|
date: 2024-11-27 00:00:00.000000000 Z
|
@@ -79,7 +79,6 @@ files:
|
|
79
79
|
- bin/setup
|
80
80
|
- lib/publicstorage.rb
|
81
81
|
- lib/publicstorage/address.rb
|
82
|
-
- lib/publicstorage/availability.rb
|
83
82
|
- lib/publicstorage/crawler.rb
|
84
83
|
- lib/publicstorage/dimensions.rb
|
85
84
|
- lib/publicstorage/facility.rb
|
@@ -97,7 +96,7 @@ metadata:
|
|
97
96
|
homepage_uri: https://github.com/ksylvest/publicstorage
|
98
97
|
source_code_uri: https://github.com/ksylvest/publicstorage
|
99
98
|
changelog_uri: https://github.com/ksylvest/publicstorage
|
100
|
-
post_install_message:
|
99
|
+
post_install_message:
|
101
100
|
rdoc_options: []
|
102
101
|
require_paths:
|
103
102
|
- lib
|
@@ -112,8 +111,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
112
111
|
- !ruby/object:Gem::Version
|
113
112
|
version: '0'
|
114
113
|
requirements: []
|
115
|
-
rubygems_version: 3.5.
|
116
|
-
signing_key:
|
114
|
+
rubygems_version: 3.5.23
|
115
|
+
signing_key:
|
117
116
|
specification_version: 4
|
118
117
|
summary: A crawler for PublicStorage.
|
119
118
|
test_files: []
|
@@ -1,30 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module PublicStorage
|
4
|
-
# The availability associated with a price.
|
5
|
-
class Availability
|
6
|
-
# @attribute [rw] available
|
7
|
-
# @return [String]
|
8
|
-
attr_accessor :available
|
9
|
-
|
10
|
-
# @param available [String]
|
11
|
-
def initialize(available:)
|
12
|
-
@available = available
|
13
|
-
end
|
14
|
-
|
15
|
-
# @return [String]
|
16
|
-
def inspect
|
17
|
-
props = [
|
18
|
-
"available=#{@available.inspect}"
|
19
|
-
]
|
20
|
-
"#<#{self.class.name} #{props.join(' ')}>"
|
21
|
-
end
|
22
|
-
|
23
|
-
# @param data [Hash]
|
24
|
-
#
|
25
|
-
# @return [Availability]
|
26
|
-
def self.parse(data:)
|
27
|
-
new(available: data['available'])
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|