weather_gov_api 0.2.1 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9a3357a492f9e0fefc3414c9f9e8cd54d4fbf1bd24caff3741021af46948b9a6
4
- data.tar.gz: 9f5f503e4db23945aa94eb18dfac9d24f1bb5fbf11d72f2040201c5db7cb004e
3
+ metadata.gz: 13b83eaaed19ffb380a72fcbe992cb5c931c3c207103f482cad4b1d2bf743f28
4
+ data.tar.gz: 493b84f0f0270463508257045fd73aad2183646588c3254e25ac42191655a5e8
5
5
  SHA512:
6
- metadata.gz: fcbe8dc202094f73f1693806a6356644b7e68b37a5c5f378b3e129377bae89d883dbe5905768db8124f74c15a83ea4d4d55b50569dc508fa731f9d4ab246b0ce
7
- data.tar.gz: d6f8cbbb7ccb653fb09d0e05a191b306ed69c650346f05a5b7c1a5baa0000a0ac29c7afa184b10918430f1e1b5f625e9cc6d05c63c4d4427b7142df07c4c2937
6
+ metadata.gz: afbfee51abe17ccdae50e3a32b6b59a351abf036b8abb524a591a99607416205763121ddb366c4703bc61c5e41af048f5364573b284c4cde688711cb8453a4db
7
+ data.tar.gz: 893b3113c9ad50908efccc2c28ad1dd5202f6c0f96d1f174ecad6ff0f2ecc5a157fdb6b638340a1bf594729dd72dab74320d1227f6f5453a5460f318012fba7a
data/.rubocop.yml CHANGED
@@ -1,30 +1,41 @@
1
- inherit_from: .rubocop_todo.yml
2
-
3
- require:
4
- - rubocop-rspec
5
-
6
- AllCops:
7
- NewCops: enable
8
- TargetRubyVersion: 3.1
9
-
10
- Style/StringLiterals:
11
- EnforcedStyle: double_quotes
12
-
13
- Style/StringLiteralsInInterpolation:
14
- EnforcedStyle: double_quotes
15
-
16
- Layout/EndOfLine:
17
- EnforcedStyle: lf
18
-
19
- # Disable documentation requirements for specs
20
- Style/Documentation:
21
- Enabled: true
22
- Exclude:
23
- - "spec/**/*"
24
-
25
- # Allow longer blocks in specs
26
- Metrics/BlockLength:
27
- Enabled: true
28
- Exclude:
29
- - "spec/**/*"
30
- - "*.gemspec"
1
+ inherit_from: .rubocop_todo.yml
2
+
3
+ plugins:
4
+ - rubocop-rspec
5
+
6
+ AllCops:
7
+ NewCops: enable
8
+ TargetRubyVersion: 3.1
9
+
10
+ Style/StringLiterals:
11
+ EnforcedStyle: double_quotes
12
+
13
+ Style/StringLiteralsInInterpolation:
14
+ EnforcedStyle: double_quotes
15
+
16
+ Layout/EndOfLine:
17
+ EnforcedStyle: lf
18
+
19
+ # Disable documentation requirements for specs
20
+ Style/Documentation:
21
+ Enabled: true
22
+ Exclude:
23
+ - "spec/**/*"
24
+
25
+ # Allow longer blocks in specs
26
+ Metrics/BlockLength:
27
+ Enabled: true
28
+ Exclude:
29
+ - "spec/**/*"
30
+ - "*.gemspec"
31
+
32
+ Metrics/ParameterLists:
33
+ CountKeywordArgs:
34
+ Enabled: false
35
+
36
+ # Allow multiple exceptions
37
+ RSpec/MultipleExpectations:
38
+ Max: 4
39
+
40
+ RSpec/ExampleLength:
41
+ Max: 30 # Adjust this value as needed
data/.rubocop_todo.yml CHANGED
@@ -1,39 +1,18 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2025-02-22 02:13:57 UTC using RuboCop version 1.72.2.
3
+ # on 2025-03-17 20:00:04 UTC using RuboCop version 1.72.2.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
7
7
  # versions of RuboCop, may require this file to be generated again.
8
8
 
