asir 1.2.1 → 1.2.2

Sign up to get free protection for your applications and to get access to all the features.
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