nats-pure 0.7.2 → 2.0.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.
data/lib/nats/io/kv.rb ADDED
@@ -0,0 +1,172 @@
1
+ # Copyright 2021 The NATS Authors
2
+ # Licensed under the Apache License, Version 2.0 (the "License");
3
+ # you may not use this file except in compliance with the License.
4
+ # You may obtain a copy of the License at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software
9
+ # distributed under the License is distributed on an "AS IS" BASIS,
10
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ # See the License for the specific language governing permissions and
12
+ # limitations under the License.
13
+ #
14
+ require_relative 'errors'
15
+
16
+ module NATS
17
+ class KeyValue
18
+ KV_OP = "KV-Operation"
19
+ KV_DEL = "DEL"
20
+ KV_PURGE = "PURGE"
21
+ MSG_ROLLUP_SUBJECT = "sub"
22
+ MSG_ROLLUP_ALL = "all"
23
+
24
+ def initialize(opts={})
25
+ @name = opts[:name]
26
+ @stream = opts[:stream]
27
+ @pre = opts[:pre]
28
+ @js = opts[:js]
29
+ end
30
+
31
+ # When a key is not found because it was deleted.
32
+ class KeyDeletedError < NATS::Error; end
33
+
34
+ # When there was no bucket present.
35
+ class BucketNotFoundError < NATS::Error; end
36
+
37
+ # When it is an invalid bucket.
38
+ class BadBucketError < NATS::Error; end
39
+
40
+ # get returns the latest value for the key.
41
+ def get(key)
42
+ msg = @js.get_last_msg(@stream, "#{@pre}#{key}")
43
+ entry = Entry.new(bucket: @name, key: key, value: msg.data, revision: msg.seq)
44
+
45
+ if not msg.headers.nil?
46
+ op = msg.headers[KV_OP]
47
+ raise KeyDeletedError.new("nats: key was deleted") if op == KV_DEL or op == KV_PURGE
48
+ end
49
+
50
+ entry
51
+ end
52
+
53
+ # put will place the new value for the key into the store
54
+ # and return the revision number.
55
+ def put(key, value)
56
+ @js.publish("#{@pre}#{key}", value)
57
+ end
58
+
59
+ # delete will place a delete marker and remove all previous revisions.
60
+ def delete(key)
61
+ hdrs = {}
62
+ hdrs[KV_OP] = KV_DEL
63
+ @js.publish("#{@pre}#{key}", header: hdrs)
64
+ end
65
+
66
+ # status retrieves the status and configuration of a bucket.
67
+ def status
68
+ info = @js.stream_info(@stream)
69
+ BucketStatus.new(info, @name)
70
+ end
71
+
72
+ Entry = Struct.new(:bucket, :key, :value, :revision, keyword_init: true) do
73
+ def initialize(opts={})
74
+ rem = opts.keys - members
75
+ opts.delete_if { |k| rem.include?(k) }
76
+ super(opts)
77
+ end
78
+ end
79
+
80
+ class BucketStatus
81
+ attr_reader :bucket
82
+
83
+ def initialize(info, bucket)
84
+ @nfo = info
85
+ @bucket = bucket
86
+ end
87
+
88
+ def values
89
+ @nfo.state.messages
90
+ end
91
+
92
+ def history
93
+ @nfo.config.max_msgs_per_subject
94
+ end
95
+
96
+ def ttl
97
+ @nfo.config.max_age / 1_000_000_000
98
+ end
99
+ end
100
+
101
+ module API
102
+ KeyValueConfig = Struct.new(:bucket, :description, :max_value_size,
103
+ :history, :ttl, :max_bytes, :storage, :replicas,
104
+ keyword_init: true) do
105
+ def initialize(opts={})
106
+ rem = opts.keys - members
107
+ opts.delete_if { |k| rem.include?(k) }
108
+ super(opts)
109
+ end
110
+ end
111
+ end
112
+
113
+ module Manager
114
+ def key_value(bucket)
115
+ stream = "KV_#{bucket}"
116
+ begin
117
+ si = stream_info(stream)
118
+ rescue NATS::JetStream::Error::NotFound
119
+ raise BucketNotFoundError.new("nats: bucket not found")
120
+ end
121
+ if si.config.max_msgs_per_subject < 1
122
+ raise BadBucketError.new("nats: bad bucket")
123
+ end
124
+
125
+ KeyValue.new(
126
+ name: bucket,
127
+ stream: stream,
128
+ pre: "$KV.#{bucket}.",
129
+ js: self,
130
+ )
131
+ end
132
+
133
+ def create_key_value(config)
134
+ config = if not config.is_a?(JetStream::API::StreamConfig)
135
+ KeyValue::API::KeyValueConfig.new(config)
136
+ else
137
+ config
138
+ end
139
+ config.history ||= 1
140
+ config.replicas ||= 1
141
+ if config.ttl
142
+ config.ttl = config.ttl * 1_000_000_000
143
+ end
144
+
145
+ stream = JetStream::API::StreamConfig.new(
146
+ name: "KV_#{config.bucket}",
147
+ subjects: ["$KV.#{config.bucket}.>"],
148
+ max_msgs_per_subject: config.history,
149
+ max_bytes: config.max_bytes,
150
+ max_age: config.ttl,
151
+ max_msg_size: config.max_value_size,
152
+ storage: config.storage,
153
+ num_replicas: config.replicas,
154
+ allow_rollup_hdrs: true,
155
+ deny_delete: true,
156
+ )
157
+ resp = add_stream(stream)
158
+
159
+ KeyValue.new(
160
+ name: config.bucket,
161
+ stream: stream.name,
162
+ pre: "$KV.#{config.bucket}.",
163
+ js: self,
164
+ )
165
+ end
166
+
167
+ def delete_key_value(bucket)
168
+ delete_stream("KV_#{bucket}")
169
+ end
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,56 @@
1
+ # Copyright 2016-2021 The NATS Authors
2
+ # Licensed under the Apache License, Version 2.0 (the "License");
3
+ # you may not use this file except in compliance with the License.
4
+ # You may obtain a copy of the License at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software
9
+ # distributed under the License is distributed on an "AS IS" BASIS,
10
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ # See the License for the specific language governing permissions and
12
+ # limitations under the License.
13
+ #
14
+ require_relative 'js'
15
+
16
+ module NATS
17
+ class Msg
18
+ attr_accessor :subject, :reply, :data, :header
19
+
20
+ # Enhance it with ack related methods from JetStream to ack msgs.
21
+ include JetStream::Msg::AckMethods
22
+
23
+ def initialize(opts={})
24
+ @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
32
+ end
33
+
34
+ def respond(data='')
35
+ return unless @nc
36
+ if self.header
37
+ dmsg = self.dup
38
+ dmsg.subject = self.reply
39
+ dmsg.data = data
40
+ @nc.publish_msg(dmsg)
41
+ else
42
+ @nc.publish(self.reply, data)
43
+ end
44
+ end
45
+
46
+ def respond_msg(msg)
47
+ return unless @nc
48
+ @nc.publish_msg(msg)
49
+ end
50
+
51
+ def inspect
52
+ hdr = ", header=#{@header}" if @header
53
+ "#<NATS::Msg(subject: \"#{@subject}\", reply: \"#{@reply}\", data: #{@data.slice(0, 10).inspect}#{hdr})>"
54
+ end
55
+ end
56
+ end
@@ -71,22 +71,22 @@ module NATS
71
71
  @buf = $'
