extraspace 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5c85ab034de8c329973cfe53e470745608a52076d9dce4a037532dc8214f5913
4
- data.tar.gz: fa535b917b40c8349b79683642ce5e367b0ea34c519fbfd16c094fba87025ebe
3
+ metadata.gz: b2d656b5fbca3894562492b6a8c3e90c97a4c785cf88d6ff7c2c65c380a5ba8e
4
+ data.tar.gz: c564ac28246067bfb3d843070aa8ea3dcaa0898e12161e15c7570439cd8d4907
5
5
  SHA512:
6
- metadata.gz: d243f120d52703c773053469004d735eb8deadd63f0706012f1b4e92abe2fc747db2010f8a99e561cce4336841b3e350ebe27376a2de1f037fce75d6da14935c
7
- data.tar.gz: 21db75d1f583a0577bc4b62ea4465eca29e69185f668b254c5fb1ee19dc4215ebb77f2abdf3234820e06964b4bc1926255b74a36b7619017cf203ea0a68a8b89
6
+ metadata.gz: 503b60e74410c4fe5cf0db8644382555cf0ddb8343790e5eed69d61c7b8d5e494be3cdbd55097fee3454f555f2bd030722b704906384da65d020e688252ad46a
7
+ data.tar.gz: d48d92e05c00b065619738803344b79254ecb29cec4e9d461a47709169c1125864d278f07ce421647b17d2e6f83efdb09c9a102fe393766edab8d5aa457639db
data/README.md CHANGED
@@ -17,22 +17,26 @@ gem install extrapsace
17
17
  ```ruby
18
18
  require 'extraspace'
19
19
 
20
- URL = 'https://www.extraspace.com/storage/facilities/us/alabama/auburn/3264/'
21
- facility = ExtraSpace::Facility.fetch(url: URL)
22
-
23
- puts "Line 1: #{facility.address.line1}"
24
- puts "Line 2: #{facility.address.line2}"
25
- puts "City: #{facility.address.city}"
26
- puts "State: #{facility.address.state}"
27
- puts "ZIP: #{facility.address.zip}"
28
- puts "Latitude: #{facility.geocode.latitude}"
29
- puts "Longitude: #{facility.geocode.longitude}"
30
- puts
31
-
32
- facility.prices.each do |price|
33
- puts "UID: #{price.uid}"
34
- puts "Dimensions: #{price.dimensions.display}"
35
- puts "Rates: $#{price.rates.street} (street) / $#{price.rates.web} (web)"
20
+ sitemap = ExtraSpace::Facility.sitemap
21
+ sitemap.links.each do |link|
22
+ url = link.loc
23
+
24
+ facility = ExtraSpace::Facility.fetch(url:)
25
+
26
+ puts "Line 1: #{facility.address.line1}"
27
+ puts "Line 2: #{facility.address.line2}"
28
+ puts "City: #{facility.address.city}"
29
+ puts "State: #{facility.address.state}"
30
+ puts "ZIP: #{facility.address.zip}"
31
+ puts "Latitude: #{facility.geocode.latitude}"
32
+ puts "Longitude: #{facility.geocode.longitude}"
36
33
  puts
34
+
35
+ facility.prices.each do |price|
36
+ puts "UID: #{price.uid}"
37
+ puts "Dimensions: #{price.dimensions.display}"
38
+ puts "Rates: $#{price.rates.street} (street) / $#{price.rates.web} (web)"
39
+ puts
40
+ end
37
41
  end
38
42
  ```
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ExtraSpace
4
+ # Used to fetch and parse either HTML or XML via a URL.
5
+ class Crawler
6
+ # Raised for unexpected HTTP responses.
7
+ class FetchError < StandardError
8
+ # @param url [String]
9
+ # @param response [HTTP::Response]
10
+ def initialize(url:, response:)
11
+ super("url=#{url} status=#{response.status.inspect} body=#{response.body.inspect}")
12
+ end
13
+ end
14
+
15
+ # @param url [String]
16
+ # @raise [FetchError]
17
+ # @return [Nokogiri::HTML::Document]
18
+ def self.html(url:)
19
+ new.html(url:)
20
+ end
21
+
22
+ # @param url [String]
23
+ # @raise [FetchError]
24
+ # @return [Nokogiri::XML::Document]
25
+ def self.xml(url:)
26
+ new.xml(url:)
27
+ end
28
+
29
+ # @param url [String]
30
+ # @return [HTTP::Response]
31
+ def fetch(url:)
32
+ response = HTTP.get(url)
33
+ raise FetchError(url:, response: response.flush) unless response.status.ok?
34
+
35
+ response
36
+ end
37
+
38
+ # @param url [String]
39
+ # @raise [FetchError]
40
+ # @return [Nokogiri::XML::Document]
41
+ def html(url:)
42
+ Nokogiri::HTML(String(fetch(url:).body))
43
+ end
44
+
45
+ # @param url [String]
46
+ # @raise [FetchError]
47
+ # @return [Nokogiri::XML::Document]
48
+ def xml(url:)
49
+ Nokogiri::XML(String(fetch(url:).body))
50
+ end
51
+ end
52
+ end
@@ -3,6 +3,8 @@
3
3
  module ExtraSpace