9
- # Offense count: 3
10
- # Configuration parameters: EnforcedStyle, AllowedGems, Include.
11
- # SupportedStyles: Gemfile, gems.rb, gemspec
12
- # Include: **/*.gemspec, **/Gemfile, **/gems.rb
13
- Gemspec/DevelopmentDependencies:
14
- Exclude:
15
- - 'weather_gov_api.gemspec'
16
-
17
- # Offense count: 2
18
- # Configuration parameters: EnforcedStyle.
19
- # SupportedStyles: native, lf, crlf
20
- Layout/EndOfLine:
21
- Exclude:
22
- - 'spec/weather_gov_api_spec.rb'
23
- - 'weather_gov_api.gemspec'
24
-
25
- # Offense count: 1
26
- # Configuration parameters: Prefixes, AllowedPatterns.
27
- # Prefixes: when, with, without
28
- RSpec/ContextWording:
9
+ # Offense count: 7
10
+ # Configuration parameters: Max, CountAsOne.
11
+ RSpec/ExampleLength:
29
12
  Exclude:
30
13
  - 'spec/weather_gov_api/client_spec.rb'
31
14
 
32
- # Offense count: 10
33
- # Configuration parameters: CountAsOne.
34
- RSpec/ExampleLength:
35
- Max: 22
36
-
37
- # Offense count: 5
38
- RSpec/MultipleExpectations:
39
- Max: 4
15
+ # Offense count: 2
16
+ # Configuration parameters: AllowSubject.
17
+ RSpec/MultipleMemoizedHelpers:
18
+ Max: 7
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.3.5
@@ -0,0 +1,6 @@
1
+ {
2
+ "recommendations": [
3
+ "shopify.ruby-lsp",
4
+ "rubocop.vscode-rubocop"
5
+ ]
6
+ }
data/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.3.0] - 2025-02-28
4
+
5
+ ### Added
6
+ - Updated version handling adding WeatherGovApi::ApiError
7
+ - added .ruby-version
8
+ - added .vsc recommend plugins
9
+
10
+ ### Fixed
11
+ - tackled rubocop_todo list
12
+ - fixed CodeQL rb/request-forgery error by ensuring same host
13
+
14
+ ## [0.2.1] - 2025-02-21
15
+
16
+ ### Added
17
+ - rubocop_todo
18
+ - simplecov and rubycritic
19
+ - updated CircleCI to require 90% overall and 80% on a file for test coverage
20
+
3
21
  ## [0.2.0] - 2025-02-19
4
22
 
5
23
  ### Added
@@ -9,7 +27,6 @@
9
27
  ## [0.1.0] - Initial Release
10
28
 
11
29
  ### Added
12
- - Basic client implementation
13
30
  - Basic client implementation for weather.gov API
14
31
  - `points` method for fetching weather data by coordinates
15
32
  - Error handling for:
data/README.md CHANGED
@@ -39,6 +39,8 @@ Fetches a list of nearby weather observation stations.
39
39
  ### current_weather(latitude:, longitude:)
40
40
  Fetches the current weather conditions from the closest observation station.
41
41
 
42
+ ### forecast(latitude:, longitude:)
43
+ Fetches the seven day forecast which will include a daytieme and nightime forecast.
42
44
 
43
45
  ## Development
44
46
 
@@ -68,17 +70,13 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/Jeffre
68
70
 
69
71
  ## TODO
70
72
 
71
- - [ ] Implement forecast retrieval functionality
72
73
  - [ ] Add validation to limit latitude and longitude to 4 decimal places (weather.gov API requirement)
73
- - [ ] Improve error handling to properly surface weather.gov API error messages
74
74
  - [ ] Implement rate limiting strategies:
75
75
  - [ ] Local rate limiting
76
76
  - [ ] Distributed rate limiting (Redis/DB-based)
77
77
  - [ ] Add caching support for API responses
78
+ - [ ] Memoize calls that might happen more than once (like points)
78
79
  - [ ] Automate CHANGELOG.md updates during release process
79
- - [ ] Configure Git repository settings:
80
- - [ ] Disable direct commits to main branch
81
- - [ ] Require passing CI builds for branch merges
82
80
 
83
81
  ## License
