schwab_rb 1.0.5 → 1.0.6

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: 8d136dcb5cef454ed8cfaca6a8ad90a8ebf25971e283e4098f73dc765d06023c
4
- data.tar.gz: 93bfb2468fcc4706b361bd987087256eed9fe84e6ef3486743659acd51e0de61
3
+ metadata.gz: 931dc11bd7c59a66e180069c36ab7f6dd44cc068db8aa3c5b58ece0aaf830a6e
4
+ data.tar.gz: e9abc3aa0cce142ffe3ac9af5cca9ee9fa1e5d1fac7209e9f39c929c1f6f8f1c
5
5
  SHA512:
6
- metadata.gz: 3d684f28d255009c90b0fb0abd401f6c452532bdcdbb1ccbc42d6a33c893aef8f6821af278ffbd9f81792e53db7f511ce561c7d29ac80a5f1d682aded578f886
7
- data.tar.gz: 2772b8c48ab306c3d821639bc68ac950bdd7b8b56995f13a55a495b4d60fa5b0f87e2114d471622e6ce2069a8d93a3d8837865f346f7545148cf5b129cb1a1d4
6
+ metadata.gz: 53af08e726ae79f7b672df90cab4486b88b1ffaedc19ebc7b666bde8362f4fe49667f78f8ffd390be215b0c975895f21b7b4a7ae34addbb41d94bbe4c3030807
7
+ data.tar.gz: 1ce70df60193ac32c02b50eb978c96a640ff75bf2715504824341daf4e51ec8a6c2e466a2598509a66195cfbba21a7724bba6f6b6a901946c5b5352df595f934
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [1.0.6] - 2026-11-13
4
+
5
+ ### Changed
6
+ - Stream services now retain one callback; registering another block for the same service replaces the previous callback.
7
+ - `Stream::AsyncClient` supports dynamic `add` and `unsub` operations.
8
+
9
+ ### Fixed
10
+ - Prevent duplicate callback invocation on repeated subscription additions and preserve callbacks when symbols are unsubscribed.
11
+ - Allow callback registration before symbols are supplied without sending an empty subscription.
12
+
3
13
  ## [1.0.5] - 2026-10-19
4
14
 
5
15
  ### Added
data/README.md CHANGED
@@ -205,6 +205,12 @@ stream.unsub(:level_one_equities, symbols: ["SPY", "QQQ"])
205
205
  stream.stop
206
206
  ```
207
207
 
208
+ Each service has one callback. Passing a block to `on` or `add` replaces its
209
+ callback; calls without a block preserve it, including after all symbols are
210
+ unsubscribed. You can register a callback with `on` before supplying symbols;
211
+ fields specified there are used when symbols are later added without fields.
212
+ Both `Stream::Client` and `Stream::AsyncClient` support `on`, `add`, and `unsub`.
213
+
208
214
  ## Data Objects
209
215
 
210
216
  The gem includes structured data objects for better handling of API responses. Most API methods support a `return_data_objects` parameter (defaults to `true`) which returns parsed Ruby objects instead of raw JSON responses:
@@ -13,6 +13,12 @@ module SchwabRb
13
13
  @stream.on(service_symbol, symbols: symbols, fields: fields, &block)
14
14
  self
15
15
  end
16
+ alias add on
17
+
18
+ def unsub(service_symbol, symbols: nil)
19
+ @stream.unsub(service_symbol, symbols: symbols)
20
+ self
21
+ end
16
22
 
17
23
  def start
18
24
  @stream.start
@@ -19,6 +19,7 @@ module SchwabRb
19
19
  def initialize(client)
20
20
  @client = client
21
21
  @handlers = {}
22
+ @service_fields = {}
22
23
  @subscriptions = {}
23
24
  @connection = nil
24
25
  @message_builder = nil
@@ -34,16 +35,18 @@ module SchwabRb
34
35
  service_name = Services.lookup(service_symbol)
35
36
  resolved_fields = fields ? Fields.resolve(service_name, fields) : nil
36
37
 
37
- handler = { symbols: Array(symbols).map(&:to_s), fields: resolved_fields, callback: block }
38
- @handlers[service_name] ||= []
39
- @handlers[service_name] << handler
38
+ @handlers[service_name] = block if block
39
+ @service_fields[service_name] = resolved_fields unless fields.nil?
40
+ symbol_list = Array(symbols).map(&:to_s)
41
+ return self if symbol_list.empty?
40
42
 
43
+ subscription = { symbols: symbol_list, fields: resolved_fields || @service_fields[service_name] }
41
44
  @subscriptions[service_name] = merge_subscription(
42
45
  @subscriptions[service_name],
43
- handler
46
+ subscription
44
47
  )
45
48
 
46
- send_subscription(service_name, handler) if @connected
49
+ send_subscription(service_name, subscription) if @connected
47
50
 
48
51
  self
49
52
  end
@@ -59,14 +62,6 @@ module SchwabRb
59
62
  @subscriptions.delete(service_name) if @subscriptions[service_name][:symbols].empty?
60
63
  end
61
64
 
62
- if @handlers[service_name]
63
- @handlers[service_name].each do |h|
64
- h[:symbols] -= symbols_to_remove
65
- end
66
- @handlers[service_name].reject! { |h| h[:symbols].empty? }
67
- @handlers.delete(service_name) if @handlers[service_name].empty?
68
- end
69
-
70
65
  if @connected
71
66
  request = @message_builder.build_request(
72
67
  service_name, Commands::UNSUBS,
@@ -206,10 +201,7 @@ module SchwabRb
206
201
  if parsed.key?("data")
207
202
  parsed["data"].each do |data_entry|
208
203
  service = data_entry["service"]
209
- handlers = @handlers[service]
210
- next unless handlers
211
-
212
- handlers.each { |h| h[:callback]&.call(data_entry) }
204
+ @handlers[service]&.call(data_entry)
213
205
  end
214
206
  end
215
207
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SchwabRb
4
- VERSION = "1.0.5"
4
+ VERSION = "1.0.6"
5
5
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: schwab_rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.5
4
+ version: 1.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joseph Platta
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2026-10-19 00:00:00.000000000 Z
10
+ date: 2026-11-14 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: async