extraspace 0.4.0 → 1.0.0

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: 5a9b243e73e7e8ded7dec1cb75998fcf2b6cfc3907e9f07ec1c6a26071c10af4
4
- data.tar.gz: 9b56a1dc1d3da8e3d83a358e1554214a58270f0936167004c29063024570445f
3
+ metadata.gz: febf4432cf5937d8fccef08a6210416fa4faa2fe37a28816832a6935bff36ac7
4
+ data.tar.gz: be2a71660eaffd362db0788a20b81c5a9688d4bb11efa4d36e5bdcce31085d78
5
5
  SHA512:
6
- metadata.gz: 44636e3f4b7229189e6a806d14bf440fb565127a952a15911e761e440364f83c2413e7167fc55b51ce145eb11f8fe4446c7f259548a5f0dca3adc25a7cb06a4d
7
- data.tar.gz: bea8280c5d3506e5358cc69ab1561419667442ca6fc2f78216400bebf60ba2be729fd282e5daee8c79b9dae7dab2f4312fc94d66ed65b5504aeaba67933ebc13
6
+ metadata.gz: 139bacf0760a498199df8a1cf5171c2da994a65f736cc971af63c082f47916df2a5665ccf329b87d65f034e0a5a92373fe85ada538717d4b2fde170f8d70395a
7
+ data.tar.gz: 91855347437bd257fbd117098d15cc30eda47aed595bc77c42095bbdcc148dff5c4c6eaf3ca31d02ee01e92afb51a2f9eac4a2edbc3d167ad5317706d62d39bc
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # ExtraSpace
1
+ # Extra Space
2
2
 
