nats-pure 2.4.0 → 2.6.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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +30 -0
  3. data/README.md +10 -3
  4. data/lib/nats/client.rb +7 -3
  5. data/lib/nats/io/client.rb +464 -328
  6. data/lib/nats/io/errors.rb +2 -0
  7. data/lib/nats/io/jetstream/api.rb +53 -50
  8. data/lib/nats/io/jetstream/errors.rb +30 -14
  9. data/lib/nats/io/jetstream/js/config.rb +9 -3
  10. data/lib/nats/io/jetstream/js/header.rb +15 -9
  11. data/lib/nats/io/jetstream/js/status.rb +11 -5
  12. data/lib/nats/io/jetstream/js/sub.rb +4 -2
  13. data/lib/nats/io/jetstream/js.rb +10 -8
  14. data/lib/nats/io/jetstream/manager.rb +103 -101
  15. data/lib/nats/io/jetstream/msg/ack.rb +22 -12
  16. data/lib/nats/io/jetstream/msg/ack_methods.rb +30 -24
  17. data/lib/nats/io/jetstream/msg/metadata.rb +10 -7
  18. data/lib/nats/io/jetstream/msg.rb +11 -4
  19. data/lib/nats/io/jetstream/pull_subscription.rb +21 -10
  20. data/lib/nats/io/jetstream/push_subscription.rb +3 -1
  21. data/lib/nats/io/jetstream.rb +102 -106
  22. data/lib/nats/io/kv/api.rb +7 -3
  23. data/lib/nats/io/kv/bucket_status.rb +7 -5
  24. data/lib/nats/io/kv/errors.rb +25 -2
  25. data/lib/nats/io/kv/manager.rb +19 -10
  26. data/lib/nats/io/kv.rb +354 -22
  27. data/lib/nats/io/msg.rb +19 -19
  28. data/lib/nats/io/parser.rb +23 -23
  29. data/lib/nats/io/rails.rb +2 -0
  30. data/lib/nats/io/subscription.rb +33 -23
  31. data/lib/nats/io/version.rb +4 -2
  32. data/lib/nats/io/websocket.rb +40 -18
  33. data/lib/nats/nuid.rb +33 -22
  34. data/lib/nats/service/callbacks.rb +22 -0
  35. data/lib/nats/service/endpoint.rb +155 -0
  36. data/lib/nats/service/errors.rb +44 -0
  37. data/lib/nats/service/group.rb +37 -0
  38. data/lib/nats/service/monitoring.rb +108 -0
  39. data/lib/nats/service/stats.rb +52 -0
  40. data/lib/nats/service/status.rb +66 -0
  41. data/lib/nats/service/validator.rb +31 -0
  42. data/lib/nats/service.rb +121 -0
  43. data/lib/nats/utils/list.rb +26 -0
  44. data/lib/nats-pure.rb +5 -0
  45. data/lib/nats.rb +10 -6
  46. metadata +176 -8
data/lib/nats/io/kv.rb CHANGED
@@ -1,4 +1,6 @@
1
- # Copyright 2021 The NATS Authors
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2021-2025 The NATS Authors
2
4
  # Licensed under the Apache License, Version 2.0 (the "License");
3
5
  # you may not use this file except in compliance with the License.
4
6
  # You may obtain a copy of the License at
@@ -12,13 +14,15 @@
12
14
  # limitations under the License.
13
15
  #
14
16
 
15
- require_relative 'kv/api'
16
- require_relative 'kv/bucket_status'
17
- require_relative 'kv/errors'
18
- require_relative 'kv/manager'
17
+ require_relative "kv/api"
18
+ require_relative "kv/bucket_status"
19
+ require_relative "kv/errors"
20
+ require_relative "kv/manager"
19
21
 
20
22
  module NATS
21
23
  class KeyValue
24
+ include MonitorMixin
25
+
22
26
  KV_OP = "KV-Operation"
23
27
  KV_DEL = "DEL"
24
28
  KV_PURGE = "PURGE"
