asir 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (121) hide show
  1. data/.gitignore +11 -0
  2. data/Gemfile +16 -0
  3. data/README.textile +50 -0
  4. data/Rakefile +83 -0
  5. data/VERSION +1 -0
  6. data/asir.gemspec +36 -0
  7. data/asir.riterate.yml +114 -0
  8. data/bin/asir +6 -0
  9. data/doc/Rakefile +8 -0
  10. data/doc/asir-sequence.pic +84 -0
  11. data/doc/asir-sequence.svg +1559 -0
  12. data/doc/sequence.pic +430 -0
  13. data/example/asir_control.sh +24 -0
  14. data/example/asir_control_client_http.rb +14 -0
  15. data/example/asir_control_client_zmq.rb +15 -0
  16. data/example/config/asir_config.rb +63 -0
  17. data/example/delayed_service.rb +15 -0
  18. data/example/ex01.rb +12 -0
  19. data/example/ex02.rb +12 -0
  20. data/example/ex03.rb +19 -0
  21. data/example/ex04.rb +33 -0
  22. data/example/ex05.rb +16 -0
  23. data/example/ex06.rb +26 -0
  24. data/example/ex07.rb +28 -0
  25. data/example/ex08.rb +30 -0
  26. data/example/ex09.rb +25 -0
  27. data/example/ex10.rb +24 -0
  28. data/example/ex11.rb +48 -0
  29. data/example/ex12.rb +34 -0
  30. data/example/ex13.rb +35 -0
  31. data/example/ex14.rb +30 -0
  32. data/example/ex15.rb +13 -0
  33. data/example/ex16.rb +33 -0
  34. data/example/ex17.rb +41 -0
  35. data/example/ex18.rb +62 -0
  36. data/example/ex19.rb +32 -0
  37. data/example/ex20.rb +28 -0
  38. data/example/ex21.rb +28 -0
  39. data/example/ex22.rb +15 -0
  40. data/example/ex23.rb +20 -0
  41. data/example/ex24.rb +35 -0
  42. data/example/example_helper.rb +51 -0
  43. data/example/sample_service.rb +162 -0
  44. data/example/unsafe_service.rb +12 -0
  45. data/hack_night/README.txt +18 -0
  46. data/hack_night/exercise/prob-1.rb +18 -0
  47. data/hack_night/exercise/prob-2.rb +21 -0
  48. data/hack_night/exercise/prob-3.rb +16 -0
  49. data/hack_night/exercise/prob-4.rb +36 -0
  50. data/hack_night/exercise/prob-5.rb +36 -0
  51. data/hack_night/exercise/prob-6.rb +95 -0
  52. data/hack_night/exercise/prob-7.rb +34 -0
  53. data/hack_night/solution/math_service.rb +11 -0
  54. data/hack_night/solution/prob-1.rb +12 -0
  55. data/hack_night/solution/prob-2.rb +15 -0
  56. data/hack_night/solution/prob-3.rb +17 -0
  57. data/hack_night/solution/prob-4.rb +37 -0
  58. data/hack_night/solution/prob-5.rb +21 -0
  59. data/hack_night/solution/prob-6.rb +33 -0
  60. data/hack_night/solution/prob-7.rb +36 -0
  61. data/lab/phony_proc.rb +31 -0
  62. data/lib/asir.rb +253 -0
  63. data/lib/asir/additional_data.rb +25 -0
  64. data/lib/asir/channel.rb +130 -0
  65. data/lib/asir/client.rb +111 -0
  66. data/lib/asir/code_block.rb +57 -0
  67. data/lib/asir/code_more.rb +50 -0
  68. data/lib/asir/coder.rb +26 -0
  69. data/lib/asir/coder/base64.rb +19 -0
  70. data/lib/asir/coder/chain.rb +30 -0
  71. data/lib/asir/coder/identity.rb +23 -0
  72. data/lib/asir/coder/json.rb +30 -0
  73. data/lib/asir/coder/marshal.rb +17 -0
  74. data/lib/asir/coder/null.rb +23 -0
  75. data/lib/asir/coder/proc.rb +22 -0
  76. data/lib/asir/coder/sign.rb +48 -0
  77. data/lib/asir/coder/xml.rb +213 -0
  78. data/lib/asir/coder/yaml.rb +33 -0
  79. data/lib/asir/coder/zlib.rb +21 -0
  80. data/lib/asir/configuration.rb +32 -0
  81. data/lib/asir/error.rb +34 -0
  82. data/lib/asir/identity.rb +36 -0
  83. data/lib/asir/initialization.rb +23 -0
  84. data/lib/asir/log.rb +82 -0
  85. data/lib/asir/main.rb +396 -0
  86. data/lib/asir/message.rb +31 -0
  87. data/lib/asir/message/delay.rb +35 -0
  88. data/lib/asir/object_resolving.rb +15 -0
  89. data/lib/asir/result.rb +39 -0
  90. data/lib/asir/retry_behavior.rb +54 -0
  91. data/lib/asir/transport.rb +241 -0
  92. data/lib/asir/transport/beanstalk.rb +217 -0
  93. data/lib/asir/transport/broadcast.rb +34 -0
  94. data/lib/asir/transport/buffer.rb +115 -0
  95. data/lib/asir/transport/composite.rb +19 -0
  96. data/lib/asir/transport/connection_oriented.rb +180 -0
  97. data/lib/asir/transport/delay.rb +38 -0
  98. data/lib/asir/transport/delegation.rb +53 -0
  99. data/lib/asir/transport/fallback.rb +36 -0
  100. data/lib/asir/transport/file.rb +88 -0
  101. data/lib/asir/transport/http.rb +54 -0
  102. data/lib/asir/transport/local.rb +21 -0
  103. data/lib/asir/transport/null.rb +14 -0
  104. data/lib/asir/transport/payload_io.rb +52 -0
  105. data/lib/asir/transport/rack.rb +73 -0
  106. data/lib/asir/transport/retry.rb +41 -0
  107. data/lib/asir/transport/stream.rb +35 -0
  108. data/lib/asir/transport/subprocess.rb +30 -0
  109. data/lib/asir/transport/tcp_socket.rb +34 -0
  110. data/lib/asir/transport/webrick.rb +50 -0
  111. data/lib/asir/transport/zmq.rb +110 -0
  112. data/lib/asir/uuid.rb +32 -0
  113. data/lib/asir/version.rb +3 -0
  114. data/spec/const_get_speed_spec.rb +33 -0
  115. data/spec/debug_helper.rb +20 -0
  116. data/spec/example_spec.rb +88 -0
  117. data/spec/json_spec.rb +128 -0
  118. data/spec/spec_helper.rb +3 -0
  119. data/spec/xml_spec.rb +144 -0
  120. data/stylesheets/slides.css +105 -0
  121. metadata +173 -0
