asir 1.1.12 → 1.2.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/.gitignore CHANGED
@@ -8,4 +8,5 @@ example/*.out
8
8
  coverage
9
9
  tmp.*
10
10
  cabar.yml
11
+ .rbx
11
12
 
data/ChangeLog CHANGED
@@ -1,5 +1,12 @@
1
- 2012-12-28 Kurt A. Stephens <ks.github@kurtstephens.com>
1
+ 2012-12-29 Kurt A. Stephens <ks.github@kurtstephens.com>
2
+
3
+ * v1.2.0: New version: API changes.
4
+ * Message::State: internal object encapsulates state between #send_message, #receive_message, #send_result, #receive_result.
5
+ * Callbacks: most callbacks take a MessageResult object instead of Message and/or Result objects.
6
+ * UUID: Added new_uuid, process_uuid, counter_uuid, thread_uuid as mixin methods.
7
+ * Identity: Removed deprecated #client, #server attributes, use additional_data.
2
8
 
9
+ 2012-12-28 Kurt A. Stephens <ks.github@kurtstephens.com>
3
10
  * v1.1.12: New version.
4
11
  * Rubinius: Rubinius -Xversion=18 and =19 support.
5
12
  * XML: Really removed Coder::XML.
data/example/ex18.rb CHANGED
@@ -9,8 +9,9 @@ begin
9
9
  :encoder => ASIR::Coder::Yaml.new(:yaml_options => { :ASCII_8BIT_ok => true }))
10
10
  tcp = ASIR::Transport::TcpSocket.new(:port => 31918,
11
11
  :encoder => ASIR::Coder::Marshal.new)
12
- start_server_proc = lambda do | transport, message |
13
- $stderr.puts "message = #{message.inspect}"
12
+ start_server_proc = lambda do | transport, message_result |
13
+ message = message_result.message
14
+ # $stderr.puts "message = #{message.inspect}"
14
15
  file.send_message(message)
15
16
  server_process do
16
17
  tcp.prepare_server!
data/example/ex26.rb CHANGED
@@ -7,7 +7,7 @@ begin
7
7
  Email.asir.transport = t =
8
8
  ASIR::Transport::Thread.new
9
9
  spawned_thread = nil
10
- t.after_thread_new = lambda do | transport, message, thread |
10
+ t.after_thread_new = lambda do | transport, message_result, thread |
11
11
  spawned_thread = thread
12
12
  $stderr.puts "\n #{$$}: Spawned Thread #{thread.inspect}"
13
13
  end
@@ -48,7 +48,12 @@ def server_process &blk
48
48
  if ENV['ASIR_JRUBY_SPAWNED']
49
49
  $stderr.puts " spawned server at #{__FILE__}:#{__LINE__}"
50
50
  puts "*** #{$$}: server process"; $stdout.flush
51
- yield
51
+ begin
52
+ yield
53
+ rescue ::Exception => exc
54
+ $stderr.puts "*** #{$$}: service ERROR: #{exc.inspect}\n #{exc.backtrace * " \n"}"
55
+ raise exc
56
+ end
52
57
  Process.exit!(0)
53
58
  # dont do client, client is our parent process.
54
59
  else
@@ -64,7 +69,12 @@ def server_process &blk
64
69
  # $stderr.puts " at #{__FILE__}:#{__LINE__}"
65
70
  $server_pid = Process.fork do
66
71
  puts "*** #{$$}: server process"; $stdout.flush
67
- yield
72
+ begin
73
+ yield
74
+ rescue ::Exception => exc
75
+ $stderr.puts "*** #{$$}: service ERROR: #{exc.inspect}\n #{exc.backtrace * " \n"}"
76
+ raise exc
77
+ end
68
78
  end
69
79
  end
70
80
  sleep 1 # wait for server to be ready.
data/lib/asir.rb CHANGED
@@ -242,6 +242,7 @@ require 'asir/code_block'
242
242
  require 'asir/code_more'
243
243
  require 'asir/message'
244
244
  require 'asir/result'
245
+ require 'asir/message/state'
245
246
  require 'asir/client'
246
247
  require 'asir/coder'
247
248
  require 'asir/coder/null'
@@ -28,19 +28,19 @@ module ASIR
28
28
 
29
29
  module ModuleMethods
30
30
  # Provide a getter method that delegates to addtional_data[...].
31
- def addr_getter *names
31
+ def addit_getter *names
32
32
  names.each do | name |
33
33
  name = name.to_sym
34
- define_method(name) { | | addtional_data[name] }
34
+ define_method(name) { | | self[name] }
35
35
  end
36
36
  end
37
37
 
38
38
  # Provide getter and setter methods that delegate to addtional_data[...].
39
- def addr_accessor *names
39
+ def addit_accessor *names
40
40
  addr_getter *names
41
41
  names.each do | name |
42
42
  name = name.to_sym
43
- define_method(:"#{name}=") { | v | addtional_data[name] = v }
43
+ define_method(:"#{name}=") { | v | self[name] = v }
44
44
  end
45
45
  end
46
46
  end
data/lib/asir/client.rb CHANGED
@@ -54,8 +54,7 @@ module ASIR
54
54
  message = Message.new(@receiver, selector, arguments, block, self)
55
55
  message = @before_send_message.call(message) if @before_send_message
56
56
  @__configure.call(message, self) if @__configure
57
- result = transport.send_message(message)
58
- result
57
+ transport.send_message(message) # => result
59
58
  end
60
59
  # Accept all other messages to be encoded and transported to a service.
61
60
  alias :method_missing :send
data/lib/asir/identity.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  require 'asir/uuid'
2
- require 'thread' # Mutex
3
2
 
4
3
  module ASIR
5
4
  # !SLIDE
@@ -8,29 +7,14 @@ module ASIR
8
7
  module Identity
9
8
  attr_accessor :identifier, :timestamp
10
9
 
11
- # Optional: Opaque data about the Client that created the Message.
12
- attr_accessor :client
13
-
14
- # Optional: Opaque data about the Service that handled the Result.
15
- attr_accessor :server
16
-
17
10
  # Creates a thread-safe unique identifier.
18
11
  def create_identifier!
19
- @identifier ||=
20
- @@identifier_mutex.synchronize do
21
- if @@uuid_pid != $$
22
- @@uuid_pid = $$
23
- @@uuid = nil
24
- end
25
- "#{@@counter += 1}-#{@@uuid ||= ::ASIR::UUID.generate}".freeze
26
- end
12
+ @identifier ||= ::ASIR::UUID.counter_uuid
27
13
  end
28
- @@counter ||= 0; @@uuid ||= nil; @@uuid_pid = nil; @@identifier_mutex ||= Mutex.new
29
14
 
30
15
  # Creates a timestamp.
31
16
  def create_timestamp!
32
- @timestamp ||=
33
- ::Time.now.gmtime
17
+ @timestamp ||= ::Time.now.gmtime
34
18
  end
35
19
  end
36
20
  end
@@ -0,0 +1,18 @@
1
+ module ASIR
2
+ class Message
3
+ # !SLIDE
4
+ # Message::State
5
+ #
6
+ # Encapsulate the Message, Result and their payloads and other state.
7
+ # This is passed between Transport#send_request, #receive_request, #send_response, #receive_response.
8
+ class State
9
+ include Initialization
10
+ attr_accessor :message, :result
11
+ attr_accessor :message_payload, :result_payload
12
+ attr_accessor :message_opaque, :result_opaque
13
+ attr_accessor :in_stream, :out_stream
14
+ attr_accessor :additional_data
15
+ end
16
+ end
17
+ end
18
+
@@ -1,6 +1,7 @@
1
1
  require 'time'
2
2
  require 'asir/thread_variable'
3
3
  require 'asir/message/delay'
4
+ require 'asir/message/state'
4
5
  require 'asir/transport/conduit'
5
6
 
6
7
  module ASIR
@@ -23,46 +24,44 @@ module ASIR
23
24
  # * Send encoded Message.
24
25
  # * Receive decoded Result.
25
26
  def send_message message
26
- @message_count ||= 0; @message_count += 1
27
+ @message_count ||= 0; @message_count += 1 # NOT THREAD-SAFE
27
28
  message.create_timestamp! if needs_message_timestamp? message
28
29
  message.create_identifier! if needs_message_identifier? message
29
- @before_send_message.call(self, message) if @before_send_message
30
30
  relative_message_delay! message
31
- message_payload = encoder.prepare.encode(message)
32
- opaque_result = _send_message(message, message_payload)
33
- receive_result(message, opaque_result)
31
+ state = Message::State.new(:message => message, :message_payload => encoder.prepare.encode(message))
32
+ @before_send_message.call(self, state) if @before_send_message
33
+ _send_message(state)
34
+ receive_result(state)
34
35
  end
35
36
 
36
37
  # !SLIDE
37
38
  # Transport#receive_message
38
39
  # Receive Message payload from stream.
39
- def receive_message stream
40
- # $stderr.puts " #{$$} #{self} receive_message #{stream}"
41
- @message_count ||= 0; @message_count += 1
42
- additional_data = { }
43
- if req_and_state = _receive_message(stream, additional_data)
44
- message = req_and_state[0] = encoder.prepare.decode(req_and_state.first)
45
- message.additional_data!.update(additional_data) if message
46
- if @after_receive_message
47
- @after_receive_message.call(self, message)
40
+ def receive_message state
41
+ @message_count ||= 0; @message_count += 1 # NOT THREAD-SAFE
42
+ if received = _receive_message(state)
43
+ if message = state.message = encoder.prepare.decode(state.message_payload)
44
+ message.additional_data!.update(state.additional_data) if state.additional_data
45
+ @after_receive_message.call(self, state) if @after_receive_message
46
+ self
48
47
  end
49
48
  end
50
- req_and_state
51
49
  end
52
50
  # !SLIDE END
53
51
 
54
52
  # !SLIDE
55
53
  # Transport#send_result
56
54
  # Send Result to stream.
57
- def send_result result, stream, message_state
58
- message = result.message
55
+ def send_result state
56
+ result = state.result
57
+ message = state.message
59
58
  if @one_way && message.block
60
59
  message.block.call(result)
61
60
  else
62
- # avoid sending back entire Message.
61
+ # Avoid sending back entire Message in Result.
63
62
  result.message = nil unless @coder_needs_result_message
64
- result_payload = decoder.prepare.encode(result)
65
- _send_result(message, result, result_payload, stream, message_state)
63
+ state.result_payload = decoder.prepare.encode(result)
64
+ _send_result(state)
66
65
  end
67
66
  end
68
67
  attr_accessor :coder_needs_result_message
@@ -75,19 +74,24 @@ module ASIR
75
74
  # * Receive Result payload
76
75
  # * Decode Result.
77
76
  # * Extract Result result or exception.
78
- def receive_result message, opaque_result
79
- result_payload = _receive_result(message, opaque_result)
80
- result = decoder.prepare.decode(result_payload)
77
+ # * Invoke Exception or return Result value.
78
+ def receive_result state
79
+ value = nil
80
+ return value unless _receive_result(state)
81
+ result = state.result ||= decoder.prepare.decode(state.result_payload)
82
+ message = state.message
81
83
  if result && ! message.one_way
84
+ result.message = message
82
85
  if exc = result.exception
83
86
  invoker.invoke!(exc, self)
84
87
  else
85
88
  if ! @one_way && message.block
86
89
  message.block.call(result)
87
90
  end
88
- result.result
91
+ value = result.result
89
92
  end
90
93
  end
94
+ value
91
95
  end
92
96
  # !SLIDE END
93
97
 
@@ -118,8 +122,8 @@ module ASIR
118
122
  # Proc to call with exception, if exception occurs within #serve_message!, but outside
119
123
  # Message#invoke!.
120
124
  #
121
- # trans.on_exception.call(trans, exception, :message, Message_instance, nil)
122
- # trans.on_exception.call(trans, exception, :result, Message_instance, Result_instance)
125
+ # trans.on_exception.call(trans, exception, :message, message_state)
126
+ # trans.on_exception.call(trans, exception, :result, message_state)
123
127
  attr_accessor :on_exception
124
128
 
125
129
  attr_accessor :needs_message_identifier, :needs_message_timestamp
@@ -139,15 +143,15 @@ module ASIR
139
143
  # !SLIDE
140
144
  # Serve a Message.
141
145
  def serve_message! in_stream, out_stream
142
- message = message_state = message_ok = result = result_ok = nil
146
+ state = message_ok = result = result_ok = nil
143
147
  exception = original_exception = unforwardable_exception = nil
144
- message, message_state = receive_message(in_stream)
145
- if message
148
+ state = Message::State.new(:in_stream => in_stream, :out_stream => out_stream)
149
+ if receive_message(state)
146
150
  message_ok = true
147
- result = invoke_message!(message)
151
+ invoke_message!(state)
148
152
  result_ok = true
149
153
  if @after_invoke_message
150
- @after_invoke_message.call(self, message, result)
154
+ @after_invoke_message.call(self, state)
151
155
  end
152
156
  self
153
157
  else
@@ -156,7 +160,7 @@ module ASIR
156
160
  rescue ::Exception => exc
157
161
  exception = original_exception = exc
158
162
  _log [ :message_error, exc ]
159
- @on_exception.call(self, exc, :message, message, nil) if @on_exception
163
+ @on_exception.call(self, exc, :message, state) if @on_exception
160
164
  ensure
161
165
  begin
162
166
  if message_ok
@@ -165,15 +169,15 @@ module ASIR
165
169
  when *Error::Unforwardable.unforwardable
166
170
  unforwardable_exception = exception = Error::Unforwardable.new(exception)
167
171
  end
168
- result = Result.new(message, nil, exception)
172
+ state.result = Result.new(state.message, nil, exception)
169
173
  end
170
174
  if out_stream
171
- send_result(result, out_stream, message_state)
175
+ send_result(state)
172
176
  end
173
177
  end
174
178
  rescue ::Exception => exc
175
- _log [ :result_error, exc ]
176
- @on_exception.call(self, exc, :result, message, result) if @on_exception
179
+ _log [ :result_error, exc, exc.backtrace ]
180
+ @on_exception.call(self, exc, :result, state) if @on_exception
177
181
  end
178
182
  raise original_exception if unforwardable_exception
179
183
  end
@@ -227,21 +231,25 @@ module ASIR
227
231
  end
228
232
 
229
233
  # Invokes the Message object, returns a Result object.
230
- def invoke_message! message
231
- result = nil
234
+ def invoke_message! state
232
235
  Transport.with_attr! :current, self do
233
- with_attr! :message, message do
234
- wait_for_delay! message
235
- result = invoker.invoke!(message, self)
236
+ with_attr! :message_state, state do
237
+ with_attr! :message, state.message do
238
+ wait_for_delay! state.message
239
+ state.result = invoker.invoke!(state.message, self)
236
240
  # Hook for Exceptions.
237
- if @on_result_exception && result.exception
238
- @on_result_exception.call(self, result)
241
+ if @on_result_exception && state.result.exception
242
+ @on_result_exception.call(self, state)
239
243
  end
240
244
  end
241
245
  end
242
- result
246
+ end
247
+ self
243
248
  end
244
- # The current Message being handled.
249
+
250
+ # The current Message::State.
251
+ attr_accessor_thread :message_state
252
+ # The current Message being invoked. DEPRECATED.
245
253
  attr_accessor_thread :message
246
254
 
247
255
  # The current active Transport.
@@ -9,23 +9,22 @@ module ASIR
9
9
  class Broadcast < self
10
10
  include Composite
11
11
 
12
- def _send_message message, message_payload
12
+ def _send_message state
13
13
  result = first_exception = nil
14
14
  transports.each do | transport |
15
15
  begin
16
- result = transport.send_message(message)
16
+ result = transport.send_message(state.message)
17
17
  rescue ::Exception => exc
18
18
  first_exception ||= exc
19
- _handle_send_message_exception! transport, message, exc
19
+ _handle_send_message_exception! transport, state, exc
20
20
  raise exc unless @continue_on_exception
21
21
  end
22
22
  end
23
23
  if first_exception && @reraise_first_exception
24
24
  raise first_exception
25
25
  end
26
- result
26
+ state.result = Result.new(state.message, result)
27
27
  end
28
-
29
28
  end
30
29
  # !SLIDE END
31
30
  end
@@ -25,15 +25,15 @@ module ASIR
25
25
 
26
26
  # If paused, queue messages,
27
27
  # Otherwise delegate immediately to #transport.
28
- def _send_message message, message_payload
28
+ def _send_message state
29
29
  return nil if @ignore
30
30
  if paused?
31
31
  @messages_mutex.synchronize do
32
- @messages << message
32
+ @messages << state.message
33
33
  end
34
34
  nil
35
35
  else
36
- @transport.send_message(message)
36
+ @transport.send_message(state.message)
37
37
  end
38
38
  end
39
39
 
@@ -97,7 +97,7 @@ module ASIR
97
97
  # Usually used in worker Thread.
98
98
  def process! non_block=false
99
99
  @running = true
100
- while @running && message = shift(non_block)
100
+ while @running && (message = shift(non_block))
101
101
  @transport.send_message(message)
102
102
  end
103
103
  message
@@ -4,7 +4,7 @@ module ASIR
4
4
  # !SLIDE
5
5
  # A Transport composed of other Transports.
6
6
  #
7
- # Classes that mix this in should define #_send_message(message, message_payload).
7
+ # Classes that mix this in should define #_send_message.
8
8
  module Composite
9
9
  include Delegation
10
10
 
@@ -5,10 +5,11 @@ module Asir
5
5
  # Conduit service support.
6
6
  module Conduit
7
7
  attr_accessor :conduit_options, :conduit_pid
8
+
8
9
  def start_conduit! options = nil
9
- opts = { :fork => true }
10
+ opts = @conduit_options ||= {}
11
+ opts.update(:fork => true)
10
12
  opts.update(options) if options
11
- @conduit_options = opts
12
13
  _log { "start_conduit! #{self}" } if @verbose >= 1
13
14
  in_fork = opts[:fork]
14
15
  raise "already running #{@conduit_pid} #{@conduit_cmd}" if @conduit_pid
@@ -19,7 +20,7 @@ module Asir
19
20
  raise "Could not exec"
20
21
  end
21
22
  _log { "start_conduit! #{self} started pid=#{@conduit_pid.inspect}" } if @verbose >= 2
22
- if pid_file = @conduit_options[:pid_file]
23
+ if pid_file = (@conduit_options || EMPTY_HASH)[:pid_file]
23
24
  File.open(pid_file, "w") { | fh | fh.puts @conduit_pid }
24
25
  end
25
26
  else
@@ -29,7 +30,7 @@ module Asir
29
30
  end
30
31
 
31
32
  def conduit_pid
32
- if ! @conduit_pid and pid_file = @conduit_options[:pid_file]
33
+ if ! @conduit_pid and pid_file = (@conduit_options || EMPTY_HASH)[:pid_file]
33
34
  @conduit_pid = (File.read(pid_file).to_i rescue nil)
34
35
  end
35
36
  @conduit_pid
@@ -37,10 +38,11 @@ module Asir
37
38
 
38
39
  def stop_conduit! opts = nil
39
40
  if conduit_pid
40
- pid_file = @conduit_options[:pid_file]
41
41
  _log { "stop_conduit! #{self} pid=#{@conduit_pid.inspect}" } if @verbose >= 1
42
42
  ::Process.kill( (opts && opts[:signal]) || 'TERM', @conduit_pid)
43
- ::File.unlink(pid_file) rescue nil if pid_file
43
+ if pid_file = (@conduit_options || EMPTY_HASH)[:pid_file]
44
+ ::File.unlink(pid_file) rescue nil
45
+ end
44
46
  ::Process.waitpid @conduit_pid
45
47
  end
46
48
  self
@@ -52,33 +52,35 @@ module ASIR
52
52
 
53
53
  # !SLIDE
54
54
  # Sends the encoded Message payload String.
55
- def _send_message message, message_payload
55
+ def _send_message state
56
56
  stream.with_stream! do | io |
57
- _write message_payload, io, message
57
+ state.in_stream = io
58
+ _write(state.message_payload, io, state)
58
59
  end
59
60
  end
60
61
 
61
62
  # !SLIDE
62
63
  # Receives the encoded Message payload String.
63
- def _receive_message stream, additional_data
64
- [ _read(stream, nil), nil ]
64
+ def _receive_message state
65
+ state.message_payload = _read(state.in_stream, state)
65
66
  end
66
67
 
67
68
  # !SLIDE
68
69
  # Sends the encoded Result payload String.
69
- def _send_result message, result, result_payload, stream, message_state
70
- unless @one_way || message.one_way
71
- _write result_payload, stream, message
70
+ def _send_result state
71
+ unless @one_way || state.message.one_way
72
+ # $stderr.write "\n _send_result #{state.result_payload.inspect}\n\n"
73
+ _write(state.result_payload, state.out_stream, state)
74
+ true
72
75
  end
73
76
  end
74
77
 
75
78
  # !SLIDE
76
79
  # Receives the encoded Result payload String.
77
- def _receive_result message, opaque_result
78
- unless @one_way || message.one_way
79
- stream.with_stream! do | io |
80
- _read io, message
81
- end
80
+ def _receive_result state
81
+ unless @one_way || state.message.one_way
82
+ state.result_payload = _read(state.in_stream, state)
83
+ true
82
84
  end
83
85
  end
84
86
 
@@ -115,6 +117,7 @@ module ASIR
115
117
  end
116
118
 
117
119
  def serve_connection!
120
+ _log { "serve_connection!: accepting connection" } if @verbose >= 2
118
121
  in_stream, out_stream = _server_accept_connection! @server
119
122
  _log { "serve_connection!: connected #{in_stream} #{out_stream}" } if @verbose >= 1
120
123
  _server_serve_stream! in_stream, out_stream
@@ -19,29 +19,27 @@ module ASIR
19
19
  self.needs_message_identifier = true
20
20
  end
21
21
 
22
- def _send_message message, message_payload
22
+ def _send_message state
23
23
  if @before_message_save
24
- @before_message_save.call(self, message, message_payload)
24
+ @before_message_save.call(self, state)
25
25
  end
26
- message_payload.save!
26
+ state.message_payload.save!
27
27
  # message[:database_id] ||= message_payload.database_id
28
28
  if @after_message_save
29
- @after_message_save.call(self, message, message_payload)
29
+ @after_message_save.call(self, state)
30
30
  end
31
- nil # message_state
32
31
  end
33
32
 
34
- def _send_result message, result, result_payload, stream, message_state
35
- return if one_way? or message.one_way?
33
+ def _send_result state
34
+ return if one_way? or state.message.one_way?
36
35
  if @before_result_save
37
- @before_result_save.call(self, message, result, result_payload)
36
+ @before_result_save.call(self, state)
38
37
  end
39
- result_payload.save!
38
+ state.result_payload.save!
40
39
  result[:database_id] = result_payload.database_id
41
40
  if @after_result_save
42
- @after_result_save.call(self, message, result, result_payload)
41
+ @after_result_save.call(self, state)
43
42
  end
44
- nil
45
43
  end
46
44
  end
47
45
  end
@@ -14,14 +14,9 @@ module ASIR
14
14
  # Proc to call(transport, message) when #send_message fails with no recourse.
15
15
  attr_accessor :on_failed_message
16
16
 
17
- # Return the subTransports#send_message result unmodified from #_send_message.
18
- def _receive_result message, opaque_result
19
- opaque_result
20
- end
21
-
22
- # Return the subTransports#send_message result unmodified from #_send_message.
23
- def receive_result message, opaque_result
24
- opaque_result
17
+ # Return the subTransports' result unmodified from #_send_message.
18
+ def _receive_result state
19
+ true
25
20
  end
26
21
 
27
22
  def needs_message_identifier? message
@@ -40,10 +35,10 @@ module ASIR
40
35
  end
41
36
 
42
37
  # Called from within _send_message rescue.
43
- def _handle_send_message_exception! transport, message, exc
44
- _log { [ :send_message, :transport_failed, exc ] }
45
- (message[:transport_exceptions] ||= [ ]) << "#{exc.inspect}: #{exc.backtrace.first}"
46
- @on_send_message_exception.call(self, message, exc) if @on_send_message_exception
38
+ def _handle_send_message_exception! transport, state, exc
39
+ _log { [ :send_message, :transport_failed, exc, exc.backtrace ] }
40
+ (state.message[:transport_exceptions] ||= [ ]) << "#{exc.inspect}: #{exc.backtrace.first}"
41
+ @on_send_message_exception.call(self, state, exc) if @on_send_message_exception
47
42
  self
48
43
  end
49
44
  end
@@ -23,8 +23,8 @@ module ASIR
23
23
  end
24
24
  end
25
25
 
26
- def _send_message message, message_payload
27
- transport.send_message(message)
26
+ def _send_message state
27
+ transport.send_message(state.message)
28
28
  end
29
29
  end
30
30
  # !SLIDE END
@@ -7,16 +7,16 @@ module ASIR
7
7
  class Fallback < self
8
8
  include Composite
9
9
 
10
- def _send_message message, message_payload
10
+ def _send_message state
11
11
  result = sent = first_exception = nil
12
12
  transports.each do | transport |
13
13
  begin
14
- result = transport.send_message(message)
14
+ result = transport.send_message(state.message)
15
15
  sent = true
16
16
  break
17
17
  rescue ::Exception => exc
18
18
  first_exception ||= exc
19
- _handle_send_message_exception! transport, message, exc
19
+ _handle_send_message_exception! transport, state, exc
20
20
  end
21
21
  end
22
22
  unless sent
@@ -25,7 +25,7 @@ module ASIR
25
25
  end
26
26
  raise FallbackError, "fallback failed"
27
27
  end
28
- result
28
+ state.result = Result.new(state.message, result)
29
29
  end
30
30
  class FallbackError < Error; end
31
31
  end
@@ -15,24 +15,24 @@ module ASIR
15
15
  def initialize opts = nil; @one_way = true; super; end
16
16
 
17
17
  # Writes a Message payload String.
18
- def _send_message message, message_payload
19
- _write message_payload, stream, message
18
+ def _send_message state
19
+ _write(state.message_payload, state.out_stream || stream, state)
20
20
  ensure
21
21
  close if file && ::File.pipe?(file)
22
22
  end
23
23
 
24
24
  # Returns a Message payload String.
25
- def _receive_message stream, additional_data
26
- [ _read(stream, nil), nil ]
25
+ def _receive_message state
26
+ state.message_payload = _read(state.in_stream || stream, state)
27
27
  end
28
28
 
29
29
  # one-way; no Result.
30
- def _send_result message, result, result_payload, stream, message_state
30
+ def _send_result state
31
31
  nil
32
32
  end
33
33
 
34
34
  # one-way; no Result.
35
- def _receive_result message, opaque_result
35
+ def _receive_result state
36
36
  nil
37
37
  end
38
38
 
@@ -63,7 +63,6 @@ module ASIR
63
63
  # Named Pipe Server
64
64
 
65
65
  def prepare_server!
66
- # _log [ :prepare_pipe_server!, file ]
67
66
  unless ::File.exist? file
68
67
  system(cmd = "mkfifo #{file.inspect}") or raise "cannot run #{cmd.inspect}"
69
68
  ::File.chmod(perms, file) rescue nil if perms
@@ -72,7 +71,6 @@ module ASIR
72
71
  alias :prepare_pipe_server! :prepare_server!
73
72
 
74
73
  def run_server!
75
- # _log [ :run_pipe_server!, file ]
76
74
  with_server_signals! do
77
75
  @running = true
78
76
  while @running
@@ -30,18 +30,23 @@ module ASIR
30
30
 
31
31
  # Send the Message payload String using HTTP POST.
32
32
  # Returns the HTTPClient::Request response object.
33
- def _send_message message, message_payload
33
+ def _send_message state
34
34
  client.with_stream! do | client |
35
- client.post(uri, message_payload)
35
+ state.in_stream =
36
+ client.post(message_uri(state), state.message_payload)
36
37
  end
37
38
  end
38
39
 
40
+ # Subclasses can override.
41
+ def message_uri state
42
+ state.message[:uri] || uri
43
+ end
44
+
39
45
  # Recieve the Result payload String from the opaque
40
46
  # HTTPClient::Request response object returned from #_send_message.
41
- def _receive_result message, http_result_message
42
- # $stderr.puts " ### http_result_message.content.encoding = #{http_result_message.content.encoding.inspect}" rescue nil
43
- # $stderr.puts " ### http_result_message.content = #{http_result_message.content.inspect}" rescue nil
44
- http_result_message.content.to_s
47
+ def _receive_result state
48
+ state.result_payload =
49
+ state.in_stream.content.to_s
45
50
  end
46
51
 
47
52
  CONTENT_TYPE = 'Content-Type'.freeze
@@ -6,14 +6,16 @@ module ASIR
6
6
  # Send Message to same process.
7
7
  # Requires Identity Coder.
8
8
  class Local < self
9
- # Returns Result object after invoking Message.
10
- def _send_message message, message_payload
11
- invoke_message!(message)
9
+ # Capture Result object after invoking Message.
10
+ def _send_message state
11
+ invoke_message!(state)
12
+ self
12
13
  end
13
14
 
14
- # Returns Result object from #send_message.
15
- def _receive_result message, opaque_result
16
- opaque_result
15
+ # Result object was captured in #_send_message.
16
+ def _receive_result state
17
+ state.result_payload = state.result
18
+ self
17
19
  end
18
20
  end
19
21
  # !SLIDE END
@@ -5,7 +5,7 @@ module ASIR
5
5
  #
6
6
  # Never send Message.
7
7
  class Null < self
8
- def _send_message message, message_payload
8
+ def _send_message state
9
9
  nil
10
10
  end
11
11
  end
@@ -14,7 +14,7 @@ module ASIR
14
14
  HEADER = "# asir_payload_size: "
15
15
  FOOTER = "\n# asir_payload_end"
16
16
 
17
- def _write payload, stream, context
17
+ def _write payload, stream, state
18
18
  stream.write HEADER
19
19
  stream.puts payload.size
20
20
  stream.write payload
@@ -23,7 +23,7 @@ module ASIR
23
23
  stream
24
24
  end
25
25
 
26
- def _read stream, context
26
+ def _read stream, state
27
27
  size = /\d+$/.match(stream.readline.chomp)[0].to_i # HEADER (size)
28
28
  payload = stream.read(size)
29
29
  stream.readline # FOOTER
@@ -7,17 +7,16 @@ module ASIR
7
7
  # Rack Transport
8
8
  class Rack < HTTP
9
9
  # Receive the Message payload String from the Rack::Request object.
10
- # Returns the [ Rack::Request, Rack::Response ] as the message_state.
11
- def _receive_message rack_req_res, additional_data
12
- body = rack_req_res.first.body.read
13
- [ body, rack_req_res ]
10
+ def _receive_message state
11
+ rack_request = state.in_stream
12
+ state.message_payload = rack_request.body.read
14
13
  end
15
14
 
16
15
  # Send the Result payload String in the Rack::Response object as application/binary.
17
- def _send_result message, result, result_payload, rack_rq_rs, message_state
18
- rack_response = rack_rq_rs[1]
16
+ def _send_result state
17
+ rack_response = state.out_stream
19
18
  rack_response[CONTENT_TYPE] = APPLICATION_BINARY
20
- rack_response.write result_payload
19
+ rack_response.write state.result_payload
21
20
  end
22
21
 
23
22
  # Constructs a Rackable App from this Transport.
@@ -41,8 +40,7 @@ module ASIR
41
40
  def call(env)
42
41
  rq = ::Rack::Request.new(env)
43
42
  rs = ::Rack::Response.new
44
- rack_rq_rs = [ rq, rs ]
45
- serve_message! rack_rq_rs, rack_rq_rs
43
+ serve_message! rq, rs
46
44
  rs.finish # => [ status, header, rbody ]
47
45
  end
48
46
 
@@ -13,19 +13,20 @@ module ASIR
13
13
  # Proc to call(transport, message) before retry.
14
14
  attr_accessor :before_retry
15
15
 
16
- def _send_message message, message_payload
16
+ def _send_message state
17
17
  first_exception = nil
18
18
  with_retry do | action, data |
19
19
  case action
20
20
  when :try
21
- transport.send_message(message)
21
+ result = transport.send_message(state.message)
22
+ state.result = Result.new(state.message, result)
22
23
  when :rescue #, exc
23
24
  first_exception ||= data
24
- _handle_send_message_exception! transport, message, data
25
+ _handle_send_message_exception! transport, state, data
25
26
  when :retry #, exc
26
- before_retry.call(self, message) if before_retry
27
+ before_retry.call(self, state) if before_retry
27
28
  when :failed
28
- @on_failed_message.call(self, message) if @on_failed_message
29
+ @on_failed_message.call(self, state) if @on_failed_message
29
30
  if first_exception && @reraise_first_exception
30
31
  $! = first_exception
31
32
  raise
@@ -11,18 +11,19 @@ module ASIR
11
11
  @one_way = true; super
12
12
  end
13
13
 
14
- def _send_message message, message_payload
14
+ def _send_message state
15
15
  Process.fork do
16
- send_result(super, nil, nil)
16
+ super
17
+ send_result(state)
17
18
  end
18
19
  end
19
20
 
20
21
  # one-way; no Result
21
- def _receive_result message, opaque_result
22
+ def _receive_result state
22
23
  end
23
24
 
24
25
  # one-way; no Result
25
- def _send_result message, result, result_payload, stream, message_state
26
+ def _send_result state
26
27
  end
27
28
  end
28
29
  # !SLIDE END
@@ -9,14 +9,14 @@ module ASIR
9
9
  # !SLIDE
10
10
  # TCP Socket Client
11
11
  def _client_connect!
12
- sock = TCPSocket.open(host, port)
12
+ sock = ::TCPSocket.open(host, port)
13
13
  end
14
14
 
15
15
  # !SLIDE
16
16
  # TCP Socket Server
17
17
  def _server!
18
- @server = TCPServer.open(port)
19
- @server.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, false)
18
+ @server = ::TCPServer.open(port)
19
+ @server.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_KEEPALIVE, false)
20
20
  end
21
21
 
22
22
  def _server_accept_connection! server
@@ -24,8 +24,8 @@ module ASIR
24
24
  [ socket, socket ] # Use same socket for in_stream and out_stream
25
25
  end
26
26
 
27
- def _server_close_connection! stream, out_stream
28
- stream.close rescue nil
27
+ def _server_close_connection! in_stream, out_stream
28
+ in_stream.close rescue nil
29
29
  end
30
30
  end
31
31
  # !SLIDE END
@@ -12,7 +12,7 @@ module ASIR
12
12
  # Defaults to ::Thread.
13
13
  attr_accessor :thread_class
14
14
 
15
- # Callback: call(self, message, thr).
15
+ # Callback: call(self, MessageState, thr).
16
16
  attr_accessor :after_thread_new
17
17
 
18
18
  def initialize *args
@@ -20,20 +20,26 @@ module ASIR
20
20
  @one_way = true; super
21
21
  end
22
22
 
23
- def _send_message message, message_payload
23
+ def _send_message state
24
24
  thr = thread_class.new do
25
- send_result(super, nil, nil)
25
+ super
26
+ send_result(state)
26
27
  end
27
- @after_thread_new.call(self, message, thr) if @after_thread_new
28
+ state.in_stream = thr
29
+ @after_thread_new.call(self, state, thr) if @after_thread_new
28
30
  thr
29
31
  end
30
32
 
31
33
  # one-way; no Result
32
- def _receive_result message, opaque_result
34
+ def _receive_result state
33
35
  end
34
36
 
35
37
  # one-way; no Result
36
- def _send_result message, result, result_payload, stream, message_state
38
+ def _send_result state
39
+ end
40
+
41
+ # one-may; no Result
42
+ def receive_result state
37
43
  end
38
44
  end
39
45
  # !SLIDE END
@@ -9,16 +9,18 @@ module ASIR
9
9
 
10
10
  # Server-side: WEBrick
11
11
 
12
- # Receive the Message payload String from the HTTP Message object.
13
- # Returns the original http_message as the message_state.
14
- def _receive_message http_message, additional_data
15
- [ http_message.body, http_message ]
12
+ # Receive the Message payload from the HTTP Message body.
13
+ def _receive_message state
14
+ http_message = state.in_stream
15
+ state.message_payload = http_message.body
16
+ state.message_opaque = http_message
16
17
  end
17
18
 
18
- # Send the Result payload String in the HTTP Response object as application/binary.
19
- def _send_result message, result, result_payload, http_result, message_state
19
+ # Send the Result payload in the HTTP Response body as application/binary.
20
+ def _send_result state
21
+ http_result = state.out_stream
20
22
  http_result[CONTENT_TYPE] = APPLICATION_BINARY
21
- http_result.body = result_payload
23
+ http_result.body = state.result_payload
22
24
  end
23
25
 
24
26
  def prepare_server! opts = { }
data/lib/asir/uuid.rb CHANGED
@@ -1,3 +1,5 @@
1
+ require 'thread'
2
+
1
3
  module ASIR
2
4
  # Provides an RFC4122-compliant random (version 4) UUID service.
3
5
  module UUID
@@ -13,19 +15,55 @@ module UUID
13
15
  PROC_SYS_FILE = "/proc/sys/kernel/random/uuid".freeze
14
16
  case
15
17
  when File.exist?(PROC_SYS_FILE)
16
- def self.generate
18
+ def new_uuid
17
19
  File.read(PROC_SYS_FILE).chomp!
18
20
  end
19
21
  when (gem 'uuid' rescue nil)
20
22
  require 'uuid'
21
- def self.generate
23
+ def new_uuid
22
24
  ::UUID.generate
23
25
  end
24
26
  else
25
- def self.generate
27
+ def new_uuid
26
28
  raise "Unimplemented"
27
29
  end
28
30
  end
31
+ UUID_REGEX = /\A[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\Z/i
32
+
33
+ def process_uuid
34
+ @@process_uuid_mutex.synchronize do
35
+ if @@pid != $$
36
+ @@pid = $$
37
+ @@process_uuid = nil
38
+ end
39
+ @@process_uuid ||= new_uuid
40
+ end
41
+ end
42
+ @@pid = @@process_uuid = nil
43
+ @@process_uuid_mutex = Mutex.new
44
+
45
+ def counter_uuid
46
+ i = @@counter_mutex.synchronize do
47
+ @@counter += 1
48
+ end
49
+ "#{i}-#{process_uuid}"
50
+ end
51
+ @@counter ||= 0
52
+ @@counter_mutex = Mutex.new
53
+ COUNTER_UUID_REGEX = /\A[0-9]+-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\Z/i
54
+
55
+ # Returns a unique counter_uuid for a Thread.
56
+ # thr defaults to Thread.current.
57
+ def thread_uuid thr = nil
58
+ thr ||= Thread.current
59
+ thr[:'ASIR::UUID.thread_uuid'] || @@thread_uuid_mutex.synchronize do
60
+ thr[:'ASIR::UUID.thread_uuid'] = counter_uuid
61
+ end
62
+ end
63
+ @@thread_uuid_mutex = Mutex.new
64
+
65
+ extend self
66
+ alias :generate :new_uuid # DEPRECATED
29
67
  end
30
68
  end
31
69
 
data/lib/asir/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module ASIR
2
- VERSION = "1.1.12"
2
+ VERSION = "1.2.0"
3
3
  end
@@ -40,10 +40,10 @@ describe "ASIR::Transport" do
40
40
  end
41
41
 
42
42
  it 'should handle on_result_exception callbacks' do
43
- _transport, _result = nil, nil
44
- p = lambda do | transport, result |
43
+ _transport, _message_result = nil, nil
44
+ p = lambda do | transport, message_result |
45
45
  _transport = transport
46
- _result = result
46
+ _message_result = message_result
47
47
  end
48
48
  transport.on_result_exception = p
49
49
 
@@ -71,12 +71,17 @@ describe "ASIR::Transport" do
71
71
 
72
72
  _transport.should == object.transport
73
73
 
74
+ _result = _message_result.result
74
75
  _result.class.should == ASIR::Result
75
76
  _result.object_id.should == result.object_id
76
77
 
77
78
  _result.message.class.should == ASIR::Message
78
79
  _result.message.object_id.should == result.message.object_id
79
80
  _result.exception.should == exc
81
+
82
+ _message = _message_result.message
83
+ _message.object_id.should == _message_result.message.object_id
84
+
80
85
  end
81
86
  end
82
87
 
data/spec/uuid_spec.rb ADDED
@@ -0,0 +1,76 @@
1
+ require 'asir/uuid'
2
+
3
+ describe "ASIR::UUID" do
4
+ attr_accessor :u
5
+ before { @u = ASIR::UUID }
6
+
7
+ it 'should return a UUID from #new_uuid.' do
8
+ x = u.new_uuid
9
+ x.should =~ ASIR::UUID::UUID_REGEX
10
+ end
11
+
12
+ it 'should return unique result from #new_uuid.' do
13
+ a = [ ]
14
+ 10.times do
15
+ x = u.new_uuid
16
+ x.should =~ ASIR::UUID::UUID_REGEX
17
+ a << u.new_uuid
18
+ end
19
+ a.uniq.size.should == 10
20
+ end
21
+
22
+ it 'should single unique result per process from #process_uuid.' do
23
+ a = [ ]
24
+ 10.times do
25
+ x = u.process_uuid
26
+ x.should =~ ASIR::UUID::UUID_REGEX
27
+ a << u.process_uuid
28
+ a << u.generate
29
+ end
30
+ a.uniq.size.should == 11
31
+ end
32
+
33
+ it 'should single unique result per process from #counter_uuid.' do
34
+ a = [ ]
35
+ 10.times do
36
+ x = u.counter_uuid
37
+ x.should =~ ASIR::UUID::COUNTER_UUID_REGEX
38
+ a << x
39
+ a << u.process_uuid
40
+ a << u.generate
41
+ end
42
+ a.uniq.size.should == 21
43
+ end
44
+
45
+ it 'should single unique result per Thread from #thread_uuid.' do
46
+ a = [ ]
47
+ 10.times do
48
+ x = u.thread_uuid
49
+ x.should =~ ASIR::UUID::COUNTER_UUID_REGEX
50
+ a << x
51
+ a << u.generate
52
+ a << u.process_uuid
53
+ end
54
+ a.uniq.size.should == 12
55
+
56
+ a = [ ]
57
+ b = [ ]
58
+ t1 = Thread.current
59
+ t2 = Thread.new do
60
+ 10.times do
61
+ b << u.thread_uuid(t1)
62
+ b << u.thread_uuid(t2)
63
+ end
64
+ end
65
+ 10.times do
66
+ a << u.thread_uuid(t1)
67
+ a << u.thread_uuid(t2)
68
+ end
69
+
70
+ t2.join
71
+ c = a + b
72
+ c.uniq.size.should == 2
73
+ end
74
+
75
+ end
76
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: asir
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.12
4
+ version: 1.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -221,6 +221,7 @@ files:
221
221
  - lib/asir/main.rb
222
222
  - lib/asir/message.rb
223
223
  - lib/asir/message/delay.rb
224
+ - lib/asir/message/state.rb
224
225
  - lib/asir/object_resolving.rb
225
226
  - lib/asir/poll_throttle.rb
226
227
  - lib/asir/result.rb
@@ -264,6 +265,7 @@ files:
264
265
  - spec/thread_pool_spec.rb
265
266
  - spec/thread_variable_spec.rb
266
267
  - spec/transport_spec.rb
268
+ - spec/uuid_spec.rb
267
269
  - spec/yaml_spec.rb
268
270
  - stylesheets/slides.css
269
271
  homepage: http://github.com/kstephens/abstracting_services_in_ruby
@@ -303,4 +305,5 @@ test_files:
303
305
  - spec/thread_pool_spec.rb
304
306
  - spec/thread_variable_spec.rb
305
307
  - spec/transport_spec.rb
308
+ - spec/uuid_spec.rb
306
309
  - spec/yaml_spec.rb