@@ -26,16 +30,35 @@ module NATS
26
30
  MSG_ROLLUP_ALL = "all"
27
31
  ROLLUP = "Nats-Rollup"
28
32
 
29
- def initialize(opts={})
33
+ VALID_BUCKET_RE = /\A[a-zA-Z0-9_-]+$/
34
+ VALID_KEY_RE = /\A[-\/_=.a-zA-Z0-9]+$/
35
+
36
+ class << self
37
+ def is_valid_key(key)
38
+ if key.nil?
39
+ false
40
+ elsif key.start_with?(".") || key.end_with?(".")
41
+ false
42
+ elsif key !~ VALID_KEY_RE
43
+ false
44
+ else
45
+ true
46
+ end
47
+ end
48
+ end
49
+
50
+ def initialize(opts = {})
30
51
  @name = opts[:name]
31
52
  @stream = opts[:stream]
32
53
  @pre = opts[:pre]
33
54
  @js = opts[:js]
34
55
  @direct = opts[:direct]
56
+ @validate_keys = opts[:validate_keys]
35
57
  end
36
58
 
37
59
  # get returns the latest value for the key.
38
- def get(key, params={})
60
+ def get(key, params = {})
61
+ raise InvalidKeyError if @validate_keys && !KeyValue.is_valid_key(key)
39
62
  entry = nil
40
63
  begin
41
64
  entry = _get(key, params)
@@ -46,19 +69,19 @@ module NATS
46
69
  entry
47
70
  end
48
71
 
49
- def _get(key, params={})
72
+ def _get(key, params = {})
50
73
  msg = nil
51
74
  subject = "#{@pre}#{key}"
52
75
 
53
- if params[:revision]
54
- msg = @js.get_msg(@stream,
55
- seq: params[:revision],
56
- direct: @direct)
76
+ msg = if params[:revision]
77
+ @js.get_msg(@stream,
78
+ seq: params[:revision],
79
+ direct: @direct)
57
80
  else
58
- msg = @js.get_msg(@stream,
59
- subject: subject,
60
- seq: params[:revision],
61
- direct: @direct)
81
+ @js.get_msg(@stream,
82
+ subject: subject,
83
+ seq: params[:revision],
84
+ direct: @direct)
62
85
  end
63
86
 
64
87
  entry = Entry.new(bucket: @name, key: key, value: msg.data, revision: msg.seq)
@@ -70,9 +93,9 @@ module NATS
70
93
  )
71
94
  end
72
95
 
73
- if not msg.headers.nil?
96
+ if !msg.headers.nil?
74
97
  op = msg.headers[KV_OP]
75
- if op == KV_DEL or op == KV_PURGE
98
+ if (op == KV_DEL) || (op == KV_PURGE)
76
99
  raise KeyDeletedError.new(entry: entry, op: op)
77
100
  end
78
101
  end
@@ -86,12 +109,16 @@ module NATS
86
109
  # put will place the new value for the key into the store
87
110
  # and return the revision number.
88
111
  def put(key, value)
112
+ raise InvalidKeyError if @validate_keys && !KeyValue.is_valid_key(key)
113
+
89
114
  ack = @js.publish("#{@pre}#{key}", value)
90
115
  ack.seq
91
116
  end
92
117
 
93
118
  # create will add the key/value pair iff it does not exist.
94
119
  def create(key, value)
120
+ raise InvalidKeyError if @validate_keys && !KeyValue.is_valid_key(key)
121
+
95
122
  pa = nil
96
123
  begin
97
124
  pa = update(key, value, last: 0)
@@ -122,7 +149,9 @@ module NATS
122
149
  EXPECTED_LAST_SUBJECT_SEQUENCE = "Nats-Expected-Last-Subject-Sequence"
123
150
 
124
151
  # update will update the value iff the latest revision matches.
125
- def update(key, value, params={})
152
+ def update(key, value, params = {})
153
+ raise InvalidKeyError if @validate_keys && !KeyValue.is_valid_key(key)
154
+
126
155
  hdrs = {}
