Class: RSMP::Collector
- Inherits:
-
Object
show all
- Includes:
- Reporting, Logging, Receiver
- Defined in:
- lib/rsmp/collect/collector.rb,
lib/rsmp/collect/collector/logging.rb,
lib/rsmp/collect/collector/reporting.rb
Overview
Collects matching messages and resolves once with an immutable Result.
Defined Under Namespace
Modules: Logging, Reporting
Instance Attribute Summary collapse
Attributes included from Logging
#archive, #logger
Instance Method Summary
collapse
Methods included from Logging
#author, #initialize_logging, #log
Methods included from Reporting
#describe_matcher, #describe_num_and_type, #describe_progress, #describe_types, #identifier
Methods included from Receiver
#accept_message?, #handle_message, #initialize_receiver, #reject_message?, #start_receiving, #stop_receiving
Constructor Details
#initialize(distributor, options = {}) ⇒ Collector
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
# File 'lib/rsmp/collect/collector.rb', line 10
def initialize(distributor, options = {})
initialize_receiver distributor, filter: options[:filter]
@options = {
cancel: {
invalid_message: true,
disconnect: true
}
}.deep_merge(options)
@timeout = options[:timeout]
@num = options[:num]
@initiator = options[:initiator]
@m_id = options[:m_id] || @initiator&.attributes&.dig('mId')
make_title(options[:title])
reset
end
|
Instance Attribute Details
#initiator ⇒ Object
Returns the value of attribute initiator.
8
9
10
|
# File 'lib/rsmp/collect/collector.rb', line 8
def initiator
@initiator
end
|
#m_id ⇒ Object
Returns the value of attribute m_id.
8
9
10
|
# File 'lib/rsmp/collect/collector.rb', line 8
def m_id
@m_id
end
|
#messages ⇒ Object
Returns the value of attribute messages.
8
9
10
|
# File 'lib/rsmp/collect/collector.rb', line 8
def messages
@messages
end
|
Instance Method Details
#acceptable?(message) ⇒ Boolean
210
211
212
|
# File 'lib/rsmp/collect/collector.rb', line 210
def acceptable?(message)
@filter.nil? || @filter.accept?(message)
end
|
#active? ⇒ Boolean
Also known as:
collecting?
42
43
44
|
# File 'lib/rsmp/collect/collector.rb', line 42
def active?
@active
end
|
#build_collection ⇒ Object
214
215
216
|
# File 'lib/rsmp/collect/collector.rb', line 214
def build_collection
Collection.new(messages: @messages)
end
|
#cancel(reason = 'Collection cancelled') ⇒ Object
Explicit cancellation is an expected caller-controlled result.
185
186
187
188
189
|
# File 'lib/rsmp/collect/collector.rb', line 185
def cancel(reason = 'Collection cancelled')
finish_failure(
Failure.new(code: :cancelled, message: reason.to_s, source: :local)
)
end
|
#collect ⇒ Object
52
53
54
55
56
57
|
# File 'lib/rsmp/collect/collector.rb', line 52
def collect(&)
start(&)
wait
ensure
stop
end
|
#collect! ⇒ Object
59
60
61
|
# File 'lib/rsmp/collect/collector.rb', line 59
def collect!(&)
collect(&).value!.messages
end
|
#complete ⇒ Object
150
151
152
153
|
# File 'lib/rsmp/collect/collector.rb', line 150
def complete
finish_success(build_collection)
log_complete
end
|
#crash(error) ⇒ Object
Unexpected receiver/callback failures reject the completion so waiters see
the original exception and stack trace.
199
200
201
202
203
204
|
# File 'lib/rsmp/collect/collector.rb', line 199
def crash(error)
return unless active?
@completion.crash(error)
stop
end
|
#describe ⇒ Object
129
|
# File 'lib/rsmp/collect/collector.rb', line 129
def describe; end
|
#done? ⇒ Boolean
146
147
148
|
# File 'lib/rsmp/collect/collector.rb', line 146
def done?
@num && @messages.size >= @num
end
|
#fail(failure) ⇒ Object
191
192
193
194
195
|
# File 'lib/rsmp/collect/collector.rb', line 191
def fail(failure)
raise ArgumentError, 'failure must be an RSMP::Failure' unless failure.is_a?(Failure)
finish_failure(failure)
end
|
#incomplete ⇒ Object
155
156
157
|
# File 'lib/rsmp/collect/collector.rb', line 155
def incomplete
log_incomplete
end
|
#inspect ⇒ Object
48
49
50
|
# File 'lib/rsmp/collect/collector.rb', line 48
def inspect
"#<#{self.class.name}:#{object_id}, message:#{@messages}>"
end
|
#keep(message) ⇒ Object
206
207
208
|
# File 'lib/rsmp/collect/collector.rb', line 206
def keep(message)
@messages << message
end
|
#make_title(title) ⇒ Object
26
27
28
29
30
31
32
33
34
|
# File 'lib/rsmp/collect/collector.rb', line 26
def make_title(title)
@title = if title
title
elsif @filter
[@filter.type].flatten.join('/')
else
''
end
end
|
131
132
133
134
135
136
137
138
139
140
141
142
143
144
|
# File 'lib/rsmp/collect/collector.rb', line 131
def perform_match?(message)
return false if reject_not_ack?(message)
return false unless acceptable?(message)
if @block
status = Array(@block.call(message))
return false unless active?
keep(message) if status.include?(:keep)
else
keep(message)
end
true
end
|
#receive(message) ⇒ Object
120
121
122
123
124
125
126
127
|
# File 'lib/rsmp/collect/collector.rb', line 120
def receive(message)
return unless active?
if perform_match?(message)
done? ? complete : incomplete
end
active?
end
|
#receive_connection_ended(event) ⇒ Object
178
179
180
181
182
|
# File 'lib/rsmp/collect/collector.rb', line 178
def receive_connection_ended(event)
return unless @options.dig(:cancel, :disconnect)
finish_failure(event.failure)
end
|
#receive_event(event) ⇒ Object
159
160
161
162
163
164
165
166
167
168
|
# File 'lib/rsmp/collect/collector.rb', line 159
def receive_event(event)
return unless active?
case event.type
when :invalid_message
receive_invalid_message(event)
when :connection_ended
receive_connection_ended(event)
end
end
|
#receive_invalid_message(event) ⇒ Object
170
171
172
173
174
175
176
|
# File 'lib/rsmp/collect/collector.rb', line 170
def receive_invalid_message(event)
return unless @options.dig(:cancel, :invalid_message)
return unless event.message
return unless acceptable?(event.message)
finish_failure(event.failure)
end
|
#reject_not_ack?(message) ⇒ Boolean
Check for a NotAck related to the initiating request.
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
|
# File 'lib/rsmp/collect/collector.rb', line 102
def reject_not_ack?(message)
return false unless @m_id
return false unless message.is_a?(MessageNotAck)
return false unless message.attribute('oMId') == @m_id
m_id_short = RSMP::Message.shorten_m_id(@m_id, 8)
finish_failure(
Failure.new(
code: :message_rejected,
message: "#{@title} #{m_id_short} was rejected with '#{message.attribute('rea')}'",
source: :peer,
context: { message: message, original_message_id: @m_id }
)
)
@distributor.log "#{identifier}: rejected by a NotAck", level: :debug
true
end
|
#reset ⇒ Object
36
37
38
39
40
|
# File 'lib/rsmp/collect/collector.rb', line 36
def reset
@messages = []
@completion = Completion.new
@active = false
end
|
#start(&block) ⇒ Object
80
81
82
83
84
85
86
87
88
89
90
91
|
# File 'lib/rsmp/collect/collector.rb', line 80
def start(&block)
raise 'collector is already active' if active?
@block = block
raise ArgumentError, 'num, timeout or block must be provided' unless @num || @timeout || @block
reset
@active = true
log_start
@distributor.add_receiver(self)
self
end
|
#stop ⇒ Object
93
94
95
96
97
98
99
|
# File 'lib/rsmp/collect/collector.rb', line 93
def stop
return self unless @active
@active = false
@distributor.remove_receiver(self)
self
end
|
#wait ⇒ Object
The configured timeout terminates this collection. Completion retains the
result, so every current or future waiter observes the same value.
65
66
67
68
69
70
71
72
73
74
|
# File 'lib/rsmp/collect/collector.rb', line 65
def wait
raise 'collector has not been started' unless active? || @completion.resolved?
observed = @completion.wait(timeout: @timeout)
if timeout_result?(observed)
finish_failure(observed.failure.with(message: describe_progress))
observed = @completion.wait
end
observed
end
|
#wait! ⇒ Object
76
77
78
|
# File 'lib/rsmp/collect/collector.rb', line 76
def wait!
wait.value!.messages
end
|