72
72
  when ERR
73
73
  @buf = $'
74
- @nc.process_err($1)
74
+ @nc.send(:process_err, $1)
75
75
  when PING
76
76
  @buf = $'
77
- @nc.process_ping
77
+ @nc.send(:process_ping)
78
78
  when PONG
79
79
  @buf = $'
80
- @nc.process_pong
80
+ @nc.send(:process_pong)
81
81
  when INFO
82
82
  @buf = $'
83
83
  # First INFO message is processed synchronously on connect,
84
84
  # and onwards we would be receiving asynchronously INFO commands
85
85
  # signaling possible changes in the topology of the NATS cluster.
86
- @nc.process_info($1)
86
+ @nc.send(:process_info, $1)
87
87
  when UNKNOWN
88
88
  @buf = $'
89
- @nc.process_err("Unknown protocol: #{$1}")
89
+ @nc.send(:process_err, "Unknown protocol: #{$1}")
90
90
  else
91
91
  # If we are here we do not have a complete line yet that we understand.
92
92
  return
@@ -98,10 +98,10 @@ module NATS
98
98
  if @header_needed
99
99
  hbuf = @buf.slice(0, @header_needed)
100
100
  payload = @buf.slice(@header_needed, (@needed-@header_needed))
101
- @nc.process_msg(@sub, @sid, @reply, payload, hbuf)
101
+ @nc.send(:process_msg, @sub, @sid, @reply, payload, hbuf)
102
102
  @buf = @buf.slice((@needed + CR_LF_SIZE), @buf.bytesize)