127
156
  last = (params[:last] ||= 0)
128
157
  hdrs[EXPECTED_LAST_SUBJECT_SEQUENCE] = last.to_s
@@ -141,7 +170,9 @@ module NATS
141
170
  end
142
171
 
143
172
  # delete will place a delete marker and remove all previous revisions.
144
- def delete(key, params={})
173
+ def delete(key, params = {})
174
+ raise InvalidKeyError if @validate_keys && !KeyValue.is_valid_key(key)
175
+
145
176
  hdrs = {}
146
177
  hdrs[KV_OP] = KV_DEL
147
178
  last = (params[:last] ||= 0)
@@ -155,6 +186,8 @@ module NATS
155
186
 
156
187
  # purge will remove the key and all revisions.
157
188
  def purge(key)
189
+ raise InvalidKeyError if @validate_keys && !KeyValue.is_valid_key(key)
190
+
158
191
  hdrs = {}
159
192
  hdrs[KV_OP] = KV_PURGE
160
193
  hdrs[ROLLUP] = MSG_ROLLUP_SUBJECT
@@ -168,11 +201,310 @@ module NATS
168
201
  end
169
202
 
170
203
  Entry = Struct.new(:bucket, :key, :value, :revision, :delta, :created, :operation, keyword_init: true) do
171
- def initialize(opts={})
204
+ def initialize(opts = {})
172
205
  rem = opts.keys - members
173
206
  opts.delete_if { |k| rem.include?(k) }
