asir 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+