asir 0.2.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. data/Gemfile +1 -2
  2. data/README.textile +4 -2
  3. data/VERSION +1 -1
  4. data/asir.gemspec +1 -4
  5. data/asir.riterate.yml +1 -0
  6. data/bin/asir +2 -1
  7. data/example/asir_control.sh +63 -1
  8. data/example/asir_control_client_http.rb +2 -2
  9. data/example/asir_control_client_resque.rb +16 -0
  10. data/example/asir_control_client_zmq.rb +3 -3
  11. data/example/config/asir_config.rb +20 -8
  12. data/example/ex02.rb +1 -1
  13. data/example/ex03.rb +2 -2
  14. data/example/ex04.rb +2 -2
  15. data/example/ex05.rb +1 -1
  16. data/example/ex06.rb +6 -5
  17. data/example/ex07.rb +2 -2
  18. data/example/ex08.rb +2 -2
  19. data/example/ex09.rb +2 -2
  20. data/example/ex10.rb +2 -2
  21. data/example/ex11.rb +5 -5
  22. data/example/ex12.rb +6 -6
  23. data/example/ex13.rb +4 -4
  24. data/example/ex14.rb +4 -4
  25. data/example/ex15.rb +2 -2
  26. data/example/ex16.rb +8 -8
  27. data/example/ex17.rb +12 -11
  28. data/example/ex18.rb +5 -5
  29. data/example/ex19.rb +3 -3
  30. data/example/ex20.rb +3 -3
  31. data/example/ex21.rb +3 -3
  32. data/example/ex22.rb +1 -1
  33. data/example/ex23.rb +2 -2
  34. data/example/ex24.rb +4 -4
  35. data/example/ex25.rb +41 -0
  36. data/example/example_helper.rb +38 -3
  37. data/example/sample_service.rb +4 -4
  38. data/hack_night/exercise/prob-3.rb +3 -3
  39. data/hack_night/exercise/prob-6.rb +2 -2
  40. data/hack_night/exercise/prob-7.rb +2 -2
  41. data/hack_night/solution/prob-2.rb +2 -2
  42. data/hack_night/solution/prob-3.rb +3 -3
  43. data/hack_night/solution/prob-6.rb +7 -6
  44. data/hack_night/solution/prob-7.rb +6 -6
  45. data/{spec → lab}/const_get_speed_spec.rb +0 -0
  46. data/lib/asir.rb +29 -7
  47. data/lib/asir/additional_data.rb +25 -0
  48. data/lib/asir/channel.rb +4 -5
  49. data/lib/asir/client.rb +29 -13
  50. data/lib/asir/config.rb +8 -0
  51. data/lib/asir/description.rb +34 -0
  52. data/lib/asir/environment.rb +96 -0
  53. data/lib/asir/error.rb +4 -1
  54. data/lib/asir/invoker.rb +14 -0
  55. data/lib/asir/main.rb +84 -103
  56. data/lib/asir/message.rb +1 -1
  57. data/lib/asir/poll_throttle.rb +53 -0
  58. data/lib/asir/retry_behavior.rb +1 -1
  59. data/lib/asir/thread_variable.rb +183 -0
  60. data/lib/asir/transport.rb +36 -23
  61. data/lib/asir/transport/beanstalk.rb +18 -52
  62. data/lib/asir/transport/conduit.rb +42 -0
  63. data/lib/asir/transport/connection_oriented.rb +32 -56
  64. data/lib/asir/transport/delegation.rb +5 -5
  65. data/lib/asir/transport/demux.rb +33 -0
  66. data/lib/asir/transport/file.rb +5 -3
  67. data/lib/asir/transport/payload_io.rb +8 -4
  68. data/lib/asir/transport/resque.rb +212 -0
  69. data/lib/asir/transport/stream.rb +19 -9
  70. data/lib/asir/transport/tcp_socket.rb +3 -2
  71. data/lib/asir/transport/zmq.rb +14 -17
  72. data/lib/asir/uri_config.rb +51 -0
  73. data/lib/asir/version.rb +1 -1
  74. data/spec/client_spec.rb +48 -0
  75. data/spec/demux_spec.rb +38 -0
  76. data/spec/json_spec.rb +0 -2
  77. data/spec/message_spec.rb +68 -0
  78. data/spec/performance_spec.rb +66 -0
  79. data/spec/spec_helper.rb +34 -0
  80. data/spec/thread_variable_spec.rb +135 -0
  81. data/spec/transport_spec.rb +82 -0
  82. metadata +28 -4