174
- super(opts)
207
+ super
208
+ end
209
+ end
210
+
211
+ # watch will be signaled when any key is updated.
212
+ def watchall(params = {})
213
+ watch(">", params)
214
+ end
215
+
216
+ # keys returns the keys from a KeyValue store.
217
+ # Optionally filters the keys based on the provided filter list.
218
+ def keys(params = {})
219
+ params[:ignore_deletes] = true
220
+ params[:meta_only] = true
221
+
222
+ w = watchall(params)
223
+ got_keys = false
224
+
225
+ Enumerator.new do |y|
226
+ w.each do |entry|
227
+ break if entry.nil?
228
+ got_keys = true
229
+ y << entry.key
230
+ end
231
+ w.stop
232
+ raise NoKeysFoundError unless got_keys
233
+ end
234
+ end
235
+
236
+ # history retrieves the entries so far for a key.
237
+ def history(key, params = {})
238
+ params[:include_history] = true
239
+ w = watch(key, params)
240
+ got_keys = false
241
+
242
+ Enumerator.new do |y|
243
+ w.each do |entry|
244
+ break if entry.nil?
245
+ got_keys = true
246
+ y << entry
247
+ end
248
+ w.stop
249
+ raise NoKeysFoundError unless got_keys
250
+ end
251
+ end
252
+
253
+ STATUS_HDR = "Status"
254
+ DESC_HDR = "Description"
255
+ CTRL_STATUS = "100"
256
+ LAST_CONSUMER_SEQ_HDR = "Nats-Last-Consumer"
257
+ LAST_STREAM_SEQ_HDR = "Nats-Last-Stream"
258
+ CONSUMER_STALLED_HDR = "Nats-Consumer-Stalled"
259
+
260
+ # watch will be signaled when a key that matches the keys
261
+ # pattern is updated.
262
+ # The first update after starting the watch is nil in case
263
+ # there are no pending updates.
264
+ def watch(keys, params = {})
265
+ params[:meta_only] ||= false
266
+ params[:include_history] ||= false
267
+ params[:ignore_deletes] ||= false
268
+ params[:idle_heartbeat] ||= 5 # seconds
269
+ params[:inactive_threshold] ||= 5 * 60 # 5 minutes
270
+ subject = "#{@pre}#{keys}"
271
+ init_setup = new_cond
272
+ init_setup_done = false
273
+ nc = @js.nc
274
+ watcher = KeyWatcher.new(@js)
275
+
276
+ deliver_policy = if !params[:include_history]
277
+ "last_per_subject"
278
+ end
279
+
280
+ ordered = {
281
+ # basic ordered consumer.
282
+ flow_control: true,
283
+ ack_policy: "none",
284
+ max_deliver: 1,
285
+ ack_wait: 22 * 3600,
286
+ idle_heartbeat: params[:idle_heartbeat],
287
+ num_replicas: 1,
288
+ mem_storage: true,
289
+ manual_ack: true,
290
+ # watch related options.
291
+ deliver_policy: deliver_policy,
292
+ headers_only: params[:meta_only],
293
+ inactive_threshold: params[:inactive_threshold]
294
+ }
295
+
296
+ # watch_updates callback.
297
+ sub = @js.subscribe(subject, config: ordered) do |msg|
298
+ synchronize do
299
+ if !init_setup_done
300
+ init_setup.wait(@js.opts[:timeout])
301
+ end
302
+ end
303
+
304
+ # Control Message like Heartbeats and Flow Control
305
+ status = msg.header[STATUS_HDR] unless msg.header.nil?
306
+ if !status.nil? && status == CTRL_STATUS
307
+ desc = msg.header[DESC_HDR]
308
+ if desc.start_with?("Idle")
309
+ # A watcher is active if it continues to receive Idle Heartbeat messages.
310
+ #
311
+ # Status: 100
312
+ # Description: Idle Heartbeat
313
+ # Nats-Last-Consumer: 185
314
+ # Nats-Last-Stream: 185
315
+ #
316
+ watcher.synchronize { watcher._active = true }
317
+ elsif desc.start_with?("FlowControl")
318
+ # HMSG _INBOX.q6Y3JAFxOnNJi4QdwQnFtg 2 $JS.FC.KV_TEST.t00CunIG.GT4W 36 36
319
+ # NATS/1.0 100 FlowControl Request
320
+ nc.publish(msg.reply)
321
+ end
322
+ # Skip processing the control message
323
+ next
324
+ end
325
+
326
+ # Track sequences
327
+ meta = msg.metadata
328
+ watcher.synchronize { watcher._active = true }
329
+ # Track the sequences
330
+ watcher.synchronize do
331
+ watcher._dseq = meta.sequence.consumer + 1
332
+ watcher._sseq = meta.sequence.stream
333
+ end
334
+
335
+ # Keys() handling
336
+ op = nil
337
+ if msg.header && msg.header[KV_OP]
338
+ op = msg.header[KV_OP]
339
+ if params[:ignore_deletes]
340
+ if (op == KV_PURGE) || (op == KV_DEL)
341
+ if (meta.num_pending == 0) && !watcher._init_done
342
+ # Push this to unblock enumerators.
343
+ watcher._updates.push(nil)
344
+ watcher._init_done = true
345
+ end
346
+ next
347
+ end
348
+ end
349
+ end
350
+
351
+ # Convert the msg into an Entry.
352
+ key = msg.subject[@pre.size...msg.subject.size]
353
+ entry = Entry.new(
354
+ bucket: @name,
355
+ key: key,
356
+ value: msg.data,
357
+ revision: meta.sequence.stream,
358
+ delta: meta.num_pending,
359
+ created: meta.timestamp,
360
+ operation: op
361
+ )
362
+ watcher._updates.push(entry)
363
+
364
+ # When there are no more updates send an empty marker
365
+ # to signal that it is done, this will unblock iterators.
366
+ if (meta.num_pending == 0) && !watcher._init_done
367
+ watcher._updates.push(nil)
368
+ watcher._init_done = true
369
+ end
370
+ end # end of callback
371
+ watcher._sub = sub
372
+
373
+ # Snapshot the deliver subject for the consumer.
374
+ deliver_subject = sub.subject
375
+
376
+ # Check from consumer info what is the number of messages
377
+ # awaiting to be consumed to send the initial signal marker.
378
+ stream_name = nil
379
+ begin
380
+ cinfo = sub.consumer_info
381
+ stream_name = cinfo.stream_name
382
+
383
+ synchronize do
384
+ init_setup_done = true
385
+ # If no delivered and/or pending messages, then signal
386
+ # that this is the start.
387
+ # The consumer subscription will start receiving messages
388
+ # so need to check those that have already made it.
389
+ received = sub.delivered
390
+ init_setup.signal
391
+
392
+ # When there are no more updates send an empty marker
393
+ # to signal that it is done, this will unblock iterators.
394
+ if (cinfo.num_pending == 0) && (received == 0)
395
+ watcher._updates.push(nil)
396
+ watcher._init_done = true
397
+ end
398
+ end
399
+ rescue => err
400
+ # cancel init
401
+ sub.unsubscribe
402
+ raise err
175
403
  end
