asir 1.2.1 → 1.2.2

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
@@ -1,7 +1,8 @@
1
1
  .DS_Store
2
2
  *.slides*
3
3
  .riterate
4
- service.fifo
4
+ *.fifo
5
+ *.pipe
5
6
  service.log
6
7
  example/*service.log
7
8
  example/*.out
data/ChangeLog CHANGED
@@ -1,3 +1,9 @@
1
+ 2012-12-31 Kurt A. Stephens <ks.github@kurtstephens.com>
2
+
3
+ * v1.2.2: New Version: JRuby support.
4
+ * File: Support for two-way named fifo services.
5
+ * JRuby: JRuby tests pass.
6
+
1
7
  2012-12-29 Kurt A. Stephens <ks.github@kurtstephens.com>
2
8
 
3
9
  * v1.2.1: New version: API changes.
@@ -22,15 +22,15 @@ Gem::Specification.new do |s|
22
22
  s.extra_rdoc_files = [ "README.textile" ]
23
23
  s.rdoc_options = ["--charset=UTF-8"]
24
24
 
25
- if RUBY_PLATFORM !~ /linux/i
26
- # see lib/asir/uuid.rb
27
- gem.add_dependency "uuid", "~> 2.3.6"
28
- end
25
+ gem.add_dependency "uuid", "~> 2.3.6"
29
26
  s.add_dependency "httpclient", "~> 2.3.0"
30
27
  s.add_dependency "rack", "~> 1.4.1"
31
28
 
32
29
  s.add_development_dependency 'rake', '>= 0.9.0'
33
30
  s.add_development_dependency 'rspec', '~> 2.12.0'
34
31
  s.add_development_dependency 'simplecov', '>= 0.1'
32
+ if (RUBY_ENGINE rescue 'UNKNOWN') =~ /jruby/i
33
+ s.add_development_dependency 'spoon', '>= 0.0.1'
34
+ end
35
35
  end
36
36
 
@@ -10,9 +10,11 @@ begin
10
10
  pr Email.asir.send_email(:pdf_invoice,
11
11
  :to => "user@email.com",
12
12
  :customer => @customer)
13
+ sleep 1
13
14
  end
14
15
 
15
16
  # !SLIDE END
17
+ # PENDING: RUBY_PLATFORM =~ /java/i
16
18
  # EXPECT: : client process
17
19
  # EXPECT: : Email.send_mail :pdf_invoice
18
20
  # EXPECT: : pr: nil
@@ -1,11 +1,9 @@
1
1
  # !SLIDE :capture_code_output true
2
2
  # One-way, named pipe service
3
3
 
4
- $stderr.puts " #{$$} at #{__FILE__}:#{__LINE__}"
5
-
6
4
  require 'example_helper'
7
5
  begin
8
- File.unlink(service_pipe = "service.pipe") rescue nil
6
+ File.unlink(service_pipe = "#{__FILE__}.service.pipe") rescue nil
9
7
  Email.asir.transport = t =
10
8
  ASIR::Transport::File.new(:file => service_pipe)
11
9
  t.encoder =
@@ -7,8 +7,8 @@ begin
7
7
  ASIR::Transport::TcpSocket.new(:port => 30909)
8
8
  t.encoder =
9
9
  ASIR::Coder::Marshal.new
10
- t.prepare_server!
11
10
  server_process do
11
+ t.prepare_server!
12
12
  t.run_server!
13
13
  end
14
14
  pr Email.asir.send_email(:pdf_invoice,
@@ -7,8 +7,8 @@ begin
7
7
  ASIR::Transport::TcpSocket.new(:port => 30910)
8
8
  t.encoder =
9
9
  ASIR::Coder::Marshal.new
10
- t.prepare_server!
11
10
  server_process do
11
+ t.prepare_server!
12
12
  t.run_server!
13
13
  end
14
14
  pr Email.asir.do_raise("Raise Me!")
@@ -11,7 +11,7 @@ begin
11
11
  ASIR::Transport::Broadcast.new(:transports => [
12
12
  file = ASIR::Transport::File.new(:file => service_log,
13
13
  :encoder => ASIR::Coder::Yaml.new(:yaml_options => { :ASCII_8BIT_ok => true })),
14
- ASIR::Transport::Subprocess.new,
14
+ ASIR::Transport::Local.new,
15
15
  ]),
16
16
  ])
17
17
  pr Email.asir.send_email(:pdf_invoice,
@@ -0,0 +1,28 @@
1
+ # !SLIDE :capture_code_output true
2
+ # Two-way, Named Pipe service
3
+
4
+ require 'example_helper'
5
+ begin
6
+ File.unlink(service_log = "#{__FILE__}.service.pipe") rescue nil
7
+ Email.asir.transport = t =
8
+ ASIR::Transport::File.new(:file => service_log, :one_way => false, :result_fifo => true)
9
+ t.encoder =
10
+ ASIR::Coder::Yaml.new(:yaml_options => { :ASCII_8BIT_ok => true })
11
+ server_process do
12
+ t.prepare_server!
13
+ t.run_server!
14
+ end
15
+ sleep 1
16
+ pr Email.asir.send_email(:pdf_invoice,
17
+ :to => "user@email.com",
18
+ :customer => @customer)
19
+ ensure
20
+ t.close rescue nil; sleep 1; server_kill
21
+ end
22
+
23
+ # !SLIDE END
24
+ # PENDING: RUBY_PLATFORM =~ /java/i
25
+ # EXPECT: : client process
26
+ # EXPECT: : server process
27
+ # EXPECT: : Email.send_mail :pdf_invoice
28
+ # EXPECT: : pr: :ok
@@ -10,9 +10,12 @@ begin
10
10
  :to => "user@email.com",
11
11
  :customer => @customer) { | resp |
12
12
  pr [ :in_block, resp.result ] })
13
+ ensure
14
+ sleep 1
13
15
  end
14
16
 
15
17
  # !SLIDE END
18
+ # PENDING: RUBY_PLATFORM =~ /java/i
16
19
  # EXPECT: : client process
17
20
  # EXPECT: : Email.send_mail :pdf_invoice
18
21
  # EXPECT: : pr: nil
@@ -1,5 +1,6 @@
1
1
  # Sample client support
2
2
  #
3
+
3
4
  require 'rubygems'
4
5
  case RUBY_PLATFORM
5
6
  when /java/i
@@ -25,6 +26,7 @@ require 'delayed_service'
25
26
  require 'unsafe_service'
26
27
 
27
28
  require 'pp'
29
+ require 'timeout'
28
30
  require File.expand_path('../../spec/debug_helper', __FILE__)
29
31
 
30
32
  @customer = 123
@@ -35,63 +37,47 @@ def pr result
35
37
  $stdout.puts "*** #{$$}: pr: #{PP.pp(result, '')}"
36
38
  end
37
39
 
40
+ # Work-around lack of fork in JRuby.
41
+ require 'asir/application'
42
+ $asir_app = ASIR::Application.new
43
+ $asir_app.inc = [ 'example', 'lib' ]
44
+ $asir_server = nil
45
+
38
46
  def server_process &blk
39
- # $stderr.puts " at #{__FILE__}:#{__LINE__}"
40
- case RUBY_PLATFORM
41
- when /java/i
42
- # JRuby cannot fork.
43
- # So we must prevent spawn a new jruby and
44
- # instruct it to only run the server blk, and not
45
- # the subsequent client code.
46
- # In other words, we cannot rely on how Process.fork
47
- # terminates within the block.
48
- if ENV['ASIR_JRUBY_SPAWNED']
49
- $stderr.puts " spawned server at #{__FILE__}:#{__LINE__}"
50
- puts "*** #{$$}: server process"; $stdout.flush
51
- begin
52
- yield
53
- rescue ::Exception => exc
54
- $stderr.puts "*** #{$$}: service ERROR: #{exc.inspect}\n #{exc.backtrace * " \n"}"
55
- raise exc
56
- end
57
- Process.exit!(0)
58
- # dont do client, client is our parent process.
59
- else
60
- $stderr.puts " spawning at #{__FILE__}:#{__LINE__}"
61
- ENV['ASIR_JRUBY_SPAWNED'] = "1"
62
- cmd = "ruby -I #{File.dirname(__FILE__)} -I #{File.expand_path('../../lib', __FILE__)} #{$0} #{ARGV * ' '}"
63
- $stderr.puts " cmd = #{cmd}"
64
- $server_pid = Spoon.spawnp(cmd)
65
- ENV.delete('ASIR_JRUBY_SPAWNED')
66
- $stderr.puts " spawned #{$server_pid} at #{__FILE__}:#{__LINE__}"
67
- end
68
- else
69
- # $stderr.puts " at #{__FILE__}:#{__LINE__}"
70
- $server_pid = Process.fork do
71
- puts "*** #{$$}: server process"; $stdout.flush
72
- begin
47
+ $asir_server = $asir_app.spawn :server do
48
+ puts "*** #{$$}: server process"; $stdout.flush
49
+ begin
50
+ Timeout.timeout(20, ASIR::Error::Fatal) do
73
51
  yield
74
- rescue ::Exception => exc
75
- $stderr.puts "*** #{$$}: service ERROR: #{exc.inspect}\n #{exc.backtrace * " \n"}"
76
- raise exc
77
52
  end
53
+ rescue ::Exception => exc
54
+ $stderr.puts "*** #{$$}: service ERROR: #{exc.inspect}\n #{exc.backtrace * " \n"}"
55
+ raise exc
78
56
  end
79
57
  end
80
- sleep 1 # wait for server to be ready.
58
+ $asir_app.main do
59
+ $asir_server.go!
60
+ $server_pid = $asir_server.pid
61
+ sleep 1 # wait for server to be ready.
62
+ end
81
63
  return false # do client.
82
64
  end
83
65
 
84
66
  def server_kill
85
67
  if $server_pid
86
- Process.kill 9, $server_pid
87
- Process.waitpid($server_pid)
68
+ $asir_server.kill
88
69
  end
89
- rescue Errno::ESRCH
90
70
  ensure
91
71
  $server_pid = nil
92
72
  end
93
73
 
74
+ end # class Object
75
+
76
+ module Process
77
+ include ASIR::Client
94
78
  end
95
79
 
96
- puts "*** #{$$}: client process"; $stdout.flush
80
+ unless $asir_app.in_spawn?
81
+ puts "*** #{$$}: client process"; $stdout.flush
82
+ end
97
83
 
@@ -7,6 +7,8 @@ require 'asir/transport/subprocess'
7
7
  require 'math_service'
8
8
  MathService.send(:include, ASIR::Client)
9
9
 
10
+ Process.exit!(0) if RUBY_PLATFORM =~ /java/i
11
+
10
12
  ######################################################################
11
13
  # Driver:
12
14
 
@@ -8,6 +8,8 @@ require 'asir/coder/marshal'
8
8
  require 'math_service'
9
9
  MathService.send(:include, ASIR::Client)
10
10
 
11
+ Process.exit!(0) if RUBY_PLATFORM =~ /java/i
12
+
11
13
  port = 3001
12
14
  begin
13
15
  t = ASIR::Transport::Webrick.new(:uri => "http://localhost:#{port}/")
@@ -9,6 +9,8 @@ require 'asir/coder/chain'
9
9
  require 'math_service'
10
10
  MathService.send(:include, ASIR::Client)
11
11
 
12
+ Process.exit!(0) if RUBY_PLATFORM =~ /java/i
13
+
12
14
  port = 3001
13
15
  begin
14
16
  t = ASIR::Transport::Webrick.new(:uri => "http://localhost:#{port}/")
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift File.expand_path('../../lib', __FILE__)
3
+ require 'asir/application'
4
+ require 'pp'
5
+
6
+ begin
7
+ app = ASIR::Application.new
8
+ spawn = app.spawn :hello do
9
+ $stderr.puts "Hello from spawn #{$$}"
10
+ end
11
+ app.main do
12
+ $stderr.puts "Hello from main #{$$}"
13
+ spawn.go!
14
+ spawn.wait
15
+ end
16
+ rescue ::Exception
17
+ $stderr.puts "ERROR: #{$!.inspect}\n #{$!.backtrace * "\n "}"
18
+ end
@@ -231,6 +231,7 @@ Asir = ASIR
231
231
 
232
232
  require "asir/version"
233
233
  require 'asir/config'
234
+ require 'asir/system'
234
235
  require 'asir/error'
235
236
  require 'asir/log'
236
237
  require 'asir/initialization'
@@ -0,0 +1,118 @@
1
+ require 'asir/system'
2
+
3
+ module ASIR
4
+ # Ugly workaround JRuby's lack of fork().
5
+ class Application
6
+ attr_accessor :inc, :verbose
7
+
8
+ def initialize
9
+ @inc = [ ]
10
+ @name_to_spawn = { }
11
+ end
12
+
13
+ def spawn name, &blk
14
+ name = name.to_sym
15
+ spawn = Spawn.new
16
+ @name_to_spawn[spawn.name = name] = spawn
17
+ spawn.app = self
18
+ spawn.blk = blk
19
+ spawn
20
+ end
21
+
22
+ class Spawn
23
+ attr_accessor :name, :blk, :app, :pid, :cmd, :tmp, :tmp_out
24
+ def verbose; app.verbose; end
25
+
26
+ def to_s
27
+ "#<#{self.class.name} #{name.inspect} #{@pid} >"
28
+ end
29
+
30
+ def go!
31
+ case RUBY_PLATFORM
32
+ when /java/i
33
+ @tmp = "/tmp/#{$$}.#{name}"
34
+ @tmp_out = "#{tmp}.out"
35
+ @tmp_run = "#{tmp}.run"
36
+ File.unlink(@tmp_out) rescue nil
37
+ File.unlink(@tmp_run) rescue nil
38
+ begin
39
+ require 'spoon'
40
+ ruby = ASIR.ruby_path
41
+ # ruby = "/usr/bin/ruby"
42
+ inc = app.inc.map{|x| "-I#{x}"}
43
+ @cmd = [ ruby ]
44
+ @cmd.concat(inc)
45
+ @cmd.concat([ $0, "--asir-spawn=#{name}", @tmp ])
46
+ $stderr.puts "#{self} cmd #{cmd * ' '}" if verbose
47
+ @pid = Spoon.spawnp(*cmd)
48
+ # Wait until started.
49
+ until File.exist?(@tmp_run)
50
+ sleep 0.1
51
+ end
52
+ ensure
53
+ File.unlink(@tmp_run) rescue nil
54
+ end
55
+ else
56
+ @pid = Process.fork(&blk)
57
+ end
58
+ @pid
59
+ end
60
+
61
+ def kill
62
+ Process.kill 9, pid
63
+ wait
64
+ end
65
+
66
+ def wait
67
+ # Wait until finished and collect output.
68
+ begin
69
+ Process.waitpid(pid)
70
+ while true
71
+ Process.kill(0, pid)
72
+ sleep 1
73
+ end
74
+ rescue Errno::ECHILD, Errno::ESRCH
75
+ ensure
76
+ $stderr.puts "Spawn #{self} finished" if verbose
77
+ @pid = nil
78
+ if tmp_out && File.exist?(tmp_out)
79
+ $stdout.write(File.read(tmp_out))
80
+ File.unlink(tmp_out)
81
+ end
82
+ @tmp_out = nil
83
+ end
84
+ end
85
+ end # class
86
+
87
+ def in_spawn?
88
+ return @in_spawn unless @in_spawn.nil?
89
+ @in_spawn = false
90
+ $stderr.puts "#{$$} ARGV = #{ARGV.inspect}" if verbose
91
+ if ARGV.size >= 1 and ARGV[0] =~ /^--asir-spawn=(.*)/
92
+ @in_spawn = $1.to_sym
93
+ end
94
+ @in_spawn
95
+ end
96
+
97
+ def main
98
+ if in_spawn?
99
+ begin
100
+ name = @in_spawn
101
+ if tmp = ARGV[1]
102
+ out = "#{tmp}.out"
103
+ run = "#{tmp}.run"
104
+ $stdout = $stderr = File.open(out, "w")
105
+ STDOUT.reopen($stdout); STDERR.reopen($stderr)
106
+ File.open(run, "w") { | fh | fh.puts $$ }
107
+ end
108
+ spawn = @name_to_spawn[name]
109
+ raise "#{$$} #{self} Cannot find spawn name #{name.inspect}" unless spawn
110
+ spawn.blk.call
111
+ ensure
112
+ Process.exit!(0)
113
+ end
114
+ end
115
+ yield if block_given?
116
+ end
117
+ end
118
+ end
@@ -1,5 +1,6 @@
1
1
  require 'asir'
2
2
  require 'asir/retry_behavior'
3
+ require 'thread'
3
4
 
4
5
  module ASIR
5
6
  # Generic I/O Channel abstraction.
@@ -20,6 +21,7 @@ module ASIR
20
21
  end
21
22
 
22
23
  def initialize opts = nil
24
+ @mutex = Mutex.new
23
25
  @on_close = ON_CLOSE
24
26
  @on_error = ON_ERROR
25
27
  # @on_retry = ON_RETRY
@@ -48,6 +50,10 @@ module ASIR
48
50
  @on_connect.call(self)
49
51
  when :retry #, exc
50
52
  exc = data
53
+ case exc
54
+ when *Error::Unrecoverable.modules
55
+ raise exc
56
+ end
51
57
  $stderr.puts "RETRY: #{n_try}: ERROR : #{data.inspect}"
52
58
  @on_retry.call(self, exc, :connect) if @on_retry
53
59
  when :failed
@@ -57,7 +63,7 @@ module ASIR
57
63
  end
58
64
  end
59
65
  end
60
-
66
+
61
67
  # Invokes @on_close.call(self, stream).
62
68
  # On Exception, invokes @on_error.call(self, exc, :close, stream).
63
69
  def close
@@ -65,6 +71,8 @@ module ASIR
65
71
  self.stream = nil
66
72
  @on_close.call(self, stream) if @on_close
67
73
  end
74
+ rescue *Error::Unrecoverable.modules
75
+ raise
68
76
  rescue ::Exception => exc
69
77
  handle_error!(exc, :close, stream)
70
78
  end
@@ -75,6 +83,8 @@ module ASIR
75
83
  x = stream
76
84
  begin
77
85
  yield x
86
+ rescue *Error::Unrecoverable.modules
87
+ raise
78
88
  rescue ::Exception => exc
79
89
  handle_error!(exc, :with_stream, x)
80
90
  end
@@ -100,6 +110,7 @@ module ASIR
100
110
  # Returns a Thread-specific mapping, unique to this process id.
101
111
  # Maps from Channel objects to actual stream.
102
112
  def _streams
113
+ @mutex.synchronize do
103
114
  streams = Thread.current[:'ASIR::Channel._streams'] ||= { }
104
115
  if @owning_process != $$ || # child process?
105
116
  @owning_process > $$ # PIDs wrapped around?
@@ -107,6 +118,7 @@ module ASIR
107
118
  streams.clear
108
119
  end
109
120
  streams
121
+ end
110
122
  end
111
123
 
112
124
  # Returns the stream for this Channel, or nil.
@@ -7,12 +7,22 @@ module ASIR
7
7
  # Unsupported Feature.
8
8
  class Unsupported < self; end
9
9
 
10
- # Unimplemented Feature
10
+ # Unimplemented Feature.
11
11
  class Unimplemented < self; end
12
12
 
13
13
  # Requested Stop.
14
14
  class Terminate < self; end
15
15
 
16
+ # Unrecoverable Errors.
17
+ class Unrecoverable < self
18
+ def self.modules; @@modules; end
19
+ def self.modules= x; @@modules = x; end
20
+ @@modules ||= [ self ]
21
+ end
22
+
23
+ # Fatal Errors.
24
+ class Fatal < Unrecoverable; end
25
+
16
26
  # Unforwardable Exception.
17
27
  #
18
28
  # This encapsulates an Exception that should never be
@@ -38,6 +48,7 @@ module ASIR
38
48
  ::Interrupt,
39
49
  ::SignalException,
40
50
  Error::Terminate,
51
+ Error::Unrecoverable,
41
52
  ]
42
53
  end
43
54
  end
@@ -0,0 +1,30 @@
1
+ module ASIR
2
+ # Module to create FIFO/Named Pipes.
3
+ module Fifo
4
+ begin
5
+ require 'ffi'
6
+ module LIBC
7
+ extend FFI::Library
8
+ ffi_lib FFI::Library::LIBC
9
+ attach_function :mkfifo, [ :string, :long ], :int
10
+ end
11
+ def mkfifo file, perms = nil
12
+ perms ||= 0600
13
+ if LIBC.mkfifo(file, perms) < 0
14
+ raise "mkfifo(#{file.inspect}, #{'0%o' % perms}) failed"
15
+ end
16
+ true
17
+ end
18
+ rescue ::Exception => exc
19
+ def mkfifo file, perms = nil
20
+ perms ||= 0600
21
+ system(cmd = "mkfifo #{file.inspect}") or raise "cannot run #{cmd.inspect}"
22
+ ::File.chmod(perms, file) rescue nil if perms
23
+ true
24
+ end
25
+ end
26
+ extend self
27
+ end
28
+ end
29
+
30
+
@@ -24,7 +24,9 @@ module ASIR
24
24
  n_try += 1
25
25
  result = yield :try, n_try
26
26
  done = true
27
- rescue *Error::Unforwardable.unforwardable => exc
27
+ rescue *Error::Unrecoverable.modules
28
+ raise
29
+ rescue *Error::Unforwardable.unforwardable
28
30
  raise
29
31
  rescue ::Exception => exc
30
32
  last_exception = exc
@@ -0,0 +1,12 @@
1
+ module ASIR
2
+ ::RUBY_ENGINE = 'UNKNOWN'.freeze unless defined? ::RUBY_ENGINE
3
+ def self.ruby_path
4
+ @@ruby_path ||=
5
+ begin
6
+ require 'rbconfig'
7
+ File.join(RbConfig::CONFIG["bindir"],
8
+ RbConfig::CONFIG["RUBY_INSTALL_NAME"] +
9
+ RbConfig::CONFIG["EXEEXT"]).freeze
10
+ end
11
+ end
12
+ end
@@ -60,9 +60,6 @@ module ASIR
60
60
  @workers << worker
61
61
  end
62
62
  worker.run!
63
- # rescue ::Exception => exc
64
- # log! "ERROR: #{exc.inspect}\n#{exc.backtrace * "\n"}"
65
- # raise exc
66
63
  ensure
67
64
  @workers_mutex.synchronize do
68
65
  @workers.delete(worker)
@@ -157,6 +157,8 @@ module ASIR
157
157
  else
158
158
  nil
159
159
  end
160
+ rescue *Error::Unrecoverable.modules
161
+ raise
160
162
  rescue ::Exception => exc
161
163
  exception = original_exception = exc
162
164
  _log [ :message_error, exc ]
@@ -175,6 +177,8 @@ module ASIR
175
177
  send_result(state)
176
178
  end
177
179
  end
180
+ rescue *Error::Unrecoverable.modules
181
+ raise
178
182
  rescue ::Exception => exc
179
183
  _log [ :result_error, exc, exc.backtrace ]
180
184
  @on_exception.call(self, exc, :result, state) if @on_exception
@@ -14,6 +14,8 @@ module ASIR
14
14
  transports.each do | transport |
15
15
  begin
16
16
  result = transport.send_message(state.message)
17
+ rescue *Error::Unrecoverable.modules
18
+ raise
17
19
  rescue ::Exception => exc
18
20
  first_exception ||= exc
19
21
  _handle_send_message_exception! transport, state, exc
@@ -14,6 +14,8 @@ module ASIR
14
14
  result = transport.send_message(state.message)
15
15
  sent = true
16
16
  break
17
+ rescue *Error::Unrecoverable.modules
18
+ raise
17
19
  rescue ::Exception => exc
18
20
  first_exception ||= exc
19
21
  _handle_send_message_exception! transport, state, exc
@@ -1,39 +1,79 @@
1
1
  require 'asir/transport/stream'
2
2
  require 'asir/transport/payload_io'
3
+ require 'asir/fifo'
3
4
 
4
5
  module ASIR
5
6
  class Transport
6
7
  # !SLIDE
7
8
  # File Transport
8
9
  #
9
- # Send Message one-way to a file.
10
+ # Send Message to a file.
10
11
  # Can be used as a log or named pipe service.
11
12
  class File < Stream
12
13
  include PayloadIO # _write, _read
13
14
  attr_accessor :file, :mode, :perms, :stream
15
+ attr_accessor :result_file, :result_fifo
14
16
 
15
17
  def initialize opts = nil; @one_way = true; super; end
16
18
 
17
- # Writes a Message payload String.
19
+ # If not one_way, create a result_file fifo.
20
+ def send_message message
21
+ unless one_way || message.one_way
22
+ result_file =
23
+ message[:result_file] ||=
24
+ self.result_file ||
25
+ begin
26
+ message.create_identifier!
27
+ "#{self.file}-result-#{message.identifier}"
28
+ end
29
+ unless ::File.exist?(result_file) and result_fifo
30
+ ::ASIR::Fifo.mkfifo(result_file, perms)
31
+ message[:result_file_created] = true
32
+ end
33
+ end
34
+ super
35
+ ensure
36
+ if message[:result_file_created]
37
+ ::File.unlink(result_file) rescue nil
38
+ end
39
+ end
40
+
18
41
  def _send_message state
19
42
  _write(state.message_payload, state.out_stream || stream, state)
20
43
  ensure
21
44
  close if file && ::File.pipe?(file)
22
45
  end
23
46
 
24
- # Returns a Message payload String.
25
47
  def _receive_message state
26
48
  state.message_payload = _read(state.in_stream || stream, state)
27
49
  end
28
50
 
29
- # one-way; no Result.
51
+ # Send result to result_file.
30
52
  def _send_result state
31
- nil
53
+ unless one_way || (message = state.message).one_way
54
+ if result_file = message[:result_file] || self.result_file
55
+ ::File.open(result_file, "a+") do | stream |
56
+ _write(state.result_payload, stream, state)
57
+ end
58
+ end
59
+ end
32
60
  end
33
61
 
34
- # one-way; no Result.
62
+ # Receive result from result_file.
35
63
  def _receive_result state
36
- nil
64
+ message = state.message
65
+ result_file = message[:result_file] || self.result_file
66
+ unless one_way || message.one_way
67
+ if result_file
68
+ ::File.open(result_file, "r") do | stream |
69
+ state.result_payload = _read(stream, state)
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ def needs_message_identifier? m
76
+ @needs_message_identifier || ! one_way || ! m.one_way
37
77
  end
38
78
 
39
79
  # !SLIDE
@@ -55,7 +95,7 @@ module ASIR
55
95
  def serve_file!
56
96
  ::File.open(file, "r") do | stream |
57
97
  @running = true
58
- _serve_stream! stream, nil # One-way: no result stream.
98
+ _serve_stream! stream, ! one_way
59
99
  end
60
100
  end
61
101
 
@@ -64,8 +104,7 @@ module ASIR
64
104
 
65
105
  def prepare_server!
66
106
  unless ::File.exist? file
67
- system(cmd = "mkfifo #{file.inspect}") or raise "cannot run #{cmd.inspect}"
68
- ::File.chmod(perms, file) rescue nil if perms
107
+ ::ASIR::Fifo.mkfifo(file, perms)
69
108
  end
70
109
  end
71
110
  alias :prepare_pipe_server! :prepare_server!
@@ -20,6 +20,8 @@ module ASIR
20
20
  while @running && ! stream_eof?(in_stream)
21
21
  begin
22
22
  serve_stream_message! in_stream, out_stream
23
+ rescue *Error::Unrecoverable.modules
24
+ raise
23
25
  rescue Error::Terminate => err
24
26
  @running = false
25
27
  _log [ :serve_stream_terminate, err ]
@@ -33,6 +33,8 @@ module ASIR
33
33
  serve_message! rq, rs
34
34
  }
35
35
  self
36
+ rescue *Error::Unrecoverable.modules
37
+ raise
36
38
  rescue ::Exception => exc
37
39
  raise Error, "Webrick Server #{uri.inspect}: #{exc.inspect}", exc.backtrace
38
40
  end
@@ -50,14 +50,14 @@ module UUID
50
50
  end
51
51
  @@counter ||= 0
52
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
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
54
 
55
55
  # Returns a unique counter_uuid for a Thread.
56
56
  # thr defaults to Thread.current.
57
57
  def thread_uuid thr = nil
58
58
  thr ||= Thread.current
59
- thr[:'ASIR::UUID.thread_uuid'] || @@thread_uuid_mutex.synchronize do
60
- thr[:'ASIR::UUID.thread_uuid'] = counter_uuid
59
+ @@thread_uuid_mutex.synchronize do
60
+ thr[:'ASIR::UUID.thread_uuid'] ||= counter_uuid
61
61
  end
62
62
  end
63
63
  @@thread_uuid_mutex = Mutex.new
@@ -1,3 +1,3 @@
1
1
  module ASIR
2
- VERSION = "1.2.1"
2
+ VERSION = "1.2.2"
3
3
  end
@@ -2,20 +2,30 @@ require File.expand_path('../spec_helper', __FILE__)
2
2
 
3
3
  $:.unshift File.expand_path('../../example', __FILE__)
4
4
 
5
+ require 'timeout'
6
+
7
+ class ASIR::Error::TestTimeout < ASIR::Error::Fatal; end
8
+
5
9
  describe "ASIR Example" do
6
10
  attr_accessor :file, :expects
7
11
 
8
12
  before(:each) do
9
13
  @expects = [ ]
14
+ @verbose = (ENV['SPEC_VERBOSE'] || 0).to_i
10
15
  end
11
16
 
12
- after(:each) do
13
- @file.should_not == nil
17
+ def file! file
18
+ @file = file
14
19
  File.open(@file) do | fh |
15
20
  until fh.eof?
16
21
  line = fh.readline
17
22
  line.chomp!
18
23
  case
24
+ when line.sub!(/^\s*#\s*PENDING:\s*/, '')
25
+ line.strip!
26
+ @pending_reason = line
27
+ @pending = ! ! (eval(line) rescue nil)
28
+ # $stderr.puts " PENDING #{@pending_reason.inspect} => #{@pending.inspect}" if ENV['SPEC_VERBOSE']
19
29
  when line.sub!(/^\s*#\s*EXPECT\/:\s*/, '')
20
30
  expect Regexp.new(line)
21
31
  when line.sub!(/^\s*#\s*EXPECT!\/:\s*/, '')
@@ -27,11 +37,23 @@ describe "ASIR Example" do
27
37
  end
28
38
  end
29
39
  end
40
+
30
41
  @output, @exit_code = run_file!(@file)
42
+
43
+ if @pending
44
+ pending @pending_reason do
45
+ check_result!
46
+ end
47
+ else
48
+ check_result!
49
+ end
50
+ end
51
+
52
+ def check_result!
31
53
  @exit_code.should == 0
32
54
  @expects.empty?.should_not == true
33
55
  @expects.each do | rx, mode |
34
- $stderr.puts " Checking #{mode} #{rx.inspect}" if ENV['SPEC_VERBOSE']
56
+ $stderr.puts " Checking #{mode} #{rx.inspect}" if @verbose >= 2
35
57
  case mode
36
58
  when :'=~'
37
59
  @output.should =~ rx
@@ -43,31 +65,36 @@ describe "ASIR Example" do
43
65
  end
44
66
  end
45
67
 
46
- def run_file! file, output = StringIO.new('')
68
+ def run_file! file
69
+ output = nil
70
+ output_file = "#{file}.out"
47
71
  progname_save, stdout_save, stderr_save = $0, $stdout, $stderr
72
+ ruby = ASIR.ruby_path
48
73
  exc = system_exit = nil; exit_code = 0
49
74
  begin
50
- if true
51
- cmd = "ASIR_EXAMPLE_SILENT=1 ruby -I example -I lib #{file}"
52
- $stderr.puts "\n Running #{cmd}:" if ENV['SPEC_VERBOSE']
53
- output = `#{cmd} 2>&1 | tee #{file}.out`
54
- else
55
- $stderr.puts "\n Loading #{file}:" if ENV['SPEC_VERBOSE']
56
- $stdout.puts "*** #{$$}: client process"; $stdout.flush
57
- $stdout = $stderr = output
58
- $0 = file
59
- Kernel.load(file, true)
60
- output = output.string if StringIO === output
75
+ Timeout.timeout(20, ASIR::Error::TestTimeout) do
76
+ cmd = "ASIR_EXAMPLE_SILENT=1 #{ruby.inspect} -I example -I lib #{file}"
77
+ if @verbose >= 1
78
+ $stderr.puts "\n Running #{cmd}:"
79
+ $stderr.puts "\n -- #{@title}" if @title
80
+ end
81
+ File.unlink(output_file) rescue nil
82
+ system("#{cmd} >#{output_file} 2>&1")
61
83
  end
84
+ rescue ASIR::Error::TestTimeout
85
+ $stderr.puts " Warning: Timeout in #{@file}"
86
+ exit_code = 0 # OK if checks pass
62
87
  rescue ::SystemExit => system_exit
63
88
  exit_code = 1 # ???
64
89
  rescue ::Exception => exc
65
90
  exit_code = -1
66
91
  end
92
+ output = File.read(output_file)
67
93
  [ output, exit_code ]
68
94
  ensure
69
95
  $0, $stdout, $stderr = progname_save, stdout_save, stderr_save
70
- $stderr.write output if ENV['SPEC_VERBOSE']
96
+ output ||= File.read(output_file)
97
+ $stderr.write output if @verbose >= 1
71
98
  if exc
72
99
  stderr_save.puts "ERROR: #{file}: #{exc.inspect}\n#{exc.backtrace * "\n"}"
73
100
  raise exc
@@ -82,7 +109,8 @@ describe "ASIR Example" do
82
109
  title = File.open(file) { | fh | fh.read(4096) }
83
110
  title = title =~ /#\s+!SLIDE[^\n]*\n\s*#\s*([^\n]+)/ && $1
84
111
  it "#{file} - #{title}" do
85
- @file = file
112
+ @title = title
113
+ file! file
86
114
  end
87
115
  end
88
116
  end
@@ -19,16 +19,19 @@ describe "ASIR::Coder::Yaml" do
19
19
  [ :Symbol, ':Symbol' ],
20
20
  ].each do | x |
21
21
  x, str = *x
22
- if x == nil and RUBY_VERSION == '1.9.2'
23
- str = '!!null '
22
+ if x == nil
23
+ if RUBY_VERSION == '1.9.2'
24
+ str = '!!null '
25
+ end
24
26
  end
25
27
  str ||= x.to_s
26
- str = "--- #{str}\n"
27
- str << "...\n" if RUBY_VERSION !~ /^1\.8/
28
+ unless x == nil and RUBY_VERSION !~ /^1\.8/ and RUBY_ENGINE =~ /jruby/i
29
+ str = " #{str}"
30
+ end
28
31
  basic_objs << [ x, str ]
29
32
  it "should handle #{x.inspect}" do
30
33
  out = @enc.prepare.encode(x)
31
- out.should == str
34
+ out.should =~ /\A---#{str} ?\n(\.\.\.\n)?\Z/
32
35
  @dec.prepare.decode(out).should == x
33
36
  end
34
37
  end
@@ -43,7 +46,7 @@ describe "ASIR::Coder::Yaml" do
43
46
  when '1.9.2'
44
47
  out.should =~ /^ :binary: |-\n/m
45
48
  else
46
- out.should =~ /^ :binary: ! "\\x04/m
49
+ out.should =~ /^ :binary: (! )?"\\x04/m
47
50
  end
48
51
  end
49
52
 
@@ -78,6 +81,10 @@ describe "ASIR::Coder::Yaml" do
78
81
  require 'socket'
79
82
  hostname = Socket.gethostname
80
83
  enc = hostname.encoding
84
+ if enc.inspect != "#<Encoding:ASCII-8BIT>" # JRUBY?
85
+ hostname.force_encoding('ASCII-8BIT')
86
+ enc = hostname.encoding
87
+ end
81
88
  enc.inspect.should == "#<Encoding:ASCII-8BIT>"
82
89
 
83
90
  str = enc.inspect
@@ -93,7 +100,7 @@ describe "ASIR::Coder::Yaml" do
93
100
  end
94
101
 
95
102
  yaml = ::YAML.dump(str, nil, :never_binary => true)
96
- yaml.should == "--- ! '#<Encoding:ASCII-8BIT>'\n"
103
+ yaml.should =~ /\A--- (! )?['"]\#<Encoding:ASCII-8BIT>['"]\n/
97
104
  end
98
105
 
99
106
  it 'should handle :never_binary options.' do
@@ -103,7 +110,12 @@ describe "ASIR::Coder::Yaml" do
103
110
  out = @enc.prepare.encode(str)
104
111
  case RUBY_VERSION
105
112
  when '1.9.2'
106
- out.should == "--- 8bitascii\n...\n"
113
+ case RUBY_ENGINE
114
+ when /jruby/i
115
+ out.should == "--- 8bitascii\n"
116
+ else
117
+ out.should == "--- 8bitascii\n...\n"
118
+ end
107
119
  else
108
120
  out.should == "--- !binary |-\n OGJpdGFzY2lp\n"
109
121
  end
@@ -112,7 +124,12 @@ describe "ASIR::Coder::Yaml" do
112
124
  @enc.yaml_options = { :never_binary => true }
113
125
  @dec.yaml_options = @enc.yaml_options
114
126
  out = @enc.prepare.encode(str)
115
- out.should == "--- 8bitascii\n...\n"
127
+ case RUBY_ENGINE
128
+ when /jruby/i
129
+ out.should == "--- 8bitascii\n"
130
+ else
131
+ out.should == "--- 8bitascii\n...\n"
132
+ end
116
133
  inp = @dec.prepare.decode(str)
117
134
  inp.should == str
118
135
  inp.encoding.inspect.should == "#<Encoding:UTF-8>"
metadata CHANGED
@@ -1,120 +1,114 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: asir
3
- version: !ruby/object:Gem::Version
4
- version: 1.2.1
5
- prerelease:
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 1
7
+ - 2
8
+ - 2
9
+ version: 1.2.2
6
10
  platform: ruby
7
- authors:
11
+ authors:
8
12
  - Kurt Stephens
9
13
  autorequire:
10
14
  bindir: bin
11
15
  cert_chain: []
12
- date: 2012-12-29 00:00:00.000000000 Z
13
- dependencies:
14
- - !ruby/object:Gem::Dependency
15
- name: uuid
16
- requirement: !ruby/object:Gem::Requirement
17
- none: false
18
- requirements:
19
- - - ~>
20
- - !ruby/object:Gem::Version
21
- version: 2.3.6
16
+
17
+ date: 2012-12-31 00:00:00 -06:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
22
21
  type: :runtime
23
- prerelease: false
24
- version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
- requirements:
22
+ version_requirements: &id001 !ruby/object:Gem::Requirement
23
+ requirements:
27
24
  - - ~>
28
- - !ruby/object:Gem::Version
25
+ - !ruby/object:Gem::Version
26
+ segments:
27
+ - 2
28
+ - 3
29
+ - 6
29
30
  version: 2.3.6
30
- - !ruby/object:Gem::Dependency
31
- name: httpclient
32
- requirement: !ruby/object:Gem::Requirement
33
- none: false
34
- requirements:
35
- - - ~>
36
- - !ruby/object:Gem::Version
37
- version: 2.3.0
38
- type: :runtime
31
+ name: uuid
32
+ requirement: *id001
39
33
  prerelease: false
40
- version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
- requirements:
34
+ - !ruby/object:Gem::Dependency
35
+ type: :runtime
36
+ version_requirements: &id002 !ruby/object:Gem::Requirement
37
+ requirements:
43
38
  - - ~>
44
- - !ruby/object:Gem::Version
39
+ - !ruby/object:Gem::Version
40
+ segments:
41
+ - 2
42
+ - 3
43
+ - 0
45
44
  version: 2.3.0
46
- - !ruby/object:Gem::Dependency
47
- name: rack
48
- requirement: !ruby/object:Gem::Requirement
49
- none: false
50
- requirements:
51
- - - ~>
52
- - !ruby/object:Gem::Version
53
- version: 1.4.1
54
- type: :runtime
45
+ name: httpclient
46
+ requirement: *id002
55
47
  prerelease: false
56
- version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
- requirements:
48
+ - !ruby/object:Gem::Dependency
49
+ type: :runtime
50
+ version_requirements: &id003 !ruby/object:Gem::Requirement
51
+ requirements:
59
52
  - - ~>
60
- - !ruby/object:Gem::Version
53
+ - !ruby/object:Gem::Version
54
+ segments:
55
+ - 1
56
+ - 4
57
+ - 1
61
58
  version: 1.4.1
62
- - !ruby/object:Gem::Dependency
63
- name: rake
64
- requirement: !ruby/object:Gem::Requirement
65
- none: false
66
- requirements:
67
- - - ! '>='
68
- - !ruby/object:Gem::Version
69
- version: 0.9.0
70
- type: :development
59
+ name: rack
60
+ requirement: *id003
71
61
  prerelease: false
72
- version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
- requirements:
75
- - - ! '>='
76
- - !ruby/object:Gem::Version
77
- version: 0.9.0
78
- - !ruby/object:Gem::Dependency
79
- name: rspec
80
- requirement: !ruby/object:Gem::Requirement
81
- none: false
82
- requirements:
83
- - - ~>
84
- - !ruby/object:Gem::Version
85
- version: 2.12.0
62
+ - !ruby/object:Gem::Dependency
86
63
  type: :development
64
+ version_requirements: &id004 !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ segments:
69
+ - 0
70
+ - 9
71
+ - 0
72
+ version: 0.9.0
73
+ name: rake
74
+ requirement: *id004
87
75
  prerelease: false
88
- version_requirements: !ruby/object:Gem::Requirement
89
- none: false
90
- requirements:
76
+ - !ruby/object:Gem::Dependency
77
+ type: :development
78
+ version_requirements: &id005 !ruby/object:Gem::Requirement
79
+ requirements:
91
80
  - - ~>
92
- - !ruby/object:Gem::Version
81
+ - !ruby/object:Gem::Version
82
+ segments:
83
+ - 2
84
+ - 12
85
+ - 0
93
86
  version: 2.12.0
94
- - !ruby/object:Gem::Dependency
95
- name: simplecov
96
- requirement: !ruby/object:Gem::Requirement
97
- none: false
98
- requirements:
99
- - - ! '>='
100
- - !ruby/object:Gem::Version
101
- version: '0.1'
87
+ name: rspec
88
+ requirement: *id005
89
+ prerelease: false
90
+ - !ruby/object:Gem::Dependency
102
91
  type: :development
92
+ version_requirements: &id006 !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ segments:
97
+ - 0
98
+ - 1
99
+ version: "0.1"
100
+ name: simplecov
101
+ requirement: *id006
103
102
  prerelease: false
104
- version_requirements: !ruby/object:Gem::Requirement
105
- none: false
106
- requirements:
107
- - - ! '>='
108
- - !ruby/object:Gem::Version
109
- version: '0.1'
110
103
  description: Abstracting Services in Ruby
111
104
  email: ks.ruby@kurtstephens.com
112
- executables:
105
+ executables:
113
106
  - asir
114
107
  extensions: []
115
- extra_rdoc_files:
108
+
109
+ extra_rdoc_files:
116
110
  - README.textile
117
- files:
111
+ files:
118
112
  - .gitignore
119
113
  - .rspec
120
114
  - .travis.yml
@@ -148,6 +142,7 @@ files:
148
142
  - example/ex13.rb
149
143
  - example/ex14.rb
150
144
  - example/ex15.rb
145
+ - example/ex16.rb
151
146
  - example/ex18.rb
152
147
  - example/ex19.rb
153
148
  - example/ex22.rb
@@ -175,8 +170,10 @@ files:
175
170
  - hack_night/solution/prob-7.rb
176
171
  - lab/const_get_speed_spec.rb
177
172
  - lab/phony_proc.rb
173
+ - lab/spoon_test.rb
178
174
  - lib/asir.rb
179
175
  - lib/asir/additional_data.rb
176
+ - lib/asir/application.rb
180
177
  - lib/asir/channel.rb
181
178
  - lib/asir/client.rb
182
179
  - lib/asir/code_block.rb
@@ -197,6 +194,7 @@ files:
197
194
  - lib/asir/description.rb
198
195
  - lib/asir/environment.rb
199
196
  - lib/asir/error.rb
197
+ - lib/asir/fifo.rb
200
198
  - lib/asir/identity.rb
201
199
  - lib/asir/initialization.rb
202
200
  - lib/asir/invoker.rb
@@ -209,6 +207,7 @@ files:
209
207
  - lib/asir/poll_throttle.rb
210
208
  - lib/asir/result.rb
211
209
  - lib/asir/retry_behavior.rb
210
+ - lib/asir/system.rb
212
211
  - lib/asir/thread_pool.rb
213
212
  - lib/asir/thread_variable.rb
214
213
  - lib/asir/transport.rb
@@ -250,32 +249,37 @@ files:
250
249
  - spec/uuid_spec.rb
251
250
  - spec/yaml_spec.rb
252
251
  - stylesheets/slides.css
252
+ has_rdoc: true
253
253
  homepage: http://github.com/kstephens/abstracting_services_in_ruby
254
254
  licenses: []
255
+
255
256
  post_install_message:
256
- rdoc_options:
257
+ rdoc_options:
257
258
  - --charset=UTF-8
258
- require_paths:
259
+ require_paths:
259
260
  - lib
260
- required_ruby_version: !ruby/object:Gem::Requirement
261
- none: false
262
- requirements:
263
- - - ! '>='
264
- - !ruby/object:Gem::Version
265
- version: '0'
266
- required_rubygems_version: !ruby/object:Gem::Requirement
267
- none: false
268
- requirements:
269
- - - ! '>='
270
- - !ruby/object:Gem::Version
271
- version: '0'
261
+ required_ruby_version: !ruby/object:Gem::Requirement
262
+ requirements:
263
+ - - ">="
264
+ - !ruby/object:Gem::Version
265
+ segments:
266
+ - 0
267
+ version: "0"
268
+ required_rubygems_version: !ruby/object:Gem::Requirement
269
+ requirements:
270
+ - - ">="
271
+ - !ruby/object:Gem::Version
272
+ segments:
273
+ - 0
274
+ version: "0"
272
275
  requirements: []
276
+
273
277
  rubyforge_project:
274
- rubygems_version: 1.8.24
278
+ rubygems_version: 1.3.6
275
279
  signing_key:
276
280
  specification_version: 3
277
281
  summary: Abstracting Services in Ruby
278
- test_files:
282
+ test_files:
279
283
  - spec/client_spec.rb
280
284
  - spec/debug_helper.rb
281
285
  - spec/demux_spec.rb