rsmp 0.4.6 → 0.5.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c975252bdf622f3e6bd545e2eca8f4fcf2c9c5a5f04acf159cd162882bf4f491
4
- data.tar.gz: ec26cc377c14611389a8f0c9b82cf2137a76523cca3ce445513bc7e2ea5466d6
3
+ metadata.gz: b6d24a7240bcac60427bcbbe4743142db412338e847c08e35803401092dd162b
4
+ data.tar.gz: 264711c071e36998292d2fad4ece08bb79938ec3a7fc997016c7ca46f7f14ad1
5
5
  SHA512:
6
- metadata.gz: 243e70433cafb8573fc9e4ffcd6a55ece6d369df6201041e6481f88810ffdf907830197854330a28c91cfc2075b767cf379590bd7f80214189a94d4a0b3c03e9
7
- data.tar.gz: ac86a77b8685491c0f08fb14bd4e15552eabf73d177b7f5415b809628bab2684a71e4df3aad451fc2822c808b44d036d4c4910998b369cb373f0a4b8b2294ece
6
+ metadata.gz: a5cd2cd0d6afacb699c5c2e1da799356e33b0bfaa207ff7496221039c83143c572b2d5088a225cf9bec92036f890ad54bc099511d2344961a26742f69dec332d
7
+ data.tar.gz: 47d1c8569e07a5603cb8622f19f042d9bf637da0c30e08e8dd706c1b2b61efbba9d2f74f4de91e93ea969f89048dbb890ca5cc1718870708c6811a4ee6c3bd52
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rsmp (0.4.6)
4
+ rsmp (0.5.3)
5
5
  async (~> 1.29.1)
6
6
  async-io (~> 1.32.1)
7
7
  colorize (~> 0.8.1)
@@ -19,6 +19,7 @@ log:
19
19
  text: true
20
20
  direction: true
21
21
  level: false
22
+ debug: true
22
23
  json: true
23
24
  acknowledgements: false
24
25
  watchdogs: false
data/config/tlc.yaml CHANGED
@@ -38,6 +38,7 @@ log:
38
38
  ip: false
39
39
  site_id: true
40
40
  level: false
41
+ debug: true
41
42
  text: true
42
43
  direction: true
43
44
  json: true
@@ -7,7 +7,12 @@ module RSMP
7
7
 
8
8
  def initialize proxy, options={}
9
9
  super proxy, options
10
- @options = options.clone
10
+ @options = {
11
+ cancel: {
12
+ schema_error: true,
13
+ disconnect: false,
14
+ }
15
+ }.deep_merge options
11
16
  @ingoing = options[:ingoing] == nil ? true : options[:ingoing]
12
17
  @outgoing = options[:outgoing] == nil ? false : options[:outgoing]
13
18
  @condition = Async::Notification.new
@@ -82,7 +87,7 @@ module RSMP
82
87
  if message.is_a?(MessageNotAck)
83
88
  if message.attribute('oMId') == @options[:m_id]
84
89
  m_id_short = RSMP::Message.shorten_m_id @options[:m_id], 8
85
- @error = RSMP::MessageRejected.new("#{@title} #{m_id_short} was rejected: #{message.attribute('rea')}")
90
+ @error = RSMP::MessageRejected.new("#{@title} #{m_id_short} was rejected with '#{message.attribute('rea')}'")
86
91
  complete
87
92
  end
88
93
  false
@@ -123,6 +128,43 @@ module RSMP
123
128
  @condition.signal
124
129
  end
125
130
 
131
+ # The proxy experienced some error.
132
+ # Check if this should cause us to cancel.
133
+ def notify_error error, options={}
134
+ case error
135
+ when RSMP::SchemaError
136
+ notify_schema_error error, options
137
+ when RSMP::ConnectionError
138
+ notify_disconnect error, options
139
+ end
140
+ end
141
+
142
+ # Cancel if we received e schema error for a message type we're collecting
143
+ def notify_schema_error error, options
144
+ return unless @options.dig(:cancel,:schema_error)
145
+ message = options[:message]
146
+ return unless message
147
+ klass = message.class.name.split('::').last
148
+ return unless [@options[:type]].flatten.include? klass
149
+ @proxy.log "Collect cancelled due to schema error in #{klass} #{message.m_id_short}", level: :debug
150
+ cancel error
151
+ end
152
+
153
+ # Cancel if we received e notificaiton about a disconnect
154
+ def notify_disconnect error, options
155
+ return unless @options.dig(:cancel,:disconnect)
156
+ @proxy.log "Collect cancelled due to a connection error: #{error.to_s}", level: :debug
157
+ cancel error
158
+ end
159
+
160
+ # Abort collection
161
+ def cancel error
162
+ @error = error if error
163
+ @done = false
164
+ @proxy.remove_listener self
165
+ @condition.signal
166
+ end
167
+
126
168
  # Store a message in the result array
