gull 1.0.0 → 1.0.1
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/CHANGELOG.md +4 -0
- data/README.md +42 -3
- data/lib/gull/alert.rb +29 -4
- data/lib/gull/client.rb +7 -0
- data/lib/gull/error.rb +3 -0
- data/lib/gull/geocode.rb +3 -0
- data/lib/gull/polygon.rb +7 -0
- data/lib/gull/version.rb +1 -1
- data/spec/alert_spec.rb +12 -0
- data/spec/client_spec.rb +12 -0
- data/spec/fixtures/features/missing_times.json +36 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7924e8188cee18f8b12e1a420d6ea68d1e0951c1c7c7e53f9d87b11b528d23f0
|
|
4
|
+
data.tar.gz: 517bb2c4a28d250207ddd22c51173b2be3392e0f1059b89110cc2af11a48af65
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 79015c143ade595b2d021986b48c0e7dbe78c83f2f42b787e9b7027914263631b3a6581a7491c607cef8037240465e056d8200f4eb9770016a9e88c83b022b71
|
|
7
|
+
data.tar.gz: 61d48682d8434559a7b14347765b3537cfc32c78f7c23e5ff137dcfe7d5571e3e98bbd8a8c79be198959351e3bd8913da7e0c02f82ad8310bf7a33ad7784d974
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
1.0.1 (03/07/2026) - Add YARD doc comments to all public classes and methods. Expand README with polygon, error handling, and client usage examples. Handle malformed JSON responses (raise `HttpError` instead of `JSON::ParserError`). Handle missing time fields in alert data (return `nil` instead of raising `TypeError`).
|
|
2
|
+
|
|
3
|
+
***
|
|
4
|
+
|
|
1
5
|
1.0.0 (03/07/2026) - Major rewrite: migrate from defunct `alerts.weather.gov` XML feed to `api.weather.gov/alerts` JSON API. Drop all runtime dependencies (`httpclient`, `nokogiri`) in favor of Ruby stdlib (`net/http`, `json`). Require Ruby >= 3.1. Add `area` option for state-based filtering. Replace Travis CI with GitHub Actions. Add RuboCop. **Breaking:** remove `url` option (use `area` instead), remove `Polygon#image_url`, remove `strict` option.
|
|
2
6
|
|
|
3
7
|
***
|
data/README.md
CHANGED
|
@@ -36,7 +36,7 @@ alert.effective_at
|
|
|
36
36
|
alert.expires_at
|
|
37
37
|
alert.published_at
|
|
38
38
|
alert.area
|
|
39
|
-
alert.polygon
|
|
39
|
+
alert.polygon # Gull::Polygon or nil
|
|
40
40
|
alert.geocode.fips6
|
|
41
41
|
alert.geocode.ugc
|
|
42
42
|
alert.urgency
|
|
@@ -51,8 +51,47 @@ To get alerts for a single state or territory, pass the area option:
|
|
|
51
51
|
alerts = Gull::Alert.fetch(area: 'OK')
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
### Polygons
|
|
55
|
+
|
|
56
|
+
Alerts with geographic boundaries include a `Polygon` object:
|
|
57
|
+
|
|
58
|
+
```ruby
|
|
59
|
+
alert.polygon.coordinates
|
|
60
|
+
# => [[34.57, -97.56], [34.77, -97.38], ...]
|
|
61
|
+
|
|
62
|
+
alert.polygon.to_s
|
|
63
|
+
# => "34.57,-97.56 34.77,-97.38 ..."
|
|
64
|
+
|
|
65
|
+
alert.polygon.to_wkt
|
|
66
|
+
# => "POLYGON((-97.56 34.57, -97.38 34.77, ...))"
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Error Handling
|
|
70
|
+
|
|
71
|
+
```ruby
|
|
72
|
+
begin
|
|
73
|
+
alerts = Gull::Alert.fetch
|
|
74
|
+
rescue Gull::TimeoutError => e
|
|
75
|
+
# request timed out
|
|
76
|
+
rescue Gull::HttpError => e
|
|
77
|
+
# non-success response or connection failure
|
|
78
|
+
e.original # wrapped exception, if any
|
|
79
|
+
end
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Advanced: Client
|
|
83
|
+
|
|
84
|
+
For direct access to unparseable features, use `Client`:
|
|
85
|
+
|
|
86
|
+
```ruby
|
|
87
|
+
client = Gull::Client.new(area: 'OK')
|
|
88
|
+
alerts = client.fetch
|
|
89
|
+
client.errors # features that could not be parsed
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Notes, Caveats
|
|
93
|
+
|
|
94
|
+
This library fetches active alerts from the [NWS API](https://api.weather.gov) (`api.weather.gov/alerts/active`), which returns GeoJSON. No authentication is required but the API does require a `User-Agent` header (set automatically by the gem). See the [NWS API docs](https://www.weather.gov/documentation/services-web-api) for more details.
|
|
56
95
|
|
|
57
96
|
The NWS will often cancel or update alerts before their expiration time. The API only returns currently active alerts.
|
|
58
97
|
|
data/lib/gull/alert.rb
CHANGED
|
@@ -1,7 +1,26 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Gull
|
|
4
|
+
# Represents a single NWS weather alert (warning, watch, or
|
|
5
|
+
# advisory). Use +Alert.fetch+ to retrieve active alerts from the
|
|
6
|
+
# NWS API.
|
|
4
7
|
class Alert
|
|
8
|
+
# @!attribute id [rw] NWS alert identifier
|
|
9
|
+
# @!attribute title [rw] Alert headline
|
|
10
|
+
# @!attribute summary [rw] Full alert description text
|
|
11
|
+
# @!attribute link [rw] Canonical URL for this alert
|
|
12
|
+
# @!attribute alert_type [rw] Event type (e.g. "Tornado Warning")
|
|
13
|
+
# @!attribute polygon [rw] Alert area as a Polygon, or nil
|
|
14
|
+
# @!attribute area [rw] Human-readable area description
|
|
15
|
+
# @!attribute effective_at [rw] Time the alert takes effect
|
|
16
|
+
# @!attribute expires_at [rw] Time the alert expires
|
|
17
|
+
# @!attribute updated_at [rw] Onset time, or sent time if absent
|
|
18
|
+
# @!attribute published_at [rw] Time the alert was sent
|
|
19
|
+
# @!attribute urgency [rw] Urgency level as a Symbol
|
|
20
|
+
# @!attribute severity [rw] Severity level as a Symbol
|
|
21
|
+
# @!attribute certainty [rw] Certainty level as a Symbol
|
|
22
|
+
# @!attribute geocode [rw] Geocode with UGC and FIPS codes
|
|
23
|
+
# @!attribute vtec [rw] VTEC string, or nil
|
|
5
24
|
attr_accessor :id, :title, :summary, :link, :alert_type, :polygon,
|
|
6
25
|
:area, :effective_at, :expires_at, :updated_at,
|
|
7
26
|
:published_at, :urgency, :severity, :certainty,
|
|
@@ -11,11 +30,13 @@ module Gull
|
|
|
11
30
|
self.geocode = Geocode.new
|
|
12
31
|
end
|
|
13
32
|
|
|
33
|
+
# Fetches active alerts from the NWS API.
|
|
14
34
|
def self.fetch(options = {})
|
|
15
35
|
client = Client.new(options)
|
|
16
36
|
client.fetch
|
|
17
37
|
end
|
|
18
38
|
|
|
39
|
+
# Populates this alert from a GeoJSON feature hash.
|
|
19
40
|
def parse(feature)
|
|
20
41
|
props = feature['properties']
|
|
21
42
|
parse_core_attributes(feature, props)
|
|
@@ -38,10 +59,14 @@ module Gull
|
|
|
38
59
|
end
|
|
39
60
|
|
|
40
61
|
def parse_times(props)
|
|
41
|
-
self.effective_at =
|
|
42
|
-
self.expires_at =
|
|
43
|
-
self.published_at =
|
|
44
|
-
self.updated_at =
|
|
62
|
+
self.effective_at = parse_time(props['effective'])
|
|
63
|
+
self.expires_at = parse_time(props['expires'])
|
|
64
|
+
self.published_at = parse_time(props['sent'])
|
|
65
|
+
self.updated_at = parse_time(props['onset'] || props['sent'])
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def parse_time(value)
|
|
69
|
+
value ? Time.parse(value) : nil
|
|
45
70
|
end
|
|
46
71
|
|
|
47
72
|
def parse_categories(props)
|
data/lib/gull/client.rb
CHANGED
|
@@ -1,21 +1,28 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Gull
|
|
4
|
+
# Low-level HTTP client for the NWS alerts API. Handles
|
|
5
|
+
# fetching, parsing, and error wrapping. Most callers should
|
|
6
|
+
# use +Alert.fetch+ instead.
|
|
4
7
|
class Client
|
|
5
8
|
URL = 'https://api.weather.gov/alerts/active'
|
|
6
9
|
USER_AGENT = "gull/#{VERSION} (Ruby #{RUBY_VERSION})".freeze
|
|
7
10
|
|
|
11
|
+
# Features that could not be parsed are collected here.
|
|
8
12
|
attr_accessor :errors
|
|
9
13
|
|
|
10
14
|
def initialize(options = {})
|
|
11
15
|
@options = options
|
|
12
16
|
end
|
|
13
17
|
|
|
18
|
+
# Fetches active alerts and returns an Array of Alert objects.
|
|
14
19
|
def fetch
|
|
15
20
|
self.errors = []
|
|
16
21
|
json = response
|
|
17
22
|
data = JSON.parse(json)
|
|
18
23
|
process(data['features'] || [])
|
|
24
|
+
rescue JSON::ParserError
|
|
25
|
+
raise HttpError, 'Unexpected response from NWS API'
|
|
19
26
|
end
|
|
20
27
|
|
|
21
28
|
private
|
data/lib/gull/error.rb
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
require 'English'
|
|
4
4
|
|
|
5
5
|
module Gull
|
|
6
|
+
# Raised when the NWS API returns a non-success response or the
|
|
7
|
+
# connection fails. Wraps the original exception, if any.
|
|
6
8
|
class HttpError < StandardError
|
|
7
9
|
attr_reader :original
|
|
8
10
|
|
|
@@ -12,6 +14,7 @@ module Gull
|
|
|
12
14
|
end
|
|
13
15
|
end
|
|
14
16
|
|
|
17
|
+
# Raised when the NWS API request times out.
|
|
15
18
|
class TimeoutError < HttpError
|
|
16
19
|
end
|
|
17
20
|
end
|
data/lib/gull/geocode.rb
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Gull
|
|
4
|
+
# Holds UGC zone codes and FIPS county codes for an alert area.
|
|
4
5
|
class Geocode
|
|
6
|
+
# @!attribute fips6 [rw] Space-separated FIPS 6 codes
|
|
7
|
+
# @!attribute ugc [rw] Space-separated UGC zone codes
|
|
5
8
|
attr_accessor :fips6, :ugc
|
|
6
9
|
end
|
|
7
10
|
end
|
data/lib/gull/polygon.rb
CHANGED
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Gull
|
|
4
|
+
# Represents the geographic boundary of an alert as a series of
|
|
5
|
+
# lat/lon coordinate pairs. Coordinates are stored in [lat, lon]
|
|
6
|
+
# order.
|
|
4
7
|
class Polygon
|
|
5
8
|
attr_accessor :coordinates
|
|
6
9
|
|
|
10
|
+
# Accepts GeoJSON coordinates ([lon, lat]) and stores them as
|
|
11
|
+
# [lat, lon].
|
|
7
12
|
def initialize(coords)
|
|
8
13
|
self.coordinates = coords.map { |point| [point[1], point[0]] }
|
|
9
14
|
end
|
|
10
15
|
|
|
16
|
+
# Returns coordinates as "lat,lon lat,lon ..." string.
|
|
11
17
|
def to_s
|
|
12
18
|
coordinates.map { |pair| pair.join(',') }.join(' ')
|
|
13
19
|
end
|
|
14
20
|
|
|
21
|
+
# Returns coordinates in Well-Known Text format.
|
|
15
22
|
def to_wkt
|
|
16
23
|
pairs = coordinates.map { |pair| "#{pair.last} #{pair.first}" }
|
|
17
24
|
.join(', ')
|
data/lib/gull/version.rb
CHANGED
data/spec/alert_spec.rb
CHANGED
|
@@ -154,6 +154,18 @@ describe Gull::Alert do
|
|
|
154
154
|
expect(alerts.size).to eq(0)
|
|
155
155
|
end
|
|
156
156
|
|
|
157
|
+
it 'should handle missing onset time' do
|
|
158
|
+
feature = load_feature('missing_times.json')
|
|
159
|
+
stub_alerts(wrap_features(feature))
|
|
160
|
+
|
|
161
|
+
alert = Gull::Alert.fetch.first
|
|
162
|
+
|
|
163
|
+
expect(alert.effective_at).to be_nil
|
|
164
|
+
expect(alert.expires_at).to eq Time.parse('2026-03-07T08:15:00-06:00')
|
|
165
|
+
expect(alert.updated_at).to eq Time.parse('2026-03-07T07:48:00-06:00')
|
|
166
|
+
expect(alert.published_at).to eq Time.parse('2026-03-07T07:48:00-06:00')
|
|
167
|
+
end
|
|
168
|
+
|
|
157
169
|
it 'should handle missing event' do
|
|
158
170
|
json = File.read 'spec/fixtures/missing_event.json'
|
|
159
171
|
stub_alerts(json)
|
data/spec/client_spec.rb
CHANGED
|
@@ -71,6 +71,18 @@ describe Gull::Client do
|
|
|
71
71
|
end
|
|
72
72
|
end
|
|
73
73
|
|
|
74
|
+
it 'should raise error on malformed JSON response' do
|
|
75
|
+
stub_request(:get, 'https://api.weather.gov/alerts/active')
|
|
76
|
+
.to_return(status: 200, body: '<html>Bad Gateway</html>',
|
|
77
|
+
headers: {})
|
|
78
|
+
|
|
79
|
+
client = Gull::Client.new
|
|
80
|
+
expect { client.fetch }
|
|
81
|
+
.to raise_error(Gull::HttpError, /Unexpected response/) do |e|
|
|
82
|
+
expect(e.original).to be_a(JSON::ParserError)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
74
86
|
it 'should filter by area' do
|
|
75
87
|
json = File.read 'spec/fixtures/alerts.json'
|
|
76
88
|
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "https://api.weather.gov/alerts/urn:oid:2.49.0.1.840.0.missing-times-test",
|
|
3
|
+
"type": "Feature",
|
|
4
|
+
"geometry": null,
|
|
5
|
+
"properties": {
|
|
6
|
+
"@id": "https://api.weather.gov/alerts/urn:oid:2.49.0.1.840.0.missing-times-test",
|
|
7
|
+
"@type": "wx:Alert",
|
|
8
|
+
"id": "urn:oid:2.49.0.1.840.0.missing-times-test",
|
|
9
|
+
"areaDesc": "Test County",
|
|
10
|
+
"geocode": {
|
|
11
|
+
"SAME": ["005001"],
|
|
12
|
+
"UGC": ["ARZ001"]
|
|
13
|
+
},
|
|
14
|
+
"affectedZones": [],
|
|
15
|
+
"references": [],
|
|
16
|
+
"sent": "2026-03-07T07:48:00-06:00",
|
|
17
|
+
"effective": null,
|
|
18
|
+
"onset": null,
|
|
19
|
+
"expires": "2026-03-07T08:15:00-06:00",
|
|
20
|
+
"ends": null,
|
|
21
|
+
"status": "Actual",
|
|
22
|
+
"messageType": "Alert",
|
|
23
|
+
"category": "Met",
|
|
24
|
+
"severity": "Moderate",
|
|
25
|
+
"certainty": "Likely",
|
|
26
|
+
"urgency": "Expected",
|
|
27
|
+
"event": "Special Weather Statement",
|
|
28
|
+
"sender": "w-nws.webmaster@noaa.gov",
|
|
29
|
+
"senderName": "NWS Test",
|
|
30
|
+
"headline": "Test alert with missing onset",
|
|
31
|
+
"description": "Test description.",
|
|
32
|
+
"instruction": null,
|
|
33
|
+
"response": "Monitor",
|
|
34
|
+
"parameters": {}
|
|
35
|
+
}
|
|
36
|
+
}
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: gull
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Seth Deckard
|
|
@@ -157,6 +157,7 @@ files:
|
|
|
157
157
|
- spec/fixtures/features/empty_geocode.json
|
|
158
158
|
- spec/fixtures/features/flood_advisory.json
|
|
159
159
|
- spec/fixtures/features/flood_warning.json
|
|
160
|
+
- spec/fixtures/features/missing_times.json
|
|
160
161
|
- spec/fixtures/features/multipolygon.json
|
|
161
162
|
- spec/fixtures/features/null_geometry.json
|
|
162
163
|
- spec/fixtures/features/polygon_no_vtec.json
|