ruby_garmin_connect 0.2.0 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9b18f0c8f89a53ffc4f5f823c156aa14efcdcdd4fdcbcc11bfcf2c96ad8026fa
4
- data.tar.gz: b041d02818b7e1cc499bfce5b24982b73d3b616a2e2f41eb6599b6f0bb9b70c1
3
+ metadata.gz: 9e992aa41910f85ae0a2f2b656e09d28c529a82c44a0118a09a53765826d49ce
4
+ data.tar.gz: ee0d8526867d3f7d85c2742cfaa3ad36a73102d43be6eba2149b687719365885
5
5
  SHA512:
6
- metadata.gz: a1975143fb61ac5c6af8ca844416415946babf90240eb6cb6ac52ec40a4a37f606c233d2d60304609066303b4a472026cc2942c7a4b6e2cd750b36c8fa900185
7
- data.tar.gz: 500020861e427e9795a78bc242cb80a7755a9e714f6f341c0141496a6292abca62a1fdfbae9420f0d894bc1049e5c11f3c371aa1e95d9cd5cc3eaba1d5eb3fe4
6
+ metadata.gz: d20db74f11c8102571dd3c08a3ffeec7e095075de9562035adbb7255d7d75a134d879e075010897557e143a2f28ab6392da82ecbedd2e86ffbebced25ae3e0ca
7
+ data.tar.gz: 8baf66019a7832c3d1579c9b164f01beef5a3271b91357120572b345cf7da0b71f331b7f295533bfcb8342b641794d6dc6cc63ae4ece8159d8efe9814a34c4b9
data/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.1 (2026-02-11)
4
+
5
+ ### Fixed
6
+
7
+ - `training_readiness` raising `TypeError: no implicit conversion of String into Integer` when Garmin returns a response with a UTF-8 BOM or unparseable JSON body
8
+ - `display_name` and `full_name` returning `nil` on login when the profile endpoint nests data under `socialProfile` or uses `userName` instead of `displayName`
9
+
10
+ ### Added
11
+
12
+ - `ParseError` exception class — raised when a response claims `application/json` content-type but the body can't be parsed (instead of silently returning a raw string)
13
+ - UTF-8 BOM stripping in response parsing
14
+ - Fallback profile extraction: tries `displayName` → `socialProfile.displayName` → `userName`
15
+
16
+ ### Improved
17
+
18
+ - Test coverage increased to 205 examples (up from 199)
19
+
3
20
  ## 0.2.0 (2026-02-11)
4
21
 
5
22
  ### Added
data/README.md CHANGED
@@ -24,10 +24,12 @@ gem install garmin_connect
24
24
  require "garmin_connect"
25
25
 
26
26
  # Login with credentials (tokens are saved to ~/.garminconnect automatically)
27
- client = GarminConnect.login(email: "you@example.com", password: "your-password")
27
+ client = GarminConnect::Client.new(email: "you@example.com", password: "your-password")
28
+ client.login
28
29
 
29
30
  # Subsequent sessions resume from saved tokens (no re-login for ~1 year)
30
- client = GarminConnect.login
31
+ client = GarminConnect::Client.new
32
+ client.login
31
33
 
32
34
  # Get today's stats
33
35
  puts client.daily_summary
@@ -23,7 +23,7 @@ module GarminConnect
23
23
  data = training_readiness(date)
24
24
  return data unless data.is_a?(Array)
25
25
 
26
- data.select { |entry| entry["calendarDate"] == format_date(date) }
26
+ data.select { |entry| entry.is_a?(Hash) && entry["calendarDate"] == format_date(date) }
27
27
  end
28
28
 
29
29
  # Get aggregated training status.
@@ -162,13 +162,32 @@ module GarminConnect
162
162
  @unit_system = settings&.dig("userData", "measurementSystem")
163
163
 
164
164
  profile = connection.get("/userprofile-service/userprofile/profile")
165
- @display_name = profile&.dig("displayName")
166
- @full_name = profile&.dig("fullName")
167
- @user_profile_pk = profile&.dig("profileId") || settings&.dig("id")
165
+ extract_profile_info(profile)
166
+
167
+ @user_profile_pk = extract_profile_pk(profile, settings)
168
168
  rescue HTTPError
169
169
  # Non-fatal: display_name may not be available
170
170
  end
171
171
 
172
+ def extract_profile_info(profile)
173
+ return unless profile.is_a?(Hash)
174
+
175
+ @display_name = profile.dig("displayName") ||
176
+ profile.dig("socialProfile", "displayName") ||
177
+ profile.dig("userName")
178
+
179
+ @full_name = profile.dig("fullName") ||
180
+ profile.dig("socialProfile", "fullName")
181
+ end
182
+
183
+ def extract_profile_pk(profile, settings)
184
+ return nil unless profile.is_a?(Hash) || settings.is_a?(Hash)
185
+
186
+ profile&.dig("profileId") ||
187
+ profile&.dig("socialProfile", "profileId") ||
188
+ settings&.dig("id")
189
+ end
190
+
172
191
  # Exposed for API modules that need it.
173
192
  def user_profile_pk
174
193
  @user_profile_pk
@@ -124,11 +124,23 @@ module GarminConnect
124
124
 
125
125
  def parse_response(resp)
126
126
  return nil if resp.status == 204
127
- return resp.body if resp.body.nil? || resp.body.empty?
128
127
 
129
- JSON.parse(resp.body)
128
+ body = resp.body
129
+ return body if body.nil? || body.empty?
130
+
131
+ # Strip UTF-8 BOM if present (some Garmin endpoints include it)
132
+ body = body.b.sub(/\A\xEF\xBB\xBF/n, "").force_encoding("UTF-8")
133
+
134
+ JSON.parse(body)
130
135
  rescue JSON::ParserError
131
- resp.body
136
+ content_type = resp.headers["content-type"].to_s
137
+ # If the server said it was JSON but we can't parse it, raise rather than
138
+ # returning a raw string that callers will misuse.
139
+ if content_type.include?("application/json")
140
+ raise ParseError, "Failed to parse JSON response: #{body[0..200]}"
141
+ end
142
+
143
+ body
132
144
  end
133
145
 
134
146
  def handle_errors!(resp)
@@ -35,6 +35,9 @@ module GarminConnect
35
35
  class TooManyRequestsError < HTTPError; end
36
36
  class ServerError < HTTPError; end
37
37
 
38
+ # Response parsing errors
39
+ class ParseError < Error; end
40
+
38
41
  # Maps HTTP status codes to error classes
39
42
  HTTP_ERRORS = {
40
43
  400 => BadRequestError,
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GarminConnect
4
- VERSION = "0.2.0"
4
+ VERSION = "0.2.1"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_garmin_connect
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - DRBragg