@@ -81,9 +81,9 @@ begin
81
81
 
82
82
  # system("curl http://localhost:#{port}/")
83
83
 
84
- MathService.client.transport = t
84
+ MathService.asir.transport = t
85
85
 
86
- MathService.client.sum([1, 2, 3])
86
+ MathService.asir.sum([1, 2, 3])
87
87
 
88
88
  rescue Exception => err
89
89
  $stderr.puts "ERROR: #{err.inspect}\n#{err.backtrace * "\n"}"
@@ -21,8 +21,8 @@ begin
21
21
  end
22
22
  sleep 1 # wait for server to start
23
23
 
24
- MathService.client.transport = t
25
- MathService.client.sum([1, 2, 3])
24
+ MathService.asir.transport = t
25
+ MathService.asir.sum([1, 2, 3])
26
26
 
27
27
  rescue Exception => err
28
28
  $stderr.puts "ERROR: #{err.inspect}\n#{err.backtrace * "\n"}"
@@ -10,6 +10,6 @@ MathService.send(:include, ASIR::Client)
10
10
  # Driver:
11
11
 
12
12
  begin
13
- MathService.client.transport._log_enabled = true
14
- puts MathService.client.sum([1, 2, 3])
13
+ MathService.asir.transport._log_enabled = true
14
+ puts MathService.asir.sum([1, 2, 3])
15
15
  end
@@ -11,7 +11,7 @@ MathService.send(:include, ASIR::Client)
11
11
  # Driver:
12
12
 
13
13
  begin
14
- MathService.client.transport = ASIR::Transport::Subprocess.new
15
- MathService.client.transport._log_enabled = true
16
- puts MathService.client.sum([1, 2, 3])
14
+ MathService.asir.transport = ASIR::Transport::Subprocess.new
15
+ MathService.asir.transport._log_enabled = true
16
+ puts MathService.asir.sum([1, 2, 3])
17
17
  end
@@ -1,7 +1,8 @@
1
1
  # Write a ASIR::Transport::HTTP class that uses HTTP::Client for transport send_result and receive_result.
2
+ # And WEBrick to handle requests.
2
3
 
3
4
  $: << File.expand_path("../../../lib", __FILE__)
4
- require 'asir/transport/http'
5
+ require 'asir/transport/webrick'
5
6
  require 'asir/coder/marshal'
6
7
 
7
8
  require 'math_service'
@@ -9,19 +10,19 @@ MathService.send(:include, ASIR::Client)
9
10
 
10
11
  port = 3001
11
12
  begin
12
- t = ASIR::Transport::HTTP.new(:uri => "http://localhost:#{port}/")
13
+ t = ASIR::Transport::Webrick.new(:uri => "http://localhost:#{port}/")
13
14
  t._log_enabled = true
14
15
  c = t.encoder = ASIR::Coder::Marshal.new
15
16
  c._log_enabled = true
16
17
 
17
18
  server_pid = Process.fork do
18
- t.setup_webrick_server!
19
- t.start_webrick_server!
19
+ t.prepare_server!
20
+ t.run_server!
20
21
  end
21
22
  sleep 1 # wait for server to start
22
23
 
23
- MathService.client.transport = t
24
- MathService.client.sum([1, 2, 3])
24
+ MathService.asir.transport = t
25
+ MathService.asir.sum([1, 2, 3])
25
26
 
26
27
  rescue Exception => err
27
28
  $stderr.puts "ERROR: #{err.inspect}\n#{err.backtrace * "\n"}"
@@ -1,7 +1,7 @@
1
1
  # Use the Marshal and Base64 coders in prob-4.rb
2
2
 
3
3
  $: << File.expand_path("../../../lib", __FILE__)
4
- require 'asir/transport/http'
4
+ require 'asir/transport/webrick'
5
5
  require 'asir/coder/marshal'
