extraspace 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 4d0f31dad347795d2fb0a8cbd14b3c6452fc8c8dfea0f9b66639bc4c1bf1c433
4
+ data.tar.gz: 35cedca68983cab7e29c76abc7c8c83d617dad6c2ef881bbd13ecced436fd89d
5
+ SHA512:
6
+ metadata.gz: e56ad3839e7a9f5a11b8298e7977c518590aade51b78a7248c201b03027b35b697ef792e311e76b797d1d6aa51d00e996d753651ff66e2fb5e00b74d2c8ab86c
7
+ data.tar.gz: 5bb15f6c216c19d628f2179dd4dafe714804ee50d9142d72039d7fba0e68a5ad7ceaaa29e54259868a3ad0f53be9e0d9c677ce207ced6a5730471a2ac0439f13
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gemspec
6
+
7
+ gem 'rake'
8
+ gem 'rspec'
9
+ gem 'rspec_junit_formatter'
10
+ gem 'rubocop'
11
+ gem 'rubocop-rake'
12
+ gem 'rubocop-rspec'
13
+ gem 'vcr'
14
+ gem 'webmock'
15
+ gem 'yard'
data/README.md ADDED
@@ -0,0 +1,38 @@
1
+ # ExtraSpace
2
+
3
+ [![LICENSE](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/ksylvest/extraspace/blob/main/LICENSE)
4
+ [![RubyGems](https://img.shields.io/gem/v/extraspace)](https://rubygems.org/gems/extraspace)
5
+ [![GitHub](https://img.shields.io/badge/github-repo-blue.svg)](https://github.com/ksylvest/extraspace)
6
+ [![Yard](https://img.shields.io/badge/docs-site-blue.svg)](https://extraspace.ksylvest.com)
7
+ [![CircleCI](https://img.shields.io/circleci/build/github/ksylvest/extraspace)](https://circleci.com/gh/ksylvest/extraspace)
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ gem install extrapsace
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ ```ruby
18
+ require 'extraspace'
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)"
36
+ puts
37
+ end
38
+ ```
data/bin/console ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'extraspace'
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ require 'irb'
11
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ExtraSpace
4
+ # e.g. https://www.extraspace.com/storage/facilities/us/alabama/auburn/3264/
5
+ class Address
6
+ # @attribute [rw] line1
7
+ # @return [String]
8
+ attr_accessor :line1
9
+
10
+ # @attribute [rw] line2
11
+ # @return [String]
12
+ attr_accessor :line2
13
+
14
+ # @attribute [rw] city
15
+ # @return [String]
16
+ attr_accessor :city
17
+
18
+ # @attribute [rw] state
19
+ # @return [String]
20
+ attr_accessor :state
21
+
22
+ # @attribute [rw] zip
23
+ # @return [String]
24
+ attr_accessor :zip
25
+
26
+ # @param line1 [String]
27
+ # @param line2 [String]
28
+ # @param city [String]
29
+ # @param state_abbreviation [String]
30
+ # @param state_name [String]
31
+ # @param postal_code [String]
32
+ def initialize(line1:, line2:, city:, state:, zip:)
33
+ @line1 = line1
34
+ @line2 = line2
35
+ @city = city
36
+ @state = state
37
+ @zip = zip
38
+ end
39
+
40
+ def inspect
41
+ props = [
42
+ "line1=#{@line1.inspect}",
43
+ "line2=#{@line2.inspect}",
44
+ "city=#{@city.inspect}",
45
+ "state=#{@state.inspect}",
46
+ "zip=#{@zip.inspect}"
47
+ ]
48
+ "#<#{self.class.name} #{props.join(' ')}>"
49
+ end
50
+
51
+ # @param data [Hash]
52
+ #
53
+ # @return [Address]
54
+ def self.parse(data:)
55
+ new(
56
+ line1: data['line1'],
57
+ line2: data['line2'],
58
+ city: data['city'],
59
+ state: data['stateName'],
60
+ zip: data['postalCode']
61
+ )
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ExtraSpace
4
+ # e.g. https://www.extraspace.com/storage/facilities/us/alabama/auburn/3264/
5
+ class Availability
6
+ # @attribute [rw] available
7
+ # @return [String]
8
+ attr_accessor :available
9
+
10
+ # @param uid [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 [Address]
26
+ def self.parse(data:)
27
+ new(available: data['available'])
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ExtraSpace
4
+ # e.g. https://www.extraspace.com/storage/facilities/us/alabama/auburn/3264/
5
+ class Dimensions
6
+ # @attribute [rw] depth
7
+ # @return [Integer]
8
+ attr_accessor :depth
9
+
10
+ # @attribute [rw] width
11
+ # @return [Integer]
12
+ attr_accessor :width
13
+
14
+ # @attribute [rw] sqft
15
+ # @return [Integer]
16
+ attr_accessor :sqft
17
+
18
+ # @attribute [rw] display
19
+ # @return [String]
20
+ attr_accessor :display
21
+
22
+ # @param uid [String]
23
+ def initialize(depth:, width:, sqft:, display:)
24
+ @depth = depth
25
+ @width = width
26
+ @sqft = sqft
27
+ @display = display
28
+ end
29
+
30
+ # @return [String]
31
+ def inspect
32
+ props = [
33
+ "depth=#{@depth.inspect}",
34
+ "width=#{@width.inspect}",
35
+ "sqft=#{@sqft.inspect}",
36
+ "display=#{@display.inspect}"
37
+ ]
38
+ "#<#{self.class.name} #{props.join(' ')}>"
39
+ end
40
+
41
+ # @param data [Hash]
42
+ #
43
+ # @return [Address]
44
+ def self.parse(data:)
45
+ new(depth: data['depth'], width: data['width'], sqft: data['squareFoot'], display: data['display'])
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ExtraSpace
4
+ # e.g. https://www.extraspace.com/storage/facilities/us/alabama/auburn/3264/
5
+ class Facility
6
+ # @attribute [rw] address
7
+ # @return [Address]
8
+ attr_accessor :address
9
+
10
+ # @attribute [rw] geocode
11
+ # @return [Geocode]
12
+ attr_accessor :geocode
13
+
14
+ # @attribute [rw] prices
15
+ # @return [Array<Price>]
16
+ attr_accessor :prices
17
+
18
+ # @param address [Address]
19
+ # @param geocode [Geocode]
20
+ # @param prices [Array<Price>]
21
+ def initialize(address:, geocode:, prices:)
22
+ @address = address
23
+ @geocode = geocode
24
+ @prices = prices
25
+ end
26
+
27
+ def inspect
28
+ props = [
29
+ "address=#{@address.inspect}",
30
+ "geocode=#{@geocode.inspect}",
31
+ "prices=#{@prices.inspect}"
32
+ ]
33
+ "#<#{self.class.name} #{props.join(' ')}>"
34
+ end
35
+
36
+ # @param url [String]
37
+ #
38
+ # @return [Facility]
39
+ def self.fetch(url:)
40
+ response = HTTP.get(url)
41
+ document = Nokogiri::HTML(String(response.body))
42
+ data = JSON.parse(document.at('#__NEXT_DATA__').text)
43
+
44
+ parse(data: data)
45
+ end
46
+
47
+ # @param data [Hash]
48
+ #
49
+ # @return [Facility]
50
+ def self.parse(data:)
51
+ page_data = data.dig('props', 'pageProps', 'pageData', 'data')
52
+ facility_data = page_data.dig('facilityData', 'data')
53
+ unit_classes = page_data.dig('unitClasses', 'data', 'unitClasses')
54
+
55
+ address = Address.parse(data: facility_data['store']['address'])
56
+ geocode = Geocode.parse(data: facility_data['store']['geocode'])
57
+ prices = unit_classes.map { |price_data| Price.parse(data: price_data) }
58
+
59
+ new(address:, geocode:, prices:)
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ExtraSpace
4
+ # e.g. https://www.extraspace.com/storage/facilities/us/alabama/auburn/3264/
5
+ class Geocode
6
+ # @attribute [rw] latitude
7
+ # @return [Float]
8
+ attr_accessor :latitude
9
+
10
+ # @attribute [rw] longitude
11
+ # @return [Float]
12
+ attr_accessor :longitude
13
+
14
+ # @param latitude [Float]
15
+ # @param longitude [Float]
16
+ def initialize(latitude:, longitude:)
17
+ @latitude = latitude
18
+ @longitude = longitude
19
+ end
20
+
21
+ # @return [String]
22
+ def inspect
23
+ props = [
24
+ "latitude=#{@latitude.inspect}",
25
+ "longitude=#{@longitude.inspect}"
26
+ ]
27
+ "#<#{self.class.name} #{props.join(' ')}>"
28
+ end
29
+
30
+ # @param data [Hash]
31
+ #
32
+ # @return [Geocode]
33
+ def self.parse(data:)
34
+ new(
35
+ latitude: data['latitude'],
36
+ longitude: data['longitude']
37
+ )
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ExtraSpace
4
+ # e.g. https://www.extraspace.com/storage/facilities/us/alabama/auburn/3264/
5
+ class Price
6
+ # @attribute [rw] uid
7
+ # @return [String]
8
+ attr_accessor :uid
9
+
10
+ # @attribute [rw] availability
11
+ # @return [Availability]
12
+ attr_accessor :availability
13
+
14
+ # @attribute [rw] dimensions
15
+ # @return [Dimensions]
16
+ attr_accessor :dimensions
17
+
18
+ # @attribute [rw] rates
19
+ # @return [Rates]
20
+ attr_accessor :rates
21
+
22
+ # @param uid [String]
23
+ # @param availability [Availability]
24
+ # @param dimensions [Dimensions]
25
+ # @param rates [Rates]
26
+ def initialize(uid:, availability:, dimensions:, rates:)
27
+ @uid = uid
28
+ @availability = availability
29
+ @dimensions = dimensions
30
+ @rates = rates
31
+ end
32
+
33
+ # @return [String]
34
+ def inspect
35
+ props = [
36
+ "uid=#{@uid.inspect}",
37
+ "availability=#{@availability.inspect}",
38
+ "dimensions=#{@dimensions.inspect}",
39
+ "rates=#{@rates.inspect}"
40
+ ]
41
+ "#<#{self.class.name} #{props.join(' ')}>"
42
+ end
43
+
44
+ # @param data [Hash]
45
+ #
46
+ # @return [Address]
47
+ def self.parse(data:)
48
+ availability = Availability.parse(data: data['availability'])
49
+ dimensions = Dimensions.parse(data: data['dimensions'])
50
+ rates = Rates.parse(data: data['rates'])
51
+ new(
52
+ uid: data['uid'],
53
+ availability: availability,
54
+ dimensions: dimensions,
55
+ rates: rates
56
+ )
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ExtraSpace
4
+ # e.g. https://www.extraspace.com/storage/facilities/us/alabama/auburn/3264/
5
+ class Rates
6
+ # @attribute [rw] nsc
7
+ # @return [Integer]
8
+ attr_accessor :nsc
9
+
10
+ # @attribute [rw] street
11
+ # @return [Integer]
12
+ attr_accessor :street
13
+
14
+ # @attribute [rw] web
15
+ # @return [Integer]
16
+ attr_accessor :web
17
+
18
+ # @param uid [String]
19
+ def initialize(nsc:, street:, web:)
20
+ @nsc = nsc
21
+ @street = street
22
+ @web = web
23
+ end
24
+
25
+ # @return [String]
26
+ def inspect
27
+ props = [
28
+ "nsc=#{@nsc.inspect}",
29
+ "street=#{@street.inspect}",
30
+ "web=#{@web.inspect}"
31
+ ]
32
+ "#<#{self.class.name} #{props.join(' ')}>"
33
+ end
34
+
35
+ # @param data [Hash]
36
+ #
37
+ # @return [Address]
38
+ def self.parse(data:)
39
+ new(
40
+ nsc: data['nsc'],
41
+ street: data['street'],
42
+ web: data['web']
43
+ )
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ExtraSpace
4
+ VERSION = '0.1.0'
5
+ end
data/lib/extraspace.rb ADDED
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'http'
4
+ require 'nokogiri'
5
+ require 'zeitwerk'
6
+
7
+ loader = Zeitwerk::Loader.for_gem
8
+ loader.inflector.inflect 'extraspace' => 'ExtraSpace'
9
+ loader.setup
10
+
11
+ module ExtraSpace
12
+ class Error < StandardError; end
13
+ end
metadata ADDED
@@ -0,0 +1,116 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: extraspace
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Kevin Sylvestre
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2024-11-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: http
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: json
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: nokogiri
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: zeitwerk
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Uses HTTP.rb to scrape extraspace.com.
70
+ email:
71
+ - kevin@ksylvest.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - Gemfile
77
+ - README.md
78
+ - bin/console
79
+ - bin/setup
80
+ - lib/extraspace.rb
81
+ - lib/extraspace/address.rb
82
+ - lib/extraspace/availability.rb
83
+ - lib/extraspace/dimensions.rb
84
+ - lib/extraspace/facility.rb
85
+ - lib/extraspace/geocode.rb
86
+ - lib/extraspace/price.rb
87
+ - lib/extraspace/rates.rb
88
+ - lib/extraspace/version.rb
89
+ homepage: https://github.com/ksylvest/extraspace
90
+ licenses:
91
+ - MIT
92
+ metadata:
93
+ rubygems_mfa_required: 'true'
94
+ homepage_uri: https://github.com/ksylvest/extraspace
95
+ source_code_uri: https://github.com/ksylvest/extraspace
96
+ changelog_uri: https://github.com/ksylvest/extraspace
97
+ post_install_message:
98
+ rdoc_options: []
99
+ require_paths:
100
+ - lib
101
+ required_ruby_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: 3.2.0
106
+ required_rubygems_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ requirements: []
112
+ rubygems_version: 3.5.22
113
+ signing_key:
114
+ specification_version: 4
115
+ summary: A crawler for ExtraSpace.
116
+ test_files: []