nwc-ruby 0.2.2 → 0.2.3

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: b847b05f148d03753548528e0f4483129cf59887e40329f28e7bbe31f0eda1cd
4
- data.tar.gz: 9c99704df81a6e7cfa9c1bfede014b49270b15bf11a3bba8663b5d6f1f3500fa
3
+ metadata.gz: d541ad9af31629ad26492d8da61d56b0a63980cc8597a460cca9949df56f382d
4
+ data.tar.gz: 8d48fbbe532bdcf56d889f68f9d7878fa2958381c1ea285fb572aebb70d17d7e
5
5
  SHA512:
6
- metadata.gz: 11e62747c2dfa88459d900a2e1f89e7a42df5420b88221508bf62df39b65ebe7f9937db357557b2ab3ee8b95be0b50b5062fa3667cf21002dbf607366f49675d
7
- data.tar.gz: c0ff4c40f5d8fa68d3b12e6b7c745ff1ff5b19da5719c0998c8cc5db6098ee77917c2c914b0ca96dc9e9301ac4240595f7e472019e6001394c996e75ffdcff6a
6
+ metadata.gz: bbc5e4a3efdac9b24a4a2c5fc183f3945ba9987fd5312c78a7e8c4ba8cff625af5c08521094cc10101fd24a0bef5f35261eb29720d533d7e0167a236ee59d106
7
+ data.tar.gz: fb2c7a1ed8d7785a886b38e250432466354b257cef1e4c8552ef24c493c304dbd446bf4c17e57debcce8bc6875666686c16f901d29474ef08541194e6e2a97f3
data/CHANGELOG.md CHANGED
@@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ### Fixed
11
+
12
+ - Silenced the noisy `Async::Task` warn log ("Task may have ended with
13
+ unhandled exception") that was emitted every `recycle_interval` (default
14
+ 5 min) by the heartbeat task. The forced recycle used to be signaled by
15
+ raising `TransportError` from inside the heartbeat's child Async task; the
16
+ parent's reconnect loop caught it correctly, but Async's console logger
17
+ logged the unhandled-in-child exception with a full backtrace first.
18
+ The recycle is now signaled by setting an `@recycle_requested` flag and
19
+ closing the websocket, which unblocks the read loop and lets
20
+ `run_one_connection` return normally without any exception crossing a task
21
+ boundary.
22
+
10
23
  ## [0.2.2] — 2026-04-21
11
24
 
12
25
  ### Fixed
data/README.md CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  A production-grade Ruby client for [Nostr Wallet Connect (NIP-47)][nip47].
4
4
 
5
+ Accept and track Bitcoin Lightning payments in your Rails app without running
6
+ any Lightning infrastructure yourself. Generate Lightning invoices for your
7
+ users to pay, then get notified the instant each one settles — all by talking
8
+ to a Nostr Wallet Connect (NWC) service that someone else operates. The gem
9
+ works with any NWC-compatible backend (Rizful, Alby Hub, Alby Cloud, CoinOs,
10
+ any NIP-47 wallet service), so you're not locked into a single provider and
11
+ can swap vendors by changing one environment variable.
12
+
5
13
  ```ruby
6
14
  require "nwc_ruby"
7
15
 
@@ -146,6 +146,7 @@ module NwcRuby
146
146
  def run_one_connection(top)
147
147
  endpoint = Async::HTTP::Endpoint.parse(@url, alpn_protocols: ['http/1.1'])
148
148
  opened_at = Async::Clock.now
149
+ @recycle_requested = false
149
150
  @logger.info("[nwc] connecting to #{@url}")
150
151
 
151
152
  Async::WebSocket::Client.connect(endpoint) do |conn|
@@ -155,6 +156,7 @@ module NwcRuby
155
156
 
156
157
  @open_cb&.call(self)
157
158
  read_loop(conn)
159
+ @logger.info("[nwc] recycling connection (#{@recycle_interval}s)") if @recycle_requested
158
160
  ensure
159
161
  heartbeat&.stop
160
162
  poll&.stop
@@ -167,6 +169,13 @@ module NwcRuby
167
169
  end
168
170
  end
169
171
 
172
+ # Heartbeat task: sends RFC 6455 ping every @ping_interval seconds and
173
+ # requests a recycle once the connection has been open longer than
174
+ # @recycle_interval. The recycle is signaled via a flag + close rather
175
+ # than by raising across task boundaries, because an exception raised
176
+ # inside a child Async task is logged at warn level by Async's console
177
+ # logger ("Task may have ended with unhandled exception") before the
178
+ # parent's rescue runs — producing a noisy backtrace on every recycle.
170
179
  def start_heartbeat(top, conn, opened_at)
171
180
  top.async do
172
181
  loop do
@@ -176,12 +185,25 @@ module NwcRuby
176
185
 
177
186
  sleep @ping_interval
178
187
  break if @stop
179
-
180
- raise TransportError, "recycle (#{@recycle_interval}s)" if Async::Clock.now - opened_at > @recycle_interval
188
+ break if recycle_due?(opened_at) && request_recycle(conn)
181
189
  end
182
190
  end
183
191
  end
184
192
 
193
+ def recycle_due?(opened_at)
194
+ Async::Clock.now - opened_at > @recycle_interval
195
+ end
196
+
197
+ def request_recycle(conn)
198
+ @recycle_requested = true
199
+ begin
200
+ conn.close
201
+ rescue StandardError
202
+ nil
203
+ end
204
+ true
205
+ end
206
+
185
207
  def start_poll(top)
186
208
  return unless @poll_interval && @poll_cb
187
209
 
@@ -200,7 +222,21 @@ module NwcRuby
200
222
  end
201
223
 
202
224
  def read_loop(conn)
203
- while (message = conn.read)
225
+ loop do
226
+ message =
227
+ begin
228
+ conn.read
229
+ rescue StandardError => e
230
+ # If we asked for the recycle/close ourselves, treat the
231
+ # resulting read error as a clean EOF rather than propagating
232
+ # it up into the reconnect-on-error path (which would log a
233
+ # spurious warning).
234
+ raise unless @recycle_requested || @stop
235
+
236
+ @logger.debug("[nwc] read interrupted by close: #{e.class}")
237
+ nil
238
+ end
239
+ break if message.nil?
204
240
  break if @stop
205
241
 
206
242
  begin
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module NwcRuby
4
- VERSION = '0.2.2'
4
+ VERSION = '0.2.3'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nwc-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - MegalithicBTC