weather_gov 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/.github/dependabot.yml +8 -0
  3. data/.github/workflows/rspec.yml +23 -0
  4. data/.github/workflows/rubocop.yml +23 -0
  5. data/.gitignore +13 -0
  6. data/.rspec +3 -0
  7. data/.rubocop.yml +29 -0
  8. data/Gemfile +6 -0
  9. data/Gemfile.lock +84 -0
  10. data/LICENSE.txt +21 -0
  11. data/README.md +40 -0
  12. data/Rakefile +8 -0
  13. data/bin/console +15 -0
  14. data/bin/setup +8 -0
  15. data/lib/weather_gov.rb +13 -0
  16. data/lib/weather_gov/address.rb +27 -0
  17. data/lib/weather_gov/alert.rb +45 -0
  18. data/lib/weather_gov/alert_collection.rb +28 -0
  19. data/lib/weather_gov/api.rb +93 -0
  20. data/lib/weather_gov/client.rb +115 -0
  21. data/lib/weather_gov/feature.rb +40 -0
  22. data/lib/weather_gov/feature_collection.rb +20 -0
  23. data/lib/weather_gov/forecast.rb +27 -0
  24. data/lib/weather_gov/forecast_period.rb +63 -0
  25. data/lib/weather_gov/gridpoint.rb +75 -0
  26. data/lib/weather_gov/identifier.rb +47 -0
  27. data/lib/weather_gov/identifier/alert.rb +13 -0
  28. data/lib/weather_gov/identifier/county_zone.rb +13 -0
  29. data/lib/weather_gov/identifier/fire_zone.rb +13 -0
  30. data/lib/weather_gov/identifier/forecast_zone.rb +13 -0
  31. data/lib/weather_gov/identifier/gridpoint.rb +29 -0
  32. data/lib/weather_gov/identifier/office.rb +13 -0
  33. data/lib/weather_gov/identifier/point.rb +21 -0
  34. data/lib/weather_gov/identifier/problem.rb +13 -0
  35. data/lib/weather_gov/identifier/station.rb +13 -0
  36. data/lib/weather_gov/observation_station.rb +27 -0
  37. data/lib/weather_gov/observation_station_collection.rb +20 -0
  38. data/lib/weather_gov/office.rb +68 -0
  39. data/lib/weather_gov/point.rb +81 -0
  40. data/lib/weather_gov/product.rb +29 -0
  41. data/lib/weather_gov/product_list.rb +17 -0
  42. data/lib/weather_gov/relative_location.rb +23 -0
  43. data/lib/weather_gov/valid_duration_value.rb +26 -0
  44. data/lib/weather_gov/valid_time.rb +48 -0
  45. data/lib/weather_gov/version.rb +5 -0
  46. data/lib/weather_gov/zone.rb +33 -0
  47. data/weather_gov.gemspec +39 -0
  48. metadata +190 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 3695a2a5f281cfa4a459a22001806ada54204d5de7bfb0f175a7bed7cd36808a