404
+
405
+ # Need to handle reconnect if missing too many heartbeats.
406
+ hb_interval = params[:idle_heartbeat] * 2
407
+ watcher._hb_task = Concurrent::TimerTask.new(execution_interval: hb_interval) do |task|
408
+ task.shutdown if nc.closed?
409
+ next unless nc.connected?
410
+
411
+ # Wait for all idle heartbeats to be received, one of them would have
412
+ # toggled the state of the consumer back to being active.
413
+ active = watcher.synchronize {
414
+ current = watcher._active
415
+ # A heartbeat or another incoming message needs to toggle back.
416
+ watcher._active = false
417
+ current
418
+ }
419
+ if !active
420
+ ccreq = ordered.dup
421
+ ccreq[:deliver_policy] = "by_start_sequence"
422
+ ccreq[:opt_start_seq] = watcher._sseq
423
+ ccreq[:deliver_subject] = deliver_subject
424
+ ccreq[:idle_heartbeat] = ordered[:idle_heartbeat]
425
+ ccreq[:inactive_threshold] = ordered[:inactive_threshold]
426
+
427
+ should_recreate = false
428
+ begin
429
+ # Check if the original is still present, if it is then do not recreate.
430
+ begin
431
+ sub.consumer_info
432
+ rescue ::NATS::JetStream::Error::ConsumerNotFound => e
433
+ e.stream ||= sub.jsi.stream
434
+ e.consumer ||= sub.jsi.consumer
435
+ @js.nc.send(:err_cb_call, @js.nc, e, sub)
436
+ should_recreate = true
437
+ end
438
+ next unless should_recreate
439
+
440
+ # Recreate consumer that went away after a restart.
441
+ cinfo = @js.add_consumer(stream_name, ccreq)
442
+ sub.jsi.consumer = cinfo.name
443
+ watcher.synchronize { watcher._dseq = 1 }
444
+ rescue => e
445
+ # Dispatch to the error NATS client error callback.
446
+ @js.nc.send(:err_cb_call, @js.nc, e, sub)
447
+ end
448
+ end
449
+ rescue => e
450
+ # WRN: Unexpected error
451
+ @js.nc.send(:err_cb_call, @js.nc, e, sub)
452
+ end
453
+ watcher._hb_task.execute
454
+
455
+ watcher
456
+ end
457
+ end
458
+
459
+ class KeyWatcher
460
+ include MonitorMixin
461
+ include Enumerable
462
+
463
+ attr_accessor :received, :pending, :_sub, :_updates, :_init_done, :_watcher_cond
464
+ attr_accessor :_sseq, :_dseq, :_active, :_hb_task
465
+
466
+ def initialize(js)
467
+ super() # required to initialize monitor
468
+ @js = js
469
+ @_sub = nil
470
+ @_updates = SizedQueue.new(256)
471
+ @_init_done = false
472
+ @pending = nil
473
+ # Ordered consumer related
474
+ @_dseq = 1
475
+ @_sseq = 0
476
+ @_cmeta = nil
477
+ @_fcr = 0
478
+ @_fciseq = 0
479
+ @_active = true
480
+ @_hb_task = nil
481
+ end
482
+
483
+ def stop
484
+ @_hb_task.shutdown
485
+ @_sub.unsubscribe
486
+ end
487
+
488
+ def updates(params = {})
489
+ params[:timeout] ||= 5
490
+ result = nil
491
+ MonotonicTime.with_nats_timeout(params[:timeout]) do
492
+ result = @_updates.pop(timeout: params[:timeout])
493
+ end
494
+
495
+ result
496
+ end
497
+
498
+ # Implements Enumerable.
499
+ def each
500
+ loop do
501
+ result = @_updates.pop
502
+ yield result
503
+ end
504
+ end
505
+
506
+ def take(n)
507
+ super.take(n).reject { |entry| entry.nil? }
176
508
  end