84
82
 
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WeatherGovApi
4
+ # Custom error class to handle errors from the Weather.gov API
5
+ class ApiError < StandardError
6
+ attr_reader :type, :title, :status, :detail, :instance, :correlation_id, :parameter_errors
7
+
8
+ # rubocop:disable Metrics/ParameterLists
9
+ def initialize(type: nil, title: nil, status: nil, detail: nil, instance: nil, correlation_id: nil, message: nil,
10
+ parameter_errors: nil)
11
+ @type = type
12
+ @title = title
13
+ @status = status
14
+ @detail = detail
15
+ @instance = instance
16
+ @correlation_id = correlation_id
17
+ @parameter_errors = parameter_errors
18
+ @message = message
19
+ super(message || title || detail || "API request failed")
20
+ end
21
+ # rubocop:enable Metrics/ParameterLists
22
+
23
+ # rubocop:disable Metrics/CyclomaticComplexity
24
+ # rubocop:disable Metrics/PerceivedComplexity
25
+ def to_s
26
+ return super unless super.empty?
27
+
28
+ parts = []
29
+ parts << "Type: #{@type}" if @type
30
+ parts << "Title: #{@title}" if @title
31
+ parts << "Status: #{@status}" if @status
32
+ parts << "Detail: #{@detail}" if @detail
33
+ parts << "Instance: #{@instance}" if @instance
34
+ parts << "CorrelationID: #{@correlation_id}" if @correlation_id
35
+ parts << "ParameterErros: #{@parameter_errors}" if @parameter_errors
36
+ parts.join(", ")
37
+ end
38
+ # rubocop:enable Metrics/PerceivedComplexity
39
+ # rubocop:enable Metrics/CyclomaticComplexity
40
+ end
41
+ end
@@ -11,44 +11,66 @@ module WeatherGovApi
11
11
  end
12
12
 
13
13
  def points(latitude:, longitude:)
14
- validate_coordinates(latitude, longitude)
15
14
  response = connection.get("/points/#{latitude},#{longitude}")
15
+ raise_api_error(response) unless response.success?
16
16
  Response.new(response)
17
17
  rescue Faraday::Error => e
18
- raise Error, "API request failed: #{e.message}"
18
+ raise WeatherGovApi::ApiError.new(message: "API request failed: #{e.message}")
19
19
  end
20
20
 
21
+ # rubocop:disable Metrics/MethodLength
21
22
  def observation_stations(latitude:, longitude:)
22
23
  points_response = points(latitude: latitude, longitude: longitude)
23
24
  stations_url = points_response.data.dig("properties", "observationStations")
24
- raise Error, "No observation stations URL found in points response" unless stations_url
25
+ unless stations_url
26
+ raise WeatherGovApi::ApiError.new(message: "No observation stations URL found in points response")
27
+ end
28
+
29
+ stations_path = observation_stations_path(stations_url)
25
30
 
26
- response = connection.get(stations_url.sub(BASE_URL, ""))
31
+ response = connection.get(stations_path)
32
+ raise_api_error(response) unless response.success?
27
33
  Response.new(response)
28
34
  rescue Faraday::Error => e
29
- raise Error, "API request failed: #{e.message}"
35
+ raise WeatherGovApi::ApiError.new(message: "API request failed: #{e.message}")
30
36
  end
37
+ # rubocop:enable Metrics/MethodLength
31
38
 
32
39
  def current_weather(latitude:, longitude:)
33
40
  stations_response = observation_stations(latitude: latitude, longitude: longitude)
34
41
  station = stations_response.data.dig("features", 0)
35
- raise Error, "No observation stations found" unless station
42
+ raise WeatherGovApi::ApiError.new(message: "No observation stations found") unless station
36
43
 
37
44
  station_id = station.dig("properties", "stationIdentifier")
38
45
  response = connection.get("/stations/#{station_id}/observations/latest")
46
+ raise_api_error(response) unless response.success?
39
47
  Response.new(response)
40
48
  rescue Faraday::Error => e
41
- raise Error, "API request failed: #{e.message}"
49
+ raise WeatherGovApi::ApiError.new(message: "API request failed: #{e.message}")
42
50
  end
43
51
 
44
- private
52
+ def forecast(latitude:, longitude:)
53
+ grid_data = points(latitude: latitude, longitude: longitude).data["properties"]
54
+ grid_id = grid_data["gridId"]
55
+ grid_x = grid_data["gridX"]
56
+ grid_y = grid_data["gridY"]
57
+
58
+ response = connection.get("/gridpoints/#{grid_id}/#{grid_x},#{grid_y}/forecast")
59
+ raise_api_error(response) unless response.success?
60
+ Response.new(response)
61
+ rescue Faraday::Error => e
62
+ raise WeatherGovApi::ApiError.new(message: "API request failed: #{e.message}")
63
+ end
45
64
 
46
- def validate_coordinates(latitude, longitude)
47
- raise ArgumentError, "Invalid latitude: must be between -90 and 90" unless latitude.between?(-90, 90)
65
+ private
48
66
 