127
169
  def keep message
128
170
  @messages << message
@@ -12,6 +12,9 @@ module RSMP
12
12
  def notify message
13
13
  end
14
14
 
15
+ def notify_error error, options={}
16
+ end
17
+
15
18
  def listen &block
16
19
  @proxy.add_listener self
17
20
  yield
@@ -102,6 +102,5 @@ module RSMP
102
102
  end
103
103
  @proxy.log "#{@title.capitalize} collect reached #{summary}", level: :debug
104
104
  end
105
-
106
105
  end
107
106
  end
@@ -12,6 +12,26 @@ module RSMP
12
12
 
13
13
  def initialize_distributor
14
14
  @listeners = []
15
+ @defer_notify = false
16
+ @notify_queue = []
17
+ end
18
+
19
+ def clear_deferred_notify &block
20
+ @notify_queue = []
21
+ end
22
+
23
+ def deferred_notify &block
24
+ was, @defer_notify = @defer_notify, true
25
+ yield
26
+ dequeue_notify
27
+ ensure
28
+ @defer_notify = was
29
+ end
30
+
31
+ def dequeue_notify
32
+ @notify_queue.each { |message| notify_without_defer message }
33
+ ensure
34
+ @notify_queue = []
15
35
  end
16
36
 
17
37
  def add_listener listener
@@ -25,7 +45,20 @@ module RSMP
25
45
  end
26
46
 
27
47
  def notify message
48
+ raise ArgumentError unless message
49
+ if @defer_notify
50
+ @notify_queue << message
51
+ else
52
+ notify_without_defer message
53
+ end
54
+ end
55
+
56
+ def notify_without_defer message
28
57
  @listeners.each { |listener| listener.notify message }
29
58
  end
59
+
60
+ def distribute_error error, options={}
61
+ @listeners.each { |listener| listener.notify_error error, options }
62
+ end
30
63
  end
31
64
  end
@@ -1,4 +1,9 @@
1
1
  module RSMP
2
+
3
+ # Class that represents an RMSP component.
4
+ # Currently this class is used by both SiteProxy and SupervisorProxy, and can
5
+ # therefore represent either a local or remote (proxy) component.
6
+
2
7
  class Component
3
8
  include Inspect
4
9
 
@@ -19,6 +24,7 @@ module RSMP
19
24
  @grouped = grouped
20
25
  @alarms = {}
21
26
  @statuses = {}
27
+ @subscribes = {}
22
28
  clear_aggregated_status
23
29
  end
24
30
 
@@ -83,5 +89,74 @@ module RSMP
83
89
  @alarms[code] = message
84
90
  end
85
91
  end