103
103
  else
104
- @nc.process_msg(@sub, @sid, @reply, @buf.slice(0, @needed), nil)
104
+ @nc.send(:process_msg, @sub, @sid, @reply, @buf.slice(0, @needed), nil)
105
105
  @buf = @buf.slice((@needed + CR_LF_SIZE), @buf.bytesize)
106
106
  end
107
107
 
@@ -0,0 +1,92 @@
1
+ # Copyright 2016-2021 The NATS Authors
2
+ # Licensed under the Apache License, Version 2.0 (the "License");
3
+ # you may not use this file except in compliance with the License.
4
+ # You may obtain a copy of the License at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software
9
+ # distributed under the License is distributed on an "AS IS" BASIS,
10
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ # See the License for the specific language governing permissions and
12
+ # limitations under the License.
13
+ #
14
+
15
+ module NATS
16
+
17
+ # A Subscription represents interest in a given subject.
18
+ #
19
+ # @example Create NATS subscription with callback.
20
+ # require 'nats/client'
21
+ #
22
+ # nc = NATS.connect("demo.nats.io")
23
+ # sub = nc.subscribe("foo") do |msg|
24
+ # puts "Received [#{msg.subject}]: #{}"
25
+ # end
26
+ #
27
+ class Subscription
28
+ include MonitorMixin
29
+
30
+ attr_accessor :subject, :queue, :future, :callback, :response, :received, :max, :pending, :sid
31
+ attr_accessor :pending_queue, :pending_size, :wait_for_msgs_t, :wait_for_msgs_cond, :is_slow_consumer
32
+ attr_accessor :pending_msgs_limit, :pending_bytes_limit
33
+ attr_accessor :nc
34
+ attr_accessor :jsi
35
+ attr_accessor :closed
36
+
37
+ def initialize
38
+ super # required to initialize monitor
39
+ @subject = ''
40
+ @queue = nil
41
+ @future = nil
42
+ @callback = nil
43
+ @response = nil
44
+ @received = 0
45
+ @max = nil
46
+ @pending = nil
47
+ @sid = nil
48
+ @nc = nil
49
+ @closed = nil
50
+
51
+ # State from async subscriber messages delivery
52
+ @pending_queue = nil
53
+ @pending_size = 0
54
+ @pending_msgs_limit = nil
55
+ @pending_bytes_limit = nil
56
+ @wait_for_msgs_t = nil
57
+ @is_slow_consumer = false
58
+
59
+ # Sync subscriber
60
+ @wait_for_msgs_cond = nil
61
+ end
62
+
63
+ # Auto unsubscribes the server by sending UNSUB command and throws away
64
+ # subscription in case already present and has received enough messages.
65
+ def unsubscribe(opt_max=nil)
66
+ @nc.send(:unsubscribe, self, opt_max)
67
+ end
68
+
69
+ # next_msg blocks and waiting for the next message to be received.
70
+ def next_msg(opts={})
71
+ timeout = opts[:timeout] ||= 0.5
72
+ synchronize do
73
+ return @pending_queue.pop if not @pending_queue.empty?
74
+
75
+ # Wait for a bit until getting a signal.
76
+ MonotonicTime::with_nats_timeout(timeout) do
77
+ wait_for_msgs_cond.wait(timeout)
78
+ end
79
+
80
+ if not @pending_queue.empty?
81
+ return @pending_queue.pop
82
+ else
83
+ raise NATS::Timeout
84
+ end
85
+ end
86
+ end
87
+
88
+ def inspect
89
+ "#<NATS::Subscription(subject: \"#{@subject}\", queue: \"#{@queue}\", sid: #{@sid})>"
90
+ end
91
+ end
92
+ end
@@ -1,4 +1,4 @@
1
- # Copyright 2016-2021 The NATS Authors
1
+ # Copyright 2016-2022 The NATS Authors
2
2
  # Licensed under the Apache License, Version 2.0 (the "License");