3
3
  [![LICENSE](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/ksylvest/extraspace/blob/main/LICENSE)
4
4
  [![RubyGems](https://img.shields.io/gem/v/extraspace)](https://rubygems.org/gems/extraspace)
@@ -6,10 +6,12 @@
6
6
  [![Yard](https://img.shields.io/badge/docs-site-blue.svg)](https://extraspace.ksylvest.com)
7
7
  [![CircleCI](https://img.shields.io/circleci/build/github/ksylvest/extraspace)](https://circleci.com/gh/ksylvest/extraspace)
8
8
 
9
+ A Ruby library offering both a CLI and API for scraping [Extra Space](https://www.extraspace.com/) self-storage facilities and prices.
10
+
9
11
  ## Installation
10
12
 
11
13
  ```bash
12
- gem install extrapsace
14
+ gem install extraspace
13
15
  ```
14
16
 
15
17
  ## Configuration
@@ -48,3 +50,7 @@ end
48
50
  ```bash
49
51
  extraspace crawl
50
52
  ```
53
+
54
+ ```bash
55
+ extrapsace crawl "https://www.extraspace.com/storage/facilities/us/california/los_angeles/900113/"
56
+ ```
@@ -21,7 +21,7 @@ module ExtraSpace
21
21
  command = argv.shift
22
22
 
23
23
  case command
24
- when 'crawl' then crawl
24
+ when 'crawl' then crawl(*argv)
25
25
  else
26
26
  warn("unsupported command=#{command.inspect}")
27
27
  exit(Code::ERROR)
@@ -30,8 +30,9 @@ module ExtraSpace
30
30
 
31
31
  private
32
32
 
33
- def crawl
34
- Crawl.run
33
+ # @param url [String] optional
34
+ def crawl(url = nil)
35
+ Crawl.run(url: url)
35
36
  exit(Code::OK)
36
37
  end
37
38
 
@@ -52,6 +53,11 @@ module ExtraSpace
52
53
 
53
54
  options.on('-h', '--help', 'help') { help(options) }
54
55
  options.on('-v', '--version', 'version') { version }
56
+
57
+ options.separator <<~COMMANDS
58
+ commands:
59
+ crawl [url]
60
+ COMMANDS
55
61
  end
56
62
  end
57
63
  end
@@ -9,19 +9,22 @@ module ExtraSpace
9
9
 
10
10
  # @param stdout [IO] optional
11
11
  # @param stderr [IO] optional
12
- # @param options [Hash] optional
13
- def initialize(stdout: $stdout, stderr: $stderr, options: {})
12
+ # @param url [String] optional
13
+ def initialize(stdout: $stdout, stderr: $stderr, url: nil)
14
14
  @stdout = stdout
15
15
  @stderr = stderr
16
- @options = options
16
+ @url = url
17
17
  end
18
18
 
19
19
  def run
20
- sitemap = Facility.sitemap
21
- @stdout.puts("count=#{sitemap.links.count}")
22
- @stdout.puts
23
-
24
- sitemap.links.each { |link| process(url: link.loc) }
20
+ if @url
21
+ process(url: @url)
22
+ else
23
+ sitemap = Facility.sitemap
24
+ @stdout.puts("count=#{sitemap.links.count}")
25
+ @stdout.puts
26
+ sitemap.links.each { |link| process(url: link.loc) }
27
+ end
25
28
  end
26
29
 
27
30
  def process(url:)
@@ -5,15 +5,6 @@ module ExtraSpace
5
5
  class Crawler
6
6
  HOST = 'https://www.extraspace.com'
7
7
 
8
- # Raised for unexpected HTTP responses.
9
- class FetchError < StandardError
10
- # @param url [String]
11
- # @param response [HTTP::Response]
12
- def initialize(url:, response:)
13
- super("url=#{url} status=#{response.status.inspect} body=#{String(response.body).inspect}")
14
- end
15
- end
16
-
17
8
  # @param url [String]
18
9
  # @raise [FetchError]
19
10
  # @return [Nokogiri::HTML::Document]
@@ -5,16 +5,31 @@ module ExtraSpace
5
5
  #
6
6
  # e.g. https://www.extraspace.com/storage/facilities/us/alabama/auburn/3264/
7
7
  class Facility
8
+ DEFAULT_EMAIL = 'info@extraspace.com'
9
+ DEFAULT_PHONE = '1-855-518-1443'
10
+
8
11
  SITEMAP_URL = 'https://www.extraspace.com/facility-sitemap.xml'
9
12
 
10
13
  # @attribute [rw] id
11
14
  # @return [String]
12
15
  attr_accessor :id
13
16
 
17
+ # @attribute [rw] url
18
+ # @return [String]
19
+ attr_accessor :url
20
+
14
21
  # @attribute [rw] name
15
22
  # @return [String]
16
23
  attr_accessor :name
17
24
 
25
+ # @attribute [rw] phone
26
+ # @return [String]
27
+ attr_accessor :phone
28
+
29
+ # @attribute [rw] email
30
+ # @return [String]
31
+ attr_accessor :email
32
+
18
33
  # @attribute [rw] address
19
34
  # @return [Address]
20
35
  attr_accessor :address
@@ -37,14 +52,15 @@ module ExtraSpace
37
52
  # @return [Facility]
38
53
  def self.fetch(url:)
39
54
  document = Crawler.html(url:)
40
- data = JSON.parse(document.at('#__NEXT_DATA__').text)
41
- parse(data:)
55
+ parse(url:, document:)
42
56
  end
43
57
 
44
- # @param data [Hash]
58
+ # @param url [String]
59
+ # @param document [Nokogiri::HTML::Document]
45
60
  #
46
61
  # @return [Facility]
47
- def self.parse(data:)
62
+ def self.parse(url:, document:)
63
+ data = parse_next_data(document: document)
48
64
  page_data = data.dig('props', 'pageProps', 'pageData', 'data')
49
65
  store_data = page_data.dig('facilityData', 'data', 'store')
50
66
  unit_classes = page_data.dig('unitClasses', 'data', 'unitClasses')
@@ -55,7 +71,16 @@ module ExtraSpace
55
71
  geocode = Geocode.parse(data: store_data['geocode'])
56
72
  prices = unit_classes.map { |price_data| Price.parse(data: price_data) }
57
73
 
58
- new(id:, name:, address:, geocode:, prices:)
74
+ new(id:, url:, name:, address:, geocode:, prices:)
75
+ end
76
+
77
+ # @param document [Nokogiri::HTML::Document]
78
+ #
79
+ # @raise [ParseError]
80
+ #
81
+ # @return [Hash]
82
+ def self.parse_next_data(document:)
83
+ JSON.parse(document.at('#__NEXT_DATA__').text)
59
84
  end
60
85
 
61
86
  def self.crawl
@@ -74,15 +99,21 @@ module ExtraSpace
74
99
  end
75
100
 
76
101
  # @param id [String]
102
+ # @param url [String]
77
103
  # @param name [String]
78
104
  # @param address [Address]
79
105
  # @param geocode [Geocode]
106
+ # @param phone [String]
107
+ # @param email [String]
80
108
  # @param prices [Array<Price>]
81
- def initialize(id:, name:, address:, geocode:, prices:)
109
+ def initialize(id:, url:, name:, address:, geocode:, phone: DEFAULT_PHONE, email: DEFAULT_EMAIL, prices: [])
82
110
  @id = id
111
+ @url = url
83
112
  @name = name
84
113
  @address = address
85
114
  @geocode = geocode
115
+ @phone = phone
116
+ @email = email
86
117
  @prices = prices
87
118
  end
88
119
 
@@ -90,8 +121,11 @@ module ExtraSpace
90
121
  def inspect
91
122
  props = [
92
123
  "id=#{@id.inspect}",
124
+ "url=#{@url.inspect}",
93
125
  "address=#{@address.inspect}",
94
126
  "geocode=#{@geocode.inspect}",
127
+ "phone=#{@phone.inspect}",
128
+ "email=#{@email.inspect}",
95
129
  "prices=#{@prices.inspect}"
96
130
  ]
97
131
  "#<#{self.class.name} #{props.join(' ')}>"
@@ -99,7 +133,7 @@ module ExtraSpace
99
133
 
100
134
  # @return [String]
101
135
  def text
102
- "#{@id} | #{@name} | #{@address.text} | #{@geocode.text}"
136
+ "#{@id} | #{@name} | #{@phone} | #{@email} | #{@address.text} | #{@geocode.text}"
103
137
  end
104
138
  end
105
139
  end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ExrtaSpace
4
+ # Raised for unexpected HTTP responses.
5
+ class FetchError < StandardError
6
+ # @param url [String]
7
+ # @param response [HTTP::Response]
8
+ def initialize(url:, response:)
9
+ super("url=#{url} status=#{response.status.inspect} body=#{String(response.body).inspect}")
10
+ end
11
+ end
12
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ExtraSpace
4
- VERSION = '0.4.0'
4
+ VERSION = '1.0.0'
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.4.0
4
+ version: 1.0.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: 2024-12-10 00:00:00.000000000 Z
11
+ date: 2024-12-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: http
@@ -102,6 +102,7 @@ files:
102
102
  - lib/extraspace/dimensions.rb
103
103
  - lib/extraspace/facility.rb
104
104
  - lib/extraspace/features.rb
105
+ - lib/extraspace/fetch_error.rb
105
106
  - lib/extraspace/geocode.rb
106
107
  - lib/extraspace/link.rb
107
108
  - lib/extraspace/price.rb