92
+
93
+ # Handle an incoming status respone, by storing the values
94
+ def handle_status_response message
95
+ store_status message, check_repeated: false
96
+ end
97
+
98
+ # Handle an incoming status update, by storing the values
99
+ def handle_status_update message
100
+ store_status message, check_repeated: true
101
+ end
102
+
103
+ # Our proxy subscribed to status updates
104
+ # Store update rates, so we can check for repeated alarm if we asked for updates only
105
+ # when there's a change, not on a regular interval.
106
+ # After subscribing, an update status us send regarless of whether values changes,
107
+ # and we store that.
108
+ def handle_status_subscribe status_list
109
+ status_list.each do |item|
110
+ sCI, n, uRt = item['sCI'], item['n'], item['uRt']
111
+
112
+ # record the update rate, so we can check for repeated status values if rate is zero
113
+ @subscribes[sCI] ||= {}
114
+ @subscribes[sCI][n] = {'uRt'=>uRt}
115
+
116
+ # record that we expect an upeate, even though the value might not change
117
+ @statuses[sCI] ||= {}
118
+ @statuses[sCI][n] ||= {}
119
+ @statuses[sCI][n][:initial] = true
120
+ end
121
+ #pp @subscribes
122
+ end
123
+
124
+ # Our proxy unsubscribed to status updates.
125
+ # Update our list of update rates.
126
+ def handle_status_unsubscribe status_list
127
+ status_list.each do |item|
128
+ sCI, n = item['sCI'], item['n']
129
+ if @subscribes[sCI]
130
+ @subscribes[sCI].delete n
131
+ end
132
+ if @subscribes[sCI].empty?
133
+ @subscribes.delete sCI
134
+ end
135
+
136
+ # remove any mark that would allow the next update to be a repeat
137
+ item = @statuses.dig sCI, n
138
+ item.delete(:initial) if item
139
+ end
140
+ end
141
+
142
+ # Store the latest status update values, optionally
143
+ # checking that we're not receiving unchanged values if we're subscribed
144
+ # wit updates only on change
145
+ def store_status message, check_repeated:
146
+ message.attribute('sS').each do |item|
147
+ sCI, n, s, q = item['sCI'], item['n'], item['s'], item['q']
148
+ uRt = @subscribes.dig(sCI,n,'uRt')
149
+ new_values = {'s'=>s,'q'=>q}
150
+ old_values = @statuses.dig(sCI,n)
151
+ if check_repeated && uRt.to_i == 0
152
+ if new_values == old_values
153
+ raise RSMP::RepeatedStatusError.new "no change for #{sCI} '#{n}'"
154
+ end
155
+ end
156
+ @statuses[sCI] ||= {}
157
+ @statuses[sCI][n] = new_values
158
+ end
159
+ end
160
+
86
161
  end
87
162
  end
data/lib/rsmp/error.rb CHANGED
@@ -32,6 +32,9 @@ module RSMP
32
32
  class FatalError < Error
33
33
  end
34
34
 
35
+ class HandshakeError < FatalError
36
+ end
37
+
35
38
  class NotReady < Error
36
39
  end
37
40
 
@@ -55,4 +58,7 @@ module RSMP
55
58
 
56
59
  class RepeatedAlarmError < Error
57
60
  end
61
+
62
+ class RepeatedStatusError < Error
63
+ end
58
64
  end
data/lib/rsmp/logger.rb CHANGED
@@ -5,6 +5,7 @@ module RSMP
5
5
 
6
6
  def initialize settings={}
7
7
  defaults = {
8
+ 'prefix'=>nil,
8
9
  'active'=>false,
9
10
  'path'=>nil,
10
11
  'stream'=>nil,
@@ -20,7 +21,8 @@ module RSMP
20
21
  'json'=>false,
21
22
  'debug'=>false,
22
23
  'statistics'=>false,
23
- 'hide_ip_and_port' => false
24
+ 'hide_ip_and_port' => false,
25
+ 'acknowledgements' => false
24
26
  }
25
27
  if settings
26
28
  @settings = defaults.merge settings
@@ -139,6 +141,7 @@ module RSMP
139
141
 
140
142
  def build_output item
141
143
  parts = []
144
+ parts << "#{@settings['prefix']} " if @settings['prefix']
142
145
  parts << item[:index].to_s.ljust(7) if @settings["index"] == true
143
146
  parts << item[:author].to_s.ljust(13) if @settings["author"] == true
144
147
  parts << Clock.to_s(item[:timestamp]).ljust(24) unless @settings["timestamp"] == false
data/lib/rsmp/message.rb CHANGED
@@ -142,7 +142,7 @@ module RSMP
142
142
  def validate schemas
143
143
  errors = RSMP::Schemer.validate attributes, schemas
144
144
  if errors
145
- error_string = errors.compact.join(', ').strip
145
+ error_string = errors.map {|item| item.reject {|e| e=='' } }.compact.join(', ').strip
146
146
  raise SchemaError.new error_string
147
147
  end
148
148
  end
data/lib/rsmp/node.rb CHANGED
@@ -38,6 +38,10 @@ module RSMP
38
38
  def do_deferred item
39
39
  end
40
40
 
41
+ def clear_deferred
42
+ @deferred.clear
43
+ end
44
+
41
45
  def do_start task
42
46
  task.annotate self.class.to_s
43
47
  @task = task
data/lib/rsmp/proxy.rb CHANGED
@@ -86,6 +86,7 @@ module RSMP
86
86
  return if @state == :stopped
87
87
  set_state :stopping
88
88
  stop_tasks
89
+ notify_error ConnectionError.new("Connection was closed")
89
90
  ensure
90
91
  close_socket
91
92
  clear
@@ -237,6 +238,7 @@ module RSMP
237
238
  if now > latest
238
239
  log "No acknowledgements for #{message.type} #{message.m_id_short} within #{timeout} seconds", level: :error
239
240
  stop
241
+ notify_error MissingAcknowledgment.new('No ack')
240
242
  end
241
243
  end
242
244
  end
@@ -315,13 +317,23 @@ module RSMP
315
317
  !skip.include?(klass)
316
318
  end
317
319
 
320
+ def process_deferred
321
+ node.process_deferred
322
+ end
323
+
324
+ def verify_sequence message
325
+ expect_version_message(message) unless @version_determined
326
+ end
327
+
318
328
  def process_packet json
319
329
  attributes = Message.parse_attributes json
320
330
  message = Message.build attributes, json
321
331
  message.validate(get_schemas) if should_validate_ingoing_message?(message)
322
- notify message
323
- expect_version_message(message) unless @version_determined
324
- process_message message
332
+ verify_sequence message
333
+ deferred_notify do
334
+ notify message
335
+ process_message message
336
+ end
325
337
  process_deferred
326
338
  message
327
339
  rescue InvalidPacket => e
@@ -338,20 +350,23 @@ module RSMP
338
350
  rescue SchemaError, RSMP::Schemer::Error => e
339
351
  str = "Received invalid #{message.type}, schema errors: #{e.message}"
340
352
  log str, message: message, level: :warning
341
- notify_error e.exception("#{str} #{message.json}")
353
+ notify_error e.exception("#{str} #{message.json}"), message: message
342
354
  dont_acknowledge message, str
343
355
  message
344
356
  rescue InvalidMessage => e
345
357
  str = "Received", "invalid #{message.type}, #{e.message}"
346
- notify_error e.exception("#{str} #{message.json}")
358
+ notify_error e.exception("#{str} #{message.json}"), message: message
347
359
  dont_acknowledge message, str
348
360
  message
349
361
  rescue FatalError => e
350
362
  str = "Rejected #{message.type},"
351
- notify_error e.exception("#{str} #{message.json}")
363
+ notify_error e.exception("#{str} #{message.json}"), message: message
352
364
  dont_acknowledge message, str, "#{e.message}"
353
365
  stop
354
366
  message
367
+ ensure
368
+ clear_deferred_notify
369
+ node.clear_deferred
355
370
  end
356
371
 
357
372
  def process_message message
@@ -402,7 +417,7 @@ module RSMP
402
417
  if candidates.any?
403
418
  @rsmp_version = candidates.sort_by { |v| Gem::Version.new(v) }.last # pick latest version
404
419
  else
405
- raise FatalError.new "RSMP versions [#{message.versions.join(',')}] requested, but only [#{versions.join(',')}] supported."
420
+ raise HandshakeError.new "RSMP versions [#{message.versions.join(',')}] requested, but only [#{versions.join(',')}] supported."
406
421
  end
407
422
  end
408
423
 
@@ -546,7 +561,7 @@ module RSMP
546
561
 
547
562
  def expect_version_message message
548
563
  unless message.is_a?(Version) || message.is_a?(MessageAck) || message.is_a?(MessageNotAck)
549
- raise FatalError.new "Version must be received first"
564
+ raise HandshakeError.new "Version must be received first"
550
565
  end
551
566
  end
552
567
 
@@ -578,7 +593,7 @@ module RSMP
578
593
  # will be raised in the parent task, and caught by rspec.
579
594
  # rspec will then show the error and record the test as failed
580
595
  m_id_short = RSMP::Message.shorten_m_id m_id, 8
581
- result = RSMP::MessageRejected.new "Aggregated status request #{m_id_short} was rejected: #{message.attribute('rea')}"
596
+ result = RSMP::MessageRejected.new "Aggregated status request #{m_id_short} was rejected with '#{message.attribute('rea')}'"
582
597
  next true # done, no more messages wanted
583
598
  end
584
599
  elsif message.is_a?(MessageAck)
@@ -587,6 +602,5 @@ module RSMP
587
602
  false
588
603
  end
589
604
  end
590
-
591
605
  end
592
606
  end
@@ -68,6 +68,10 @@ module RSMP
68
68
  else
69
69
  super message
70
70
  end
71
+ rescue RSMP::RepeatedAlarmError, RSMP::RepeatedStatusError => e
72
+ str = "Rejected #{message.type} message,"
73
+ dont_acknowledge message, str, "#{e}"
74
+ notify_error e.exception("#{str}#{e.message} #{message.json}")
71
75
  end
72
76
 
73
77
  def process_command_response message
@@ -75,10 +79,6 @@ module RSMP
75
79
  acknowledge message
76
80
  end
77
81
 
78
- def process_deferred
79
- supervisor.process_deferred
80
- end
81
-
82
82
  def version_accepted message
83
83
  log "Received Version message for site #{@site_id}", message: message, level: :log
84
84
  start_timer
@@ -149,10 +149,6 @@ module RSMP
149
149
  asp = message.attribute("aSp")
150
150
  log "Received #{message.type}, #{alarm_code} #{asp} [#{status}]", message: message, level: :log
151
151
  acknowledge message
152
- rescue RSMP::RepeatedAlarmError => e
153
- str = "Rejected #{message.type} message, "
154
- dont_acknowledge message, str, "#{e}"
155
- notify_error e.exception("#{str}#{e.message} #{message.json}")
156
152
  end
157
153
 
158
154
  def version_acknowledged
@@ -191,6 +187,8 @@ module RSMP
191
187
  end
192
188
 
193
189
  def process_status_response message
190
+ component = find_component message.attribute("cId")
191
+ component.handle_status_response message
194
192
  log "Received #{message.type}", message: message, level: :log
195
193
  acknowledge message
196
194
  end
@@ -202,7 +200,7 @@ module RSMP
202
200
  message
203
201
  end
204
202
 
205
- def subscribe_to_status component, status_list, options={}
203
+ def subscribe_to_status component_id, status_list, options={}
206
204
  validate_ready 'subscribe to status'
207
205
  m_id = options[:m_id] || RSMP::Message.make_m_id
208
206
 
@@ -210,10 +208,13 @@ module RSMP
210
208
  # but must to remove from the subscribe message
211
209
  subscribe_list = status_list.map { |item| item.slice('sCI','n','uRt') }
212
210
 
211
+ component = find_component component_id
212
+ component.handle_status_subscribe subscribe_list
213
+
213
214
  message = RSMP::StatusSubscribe.new({
214
215
  "ntsOId" => '',
215
216
  "xNId" => '',
216
- "cId" => component,
217
+ "cId" => component_id,
217
218
  "sS" => subscribe_list,
218
219
  'mId' => m_id
219
220
  })
@@ -222,12 +223,15 @@ module RSMP
222
223
  end
223
224
  end
224
225
 
225
- def unsubscribe_to_status component, status_list, options={}
226
+ def unsubscribe_to_status component_id, status_list, options={}
227
+ component = find_component component_id
228
+ component.handle_status_subscribe status_list
229
+
226
230
  validate_ready 'unsubscribe to status'
227
231
  message = RSMP::StatusUnsubscribe.new({
228
232
  "ntsOId" => '',
229
233
  "xNId" => '',
230
- "cId" => component,
234
+ "cId" => component_id,
231
235
  "sS" => status_list
232
236
  })
233
237
  send_message message, validate: options[:validate]
@@ -235,6 +239,8 @@ module RSMP
235
239
  end
236
240
 
237
241
  def process_status_update message
242
+ component = find_component message.attribute("cId")
243
+ component.handle_status_update message
238
244
  log "Received #{message.type}", message: message, level: :log
239
245
  acknowledge message
240
246
  end
@@ -333,6 +339,7 @@ module RSMP
333
339
 
334
340
  def notify_error e, options={}
335
341
  @supervisor.notify_error e, options if @supervisor
342
+ distribute_error e, options
336
343
  end
337
344
 
338
345
  def collect_alarms parent_task, options={}
@@ -172,7 +172,11 @@ module RSMP
172
172
  id = peek_version_message protocol
173
173
  proxy = find_site id
174
174
  if proxy
175
- proxy.revive settings
175
+ if proxy.connected?
176
+ raise ConnectionError.new("Site #{id} alredy connected from port #{proxy.port}")
177
+ else
178
+ proxy.revive settings
179
+ end
176
180
  else
177
181
  check_max_sites
178
182
  proxy = build_proxy settings.merge(site_id:id) # keep the id learned by peeking above
@@ -246,7 +250,7 @@ module RSMP
246
250
 
247
251
  def check_site_already_connected site_id
248
252
  site = find_site(site_id)
249
- raise FatalError.new "Site '#{site_id}' already connected" if site != nil && site != self
253
+ raise HandshakeError.new "Site '#{site_id}' already connected" if site != nil && site != self
250
254
  end
251
255
 
252
256
  def site_id_to_site_setting site_id
@@ -256,7 +260,7 @@ module RSMP
256
260
  return settings
257
261
  end
258
262
  end
259
- raise FatalError.new "site id #{site_id} unknown"
263
+ raise HandshakeError.new "site id #{site_id} unknown"
260
264
  end
261
265
 
262
266
  def ip_to_site_settings ip
@@ -86,10 +86,6 @@ module RSMP
86
86
  dont_acknowledge message, '', e.to_s
87
87
  end
88
88
 
89
- def process_deferred
90
- site.process_deferred
91
- end
92
-
93
89
  def acknowledged_first_ingoing message
94
90
  # TODO
95
91
  # aggregateds status should only be send for later version of rsmp
@@ -229,7 +225,6 @@ module RSMP
229
225
  def process_status_subcribe message
230
226
  log "Received #{message.type}", message: message, level: :log
231
227
 
232
-
233
228
  # @status_subscriptions is organized by component/code/name, for example:
234
229
  #
235
230
  # {"AA+BBCCC=DDDEE002"=>{"S001"=>["number"]}}
@@ -238,21 +233,14 @@ module RSMP
238
233
  # for each component, containing all the requested statuses
239
234
 
240
235
  update_list = {}
241
-
242
236
  component = message.attributes["cId"]
243
-
244
237
  @status_subscriptions[component] ||= {}
245
238
  update_list[component] ||= {}
246
-
247
- subs = @status_subscriptions[component]
248
239
  now = Time.now # internal timestamp
249
240
 
250
241
  message.attributes["sS"].each do |arg|
251
242
  sCI = arg["sCI"]
252
243
  subcription = {interval: arg["uRt"].to_i, last_sent_at: now}
253
- subs[sCI] ||= {}
254
- subs[sCI][arg["n"]] = subcription
255
-
256
244
  update_list[component][sCI] ||= []
257
245
  update_list[component][sCI] << arg["n"]
258
246
  end
@@ -291,11 +279,15 @@ module RSMP
291
279
  end
292
280
  end
293
281
 
294
- def store_last_sent_status component, code, name, value
282
+ def store_last_sent_status message
283
+ component_id = message.attribute('cId')
295
284
  @last_status_sent ||= {}
296
- @last_status_sent[component] ||= {}
297
- @last_status_sent[component][code] ||= {}
298
- @last_status_sent[component][code][name] = value
285
+ @last_status_sent[component_id] ||= {}
286
+ message.attribute('sS').each do |item|
287
+ sCI, n, s = item['sCI'], item['n'], item['s']
288
+ @last_status_sent[component_id][sCI] ||= {}
289
+ @last_status_sent[component_id][sCI][n] = s
290
+ end
299
291
  end
300
292
 
301
293
  def status_update_timer now
@@ -316,7 +308,6 @@ module RSMP
316
308
  last_sent = fetch_last_sent_status component, code, name
317
309
  if current != last_sent
318
310
  should_send = true
319
- store_last_sent_status component, code, name, current
320
311
  end
321
312
  else
322
313
  # send at regular intervals
@@ -360,6 +351,7 @@ module RSMP
360
351
  "sS"=>sS
361
352
  })
362
353
  send_message update
354
+ store_last_sent_status update
363
355
  end
364
356
  end
365
357
 
data/lib/rsmp/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module RSMP
2
- VERSION = "0.4.6"
2
+ VERSION = "0.5.3"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rsmp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.6
4
+ version: 0.5.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Emil Tin
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-11-01 00:00:00.000000000 Z
11
+ date: 2021-11-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: async