Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: CI
on:
push:
branches: [master]
pull_request:
branches: [master]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
ruby-version: ['3.1', '3.2', '3.3', '3.4']
steps:
- uses: actions/checkout@v4
- name: Set up Ruby ${{ matrix.ruby-version }}
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby-version }}
bundler-cache: true
- name: Run tests
run: bundle exec rspec
- name: Run linter
run: bundle exec rubocop
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
/coverage
/pkg
/tmp
.Gemfile.lock
Gemfile.lock
.ruby-version
3 changes: 0 additions & 3 deletions .hound.yml

This file was deleted.

35 changes: 34 additions & 1 deletion .rubocop.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,38 @@
AllCops:
TargetRubyVersion: 3.1
NewCops: enable
SuggestExtensions: false
Exclude:
- 'spec/spec_helper.rb'
- 'gull.gemspec'
- 'vendor/**/*'
Metrics/BlockLength:
Exclude:
- 'spec/**/*'
Metrics/MethodLength:
Max: 20
Exclude:
- 'spec/**/*'
Metrics/AbcSize:
Max: 35
Exclude:
- 'spec/**/*'
Metrics/ClassLength:
Enabled: false
Metrics/CyclomaticComplexity:
Enabled: false
Metrics/PerceivedComplexity:
Enabled: false
Layout/LineLength:
Max: 80
Exclude:
- 'spec/**/*'
Style/StringLiterals:
EnforcedStyle: single_quotes
EnforcedStyle: single_quotes
Style/StringConcatenation:
Exclude:
- 'spec/**/*'
Style/Documentation:
Enabled: false
Naming/PredicateMethod:
Enabled: false
8 changes: 0 additions & 8 deletions .travis.yml

This file was deleted.

27 changes: 27 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# AGENTS.md

## Development Workflow

- Git workflow: GitHub flow
- Small (but logical) commits that can each be deployed independently
- Each commit must not break CI
- Prefer incremental changes over large feature branches

### Commit Messages

**Subject:** Max 50 chars, capitalized, no period, imperative mood ("Add" not "Added")

**Body:** Wrap at 72 chars, explain what/why not how, blank line after subject

**Leading verbs:** Add, Remove, Fix, Upgrade, Refactor, Reformat, Document, Reword

## Development Standards

- README updated with API changes
- **Tests must cover all behavior** - check with `coverage/index.html` after running specs
- RuboCop enforces 80-char line limit and other style

## Deployment

- Kicking off PR: `ghprcw`
- Never deploy anything
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
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.

***

0.4.0 (07/26/2016) - Merged pull request #2 from [schrockwell](https://github.com/schrockwell), which adds `to_wkt` to `Polygon` which formats the polygon points as Well-Known Text (WKT). Removed `centroid` method, use new `to_wkt` method with an external geospatial library instead.

***
Expand Down Expand Up @@ -43,7 +47,7 @@
***

0.2.0 (10/02/2014) - Introduced Polygon type.

***

0.1.1 (10/01/2014) - Refactored and simplified alert processing.
Expand Down
1 change: 1 addition & 0 deletions CLAUDE.md
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

source 'https://rubygems.org'

# Specify your gem's dependencies in gull.gemspec
Expand Down
2 changes: 2 additions & 0 deletions Guardfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

guard :rspec, cmd: 'bundle exec rspec' do
watch(%r{^spec/.+_spec\.rb$})
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
Expand Down
2 changes: 1 addition & 1 deletion LICENSE.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright (c) 2014 Seth Deckard
Copyright (c) 2014-2026 Seth Deckard

MIT License