@@ -0,0 +1,36 @@
1
+ # Use the Marshal and Base64 coders in prob-4.rb
2
+
3
+ $: << File.expand_path("../../../lib", __FILE__)
4
+ require 'asir/transport/http'
5
+ require 'asir/coder/marshal'
6
+ require 'asir/coder/base64'
7
+ require 'asir/coder/chain'
8
+
9
+ require 'math_service'
10
+ MathService.send(:include, ASIR::Client)
11
+
12
+ port = 3001
13
+ begin
14
+ t = ASIR::Transport::HTTP.new(:uri => "http://localhost:#{port}/")
15
+ t._log_enabled = true
16
+ c = t.encoder = ASIR::Coder::Chain.new(:encoders =>
17
+ [
18
+ ASIR::Coder::Marshal.new,
19
+ ASIR::Coder::Base64.new,
20
+ ])
21
+ c._log_enabled = true
22
+
23
+ server_pid = Process.fork do
24
+ t.setup_webrick_server!
25
+ t.start_webrick_server!
26
+ end
27
+ sleep 1 # wait for server to start
28
+
29
+ MathService.client.transport = t
30
+ MathService.client.sum([1, 2, 3])
31
+ rescue Exception => err
32
+ $stderr.puts "ERROR: #{err.inspect}\n#{err.backtrace * "\n"}"
33
+ ensure
34
+ Process.kill(9, server_pid)
35
+ end
36
+
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env ruby
2
+ require 'pp'
3
+
4
+ class PhonyProc
5
+ def initialize hash
6
+ @data = hash
7
+ end
8
+ def to_proc
9
+ self
10
+ end
11
+ def call lang, *args, &blk
12
+ @data[lang].call(*args, &blk)
13
+ end
14
+ end
15
+
16
+ def with_block something, &block
17
+ pp block
18
+ block.call(:ruby, something)
19
+ end
20
+
21
+ with_block(123, &PhonyProc.new(:ruby => proc { | x | x * 2 },
22
+ :js => 'function (x) { return x * 2; }'))
23
+
24
+ =begin
25
+
26
+ > ruby lab/phony_proc.rb
27
+ lab/phony_proc.rb:21: PhonyProc#to_proc should return Proc (TypeError)
28
+
29
+ wah-wah.. :(
30
+
31
+ =end
@@ -0,0 +1,253 @@
1
+ # !SLIDE
2
+ # Abstracting Services in Ruby
3
+ #
4
+ # * Kurt Stephens
5
+ # * Enova Financial
6
+ # * 2012/04/02
7
+ # * Slides -- "":http://kurtstephens.com/pub/ruby/abstracting_services_in_ruby/asir.slides/
8
+ # * Code -- "":http://kurtstephens.com/pub/ruby/abstracting_services_in_ruby/
9
+ # * Git -- "":http://github.com/kstephens/abstracting_services_in_ruby
10
+ #
11
+ # !SLIDE END
12
+
13
+ # !SLIDE
14
+ # Issues
15
+ #
16
+ # * Problem Domain, Solution Domain
17
+ # * Service Middleware Semantics
18
+ # * Testing, Debugging, Diagnostics
19
+ # * Productivity
20
+ #
21
+ # !SLIDE END
22
+
23
+ # !SLIDE
24
+ # Problem Domain, Solution Domain
25
+ #
26
+ # * Client knows too much about infrastructure.
27
+ # * Evaluating and switching infrastructures.
28
+ #
29
+ # !SLIDE END
30
+
31
+ # !SLIDE
32
+ # Service Middleware Semantics
33
+ #
34
+ # * Directionality: One-way, Two-way
35
+ # * Synchronicity: Synchronous, Asynchronous, Delayed, Buffered
36
+ # * Distribution: Local Thread, Local Process, Distributed
37
+ # * Transport: File, IPC, Pipe, Network
38
+ # * Robustness: Retry, Replay, Fallback
39
+ # * Encoding: XML, JSON, YAML, Base64, Compression
40
+ #
41
+ # !SLIDE END
42
+
43
+ # !SLIDE
44
+ # Testing, Debugging, Diagnostics
45
+ #
46
+ # * Configuration for testing and QA is more complex.
47
+ # * Measuring test coverage of remote services.
48
+ # * Debugging the root cause of remote service errors.
49
+ # * Diagnostic hooks.
50
+ #
51
+ # !SLIDE END
52
+
53
+ # !SLIDE
54
+ # Objectives
55
+ #
56
+ # * Simplify service/client definitions and interfaces.
57
+ # * Anticipate new encoding, delivery and security requirements.
58
+ # * Separate encoding and transport concerns.
59
+ # * Composition over Configuration.
60
+ # * Elide deployment decisions.
61
+ # * Integrate diagnostics and logging.
62
+ # * Simplify testing.
63
+ #
64
+ # !SLIDE END
65
+
66
+ # !SLIDE
67
+ # Foundations of Objects
68
+ #
69
+ # * Message
70
+ # * State
71
+ # * Behavior
72
+
73
+ # !SLIDE
74
+ # Messaging
75
+ #
76
+ # * "Call a Method", "Call a Function" are all the same, in *all* languages.
77
+ # ** Decomposed into lookup() and apply().
78
+ # * "Send Message", not "Call a Method".
79
+ # * Messaging abstracts:
80
+ # ** Object use from its implementation.
81
+ # ** Transfer of control (method, function invocation, RPC, etc).
82
+
83
+ # !SLIDE
84
+ # REST
85
+ #
86
+ # "Roy Fielding - Architectural Styles and the Design of Network-based Software Architectures":http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm
87
+ #
88
+ # * Imperative Action .vs. Behavorial Resource
89
+ # * REST Connector .vs. REST Component
90
+ # * "Generality of connectors leads to middleware..."
91
+ # * "Modifiability is about the ease with which a change can be made to an application architecture... broken down into evolvability, extensibility, customizability, configurability, and reusability..."
92
+ #
93
+ # !SLIDE END
94
+
95
+ # !SLIDE
96
+ # Design
97
+ #
98
+ # * Nouns -> Objects -> Classes
99
+ # * Verbs -> Responsibilities -> Methods
100
+ #
101
+ # h3. Book: "Designing Object-Oriented Software"
102
+ # * Wirfs-Brock, Wilkerson, Wiener
103
+ #
104
+ # !SLIDE END
105
+
106
+ # !SLIDE
107
+ # Design: Nouns
108
+ #
109
+ # * Service -> Object
110
+ # * Client -> Just a Ruby message sender.
111
+ # * Proxy
112
+ # * Message -> Just a Ruby message.
113
+ # * Result, Exception (two-way) -> Return value or else.
114
+ # * Transport -> (file, pipe, http, queue, ActiveResource)
115
+ # * Encoder, Decoder -> Coder (Marshal, XML, JSON, ActiveResource)
116
+ #
117
+ # !SLIDE END
118
+
119
+ # !SLIDE
120
+ # Design: Verbs
121
+ #
122
+ # * Intercept Message -> Proxy
123
+ # * Invoke Message -> Message
124
+ # * Return Result, Invoke Exception -> Result
125
+ # * Send Message, Recieve Message -> Transport
126
+ # * Encode Object, Decode Object -> Coder
127
+ #
128
+ # !SLIDE END
129
+
130
+ # !SLIDE
131
+ # Simple
132
+ #
133
+ # !IMAGE BEGIN PIC width:800 height:300
134
+ #
135
+ # box "Client" "(CustomersController" "#send_invoice)" wid 4.0 ht 2.5; arrow;
136
+ # ellipse "Send" "Message" "(Ruby message)" wid 4.0 ht 2.5; arrow;
137
+ # box "Service" "(Email.send_email)" wid 4.0 ht 2.5;
138
+ #
139
+ # !IMAGE END
140
+ #
141
+ # !SLIDE END
142
+
143
+ # !SLIDE
144
+ # Client-Side Message
145
+ #
146
+ # !IMAGE BEGIN PIC width:800 height:300
147
+ # box "Client" wid 2.5 ht 2.5; arrow;
148
+ # ellipse "Proxy" wid 2.5 ht 2.5; arrow;
149
+ # ellipse "Create" "Message" wid 2.5 ht 2.5; arrow;
150
+ # ellipse "Encode" "Message" wid 2.5 ht 2.5; arrow;
151
+ # ellipse "Send" "Message" wid 2.5 ht 2.5;
152
+ # line; down; arrow;
153
+ # !IMAGE END
154
+ #
155
+ # !SLIDE END
156
+
157
+ # !SLIDE
158
+ # Server-Side
159
+ #
160
+ # !IMAGE BEGIN PIC width:800 height:300
161
+ # down; line; right; arrow;
162
+ # ellipse "Receive" "Message" wid 2.5 ht 2.5; arrow;
163
+ # ellipse "Decode" "Message" wid 2.5 ht 2.5; arrow;
164
+ # ellipse "Message" wid 2.5 ht 2.5;
165
+ # line; down; arrow;
166
+ # IR: ellipse "Invoke" "Message" wid 2.5 ht 2.5;
167
+ # right; move; move;
168
+ # Service: box "Service" wid 2.5 ht 2.5 with .w at IR.e + (movewid, 0);
169
+ # arrow <-> from IR.e to Service.w;
170
+ # move to IR.s; down; line;
171
+ # left; arrow;
172
+ # ellipse "Create" "Result" wid 2.5 ht 2.5; arrow;
173
+ # ellipse "Encode" "Result" wid 2.5 ht 2.5; arrow;
174
+ # ellipse "Send" "Result" wid 2.5 ht 2.5;
175
+ # line; down; arrow
176
+ # !IMAGE END
177
+ #
178
+ # !SLIDE END
179
+
180
+ # !SLIDE
181
+ # Client-Side Result
182
+ #
183
+ # !IMAGE BEGIN PIC width:800 height:300
184
+ # down; line; left; arrow;
185
+ # ellipse "Receive" "Result" wid 2.5 ht 2.5; arrow;
186
+ # ellipse "Decode" "Result" wid 2.5 ht 2.5; arrow;
187
+ # ellipse "Result" wid 2.5 ht 2.5; arrow;
188
+ # ellipse "Proxy" wid 2.5 ht 2.5; arrow;
189
+ # box "Client" wid 2.5 ht 2.5;
190
+ # !IMAGE END
191
+ #
192
+ # !SLIDE END
193
+
194
+ # !SLIDE
195
+ # Implementation
196
+ #
197
+ # * Primary Base classes: Transport, Coder.
198
+ # * Primary API: Proxy via Client mixin.
199
+ # * Handful of mixins.
200
+ #
201
+ # !SLIDE END
202
+
203
+ # !SLIDE
204
+ # Modules and Classes
205
+ module ASIR
206
+ # Reusable constants to avoid unnecessary garbage.
207
+ EMPTY_ARRAY = [ ].freeze; EMPTY_HASH = { }.freeze; EMPTY_STRING = ''.freeze
208
+ MODULE_SEP = '::'.freeze; IDENTITY_LAMBDA = lambda { | x | x }
209
+ end
210
+ # !SLIDE END
211
+ Asir = ASIR
212
+
213
+ require "asir/version"
214
+ require 'asir/error'
215
+ require 'asir/log'
216
+ require 'asir/initialization'
217
+ require 'asir/additional_data'
218
+ require 'asir/object_resolving'
219
+ require 'asir/identity'
220
+ require 'asir/code_block'
221
+ require 'asir/code_more'
222
+ require 'asir/message'
223
+ require 'asir/result'
224
+ require 'asir/client'
225
+ require 'asir/coder'
226
+ require 'asir/coder/null'
227
+ require 'asir/coder/identity'
228
+ require 'asir/transport'
229
+ require 'asir/channel'
230
+ require 'asir/transport/local'
231
+
232
+ # !SLIDE
233
+ # Synopsis
234
+ #
235
+ # * Services are easy to abstract away.
236
+ # * Separation of transport, encoding.
237
+ #
238
+ # !SLIDE END
239
+
240
+ # !SLIDE
241
+ # Slide Tools
242
+ #
243
+ # * Riterate -- "":http://github.com/kstephens/riterate
244
+ # * Scarlet -- "":http://github.com/kstephens/scarlet
245
+ #
246
+ # !SLIDE END
247
+
248
+ # !SLIDE
249
+ # Appendix
250
+ #
251
+ #
252
+ # !SLIDE END
253
+
@@ -0,0 +1,25 @@
1
+ module ASIR
2
+ # !SLIDE
3
+ # Addtional Data
4
+ #
5
+ # Support additional data attached to any object.
6
+ module AdditionalData
7
+ def additional_data
8
+ @additional_data || EMPTY_HASH
9
+ end
10
+ def additional_data!
11
+ @additional_data ||= { }
12
+ end
13
+ def additional_data= x
14
+ @additional_data = x
15
+ end
16
+ def [] key
17
+ @additional_data && @additional_data[key]
18
+ end
19
+ def []= key, value
20
+ (@additional_data ||= { })[key] = value
21
+ end
22
+ end
23
+ # !SLIDE END
24
+ end
25
+
@@ -0,0 +1,130 @@
1
+ require 'asir'
2
+ require 'asir/retry_behavior'
3
+
4
+ module ASIR
5
+ # Generic I/O Channel abstraction.
6
+ # Handles stream per Thread and forked child processes.
7
+ class Channel
8
+ include Initialization, RetryBehavior
9
+
10
+ attr_accessor :on_connect, :on_close, :on_retry, :on_error
11
+
12
+ ON_ERROR = lambda do | channel, exc, action, stream |
13
+ channel.close rescue nil if stream
14
+ raise exc
15
+ end
16
+ ON_CLOSE = lambda do | channel, stream |
17
+ stream.close
18
+ end
19
+ ON_RETRY = lambda do | channel, exc, action |
20
+ end
21
+
22
+ def initialize opts = nil
23
+ @on_close = ON_CLOSE
24
+ @on_error = ON_ERROR
25
+ # @on_retry = ON_RETRY
26
+ self.try_max = 10
27
+ self.try_sleep = 0.1
28
+ self.try_sleep_increment = 0.1
29
+ self.try_sleep_max = 10
30
+ super
31
+ end
32
+
33
+ # Returns IO stream for current Thread.
34
+ # Automatically calls #connect! if stream is created.
35
+ def stream
36
+ _streams[self] ||=
37
+ connect!
38
+ end
39
+
40
+ # Invokes @on_connect.call(self).
41
+ # On Exception, invokes @on_error.call(self, exc, :connect, nil).
42
+ def connect!
43
+ n_try = nil
44
+ with_retry do | action, data |
45
+ case action
46
+ when :try
47
+ n_try = data
48
+ @on_connect.call(self)
49
+ when :retry #, exc
50
+ exc = data
51
+ $stderr.puts "RETRY: #{n_try}: ERROR : #{data.inspect}"
52
+ @on_retry.call(self, exc, :connect) if @on_retry
53
+ when :failed
54
+ exc = data
55
+ $stderr.puts "FAILED: #{n_try}: ERROR : #{data.inspect}"
56
+ handle_error!(exc, :connect, nil)
57
+ end
58
+ end
59
+ end
60
+
61
+ # Invokes @on_close.call(self, stream).
62
+ # On Exception, invokes @on_error.call(self, exc, :close, stream).
63
+ def close
64
+ if stream = _stream
65
+ self.stream = nil
66
+ @on_close.call(self, stream) if @on_close
67
+ end
68
+ rescue ::Exception => exc
69
+ handle_error!(exc, :close, stream)
70
+ end
71
+
72
+ # Yield #stream to block.
73
+ # On Exception, invokes @on_error.call(self, exc, :with_stream, stream).
74
+ def with_stream!
75
+ x = stream
76
+ begin
77
+ yield x
78
+ rescue ::Exception => exc
79
+ handle_error!(exc, :with_stream, x)
80
+ end
81
+ end
82
+
83
+ # Delegate to actual stream.
84
+ def method_missing sel, *args, &blk
85
+ with_stream! do | obj |
86
+ obj.__send__(sel, *args, &blk)
87
+ end
88
+ end
89
+
90
+ # Dispatches exception and arguments if @on_error is defined.
91
+ # Otherwise, reraise exception.
92
+ def handle_error! exc, action, stream
93
+ if @on_error
94
+ @on_error.call(self, exc, action, stream)
95
+ else
96
+ raise exc
97
+ end
98
+ end
99
+
100
+ # Returns a Thread-specific mapping, unique to this process id.
101
+ # Maps from Channel objects to actual stream.
102
+ def _streams
103
+ streams = Thread.current[:'ASIR::Channel._streams'] ||= { }
104
+ if ! @owning_process ||
105
+ @owning_process != $$ || # child process?
106
+ @owning_process > $$ # PIDs wrapped around?
107
+ @owning_process = $$
108
+ streams.clear
109
+ end
110
+ streams
111
+ end
112
+
113
+ # Returns the stream for this Channel, or nil.
114
+ def _stream
115
+ _streams[self]
116
+ end
117
+
118
+ # Sets the stream for this Channel, or nil.
119
+ def stream= x
120
+ if x == nil
121
+ _streams.delete(self)
122
+ else
123
+ _streams[self] = x
124
+ end
125
+ end
126
+
127
+ end # class Channel
128
+ end # module
129
+
130
+