3
3
  # you may not use this file except in compliance with the License.
4
4
  # You may obtain a copy of the License at
@@ -14,9 +14,13 @@
14
14
 
15
15
  module NATS
16
16
  module IO
17
- # NOTE: These are all announced to the server on CONNECT
18
- VERSION = "0.7.2"
19
- LANG = "#{RUBY_ENGINE}#{RUBY_VERSION}".freeze
17
+ # VERSION is the version of the client announced on CONNECT to the server.
18
+ VERSION = "2.0.0".freeze
19
+
20
+ # LANG is the lang runtime of the client announced on CONNECT to the server.
21
+ LANG = "#{RUBY_ENGINE}#{RUBY_VERSION}".freeze
22
+
23
+ # PROTOCOL is the supported version of the protocol in the client.
20
24
  PROTOCOL = 1
21
25
  end
22
26
  end
data/lib/nats.rb ADDED
@@ -0,0 +1,39 @@
1
+ # Copyright 2021 The NATS Authors
2
+ # Licensed under the Apache License, Version 2.0 (the "License");
3
+ # you may not use this file except in compliance with the License.
4
+ # You may obtain a copy of the License at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software
9
+ # distributed under the License is distributed on an "AS IS" BASIS,
10
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ # See the License for the specific language governing permissions and
12
+ # limitations under the License.
13
+ #
14
+
15
+ require 'nats/io/client'
16
+ require 'nats/nuid'
17
+
18
+ # A thread safe Ruby client for the NATS messaging system (https://nats.io).
19
+ #
20
+ # @example Service example
21
+ # nc = NATS.connect("demo.nats.io")
22
+ # nc.subscribe("foo") do |msg|
23
+ # msg.respond("Hello World")
24
+ # end
25
+ #
26
+ # resp = nc.request("foo")
27
+ # puts "Received: #{msg.data}"
28
+ #
29
+ #
30
+ # @example Stream example
31
+ # nc = NATS.connect("demo.nats.io")
32
+ # sub = nc.subscribe("foo")
33
+ #
34
+ # nc.publish("foo")
35
+ # msg = sub.next_msg
36
+ # puts "Received: #{msg.data}"
37
+ #
38
+ module NATS
39
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nats-pure
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.2
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Waldemar Quevedo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-08-25 00:00:00.000000000 Z
11
+ date: 2022-01-29 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: NATS is an open-source, high-performance, lightweight cloud messaging
14
14
  system.
@@ -18,8 +18,15 @@ executables: []
18
18
  extensions: []
19
19
  extra_rdoc_files: []
20
20
  files:
21
+ - lib/nats.rb
22
+ - lib/nats/client.rb
21
23
  - lib/nats/io/client.rb
24
+ - lib/nats/io/errors.rb
25
+ - lib/nats/io/js.rb
26
+ - lib/nats/io/kv.rb
27
+ - lib/nats/io/msg.rb
22
28
  - lib/nats/io/parser.rb
29
+ - lib/nats/io/subscription.rb
23
30
  - lib/nats/io/version.rb
24
31
  - lib/nats/nuid.rb
25
32
  homepage: https://nats.io