4
4
  # e.g. https://www.extraspace.com/storage/facilities/us/alabama/auburn/3264/
5
5
  class Facility
6
+ SITEMAP_URL = 'https://www.extraspace.com/facility-sitemap.xml'
7
+
6
8
  # @attribute [rw] address
7
9
  # @return [Address]
8
10
  attr_accessor :address
@@ -34,15 +36,18 @@ module ExtraSpace
34
36
  "#<#{self.class.name} #{props.join(' ')}>"
35
37
  end
36
38
 
39
+ # @return [Sitemap]
40
+ def self.sitemap
41
+ Sitemap.fetch(url: SITEMAP_URL)
42
+ end
43
+
37
44
  # @param url [String]
38
45
  #
39
46
  # @return [Facility]
40
47
  def self.fetch(url:)
41
- response = HTTP.get(url)
42
- document = Nokogiri::HTML(String(response.body))
48
+ document = Crawler.html(url:)
43
49
  data = JSON.parse(document.at('#__NEXT_DATA__').text)
44
-
45
- parse(data: data)
50
+ parse(data:)
46
51
  end
47
52
 
48
53
  # @param data [Hash]
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ExtraSpace
4
+ # A link in a sitemap.
5
+ class Link
6
+ # @attribute [rw] loc
7
+ # @return [String]
8
+ attr_accessor :loc
9
+
10
+ # @attribute [rw] lastmod
11
+ # @return [Time]
12
+ attr_accessor :lastmod
13
+
14
+ # @param loc [String]
15
+ # @param lastmod [String]
16
+ def initialize(loc:, lastmod:)
17
+ @loc = loc
18
+ @lastmod = Time.parse(lastmod)
19
+ end
20
+
21
+ # @return [String]
22
+ def inspect
23
+ "#<#{self.class.name} loc=#{@loc.inspect} lastmod=#{@lastmod.inspect}>"
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ExtraSpace
4
+ # e.g. https://www.extraspace.com/facility-sitemap.xml
5
+ class Sitemap
6
+ # @attribute [rw] links
7
+ # @return [Array<Link>]
8
+ attr_accessor :links
9
+
10
+ # @param document [NokoGiri::XML::Document]
11
+ #
12
+ # @return [Sitemap]
13
+ def self.parse(document:)
14
+ links = document.xpath('//xmlns:url').map do |url|
15
+ loc = url.at_xpath('xmlns:loc')&.text
16
+ lastmod = url.at_xpath('xmlns:lastmod')&.text
17
+ Link.new(loc:, lastmod:)
18
+ end
19
+
20
+ new(links: links)
21
+ end
22
+
23
+ # @param url [String]
24
+ #
25
+ # @return [Sitemap]
26
+ def self.fetch(url:)
27
+ document = Crawler.xml(url:)
28
+ parse(document:)
29
+ end
30
+
31
+ # @param links [Array<Link>]
32
+ def initialize(links:)
33
+ @links = links
34
+ end
35
+
36
+ # @return [String]
37
+ def inspect
38
+ "#<#{self.class.name} links=#{@links.inspect}>"
39
+ end
40
+ end
41
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ExtraSpace
4
- VERSION = '0.1.1'
4
+ VERSION = '0.1.2'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: extraspace
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Sylvestre
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-11-21 00:00:00.000000000 Z
11
+ date: 2024-11-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: http
@@ -80,11 +80,14 @@ files:
80
80
  - lib/extraspace.rb
81
81
  - lib/extraspace/address.rb
82
82
  - lib/extraspace/availability.rb
83
+ - lib/extraspace/crawler.rb
83
84
  - lib/extraspace/dimensions.rb
84
85
  - lib/extraspace/facility.rb
85
86
  - lib/extraspace/geocode.rb
87
+ - lib/extraspace/link.rb
86
88
  - lib/extraspace/price.rb
87
89
  - lib/extraspace/rates.rb
90
+ - lib/extraspace/sitemap.rb
88
91
  - lib/extraspace/version.rb
89
92
  homepage: https://github.com/ksylvest/extraspace
90
93
  licenses: