rsmp 0.29.0 → 0.31.0

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: 128be65ff4ece071590a4ed3dc113cbfdf1a697115033a02a113d894935a6cad
4
- data.tar.gz: 4f025722621f8edb751dcbf6f38acbb146c26fddac53a29f14673e8751609ddb
3
+ metadata.gz: fd4daeeb9fb712ab275d4b0711a6ea69e766124a2fcd55ea955eeb1c46f05ed3
4
+ data.tar.gz: a47c38c49b46fb2ad412ced388f6e654653292dd439ea31d2bbaf33988f024f0
5
5
  SHA512:
6
- metadata.gz: 89086c19b7ec7b64f234ec19ec70688ae08e62524ec307b66ccb27e159c3d5ea9eee5bcb360de41d457ad3db3174d9b31d159802a92759596cf10dc24525dcc6
7
- data.tar.gz: 6ee2489e60c466c9de16d4698b0cfef4e8bb065e9f358b9e0f55133759eab6b5adab248f0ab29ca021bae3cf9138b32c54d955ef2fbf629e2815a6a7f57947f1
6
+ metadata.gz: ec4a4057b53a27d12e1c81af4623b03bcd7cf93b39bad72f1d228c1a0dfd74495541abae496a3fa783c159a16df2e161eb0f5fbaa13d96daa8d8c99a847a0f9f
7
+ data.tar.gz: 704c11a6d854a0fb48f9cb6d84514ac40791bb85fe56186b9ff217e876458be6a214fbb69b8c3aaf09db35bd334345f7ca6ee31da8f9fbf717a3ae790e77252a
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rsmp (0.29.0)
4
+ rsmp (0.31.0)
5
5
  async (~> 2.12.0)
6
6
  async-io (~> 1.43.0)
7
7
  colorize (~> 1.1)
@@ -16,16 +16,16 @@ GEM
16
16
  cucumber (>= 8.0, < 10.0)
17
17
  rspec-expectations (~> 3.4)
18
18
  thor (~> 1.0)
19
- async (2.12.0)
19
+ async (2.12.1)
20
20
  console (~> 1.25, >= 1.25.2)
21
21
  fiber-annotation
22
- io-event (~> 1.6)
22
+ io-event (~> 1.6, >= 1.6.5)
23
23
  async-io (1.43.2)
24
24
  async
25
25
  bigdecimal (3.1.8)
26
26
  builder (3.3.0)
27
27
  colorize (1.1.0)
28
- console (1.25.2)
28
+ console (1.27.0)
29
29
  fiber-annotation
30
30
  fiber-local (~> 1.1)
31
31
  json
@@ -64,9 +64,9 @@ GEM
64
64
  fiber-annotation (0.2.0)
65
65
  fiber-local (1.1.0)
66
66
  fiber-storage
67
- fiber-storage (0.1.2)
67
+ fiber-storage (1.0.0)
68
68
  hana (1.3.7)
69
- io-event (1.6.4)
69
+ io-event (1.6.5)
70
70
  json (2.7.2)
71
71
  json_schemer (2.3.0)
72
72
  bigdecimal
@@ -3,30 +3,45 @@ You often need to collect messages or responses. The collector classes are used
3
3
 
4
4
  A collector can collect ingoing and/or outgoing messages.
5
5
 
6
- An object that includes the Notifier module (or implements the same functionality) must be provided when you construct a Collected. The collector will attach itself to this notifier when it starts collecting, to receive messages. The SiteProxy and SupervisorProxy classes both include the Notifier module, and can therefore be used as message sources.
6
+ An object that includes the Distributor module (or implements the same functionality) must be provided when you construct a Collected. The collector will attach itself to this distributor when it starts collecting, to receive messages. The SiteProxy and SupervisorProxy classes both include the Distributor module, and can therefore be used as message sources.
7
7
 
8
8
  Messages that match the relevant criteria are stored by the collector.
9
9
 
10
- When the collection is done, the collector detaches from the notifier, and returns the status.
10
+ When the collection is done, the collector detaches from the distributor, and returns the status.
11
11
 
12
12
 
13
13
  ## Collector
14
- Class uses for collecting messages filtered by message type, direction and/or component id. A block can be used for custom filtering.
14
+ Class used for collecting messages filtered by message type, direction and/or component id. A block can be used for custom filtering.
15
15
 
16
16
  You can choose to collect a specific number of message and/or for a specific duration.
17
17
 
18
18
  A collector has a status, which is `:ready` initialialy. When you start collecting, it changes to `:collecting`. It will be `:ok` once collection completes successfully, or `:cancel` if it was cancelled to to some error or by a filter block.
19
19
 
20
20
  ### Initialization
21
- When you create a collector, you specify the messages types you want to collect.
22
- You can also specify ingoing and/or outgoing direction and the RSMP component.
21
+ When you create a collector, you provide a Filter to specify the messages types you want to collect. You can also specify ingoing and/or outgoing direction and the RSMP component.
23
22
 
24
23
  ```ruby
25
- collector = MessageCollector.new notifier, num: 10, ingoing: true, outgoing: true
24
+ collector = MessageCollector.new(distributor,
25
+ num: 10,
26
+ filter: Filter.new(ingoing: true, outgoing: true)
26
27
  ```
27
28
 
28
29
  num: The number of messages to collect. If not provided, a timeout must be set instead.
29
- timeout: The number of seconds to collect
30
+ filter: filter to identify the types of messages to look for.
31
+
32
+ ### Filter
33
+ The Filter class is used to filter messages according to message type, direction and component.
34
+
35
+ ```ruby
36
+ filter = Filter.new(
37
+ type: 'Alarm',
38
+ ingoing: true,
39
+ outgoing: false,
40
+ component: 'DL1'
41
+ )
42
+ ```
43
+
44
+ type: a string, or an array of string, specifiying one or more RSMP message types.
30
45
  ingoing: Whether to collect ingoing messages. Defaults to true
31
46
  outgoing: Whether to collect outgoing messages. Defaults to true
32
47
  component: An RSMP component id.
@@ -48,7 +63,7 @@ result = collector.wait
48
63
  ```
49
64
 
50
65
  ### Custom filtering
51
- You can use a block to do extra filtering. The block will be callled for each messages that fulfils the correct message type, direction and component id.
66
+ You can use a block to do extra filtering. The block will be callled for each messages that passes the Filter provided when initializing the collector.
52
67
 
53
68
  The block must return nil or a list of symbols to indicate whether the message should be kept, and whether collection should be cancelled.
54
69
 
@@ -69,7 +84,7 @@ Exceptions in the block will cause the collector to abort. If the collect! or wa
69
84
  The method collect!() will raise exceptions in case of errors, and will return the collect message directly.
70
85
 
71
86
  ```ruby
72
- message = collector.collect # => collected message.
87
+ message = collector.collect! # => collected message.
73
88
  ```
74
89
 
75
90
  Similar, `wait!()` will raise an exception in case of timeouts or errors:
@@ -181,7 +196,7 @@ result[:collector].messages # => list of collected messages
181
196
 
182
197
  ### Processing responses
183
198
  If you pass a block, the block will be used to construct a collector. The block will be called for each matching status item received.
184
- Collection will continue until the block returns :cancel, or it times.
199
+ Collection will continue until the block returns :cancel, or it times out.
185
200
 
186
201
  ```ruby
187
202
  options = {
@@ -1,23 +1,23 @@
1
1
  # Message distribution
2
2
 
3
- Proxy - - Notifier --> Listeners
3
+ Proxy - - Distributor --> Receivers
4
4
 
5
- A proxy distributes messages to listeners, when they are installed.
5
+ A proxy distributes messages to receivers, when they are installed.
6
6
 
7
- Collectors are special listenerws that waits for specific message, and are used to implement methods for waiting for RMSP responses, statuses, alarms, etc.
7
+ Collectors are special receiverws that waits for specific message, and are used to implement methods for waiting for RMSP responses, statuses, alarms, etc.
8
8
 
9
- Note that Archive is not a listener, and does not receive messages via the Notifier. Instead the Archive gets and stores messages via the log() interface in the Logging module. The reason is that the items that the Archive and the Logger contain other data as well as the message, like error messages, warnings, text descriptions, colors codes, etc. The Distributor and Receiver handles only Message objects.
9
+ Note that Archive is not a receiver, and does not receive messages via the Distributor. Instead the Archive gets and stores messages via the log() interface in the Logging module. The reason is that the items that the Archive and the Logger contain other data as well as the message, like error messages, warnings, text descriptions, colors codes, etc. The Distributor and Receiver handles only Message objects.
10
10
 
11
- ## Notifier
11
+ ## Distributor
12
12
  A module that handles distributing messages to receivers.
13
13
 
14
- ## Listener
14
+ ## Receiver
15
15
  Receives messages as long as it's installed into a distributor.
16
16
 
17
17
  ## Collector
18
- A subclass of Receiver that wait for specific messages. Once received
18
+ Includes the Receiver module to wait for specific messages. Once received
19
19
  the client receives the collection.
20
20
 
21
21
  ## Proxy
22
- A proxy includes the Notifier module and distributes each message to listerens after processing it.
22
+ A proxy includes the Distributor module and distributes each message to receivers after processing it.
23
23
 
@@ -3,14 +3,16 @@ module RSMP
3
3
  class AckCollector < Collector
4
4
  def initialize proxy, options={}
5
5
  raise ArgumentError.new("m_id must be provided") unless options[:m_id]
6
- required = { type: 'MessageAck', num: 1, title: 'message acknowledgement' }
7
- super proxy, options.merge(required)
6
+ super proxy, options.merge(
7
+ filter: RSMP::Filter.new(ingoing: true, outgoing: false, type: 'MessageAck'),
8
+ num: 1,
9
+ title: 'message acknowledgement'
10
+ )
8
11
  end
9
12
 
10
13
  # Check if we the MessageAck related to initiating request, identified by @m_id.
11
- def type_match? message
12
- return false if super(message) == false
13
- return message.attribute('oMId') == @options[:m_id]
14
+ def acceptable? message
15
+ super(message) && message.attribute('oMId') == @m_id
14
16
  end
15
17
  end
16
18
  end
@@ -2,8 +2,10 @@ module RSMP
2
2
  # Class for waiting for an aggregated status response
3
3
  class AggregatedStatusCollector < Collector
4
4
  def initialize proxy, options={}
5
- required = { type: 'AggregatedStatus', title: 'aggregated status' }
6
- super proxy, options.merge(required)
5
+ super proxy, options.merge(
6
+ filter: RSMP::Filter.new(ingoing: true, outgoing: false, type: 'AggregatedStatus'),
7
+ title: 'aggregated status'
8
+ )
7
9
  end
8
10
  end
9
11
  end
@@ -2,20 +2,20 @@ module RSMP
2
2
  # Class for waiting for specific command responses
3
3
  class AlarmCollector < Collector
4
4
  def initialize proxy,options={}
5
- @query = options[:query] || {}
5
+ @matcher = options[:matcher] || {}
6
6
  super proxy, options.merge(
7
- type: 'Alarm',
7
+ filter: RSMP::Filter.new(ingoing: true, outgoing: false, type: 'Alarm'),
8
8
  title:'alarm'
9
9
  )
10
10
  end
11
11
 
12
12
  # match alarm attributes
13
- def type_match? message
13
+ def acceptable? message
14
14
  return false if super(message) == false
15
15
 
16
16
  # match fixed attributes
17
17
  %w{cId aCId aSp ack aS sS cat pri}.each do |key|
18
- want = @query[key]
18
+ want = @matcher[key]
19
19
  got = message.attribute(key)
20
20
  case want
21
21
  when Regexp
@@ -26,13 +26,13 @@ module RSMP
26
26
  end
27
27
 
28
28
  # match rvs items
29
- if @query['rvs']
30
- query_rvs = @query['rvs']
29
+ if @matcher['rvs']
30
+ matcher_rvs = @matcher['rvs']
31
31
  message_rvs = message.attributes['rvs']
32
32
  return false unless message_rvs
33
- return false unless query_rvs.all? do |query_item|
33
+ return false unless matcher_rvs.all? do |matcher_item|
34
34
  return false unless message_rvs.any? do |message_item|
35
- next message_item['n'] == query_item['n'] && message_item['v'] == query_item['v']
35
+ next message_item['n'] == matcher_item['n'] && message_item['v'] == matcher_item['v']
36
36
  end
37
37
  next true
38
38
  end
@@ -41,8 +41,8 @@ module RSMP
41
41
  end
42
42
 
43
43
  # return a string that describes what we're collecting
44
- def describe_query
45
- "#{describe_num_and_type} #{ {component: @options[:component]}.merge(@query).compact }"
44
+ def describe_matcher
45
+ "#{describe_num_and_type} #{ {component: @options[:component]}.merge(@matcher).compact }"
46
46
  end
47
47
 
48
48
  end
@@ -1,7 +1,7 @@
1
1
  module RSMP
2
2
  # Match a specific alarm
3
- class AlarmQuery < Query
4
- # Match an alarm value against a query
3
+ class AlarmMatcher < Matcher
4
+ # Match an alarm value against a matcher
5
5
  def match? item
6
6
  return false if @want['n'] && @want['n'] != item['n']
7
7
  if @want['v'].is_a? Regexp
@@ -1,40 +1,53 @@
1
1
  module RSMP
2
2
 
3
- # Collects messages from a notifier.
3
+ # Collects messages from a distributor.
4
4
  # Can filter by message type, componet and direction.
5
5
  # Wakes up the once the desired number of messages has been collected.
6
- class Collector < Listener
7
- attr_reader :condition, :messages, :status, :error, :task
6
+ class Collector
7
+ include Receiver
8
+ attr_reader :condition, :messages, :status, :error, :task, :m_id
8
9
 
9
- def initialize notifier, options={}
10
- super notifier, options
10
+ def initialize distributor, options={}
11
+ initialize_receiver distributor, filter: options[:filter]
11
12
  @options = {
12
13
  cancel: {
13
14
  schema_error: true,
14
15
  disconnect: false,
15
16
  }
16
17
  }.deep_merge options
17
- @ingoing = options[:ingoing] == nil ? true : options[:ingoing]
18
- @outgoing = options[:outgoing] == nil ? false : options[:outgoing]
18
+ @timeout = options[:timeout]
19
+ @num = options[:num]
20
+ @m_id = options[:m_id]
19
21
  @condition = Async::Notification.new
20
- @title = options[:title] || [@options[:type]].flatten.join('/')
21
- if options[:task]
22
- @task = options[:task]
22
+ make_title options[:title]
23
+
24
+ if task
25
+ @task = task
23
26
  else
24
- # if notifier is a Proxy, or some other object that implements task(),
27
+ # if distributor is a Proxy, or some other object that implements task(),
25
28
  # then try to get the task that way
26
- if notifier.respond_to? 'task'
27
- @task = notifier.task
29
+ if distributor.respond_to? 'task'
30
+ @task = distributor.task
28
31
  end
29
32
  end
30
33
  reset
31
34
  end
32
35
 
36
+ def make_title title
37
+ if title
38
+ @title = title
39
+ elsif @filter
40
+ @title = [@filter.type].flatten.join('/')
41
+ else
42
+ @title = ""
43
+ end
44
+ end
45
+
33
46
  def use_task task
34
47
  @task = task
35
48
  end
36
49
 
37
- # Clear all query results
50
+ # Clear all matcher results
38
51
  def reset
39
52
  @messages = []
40
53
  @error = nil
@@ -95,7 +108,7 @@ module RSMP
95
108
  wait
96
109
  @status
97
110
  ensure
98
- @notifier.remove_listener self if @notifier
111
+ @distributor.remove_receiver self if @distributor
99
112
  end
100
113
 
101
114
  # Collect message
@@ -110,8 +123,8 @@ module RSMP
110
123
  # the desired messages have been collected, or timeout is reached.
111
124
  def wait
112
125
  if collecting?
113
- if @options[:timeout]
114
- @task.with_timeout(@options[:timeout]) { @condition.wait }
126
+ if @timeout
127
+ @task.with_timeout(@timeout) { @condition.wait }
115
128
  else
116
129
  @condition.wait
117
130
  end
@@ -136,39 +149,41 @@ module RSMP
136
149
  def start &block
137
150
  raise RuntimeError.new("Can't start collectimng unless ready (currently #{@status})") unless ready?
138
151
  @block = block
139
- raise ArgumentError.new("Num, timeout or block must be provided") unless @options[:num] || @options[:timeout] || @block
152
+ raise ArgumentError.new("Num, timeout or block must be provided") unless @num || @timeout || @block
140
153
  reset
141
154
  @status = :collecting
142
155
  log_start
143
- @notifier.add_listener self if @notifier
156
+ @distributor.add_receiver self if @distributor
144
157
  end
145
158
 
146
159
  # Build a string describing how how progress reached before timeout
147
160
  def describe_progress
148
161
  str = "#{identifier}: #{@title.capitalize} collection "
149
- str << "in response to #{@options[:m_id]} " if @options[:m_id]
150
- str << "didn't complete within #{@options[:timeout]}s, "
151
- str << "reached #{@messages.size}/#{@options[:num]}"
162
+ str << "in response to #{@m_id} " if @m_id
163
+ str << "didn't complete within #{@timeout}s, "
164
+ str << "reached #{@messages.size}/#{@num}"
152
165
  str
153
166
  end
154
167
 
155
168
  # Check if we receive a NotAck related to initiating request, identified by @m_id.
156
169
  def reject_not_ack message
157
- return unless @options[:m_id]
170
+ return unless @m_id
158
171
  if message.is_a?(MessageNotAck)
159
- if message.attribute('oMId') == @options[:m_id]
160
- m_id_short = RSMP::Message.shorten_m_id @options[:m_id], 8
172
+ if message.attribute('oMId') == @m_id
173
+ m_id_short = RSMP::Message.shorten_m_id @m_id, 8
161
174
  cancel RSMP::MessageRejected.new("#{@title} #{m_id_short} was rejected with '#{message.attribute('rea')}'")
162
- @notifier.log "#{identifier}: cancelled due to a NotAck", level: :debug
175
+ @distributor.log "#{identifier}: cancelled due to a NotAck", level: :debug
163
176
  true
164
177
  end
165
178
  end
166
179
  end
167
180
 
168
181
  # Handle message. and return true when we're done collecting
169
- def notify message
182
+ def receive message
170
183
  raise ArgumentError unless message
171
- raise RuntimeError.new("can't process message when status is :#{@status}, title: #{@title}, desc: #{describe}") unless ready? || collecting?
184
+ unless ready? || collecting?
185
+ raise RuntimeError.new("can't process message when status is :#{@status}, title: #{@title}, desc: #{describe}")
186
+ end
172
187
  if perform_match message
173
188
  if done?
174
189
  complete
@@ -185,8 +200,8 @@ module RSMP
185
200
  # Match message against our collection criteria
186
201
  def perform_match message
187
202
  return false if reject_not_ack(message)
188
- return false unless type_match?(message)
189
- #@notifier.log "#{identifier}: Looking at #{message.type} #{message.m_id_short}", level: :collect
203
+ return false unless acceptable?(message)
204
+ #@distributor.log "#{identifier}: Looking at #{message.type} #{message.m_id_short}", level: :collect
190
205
  if @block
191
206
  status = [@block.call(message)].flatten
192
207
  return unless collecting?
@@ -198,10 +213,10 @@ module RSMP
198
213
 
199
214
  # Have we collected the required number of messages?
200
215
  def done?
201
- @options[:num] && @messages.size >= @options[:num]
216
+ @num && @messages.size >= @num
202
217
  end
203
218
 
204
- # Called when we're done collecting. Remove ourself as a listener,
219
+ # Called when we're done collecting. Remove ourself as a receiver,
205
220
  # se we don't receive message notifications anymore
206
221
  def complete
207
222
  @status = :ok
@@ -214,39 +229,39 @@ module RSMP
214
229
  log_incomplete
215
230
  end
216
231
 
217
- # Remove ourself as a listener, so we don't receive message notifications anymore,
232
+ # Remove ourself as a receiver, so we don't receive message notifications anymore,
218
233
  # and wake up the async condition
219
234
  def do_stop
220
- @notifier.remove_listener self
235
+ @distributor.remove_receiver self
221
236
  @condition.signal
222
237
  end
223
238
 
224
239
  # An error occured upstream.
225
240
  # Check if we should cancel.
226
- def notify_error error, options={}
241
+ def receive_error error, options={}
227
242
  case error
228
243
  when RSMP::SchemaError
229
- notify_schema_error error, options
244
+ receive_schema_error error, options
230
245
  when RSMP::DisconnectError
231
- notify_disconnect error, options
246
+ receive_disconnect error, options
232
247
  end
233
248
  end
234
249
 
235
250
  # Cancel if we received e schema error for a message type we're collecting
236
- def notify_schema_error error, options
251
+ def receive_schema_error error, options
237
252
  return unless @options.dig(:cancel,:schema_error)
238
253
  message = options[:message]
239
254
  return unless message
240
255
  klass = message.class.name.split('::').last
241
- return unless @options[:type] == nil || [@options[:type]].flatten.include?(klass)
242
- @notifier.log "#{identifier}: cancelled due to schema error in #{klass} #{message.m_id_short}", level: :debug
256
+ return unless @filter&.type == nil || [@filter&.type].flatten.include?(klass)
257
+ @distributor.log "#{identifier}: cancelled due to schema error in #{klass} #{message.m_id_short}", level: :debug
243
258
  cancel error
244
259
  end
245
260
 
246
261
  # Cancel if we received e notificaiton about a disconnect
247
- def notify_disconnect error, options
262
+ def receive_disconnect error, options
248
263
  return unless @options.dig(:cancel,:disconnect)
249
- @notifier.log "#{identifier}: cancelled due to a connection error: #{error.to_s}", level: :debug
264
+ @distributor.log "#{identifier}: cancelled due to a connection error: #{error.to_s}", level: :debug
250
265
  cancel error
251
266
  end
252
267
 
@@ -264,39 +279,27 @@ module RSMP
264
279
 
265
280
  # Check a message against our match criteria
266
281
  # Return true if there's a match, false if not
267
- def type_match? message
268
- return false if message.direction == :in && @ingoing == false
269
- return false if message.direction == :out && @outgoing == false
270
- if @options[:type]
271
- if @options[:type].is_a? Array
272
- return false unless @options[:type].include? message.type
273
- else
274
- return false unless message.type == @options[:type]
275
- end
276
- end
277
- if @options[:component]
278
- return false if message.attributes['cId'] && message.attributes['cId'] != @options[:component]
279
- end
280
- true
282
+ def acceptable? message
283
+ @filter == nil || @filter.accept?(message)
281
284
  end
282
285
 
283
286
  # return a string describing the types of messages we're collecting
284
287
  def describe_types
285
- [@options[:type]].flatten.join('/')
288
+ [@filter&.type].flatten.join('/')
286
289
  end
287
290
 
288
291
  # return a string that describes whe number of messages, and type of message we're collecting
289
292
  def describe_num_and_type
290
- if @options[:num] && @options[:num] > 1
291
- "#{@options[:num]} #{describe_types}s"
293
+ if @num && @num > 1
294
+ "#{@num} #{describe_types}s"
292
295
  else
293
296
  describe_types
294
297
  end
295
298
  end
296
299
 
297
300
  # return a string that describes the attributes that we're looking for
298
- def describe_query
299
- h = {component: @options[:component]}.compact
301
+ def describe_matcher
302
+ h = {component: @filter&.component}.compact
300
303
  if h.empty?
301
304
  describe_num_and_type
302
305
  else
@@ -306,8 +309,8 @@ module RSMP
306
309
 
307
310
  # return a string that describe how many many messages have been collected
308
311
  def describe_progress
309
- if @options[:num]
310
- "#{@messages.size} of #{@options[:num]} message#{'s' if @messages.size!=1} collected"
312
+ if @num
313
+ "#{@messages.size} of #{@num} message#{'s' if @messages.size!=1} collected"
311
314
  else
312
315
  "#{@messages.size} message#{'s' if @messages.size!=1} collected"
313
316
  end
@@ -315,17 +318,17 @@ module RSMP
315
318
 
316
319
  # log when we start collecting
317
320
  def log_start
318
- @notifier.log "#{identifier}: Waiting for #{describe_query}".strip, level: :collect
321
+ @distributor.log "#{identifier}: Waiting for #{describe_matcher}".strip, level: :collect
319
322
  end
320
323
 
321
324
  # log current progress
322
325
  def log_incomplete
323
- @notifier.log "#{identifier}: #{describe_progress}", level: :collect
326
+ @distributor.log "#{identifier}: #{describe_progress}", level: :collect
324
327
  end
325
328
 
326
329
  # log when we end collecting
327
330
  def log_complete
328
- @notifier.log "#{identifier}: Done", level: :collect
331
+ @distributor.log "#{identifier}: Done", level: :collect
329
332
  end
330
333
 
331
334
  # get a short id in hex format, identifying ourself
@@ -1,7 +1,7 @@
1
1
  module RSMP
2
2
  # Match a specific command responses
3
- class CommandQuery < Query
4
- # Match a return value item against a query
3
+ class CommandMatcher < Matcher
4
+ # Match a return value item against a matcher
5
5
  def match? item
6
6
  return nil if @want['cCI'] && @want['cCI'] != item['cCI']
7
7
  return nil if @want['n'] && @want['n'] != item['n']
@@ -3,13 +3,13 @@ module RSMP
3
3
  class CommandResponseCollector < StateCollector
4
4
  def initialize proxy, want, options={}
5
5
  super proxy, want, options.merge(
6
- type: 'CommandResponse',
6
+ filter: RSMP::Filter.new(ingoing: true, outgoing: false, type: 'CommandResponse'),
7
7
  title:'command response'
8
8
  )
9
9
  end
10
10
 
11
- def build_query want
12
- CommandQuery.new want
11
+ def build_matcher want
12
+ CommandMatcher.new want
13
13
  end
14
14
 
15
15
  # Get items, in our case the return values
@@ -0,0 +1,65 @@
1
+ # Distributes messages to receivers
2
+
3
+ module RSMP
4
+ module Distributor
5
+ attr_reader :receivers
6
+
7
+ include Inspect
8
+
9
+ def inspect
10
+ "#<#{self.class.name}:#{self.object_id}, #{inspector(:@receivers)}>"
11
+ end
12
+
13
+ def initialize_distributor
14
+ @receivers = []
15
+ @defer_distribution = false
16
+ @deferred_messages = []
17
+ end
18
+
19
+ def clear_deferred_distribution &block
20
+ @deferred_messages = []
21
+ end
22
+
23
+ def with_deferred_distribution &block
24
+ was, @defer_distribution = @defer_distribution, true
25
+ yield
26
+ distribute_queued
27
+ ensure
28
+ @defer_distribution = was
29
+ @deferred_messages = []
30
+ end
31
+
32
+ def distribute_queued
33
+ @deferred_messages.each { |message| distribute_immediately message }
34
+ ensure
35
+ @deferred_messages = []
36
+ end
37
+
38
+ def add_receiver receiver
39
+ raise ArgumentError unless receiver
40
+ @receivers << receiver unless @receivers.include? receiver
41
+ end
42
+
43
+ def remove_receiver receiver
44
+ raise ArgumentError unless receiver
45
+ @receivers.delete receiver
46
+ end
47
+
48
+ def distribute message
49
+ raise ArgumentError unless message
50
+ if @defer_distribution
51
+ @deferred_messages << message
52
+ else
53
+ distribute_immediately message
54
+ end
55
+ end
56
+
57
+ def distribute_immediately message
58
+ @receivers.each { |receiver| receiver.receive message }
59
+ end
60
+
61
+ def distribute_error error, options={}
62
+ @receivers.each { |receiver| receiver.receive_error error, options }
63
+ end
64
+ end
65
+ end