nats-pure 2.3.0 → 2.5.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 +3 -0
  3. data/README.md +10 -3
  4. data/lib/nats/client.rb +7 -2
  5. data/lib/nats/io/client.rb +304 -282
  6. data/lib/nats/io/errors.rb +2 -0
  7. data/lib/nats/io/jetstream/api.rb +54 -47
  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 +104 -83
  15. data/lib/nats/io/jetstream/msg/ack.rb +15 -9
  16. data/lib/nats/io/jetstream/msg/ack_methods.rb +24 -22
  17. data/lib/nats/io/jetstream/msg/metadata.rb +9 -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 +125 -54
  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 +359 -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 +25 -22
  31. data/lib/nats/io/version.rb +4 -2
  32. data/lib/nats/io/websocket.rb +10 -8
  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 -5
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,315 @@ 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
175
208
  end
176
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
+ #
331
+ # $JS.ACK.KV_TEST.CKRGrWpf.1.10.10.1739859923871837000.0
332
+ #
333
+ tokens = msg.reply.split(".")
334
+ sseq = tokens[5]
335
+ dseq = tokens[6]
336
+ watcher.synchronize do
337
+ watcher._dseq = dseq.to_i + 1
338
+ watcher._sseq = sseq.to_i
339
+ end
340
+
341
+ # Keys() handling
342
+ op = nil
343
+ if msg.header && msg.header[KV_OP]
344
+ op = msg.header[KV_OP]
345
+ if params[:ignore_deletes]
346
+ if (op == KV_PURGE) || (op == KV_DEL)
347
+ if (meta.num_pending == 0) && !watcher._init_done
348
+ # Push this to unblock enumerators.
349
+ watcher._updates.push(nil)
350
+ watcher._init_done = true
351
+ end
352
+ next
353
+ end
354
+ end
355
+ end
356
+
357
+ # Convert the msg into an Entry.
358
+ key = msg.subject[@pre.size...msg.subject.size]
359
+ entry = Entry.new(
360
+ bucket: @name,
361
+ key: key,
362
+ value: msg.data,
363
+ revision: meta.sequence.stream,
364
+ delta: meta.num_pending,
365
+ created: meta.timestamp,
366
+ operation: op
367
+ )
368
+ watcher._updates.push(entry)
369
+
370
+ # When there are no more updates send an empty marker
371
+ # to signal that it is done, this will unblock iterators.
372
+ if (meta.num_pending == 0) && !watcher._init_done
373
+ watcher._updates.push(nil)
374
+ watcher._init_done = true
375
+ end
376
+ end # end of callback
377
+ watcher._sub = sub
378
+
379
+ # Snapshot the deliver subject for the consumer.
380
+ deliver_subject = sub.subject
381
+
382
+ # Check from consumer info what is the number of messages
383
+ # awaiting to be consumed to send the initial signal marker.
384
+ stream_name = nil
385
+ begin
386
+ cinfo = sub.consumer_info
387
+ stream_name = cinfo.stream_name
388
+
389
+ synchronize do
390
+ init_setup_done = true
391
+ # If no delivered and/or pending messages, then signal
392
+ # that this is the start.
393
+ # The consumer subscription will start receiving messages
394
+ # so need to check those that have already made it.
395
+ received = sub.delivered
396
+ init_setup.signal
397
+
398
+ # When there are no more updates send an empty marker
399
+ # to signal that it is done, this will unblock iterators.
400
+ if (cinfo.num_pending == 0) && (received == 0)
401
+ watcher._updates.push(nil)
402
+ watcher._init_done = true
403
+ end
404
+ end
405
+ rescue => err
406
+ # cancel init
407
+ sub.unsubscribe
408
+ raise err
409
+ end
410
+
411
+ # Need to handle reconnect if missing too many heartbeats.
412
+ hb_interval = params[:idle_heartbeat] * 2
413
+ watcher._hb_task = Concurrent::TimerTask.new(execution_interval: hb_interval) do |task|
414
+ task.shutdown if nc.closed?
415
+ next unless nc.connected?
416
+
417
+ # Wait for all idle heartbeats to be received, one of them would have
418
+ # toggled the state of the consumer back to being active.
419
+ active = watcher.synchronize {
420
+ current = watcher._active
421
+ # A heartbeat or another incoming message needs to toggle back.
422
+ watcher._active = false
423
+ current
424
+ }
425
+ if !active
426
+ ccreq = ordered.dup
427
+ ccreq[:deliver_policy] = "by_start_sequence"
428
+ ccreq[:opt_start_seq] = watcher._sseq
429
+ ccreq[:deliver_subject] = deliver_subject
430
+ ccreq[:idle_heartbeat] = ordered[:idle_heartbeat]
431
+ ccreq[:inactive_threshold] = ordered[:inactive_threshold]
432
+
433
+ should_recreate = false
434
+ begin
435
+ # Check if the original is still present, if it is then do not recreate.
436
+ begin
437
+ sub.consumer_info
438
+ rescue ::NATS::JetStream::Error::ConsumerNotFound => e
439
+ e.stream ||= sub.jsi.stream
440
+ e.consumer ||= sub.jsi.consumer
441
+ @js.nc.send(:err_cb_call, @js.nc, e, sub)
442
+ should_recreate = true
443
+ end
444
+ next unless should_recreate
445
+
446
+ # Recreate consumer that went away after a restart.
447
+ cinfo = @js.add_consumer(stream_name, ccreq)
448
+ sub.jsi.consumer = cinfo.name
449
+ watcher.synchronize { watcher._dseq = 1 }
450
+ rescue => e
451
+ # Dispatch to the error NATS client error callback.
452
+ @js.nc.send(:err_cb_call, @js.nc, e, sub)
453
+ end
454
+ end
455
+ rescue => e
456
+ # WRN: Unexpected error
457
+ @js.nc.send(:err_cb_call, @js.nc, e, sub)
458
+ end
459
+ watcher._hb_task.execute
460
+
461
+ watcher
462
+ end
463
+ end
464
+
465
+ class KeyWatcher
466
+ include MonitorMixin
467
+ include Enumerable
468
+ attr_accessor :received, :pending, :_sub, :_updates, :_init_done, :_watcher_cond
469
+ attr_accessor :_sseq, :_dseq, :_active, :_hb_task
470
+
471
+ def initialize(js)
472
+ super() # required to initialize monitor
473
+ @js = js
474
+ @_sub = nil
475
+ @_updates = SizedQueue.new(256)
476
+ @_init_done = false
477
+ @pending = nil
478
+ # Ordered consumer related
479
+ @_dseq = 1
480
+ @_sseq = 0
481
+ @_cmeta = nil
482
+ @_fcr = 0
483
+ @_fciseq = 0
484
+ @_active = true
485
+ @_hb_task = nil
486
+ end
487
+
488
+ def stop
489
+ @_hb_task.shutdown
490
+ @_sub.unsubscribe
491
+ end
492
+
493
+ def updates(params = {})
494
+ params[:timeout] ||= 5
495
+ result = nil
496
+ MonotonicTime.with_nats_timeout(params[:timeout]) do
497
+ result = @_updates.pop(timeout: params[:timeout])
498
+ end
499
+
500
+ result
501
+ end
502
+
503
+ # Implements Enumerable.
504
+ def each
505
+ loop do
506
+ result = @_updates.pop
507
+ yield result
508
+ end
509
+ end
510
+
511
+ def take(n)
512
+ super.take(n).reject { |entry| entry.nil? }
513
+ end
177
514
  end
178
515
  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