Expand Down
43 changes: 10 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
[![Gem Version](https://badge.fury.io/rb/gull.svg)](http://badge.fury.io/rb/gull)
[![Build Status](https://travis-ci.org/sethdeckard/gull.svg?branch=master)](https://travis-ci.org/sethdeckard/gull)
[![Coverage Status](https://coveralls.io/repos/sethdeckard/gull/badge.svg?branch=master)](https://coveralls.io/r/sethdeckard/gull)
[![Code Climate](https://codeclimate.com/github/sethdeckard/gull/badges/gpa.svg)](https://codeclimate.com/github/sethdeckard/gull)
[![Dependency Status](https://gemnasium.com/sethdeckard/gull.svg)](https://gemnasium.com/sethdeckard/gull)
[![security](https://hakiri.io/github/sethdeckard/gull/master.svg)](https://hakiri.io/github/sethdeckard/gull/master)
[![CI](https://github.com/sethdeckard/gull/actions/workflows/ci.yml/badge.svg)](https://github.com/sethdeckard/gull/actions/workflows/ci.yml)
# Gull

Ruby client for parsing NOAA/NWS alerts, warnings, and watches. The name comes from the type of bird featured on the NOAA logo. Please read the Notes/Caveats section for limitations.
Ruby client for parsing NOAA/NWS alerts, warnings, and watches. The name comes from the type of bird featured on the NOAA logo. Zero runtime dependencies -- uses only Ruby stdlib (`net/http`, `json`).

## Installation

Expand Down Expand Up @@ -49,49 +45,30 @@ alert.certainty
alert.vtec
```

To get alerts for a single state, territory, or marine zone just pass an optional URL
To get alerts for a single state or territory, pass the area option:

```ruby
oklahoma_url = 'http://alerts.weather.gov/cap/ok.php?x=1'
alerts = Gull::Alert.fetch(url: oklahoma_url)
```

You can also generate a map (a really long URL pointing to a map) of the polygon if alert has one (requires Google Static Maps API Key)

```ruby
alert.polygon.image_url 'YOUR_GOOGLE_API_KEY'

=> "http://maps.googleapis.com/maps/api/staticmap?size=640x640&maptype=roadmap&path=color:0xff0000|weight:3|fillcolor:0xff000060|38.73,-94.22|38.75,-94.16|38.57,-93.94|38.4,-93.84|38.4,-93.91|38.73,-94.22&key=YOUR_GOOGLE_API_KEY"
```

Options can be passed for map to override defaults

```ruby
options = { width: 600, height: 300, color: '0xfbf000', weight: 4,
fillcolor: '0xfbf00070', maptype: 'hybrid' }
alert.polygon.image_url 'YOUR_GOOGLE_API_KEY', options
alerts = Gull::Alert.fetch(area: 'OK')
```

##Notes, Caveats
This library provides a simplified/flattened model of the [Common Alerting Protocol](http://docs.oasis-open.org/emergency/cap/v1.2/CAP-v1.2-os.html) based only on the elements NWS utilizes in their [public Atom feeds](http://alerts.weather.gov/). If you need a complete CAP parser I suggest looking at [RCAP](https://github.com/farrel/RCAP).

The NWS will often cancel or update alerts before their expiration time. The public Atom feeds only provide current active alerts and do not include these separate update and cancellation CAP messages.
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).

The public Atom feeds have not always been reliable in terms of uptime and are often delayed by 2-3 minutes. If you are considering using this (or another gem/library that accesses the public Atom feeds) for mission critical purposes then you should explore other options.
The NWS will often cancel or update alerts before their expiration time. The API only returns currently active alerts.

### Urgency

| Symbol | Definition
| Symbol | Definition
| :------------- |:-------------
| :immediate | Responsive action should betaken immediately
| :immediate | Responsive action should be taken immediately
| :expected | Responsive action should be taken soon (within next hour)
| :future | Responsive action should be taken in the near future
| :past | Responsive action is no longer required
| :unknown | Urgency not known

### Severity

| Symbol | Definition
| Symbol | Definition
| :------------- |:-------------
| :extreme | Extraordinary threat to life or property
| :severe | Significant threat to life or property
Expand All @@ -101,7 +78,7 @@ The public Atom feeds have not always been reliable in terms of uptime and are o

### Certainty

| Symbol | Definition
| Symbol | Definition
| :------------- |:-------------
| :very_likely | Highly likely (p > ~ 85%) or certain
| :likely | Likely (p > ~50%)
Expand Down
2 changes: 2 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

require 'bundler/gem_tasks'
require 'rspec/core/rake_task'

Expand Down
30 changes: 19 additions & 11 deletions gull.gemspec
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# coding: utf-8
lib = File.expand_path('../lib', __FILE__)
# frozen_string_literal: true

lib = File.expand_path('lib', __dir__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'gull/version'

Expand All @@ -9,22 +10,29 @@ Gem::Specification.new do |spec|
spec.authors = ['Seth Deckard']
spec.email = ['seth@deckard.me']
spec.summary = 'Client for parsing NOAA/NWS alerts, warnings, and watches.'
spec.description = 'Client for parsing NOAA/NWS alerts, warnings, and watches.'
spec.description = 'Fetches and parses NOAA/NWS alerts, warnings, and watches from api.weather.gov. Zero runtime dependencies.'
spec.homepage = 'https://github.com/sethdeckard/gull'
spec.license = 'MIT'
spec.required_ruby_version = '>= 3.1'

spec.metadata = {
'source_code_uri' => 'https://github.com/sethdeckard/gull',
'changelog_uri' => 'https://github.com/sethdeckard/gull/blob/master/CHANGELOG.md',
'bug_tracker_uri' => 'https://github.com/sethdeckard/gull/issues'
}

spec.files = `git ls-files -z`.split("\x0")
spec.files = `git ls-files -z`.split("\x0").reject do |f|
f.match(%r{^(AGENTS|CLAUDE)\.md$})
end
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
spec.require_paths = ['lib']

spec.add_runtime_dependency 'httpclient'
spec.add_runtime_dependency 'nokogiri', '>= 1.6.8'

spec.add_development_dependency 'bundler'
spec.add_development_dependency 'guard-rspec'
spec.add_development_dependency 'rake'
spec.add_development_dependency 'rspec', '>=3.0'
spec.add_development_dependency 'coveralls'
spec.add_development_dependency 'guard'
spec.add_development_dependency 'rspec', '>= 3.0'
spec.add_development_dependency 'rubocop'
spec.add_development_dependency 'rubocop-rake'
spec.add_development_dependency 'simplecov'
spec.add_development_dependency 'webmock'
end
13 changes: 10 additions & 3 deletions lib/gull.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
# frozen_string_literal: true

require 'json'
require 'net/http'
require 'time'
require 'uri'

require 'gull/version'
require 'gull/error'
require 'gull/client'
require 'gull/alert'
require 'gull/polygon'
require 'gull/geocode'
require 'gull/polygon'
require 'gull/alert'
require 'gull/client'

module Gull
end
Loading