177
509
  end
178
510
  end
data/lib/nats/io/msg.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright 2016-2021 The NATS Authors
2
4
  # Licensed under the Apache License, Version 2.0 (the "License");
3
5
  # you may not use this file except in compliance with the License.
@@ -10,36 +12,34 @@
10
12
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
13
  # See the License for the specific language governing permissions and
12
14
  # limitations under the License.
13
- #
14
- require_relative 'jetstream'
15
15
 
16
16
  module NATS
17
17
  class Msg
18
18
  attr_accessor :subject, :reply, :data, :header
19
+ attr_accessor :nc, :sub
19
20
 
20
- # Enhance it with ack related methods from JetStream to ack msgs.
21
- include JetStream::Msg::AckMethods
22
-
23
- def initialize(opts={})
21
+ def initialize(opts = {})
24
22
  @subject = opts[:subject]
25
- @reply = opts[:reply]
26
- @data = opts[:data]
27
- @header = opts[:header]
28
- @nc = opts[:nc]
29
- @sub = opts[:sub]
30
- @ackd = false
31
- @meta = nil
23
+ @reply = opts[:reply]
24
+ @data = opts[:data]
25
+ @header = opts[:header]
26
+ @nc = opts[:nc]
27
+ @sub = opts[:sub]
28
+
29
+ # JS related
30
+ @ackd = false
31
+ @meta = nil
32
32
  end
33
33
 
34
- def respond(data='')
34
+ def respond(data = "")
35
35
  return unless @nc
36
- if self.header
37
- dmsg = self.dup
38
- dmsg.subject = self.reply
36
+ if header
37
+ dmsg = dup
38
+ dmsg.subject = reply
39
39
  dmsg.data = data
40
40
  @nc.publish_msg(dmsg)
41
41
  else
42
- @nc.publish(self.reply, data)
42
+ @nc.publish(reply, data)
43
43
  end
44
44
  end
45
45
 
@@ -50,7 +50,7 @@ module NATS
50
50
 
51
51
  def inspect
52
52
  hdr = ", header=#{@header}" if @header
53
- dot = '...' if @data.length > 10
53
+ dot = "..." if @data.length > 10
54
54
  dat = "#{data.slice(0, 10)}#{dot}"
55
55
  "#<NATS::Msg(subject: \"#{@subject}\", reply: \"#{@reply}\", data: #{dat.inspect}#{hdr})>"
56
56
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright 2016-2018 The NATS Authors
2
4
  # Licensed under the Apache License, Version 2.0 (the "License");
3
5
  # you may not use this file except in compliance with the License.
@@ -14,27 +16,26 @@
14
16
 
15
17
  module NATS
16
18
  module Protocol