49
- return if longitude.between?(-180, 180)
67
+ def observation_stations_path(url)
68
+ uri = URI.parse(url)
69
+ unless uri.host == URI.parse(BASE_URL).host
70
+ raise WeatherGovApi::ApiError.new(message: "Invalid observation stations URL: #{url}")
71
+ end
50
72
 
51
- raise ArgumentError, "Invalid longitude: must be between -180 and 180"
73
+ uri.path
52
74
  end
53
75
 
54
76
  def connection
@@ -58,5 +80,26 @@ module WeatherGovApi
58
80
  faraday.adapter Faraday.default_adapter
59
81
  end
60
82
  end
83
+
84
+ # rubocop:disable Metrics/MethodLength
85
+ def raise_api_error(response)
86
+ return if response.success?
87
+
88
+ begin
89
+ error_data = JSON.parse(response.body)
90
+ rescue JSON::ParserError
91
+ raise WeatherGovApi::ApiError.new(message: "API request failed with status #{response.status}\
92
+ but could not parse error response.")
93
+ end
94
+
95
+ raise WeatherGovApi::ApiError.new(
96
+ type: error_data["type"],
97
+ title: error_data["title"],
98
+ status: error_data["status"],
99
+ detail: error_data["detail"],
100
+ instance: error_data["instance"]
101
+ )
102
+ end
103
+ # rubocop:enable Metrics/MethodLength
61
104
  end
62
105
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module WeatherGovApi
4
- VERSION = "0.2.1"
4
+ VERSION = "0.4.0"
5
5
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "weather_gov_api/version"
4
+ require_relative "weather_gov_api/api_errors"
4
5
  require_relative "weather_gov_api/response"
5
6
  require_relative "weather_gov_api/client"
6
7
 
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: weather_gov_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - JeffreyMPrice
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2025-02-22 00:00:00.000000000 Z
10
+ date: 2025-03-23 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: faraday
@@ -24,48 +23,6 @@ dependencies:
24
23
  - - "~>"
25
24
  - !ruby/object:Gem::Version
26
25
  version: '2.0'
27
- - !ruby/object:Gem::Dependency
28
- name: rspec
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: '3.0'
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
39
- - !ruby/object:Gem::Version
40
- version: '3.0'
41
- - !ruby/object:Gem::Dependency
42
- name: vcr
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - "~>"
46
- - !ruby/object:Gem::Version
47
- version: '6.0'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - "~>"
53
- - !ruby/object:Gem::Version
54
- version: '6.0'
55
- - !ruby/object:Gem::Dependency
56
- name: webmock
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - "~>"
60
- - !ruby/object:Gem::Version
61
- version: '3.0'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - "~>"
67
- - !ruby/object:Gem::Version
68
- version: '3.0'
69
26
  description: |-
70
27
  A Ruby gem for accessing weather data from the National Weather Service (weather.gov) API.
71
28
  Provides a simple interface to fetch weather data for US locations.
@@ -78,12 +35,15 @@ files:
78
35
  - ".rspec"
79
36
  - ".rubocop.yml"
80
37
  - ".rubocop_todo.yml"
38
+ - ".ruby-version"
39
+ - ".vscode/extensions.json"
81
40
  - CHANGELOG.md
82
41
  - CODE_OF_CONDUCT.md
83
42
  - LICENSE.txt
84
43
  - README.md
85
44
  - Rakefile
86
45
  - lib/weather_gov_api.rb
46
+ - lib/weather_gov_api/api_errors.rb
87
47
  - lib/weather_gov_api/client.rb
88
48
  - lib/weather_gov_api/response.rb
89
49
  - lib/weather_gov_api/version.rb
@@ -97,7 +57,6 @@ metadata:
97
57
  source_code_uri: https://github.com/JeffreyMPrice/weather_gov_api
98
58
  changelog_uri: https://github.com/JeffreyMPrice/weather_gov_api/blob/main/CHANGELOG.md
99
59
  rubygems_mfa_required: 'true'
100
- post_install_message:
101
60
  rdoc_options: []
102
61
  require_paths:
103
62
  - lib
@@ -112,8 +71,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
112
71
  - !ruby/object:Gem::Version
113
72
  version: '0'
114
73
  requirements: []
115
- rubygems_version: 3.5.16
116
- signing_key:
74
+ rubygems_version: 3.6.5
117
75
  specification_version: 4
118
76
  summary: Ruby wrapper for the Weather.gov API
119
77
  test_files: []