nats-pure 2.4.0 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
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 -3
  5. data/lib/nats/io/client.rb +303 -280
  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 +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 +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 +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