17
-
18
- MSG = /\AMSG\s+([^\s]+)\s+([^\s]+)\s+(([^\s]+)[^\S\r\n]+)?(\d+)\r\n/i
19
- HMSG = /\AHMSG\s+([^\s]+)\s+([^\s]+)\s+(([^\s]+)[^\S\r\n]+)?([\d]+)\s+(\d+)\r\n/i
20
- OK = /\A\+OK\s*\r\n/i
21
- ERR = /\A-ERR\s+('.+')?\r\n/i
22
- PING = /\APING\s*\r\n/i
23
- PONG = /\APONG\s*\r\n/i
24
- INFO = /\AINFO\s+([^\r\n]+)\r\n/i
25
- UNKNOWN = /\A(.*)\r\n/
19
+ MSG = /\AMSG\s+([^\s]+)\s+([^\s]+)\s+(([^\s]+)[^\S\r\n]+)?(\d+)\r\n/i
20
+ HMSG = /\AHMSG\s+([^\s]+)\s+([^\s]+)\s+(([^\s]+)[^\S\r\n]+)?(\d+)\s+(\d+)\r\n/i
21
+ OK = /\A\+OK\s*\r\n/i
22
+ ERR = /\A-ERR\s+('.+')?\r\n/i
23
+ PING = /\APING\s*\r\n/i
24
+ PONG = /\APONG\s*\r\n/i
25
+ INFO = /\AINFO\s+([^\r\n]+)\r\n/i
26
+ UNKNOWN = /\A(.*)\r\n/
26
27
 
27
28
  AWAITING_CONTROL_LINE = 1
28
- AWAITING_MSG_PAYLOAD = 2
29
+ AWAITING_MSG_PAYLOAD = 2
29
30
 
30
- CR_LF = ("\r\n".freeze)
31
- CR_LF_SIZE = (CR_LF.bytesize)
31
+ CR_LF = "\r\n"
32
+ CR_LF_SIZE = CR_LF.bytesize
32
33
 
33
- PING_REQUEST = ("PING#{CR_LF}".freeze)
34
- PONG_RESPONSE = ("PONG#{CR_LF}".freeze)
34
+ PING_REQUEST = "PING#{CR_LF}".freeze
35
+ PONG_RESPONSE = "PONG#{CR_LF}".freeze
35
36
 
36
- SUB_OP = ('SUB'.freeze)
37
- EMPTY_MSG = (''.freeze)
37
+ SUB_OP = "SUB"
38
+ EMPTY_MSG = ""
38
39
 
39
40
  class Parser
40
41
  def initialize(nc)
@@ -55,7 +56,7 @@ module NATS
55
56
 
56
57
  def parse(data)
57
58
  @buf = @buf ? @buf << data : data
58
- while (@buf)
59
+ while @buf
59
60
  case @parse_state
60
61
  when AWAITING_CONTROL_LINE
61
62
  case @buf
@@ -91,23 +92,22 @@ module NATS
91
92
  # If we are here we do not have a complete line yet that we understand.
92
93
  return
93
94
  end
94
- @buf = nil if (@buf && @buf.empty?)
95
+ @buf = nil if @buf && @buf.empty?
95
96
 
96
97
  when AWAITING_MSG_PAYLOAD
97
- return unless (@needed && @buf.bytesize >= (@needed + CR_LF_SIZE))
98
+ return unless @needed && @buf.bytesize >= (@needed + CR_LF_SIZE)
98
99
  if @header_needed
99
100
  hbuf = @buf.slice(0, @header_needed)
100
- payload = @buf.slice(@header_needed, (@needed-@header_needed))
101
+ payload = @buf.slice(@header_needed, @needed - @header_needed)
101
102
  @nc.send(:process_msg, @sub, @sid, @reply, payload, hbuf)
102
- @buf = @buf.slice((@needed + CR_LF_SIZE), @buf.bytesize)
103
103
  else
104
104
  @nc.send(:process_msg, @sub, @sid, @reply, @buf.slice(0, @needed), nil)
105
- @buf = @buf.slice((@needed + CR_LF_SIZE), @buf.bytesize)
106
105
  end
106
+ @buf = @buf.slice(@needed + CR_LF_SIZE, @buf.bytesize)
107
107
 
108
108
  @sub = @sid = @reply = @needed = @header_needed = nil
109
109
  @parse_state = AWAITING_CONTROL_LINE
110
- @buf = nil if (@buf && @buf.empty?)
110
+ @buf = nil if @buf && @buf.empty?
111
111
  end
112
112
  end
113
113
  end
data/lib/nats/io/rails.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "rails"
2
4
 
3
5
  module NATS