4
+ data.tar.gz: 7daddbe58419911e0ecb0bdc79796fc4ef728773ec8aca5557bd06e6f059c1f1
5
+ SHA512:
6
+ metadata.gz: a45fc78d81da76809428d727d27e26dfcd9336f7dd132fd7fb7a5d2f0b0da11aa8b50f614fcbc668ea96aa6d2aa585d48d1829dc82868ab9b4bcbea93c92e82e
7
+ data.tar.gz: 77220225658632bff07594a49b65a790f1082f5cff7f931ff8eeed397f9aba0cc2226eb7df1ca12c4dd3e319123796cca4114a82d861d2dce74a81e4185bd202
@@ -0,0 +1,8 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: bundler
4
+ directory: "/"
5
+ schedule:
6
+ interval: daily
7
+ time: "13:00"
8
+ open-pull-requests-limit: 10
@@ -0,0 +1,23 @@
1
+ name: rspec
2
+
3
+ on:
4
+ push:
5
+ branches: [ main ]
6
+ pull_request:
7
+ branches: [ main ]
8
+
9
+ jobs:
10
+ rspec:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ ruby-version: ['2.7.2']
15
+ steps:
16
+ - uses: actions/checkout@v2
17
+ - name: Set up Ruby
18
+ uses: ruby/setup-ruby@v1
19
+ with:
20
+ ruby-version: ${{ matrix.ruby-version }}
21
+ bundler-cache: true
22
+ - name: Run rspec tests
23
+ run: bundle exec rspec spec
@@ -0,0 +1,23 @@
1
+ name: rubocop
2
+
3
+ on:
4
+ push:
5
+ branches: [ main ]
6
+ pull_request:
7
+ branches: [ main ]
8
+
9
+ jobs:
10
+ rubocop:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ ruby-version: ['2.7.2']
15
+ steps:
16
+ - uses: actions/checkout@v2
17
+ - name: Set up Ruby
18
+ uses: ruby/setup-ruby@v1
19
+ with:
20
+ ruby-version: ${{ matrix.ruby-version }}
21
+ bundler-cache: true
22
+ - name: Run rubocop style check
23
+ run: bundle exec rubocop
data/.gitignore ADDED
@@ -0,0 +1,13 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ /vendor/
10
+ /.idea/
11
+
12
+ # rspec failure tracking
13
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,29 @@
1
+ require:
2
+ - rubocop-rspec
3
+
4
+ AllCops:
5
+ Include:
6
+ - "bin/**/*.rb"
7
+ - "lib/**/*.rb"
8
+ - "spec/**/*.rb"
9
+ - "Gemfile"
10
+ - "*.gemspec"
11
+ NewCops: enable
12
+
13
+ Layout/HashAlignment:
14
+ EnforcedColonStyle: key
15
+
16
+ Style/StringLiterals:
17
+ EnforcedStyle: double_quotes
18
+
19
+ Style/Documentation:
20
+ Enabled: false
21
+
22
+ Style/TrailingCommaInHashLiteral:
23
+ EnforcedStyleForMultiline: consistent_comma
24
+
25
+ Style/TrailingCommaInArrayLiteral:
26
+ EnforcedStyleForMultiline: consistent_comma
27
+
28
+ Style/WordArray:
29
+ EnforcedStyle: brackets
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in weather_gov.gemspec
6
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,84 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ weather_gov (0.1.0)
5
+ activesupport (~> 6.1)
6
+ httparty (~> 0.18.1)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ activesupport (6.1.4)
12
+ concurrent-ruby (~> 1.0, >= 1.0.2)
13
+ i18n (>= 1.6, < 2)
14
+ minitest (>= 5.1)
15
+ tzinfo (~> 2.0)
16
+ zeitwerk (~> 2.3)
17
+ ast (2.4.2)
18
+ concurrent-ruby (1.1.9)
19
+ diff-lcs (1.4.4)
20
+ httparty (0.18.1)
21
+ mime-types (~> 3.0)
22
+ multi_xml (>= 0.5.2)
23
+ i18n (1.8.10)
24
+ concurrent-ruby (~> 1.0)
25
+ mime-types (3.3.1)
26
+ mime-types-data (~> 3.2015)
27
+ mime-types-data (3.2021.0225)
28
+ minitest (5.14.4)
29
+ multi_xml (0.6.0)
30
+ parallel (1.20.1)
31
+ parser (3.0.1.1)
32
+ ast (~> 2.4.1)
33
+ rainbow (3.0.0)
34
+ rake (13.0.3)
35
+ regexp_parser (2.1.1)
36
+ rexml (3.2.5)
37
+ rspec (3.10.0)
38
+ rspec-core (~> 3.10.0)
39
+ rspec-expectations (~> 3.10.0)
40
+ rspec-mocks (~> 3.10.0)
41
+ rspec-core (3.10.1)
42
+ rspec-support (~> 3.10.0)
43
+ rspec-expectations (3.10.1)
44
+ diff-lcs (>= 1.2.0, < 2.0)
45
+ rspec-support (~> 3.10.0)
46
+ rspec-mocks (3.10.2)
47
+ diff-lcs (>= 1.2.0, < 2.0)
48
+ rspec-support (~> 3.10.0)
49
+ rspec-support (3.10.2)
50
+ rubocop (1.18.2)
51
+ parallel (~> 1.10)
52
+ parser (>= 3.0.0.0)
53
+ rainbow (>= 2.2.2, < 4.0)
54
+ regexp_parser (>= 1.8, < 3.0)
55
+ rexml
56
+ rubocop-ast (>= 1.7.0, < 2.0)
57
+ ruby-progressbar (~> 1.7)
58
+ unicode-display_width (>= 1.4.0, < 3.0)
59
+ rubocop-ast (1.7.0)
60
+ parser (>= 3.0.1.1)
61
+ rubocop-rake (0.6.0)
62
+ rubocop (~> 1.0)
63
+ rubocop-rspec (2.4.0)
64
+ rubocop (~> 1.0)
65
+ rubocop-ast (>= 1.1.0)
66
+ ruby-progressbar (1.11.0)
67
+ tzinfo (2.0.4)
68
+ concurrent-ruby (~> 1.0)
69
+ unicode-display_width (2.0.0)
70
+ zeitwerk (2.4.2)
71
+
72
+ PLATFORMS
73
+ ruby
74
+
75
+ DEPENDENCIES
76
+ rake (~> 13.0)
77
+ rspec (~> 3.0)
78
+ rubocop (~> 1.18)
79
+ rubocop-rake (~> 0.6.0)
80
+ rubocop-rspec (~> 2.4)
81
+ weather_gov!
82
+
83
+ BUNDLED WITH
84
+ 2.1.4
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 Jeremy Cole
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,40 @@
1
+ # WeatherGov
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/weather_gov`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'weather_gov'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle install
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install weather_gov
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
29
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
+
31
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/weather_gov.
36
+
37
+
38
+ ## License
39
+
40
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "weather_gov"
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
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require "irb"
15
+ 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,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "weather_gov/version"
4
+
5
+ module WeatherGov
6
+ class Error < StandardError; end
7
+
8
+ API_BASE_URI = "https://api.weather.gov"
9
+ end
10
+
11
+ require "weather_gov/identifier"
12
+ require "weather_gov/api"
13
+ require "weather_gov/client"
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WeatherGov
4
+ class Address
5
+ attr_reader :data
6
+
7
+ def initialize(data)
8
+ @data = data
9
+ end
10
+
11
+ def street_address
12
+ data.fetch("streetAddress")
13
+ end
14
+
15
+ def address_locality
16
+ data.fetch("addressLocality")
17
+ end
18
+
19
+ def address_region
20
+ data.fetch("addressRegion")
21
+ end
22
+
23
+ def postal_code
24
+ data.fetch("postalCode")
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WeatherGov
4
+ class Alert < Feature
5
+ SUMMARY_KEYS = [
6
+ "sent",
7
+ "effective",
8
+ "onset",
9
+ "expires",
10
+ "ends",
11
+ "status",
12
+ "severity",
13
+ "certainty",
14
+ "urgency",
15
+ "event",
16
+ "headline",
17
+ "description",
18
+ "instruction",
19
+ ].freeze
20
+
21
+ def sent_time
22
+ Time.parse(properties.fetch("sent"))
23
+ end
24
+
25
+ def effective_time
26
+ Time.parse(properties.fetch("effective"))
27
+ end
28
+
29
+ def expires_time
30
+ Time.parse(properties.fetch("expires"))
31
+ end
32
+
33
+ def active_time_range
34
+ effective_time...expires_time
35
+ end
36
+
37
+ def active?
38
+ active_time_range.include?(Time.now)
39
+ end
40
+
41
+ def summary_hash
42
+ properties.slice(*SUMMARY_KEYS)
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "weather_gov/feature_collection"
4
+ require "weather_gov/alert"
5
+
6
+ module WeatherGov
7
+ class AlertCollection < FeatureCollection
8
+ def self.feature_class
9
+ Alert
10
+ end
11
+
12
+ def update_time
13
+ @update_time ||= Time.parse(data.fetch("updated"))
14
+ end
15
+
16
+ def title
17
+ data.fetch("title")
18
+ end
19
+
20
+ def alerts
21
+ features
22
+ end
23
+
24
+ def summary
25
+ features.map(&:summary_hash)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "httparty"
4
+
5
+ module WeatherGov
6
+ class API
7
+ class RequestError < RuntimeError; end
8
+
9
+ include HTTParty
10
+ base_uri API_BASE_URI
11
+ format :json
12
+
13
+ def initialize(user_agent:)
14
+ @headers = {
15
+ "User-Agent" => user_agent,
16
+ }
17
+ end
18
+
19
+ def format_coord(value)
20
+ format("%.2f", value).sub(/0+$/, "")
21
+ end
22
+
23
+ def format_coords(lat, lon)
24
+ "#{format_coord(lat)},#{format_coord(lon)}"
25
+ end
26
+
27
+ def get(uri: nil, path: nil)
28
+ response = self.class.get(uri || path)
29
+
30
+ unless response.ok?
31
+ if response.parsed_response
32
+ error_type_id = Identifier::Problem.new(response.parsed_response.fetch("type")).id
33
+ error_detail = response.parsed_response.fetch("detail", response.code)
34
+ raise RequestError, "#{error_type_id}: #{error_detail}"
35
+ end
36
+
37
+ raise RequestError, "Error: Code #{response.code}"
38
+ end
39
+
40
+ response
41
+ end
42
+
43
+ def station(id:)
44
+ get(path: "/stations/#{id}")
45
+ end
46
+
47
+ def office(id:)
48
+ get(path: "/offices/#{id}")
49
+ end
50
+
51
+ def point(lat:, lon:)
52
+ get(path: "/points/#{format_coords(lat, lon)}")
53
+ end
54
+
55
+ def gridpoint(office_id:, grid_x:, grid_y:)
56
+ get(path: "/gridpoints/#{office_id}/#{grid_x},#{grid_y}")
57
+ end
58
+
59
+ def zone(type:, id:)
60
+ get(path: "/zones/#{type}/#{id}")
61
+ end
62
+
63
+ def product_locations(type: nil)
64
+ return get(path: "/products/types/#{type}/locations") if type
65
+
66
+ get(path: "/products/locations")
67
+ end
68
+
69
+ def product_types(location: nil)
70
+ return get(path: "/products/locations/#{location}/types") if location
71
+
72
+ get(path: "/products/types")
73
+ end
74
+
75
+ def products(type: nil, location: nil)
76
+ return get(path: "/products/types/#{type}/locations/#{location}") if type && location
77
+
78
+ get(path: "/products")
79
+ end
80
+
81
+ def product(id:)
82
+ get(path: "/products/#{id}")
83
+ end
84
+
85
+ def alerts_active(zone: nil, area: nil, region: nil)
86
+ return get(path: "/alerts/active/zone/#{zone}") if zone
87
+ return get(path: "/alerts/active/area/#{area}") if area
88
+ return get(path: "/alerts/active/region/#{region}") if region
89
+
90
+ get(path: "/alerts/active")
91
+ end
92
+ end
93
+ end