6
6
  require 'asir/coder/base64'
7
7
  require 'asir/coder/chain'
@@ -11,7 +11,7 @@ MathService.send(:include, ASIR::Client)
11
11
 
12
12
  port = 3001
13
13
  begin
14
- t = ASIR::Transport::HTTP.new(:uri => "http://localhost:#{port}/")
14
+ t = ASIR::Transport::Webrick.new(:uri => "http://localhost:#{port}/")
15
15
  t._log_enabled = true
16
16
  c = t.encoder = ASIR::Coder::Chain.new(:encoders =>
17
17
  [
@@ -21,13 +21,13 @@ begin
21
21
  c._log_enabled = true
22
22
 
23
23
  server_pid = Process.fork do
24
- t.setup_webrick_server!
25
- t.start_webrick_server!
24
+ t.prepare_server!
25
+ t.run_server!
26
26
  end
27
27
  sleep 1 # wait for server to start
28
28
 
29
- MathService.client.transport = t
30
- MathService.client.sum([1, 2, 3])
29
+ MathService.asir.transport = t
30
+ MathService.asir.sum([1, 2, 3])
31
31
  rescue Exception => err
32
32
  $stderr.puts "ERROR: #{err.inspect}\n#{err.backtrace * "\n"}"
33
33
  ensure
File without changes
data/lib/asir.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  #
4
4
  # * Kurt Stephens
5
5
  # * Enova Financial
6
- # * 2012/04/02
6
+ # * 2012/07/18
7
7
  # * Slides -- "":http://kurtstephens.com/pub/ruby/abstracting_services_in_ruby/asir.slides/
8
8
  # * Code -- "":http://kurtstephens.com/pub/ruby/abstracting_services_in_ruby/
9
9
  # * Git -- "":http://github.com/kstephens/abstracting_services_in_ruby
@@ -34,7 +34,7 @@
34
34
  # * Directionality: One-way, Two-way
35
35
  # * Synchronicity: Synchronous, Asynchronous, Delayed, Buffered
36
36
  # * Distribution: Local Thread, Local Process, Distributed
37
- # * Transport: File, IPC, Pipe, Network
37
+ # * Transport: File, IPC, Pipe, Network, Beanstalk, ZMQ, Resque
38
38
  # * Robustness: Retry, Replay, Fallback
39
39
  # * Encoding: XML, JSON, YAML, Base64, Compression
40
40
  #
@@ -73,11 +73,11 @@
73
73
  # !SLIDE
74
74
  # Messaging
75
75
  #
76
- # * "Call a Method", "Call a Function" are all the same, in *all* languages.
76
+ # * "Send a Message", "Call a Function" are all the same, in *all* languages.
77
77
  # ** Decomposed into lookup() and apply().
78
- # * "Send Message", not "Call a Method".
78
+ # * "Send a Message", not "Call a Method".
79
79
  # * Messaging abstracts:
80
- # ** Object use from its implementation.
80
+ # ** Behavior from Implementation.
81
81
  # ** Transfer of control (method, function invocation, RPC, etc).
82
82
 
83
83
  # !SLIDE
@@ -111,8 +111,8 @@
111
111
  # * Proxy
112
112
  # * Message -> Just a Ruby message.
113
113
  # * Result, Exception (two-way) -> Return value or else.
114
- # * Transport -> (file, pipe, http, queue, ActiveResource)
115
- # * Encoder, Decoder -> Coder (Marshal, XML, JSON, ActiveResource)
114
+ # * Transport -> (file, pipe, http, queue, socket, ZMQ).
115
+ # * Encoder, Decoder -> Coder (Marshal, XML, JSON, zlib).
116
116
  #
117
117
  # !SLIDE END
118
118
 
@@ -127,6 +127,25 @@
127
127
  #
128
128
  # !SLIDE END
129
129
 
130
+ # !SLIDE
131
+ # Abstraction Leads to Rich Features
132
+ #
133
+ # * One-way and two-way requests as Module or instance methods.
134
+ # * Message support:
135
+ # ** Delayed Messages.
136
+ # * Transports:
137
+ # ** Null, Local, File, Named Pipe, TCP.
138
+ # ** HTTP under WEBrick or as Rack application.
139
+ # ** Beanstalkd, ZeroMQ, Resque.
140
+ # ** Buffered, Broadcast, Fallback, Demux transports.
141
+ # ** Time-decaying retry logic.
142
+ # * Encodings:
143
+ # ** Marshal, XML, JSON, YAML, Base64, ZLib.
144
+ # ** Chained encodings.
145
+ # ** Signed payloads.
146
+ #
147
+ # !SLIDE END
148
+
130
149
  # !SLIDE
131
150
  # Simple
132
151
  #
@@ -211,11 +230,13 @@ end
211
230
  Asir = ASIR
212
231
 
213
232
  require "asir/version"
233
+ require 'asir/config'
214
234
  require 'asir/error'
215
235
  require 'asir/log'
216
236
  require 'asir/initialization'
217
237
  require 'asir/additional_data'
218
238
  require 'asir/object_resolving'
239
+ require 'asir/thread_variable'
219
240
  require 'asir/identity'
220
241
  require 'asir/code_block'
221
242
  require 'asir/code_more'
@@ -226,6 +247,7 @@ require 'asir/coder'
226
247
  require 'asir/coder/null'
227
248
  require 'asir/coder/identity'
228
249
  require 'asir/transport'
250
+ require 'asir/invoker'
229
251
  require 'asir/channel'
230
252
  require 'asir/transport/local'
231
253
 
@@ -19,6 +19,31 @@ module ASIR
19
19
  def []= key, value
20
20
  (@additional_data ||= { })[key] = value
21
21
  end
22
+
23
+ def self.included target
24
+ super
25
+ target.extend(ModuleMethods)
26
+ end
27
+
28
+ module ModuleMethods
29
+ # Provide a getter method that delegates to addtional_data[...].
30
+ def addr_getter *names
31
+ names.each do | name |
32
+ name = name.to_sym
33
+ define_method(name) { | | addtional_data[name] }
34
+ end
35
+ end
36
+
37
+ # Provide getter and setter methods that delegate to addtional_data[...].
38
+ def addr_accessor *names
39
+ addr_getter *names
40
+ names.each do | name |
41
+ name = name.to_sym
42
+ define_method(:"#{name}=") { | v | addtional_data[name] = v }
43
+ end
44
+ end
45
+ end
46
+
22
47
  end
23
48
  # !SLIDE END
24
49
  end
data/lib/asir/channel.rb CHANGED
@@ -10,11 +10,11 @@ module ASIR
10
10
  attr_accessor :on_connect, :on_close, :on_retry, :on_error
11
11
 
12
12
  ON_ERROR = lambda do | channel, exc, action, stream |
13
- channel.close rescue nil if stream
13
+ channel.close rescue nil
14
14
  raise exc
15
15
  end
16
16
  ON_CLOSE = lambda do | channel, stream |
17
- stream.close
17
+ stream.close rescue nil if stream
18
18
  end
19
19
  ON_RETRY = lambda do | channel, exc, action |
20
20
  end
@@ -101,9 +101,8 @@ module ASIR
101
101
  # Maps from Channel objects to actual stream.
102
102
  def _streams
103
103
  streams = Thread.current[:'ASIR::Channel._streams'] ||= { }
104
- if ! @owning_process ||
105
- @owning_process != $$ || # child process?
106
- @owning_process > $$ # PIDs wrapped around?
104
+ if @owning_process != $$ || # child process?
105
+ @owning_process > $$ # PIDs wrapped around?
107
106
  @owning_process = $$
108
107
  streams.clear
109
108
  end
data/lib/asir/client.rb CHANGED
@@ -13,27 +13,32 @@ module ASIR
13
13
  end
14
14
 
15
15
  module CommonMethods
16
- def client_options &blk
17
- client._configure(&blk)
16
+ def asir_options &blk
17
+ asir._configure(&blk)
18
18
  end
19
+ alias_method :"#{ASIR::Config.client_method}_options", :asir_options if ASIR::Config.client_method
19
20
  end
20
21
 
21
22
  module ModuleMethods
22
23
  include CommonMethods
23
24
  # Proxies are cached for Module/Class methods because serialization will not include
24
25
  # Transport.
25
- def client
26
- @_asir_client ||= ASIR::Client::Proxy.new(self)
26
+ def asir
27
+ @_asir ||= ASIR::Client::Proxy.new(self, self)
27
28
  end
29
+ alias_method ASIR::Config.client_method, :asir if ASIR::Config.client_method
28
30
  end
29
31
 
30
32
  module InstanceMethods
31
33
  include CommonMethods
32
34
  # Proxies are not cached in instances because receiver is to be serialized by
33
35
  # its Transport's Coder.
34
- def client
35
- ASIR::Client::Proxy.new(self)
36
+ def asir
37
+ proxy = self.class.asir.dup
38
+ proxy.receiver = self
39
+ proxy
36
40
  end
41
+ alias_method ASIR::Config.client_method, :asir if ASIR::Config.client_method
37
42
  end
38
43
 
39
44
  # !SLIDE
@@ -41,14 +46,14 @@ module ASIR
41
46
  #
42
47
  # Provide client interface proxy to a service.
43
48
  class Proxy
44
- attr_accessor :receiver
49
+ attr_accessor :receiver, :receiver_class
45
50
 
46
51
  # Accept messages as a proxy for the receiver.
47
52
  # Blocks are used represent a "continuation" for the Result.
48
53
  def send selector, *arguments, &block
49
54
  message = Message.new(@receiver, selector, arguments, block, self)
50
55
  message = @before_send_message.call(message) if @before_send_message
51
- @__configure.call(message) if @__configure
56
+ @__configure.call(message, self) if @__configure
52
57
  result = transport.send_message(message)
53
58
  result
54
59
  end
@@ -80,8 +85,12 @@ module ASIR
80
85
  # Returns a new Client Proxy with a block to be called with the Message.
81
86
  # This block can configure additional options of the Message before
82
87
  # it is sent to the Transport.
88
+ #
89
+ # Call sequence:
90
+ #
91
+ # proxy.__configure.call(message, proxy).
83
92
  def _configure &blk
84
- proxy = self.dup
93
+ proxy = @receiver == @receiver_class ? self.dup : self
85
94
  proxy.__configure = blk
86
95
  proxy
87
96
  end
@@ -90,12 +99,19 @@ module ASIR
90
99
  # !SLIDE
91
100
  # Configuration Callbacks
92
101
 
93
- def initialize rcvr
94
- key = Module === (@receiver = rcvr) ? @receiver : @receiver.class
95
- (@@config_callbacks[key] ||
96
- @@config_callbacks[key.name] ||
102
+ def initialize rcvr, rcvr_class
103
+ @receiver = rcvr
104
+ @receiver_class = rcvr_class
105
+ _configure!
106
+ end
107
+
108
+ def _configure!
109
+ key = @receiver_class
110
+ (@@config_callbacks[key] ||
111
+ @@config_callbacks[key.name] ||
97
112
  @@config_callbacks[nil] ||
98
113
  IDENTITY_LAMBDA).call(self)
114
+ self
99
115
  end
100
116
 
101
117
  @@config_callbacks ||= { }
@@ -0,0 +1,8 @@
1
+ module ASIR
2
+ # Low-level ASIR configuration
3
+ module Config
4
+ @@client_method = nil
5
+ def self.client_method; @@client_method; end
6
+ def self.client_method= x; @@client_method = x; end
7
+ end
8
+ end
@@ -0,0 +1,34 @@
1
+ module ASIR
2
+ module Description
3
+ def describe obj, indent = "", more = nil
4
+ case obj
5
+ when nil, ASIR::Coder::Identity
6
+ s = ""; more = nil
7
+ when ASIR::Transport
8
+ s = "#{describe(obj.encoder, indent, "->\n")}#{indent}#{obj.class.name}"
9
+ opts = [ :file, :uri ].
10
+ select { | x | obj.respond_to?(x) && obj.send(x) != nil }.
11
+ map { | x | "#{x}: #{obj.send(x).inspect}" } * ","
12
+ s << "(" << opts << ")" unless opts.empty?
13
+ case
14
+ when obj.respond_to?(:transports)
15
+ s << "->[\n"
16
+ s << obj.transports.map { | x | describe(x, indent + " ") } * ",\n"
17
+ s << "]"
18
+ when obj.respond_to?(:transport)
19
+ s << "->\n" << describe(obj.transport, indent + " ")
20
+ end
21
+ when ASIR::Coder::Chain
22
+ s = "#{indent}Chain(\n"
23
+ s << obj.encoders.map { | x | describe(x, indent + " ") } * "->\n"
24
+ s << ")"
25
+ else
26
+ s = "#{indent}#{obj.class.name}"
27
+ end
28
+ s << more if more
29
+ s
30
+ end
31
+
32
+ extend self
33
+ end
34
+ end
@@ -0,0 +1,96 @@
1
+ require 'asir'
2
+
3
+ module ASIR; class Environment
4
+ attr_accessor :phase
5
+ attr_accessor :verb, :adjective, :object, :identifier
6
+ attr_accessor :config_rb, :config
7
+ attr_accessor :log_dir, :log_file, :pid_dir
8
+ attr_accessor :verbose
9
+
10
+ def initialize
11
+ @verbose = 0
12
+ @exit_code = 0
13
+ end
14
+
15
+ def log_dir
16
+ @log_dir ||= find_writable_directory :log_dir,
17
+ ENV['ASIR_LOG_DIR'],
18
+ '/var/log/asir',
19
+ '~/asir/log',
20
+ '/tmp'
21
+ end
22
+
23
+ def pid_dir
24
+ @pid_dir ||= find_writable_directory :pid_dir,
25
+ ENV['ASIR_PID_DIR'],
26
+ '/var/run/asir',
27
+ '~/asir/run',
28
+ '/tmp'
29
+ end
30
+
31
+ def find_writable_directory kind, *list
32
+ list.
33
+ reject { | p | ! p }.
34
+ map { | p | File.expand_path(p) }.
35
+ find { | p | File.writable?(p) } or
36
+ raise "Cannot find writable directory for #{kind}"
37
+ end
38
+
39
+ def pid_file
40
+ @pid_file ||=
41
+ "#{pid_dir}/#{asir_basename}.pid"
42
+ end
43
+
44
+ def log_file
45
+ @log_file ||=
46
+ "#{log_dir}/#{asir_basename}.log"
47
+ end
48
+
49
+ def asir_basename
50
+ "asir-#{adjective}-#{object}-#{identifier}"
51
+ end
52
+
53
+ def config_rb
54
+ @config_rb ||=
55
+ File.expand_path(ENV['ASIR_CONFIG_RB'] || 'config/asir_config.rb')
56
+ end
57
+
58
+ def config! phase, opts = { }
59
+ ((@config ||= { })[phase] ||= [
60
+ begin
61
+ opts[:phase] = phase
62
+ save = { }
63
+ opts.each do | k, v |
64
+ save[k] = send(k)
65
+ send(:"#{k}=", v)
66
+ end
67
+ $stderr.puts "#{self.class} calling #{config_rb} asir.phase=#{phase.inspect} ..." if @verbose >= 1
68
+ value = config_lambda.call(self)
69
+ $stderr.puts "#{self.class} calling #{config_rb} asir.phase=#{phase.inspect} DONE" if @verbose >= 1
70
+ value
71
+ ensure
72
+ save.each do | k , v |
73
+ send(:"#{k}=", v)
74
+ end
75
+ end
76
+ ]).first
77
+ end
78
+
79
+ def config_lambda
80
+ @config_lambda ||=
81
+ (
82
+ file = config_rb
83
+ $stderr.puts "#{self.class} loading #{file} ..." if @verbose >= 1
84
+ expr = File.read(file)
85
+ expr = "begin; lambda do | asir |; #{expr}\n end; end"
86
+ cfg = Object.new.send(:eval, expr, binding, file, 1)
87
+ # cfg = load file
88
+ # $stderr.puts "#{self.class} loading #{file} DONE" if @verbose >= 1
89
+ raise "#{file} did not return a Proc, returned a #{cfg.class}" unless Proc === cfg
90
+ cfg
91
+ )
92
+ end
93
+
94
+ end; end
95
+
96
+