asir 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +11 -0
- data/Gemfile +16 -0
- data/README.textile +50 -0
- data/Rakefile +83 -0
- data/VERSION +1 -0
- data/asir.gemspec +36 -0
- data/asir.riterate.yml +114 -0
- data/bin/asir +6 -0
- data/doc/Rakefile +8 -0
- data/doc/asir-sequence.pic +84 -0
- data/doc/asir-sequence.svg +1559 -0
- data/doc/sequence.pic +430 -0
- data/example/asir_control.sh +24 -0
- data/example/asir_control_client_http.rb +14 -0
- data/example/asir_control_client_zmq.rb +15 -0
- data/example/config/asir_config.rb +63 -0
- data/example/delayed_service.rb +15 -0
- data/example/ex01.rb +12 -0
- data/example/ex02.rb +12 -0
- data/example/ex03.rb +19 -0
- data/example/ex04.rb +33 -0
- data/example/ex05.rb +16 -0
- data/example/ex06.rb +26 -0
- data/example/ex07.rb +28 -0
- data/example/ex08.rb +30 -0
- data/example/ex09.rb +25 -0
- data/example/ex10.rb +24 -0
- data/example/ex11.rb +48 -0
- data/example/ex12.rb +34 -0
- data/example/ex13.rb +35 -0
- data/example/ex14.rb +30 -0
- data/example/ex15.rb +13 -0
- data/example/ex16.rb +33 -0
- data/example/ex17.rb +41 -0
- data/example/ex18.rb +62 -0
- data/example/ex19.rb +32 -0
- data/example/ex20.rb +28 -0
- data/example/ex21.rb +28 -0
- data/example/ex22.rb +15 -0
- data/example/ex23.rb +20 -0
- data/example/ex24.rb +35 -0
- data/example/example_helper.rb +51 -0
- data/example/sample_service.rb +162 -0
- data/example/unsafe_service.rb +12 -0
- data/hack_night/README.txt +18 -0
- data/hack_night/exercise/prob-1.rb +18 -0
- data/hack_night/exercise/prob-2.rb +21 -0
- data/hack_night/exercise/prob-3.rb +16 -0
- data/hack_night/exercise/prob-4.rb +36 -0
- data/hack_night/exercise/prob-5.rb +36 -0
- data/hack_night/exercise/prob-6.rb +95 -0
- data/hack_night/exercise/prob-7.rb +34 -0
- data/hack_night/solution/math_service.rb +11 -0
- data/hack_night/solution/prob-1.rb +12 -0
- data/hack_night/solution/prob-2.rb +15 -0
- data/hack_night/solution/prob-3.rb +17 -0
- data/hack_night/solution/prob-4.rb +37 -0
- data/hack_night/solution/prob-5.rb +21 -0
- data/hack_night/solution/prob-6.rb +33 -0
- data/hack_night/solution/prob-7.rb +36 -0
- data/lab/phony_proc.rb +31 -0
- data/lib/asir.rb +253 -0
- data/lib/asir/additional_data.rb +25 -0
- data/lib/asir/channel.rb +130 -0
- data/lib/asir/client.rb +111 -0
- data/lib/asir/code_block.rb +57 -0
- data/lib/asir/code_more.rb +50 -0
- data/lib/asir/coder.rb +26 -0
- data/lib/asir/coder/base64.rb +19 -0
- data/lib/asir/coder/chain.rb +30 -0
- data/lib/asir/coder/identity.rb +23 -0
- data/lib/asir/coder/json.rb +30 -0
- data/lib/asir/coder/marshal.rb +17 -0
- data/lib/asir/coder/null.rb +23 -0
- data/lib/asir/coder/proc.rb +22 -0
- data/lib/asir/coder/sign.rb +48 -0
- data/lib/asir/coder/xml.rb +213 -0
- data/lib/asir/coder/yaml.rb +33 -0
- data/lib/asir/coder/zlib.rb +21 -0
- data/lib/asir/configuration.rb +32 -0
- data/lib/asir/error.rb +34 -0
- data/lib/asir/identity.rb +36 -0
- data/lib/asir/initialization.rb +23 -0
- data/lib/asir/log.rb +82 -0
- data/lib/asir/main.rb +396 -0
- data/lib/asir/message.rb +31 -0
- data/lib/asir/message/delay.rb +35 -0
- data/lib/asir/object_resolving.rb +15 -0
- data/lib/asir/result.rb +39 -0
- data/lib/asir/retry_behavior.rb +54 -0
- data/lib/asir/transport.rb +241 -0
- data/lib/asir/transport/beanstalk.rb +217 -0
- data/lib/asir/transport/broadcast.rb +34 -0
- data/lib/asir/transport/buffer.rb +115 -0
- data/lib/asir/transport/composite.rb +19 -0
- data/lib/asir/transport/connection_oriented.rb +180 -0
- data/lib/asir/transport/delay.rb +38 -0
- data/lib/asir/transport/delegation.rb +53 -0
- data/lib/asir/transport/fallback.rb +36 -0
- data/lib/asir/transport/file.rb +88 -0
- data/lib/asir/transport/http.rb +54 -0
- data/lib/asir/transport/local.rb +21 -0
- data/lib/asir/transport/null.rb +14 -0
- data/lib/asir/transport/payload_io.rb +52 -0
- data/lib/asir/transport/rack.rb +73 -0
- data/lib/asir/transport/retry.rb +41 -0
- data/lib/asir/transport/stream.rb +35 -0
- data/lib/asir/transport/subprocess.rb +30 -0
- data/lib/asir/transport/tcp_socket.rb +34 -0
- data/lib/asir/transport/webrick.rb +50 -0
- data/lib/asir/transport/zmq.rb +110 -0
- data/lib/asir/uuid.rb +32 -0
- data/lib/asir/version.rb +3 -0
- data/spec/const_get_speed_spec.rb +33 -0
- data/spec/debug_helper.rb +20 -0
- data/spec/example_spec.rb +88 -0
- data/spec/json_spec.rb +128 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/xml_spec.rb +144 -0
- data/stylesheets/slides.css +105 -0
- metadata +173 -0
data/lib/asir/client.rb
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
module ASIR
|
2
|
+
# !SLIDE
|
3
|
+
# Client support for any Module
|
4
|
+
#
|
5
|
+
# Extend Module with #client proxy support.
|
6
|
+
module Client
|
7
|
+
# !SLIDE
|
8
|
+
# Client Mixin
|
9
|
+
def self.included target
|
10
|
+
super
|
11
|
+
target.extend ModuleMethods if Module === target
|
12
|
+
target.send(:include, InstanceMethods) if Class === target
|
13
|
+
end
|
14
|
+
|
15
|
+
module CommonMethods
|
16
|
+
def client_options &blk
|
17
|
+
client._configure(&blk)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module ModuleMethods
|
22
|
+
include CommonMethods
|
23
|
+
# Proxies are cached for Module/Class methods because serialization will not include
|
24
|
+
# Transport.
|
25
|
+
def client
|
26
|
+
@_asir_client ||= ASIR::Client::Proxy.new(self)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
module InstanceMethods
|
31
|
+
include CommonMethods
|
32
|
+
# Proxies are not cached in instances because receiver is to be serialized by
|
33
|
+
# its Transport's Coder.
|
34
|
+
def client
|
35
|
+
ASIR::Client::Proxy.new(self)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# !SLIDE
|
40
|
+
# Client Proxy
|
41
|
+
#
|
42
|
+
# Provide client interface proxy to a service.
|
43
|
+
class Proxy
|
44
|
+
attr_accessor :receiver
|
45
|
+
|
46
|
+
# Accept messages as a proxy for the receiver.
|
47
|
+
# Blocks are used represent a "continuation" for the Result.
|
48
|
+
def send selector, *arguments, &block
|
49
|
+
message = Message.new(@receiver, selector, arguments, block, self)
|
50
|
+
message = @before_send_message.call(message) if @before_send_message
|
51
|
+
@__configure.call(message) if @__configure
|
52
|
+
result = transport.send_message(message)
|
53
|
+
result
|
54
|
+
end
|
55
|
+
# Accept all other messages to be encoded and transported to a service.
|
56
|
+
alias :method_missing :send
|
57
|
+
|
58
|
+
# !SLIDE
|
59
|
+
# Client Transport
|
60
|
+
attr_accessor :transport
|
61
|
+
|
62
|
+
def transport
|
63
|
+
@transport ||= Transport::Local.new
|
64
|
+
end
|
65
|
+
|
66
|
+
# !SLIDE
|
67
|
+
# Proxy Configuration
|
68
|
+
|
69
|
+
# A Proc to call with the Message object before sending to transport#send_message(message).
|
70
|
+
# Must return a Message object.
|
71
|
+
attr_accessor :before_send_message
|
72
|
+
|
73
|
+
# If true, this Message is one-way, even if the Transport is bi-directional.
|
74
|
+
attr_accessor :_one_way
|
75
|
+
|
76
|
+
# A Proc to call with the Message object before sending to transport#send_message(message).
|
77
|
+
# See #_configure.
|
78
|
+
attr_accessor :__configure
|
79
|
+
|
80
|
+
# Returns a new Client Proxy with a block to be called with the Message.
|
81
|
+
# This block can configure additional options of the Message before
|
82
|
+
# it is sent to the Transport.
|
83
|
+
def _configure &blk
|
84
|
+
proxy = self.dup
|
85
|
+
proxy.__configure = blk
|
86
|
+
proxy
|
87
|
+
end
|
88
|
+
alias :_options :_configure
|
89
|
+
|
90
|
+
# !SLIDE
|
91
|
+
# Configuration Callbacks
|
92
|
+
|
93
|
+
def initialize rcvr
|
94
|
+
key = Module === (@receiver = rcvr) ? @receiver : @receiver.class
|
95
|
+
(@@config_callbacks[key] ||
|
96
|
+
@@config_callbacks[key.name] ||
|
97
|
+
@@config_callbacks[nil] ||
|
98
|
+
IDENTITY_LAMBDA).call(self)
|
99
|
+
end
|
100
|
+
|
101
|
+
@@config_callbacks ||= { }
|
102
|
+
def self.config_callbacks
|
103
|
+
@@config_callbacks
|
104
|
+
end
|
105
|
+
|
106
|
+
# !SLIDE END
|
107
|
+
end
|
108
|
+
# !SLIDE END
|
109
|
+
end
|
110
|
+
# !SLIDE END
|
111
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module ASIR
|
2
|
+
# !SLIDE
|
3
|
+
# Code Block
|
4
|
+
#
|
5
|
+
# Encode/decode Message#block.
|
6
|
+
module CodeBlock
|
7
|
+
# Most coders cannot serialize Procs.
|
8
|
+
# But we can attempt to serialize a String representing a Proc.
|
9
|
+
def encode_block!
|
10
|
+
obj = nil
|
11
|
+
if @block && ! ::String === @block_code
|
12
|
+
obj ||= self.dup
|
13
|
+
obj.block_code = CodeBlock.block_to_code(obj.block)
|
14
|
+
obj.block = nil
|
15
|
+
end
|
16
|
+
obj
|
17
|
+
end
|
18
|
+
|
19
|
+
def decode_block!
|
20
|
+
if ::String === @block_code
|
21
|
+
@block ||= CodeBlock.code_to_block(@block_code)
|
22
|
+
@block_code = nil
|
23
|
+
end
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
# Returns a block_cache Hash.
|
28
|
+
# Flushed every 1000 accesses.
|
29
|
+
def self.block_cache
|
30
|
+
cache = Thread.current[:'ASIR::CodeBlock.block_cache'] ||= { }
|
31
|
+
count = Thread.current[:'ASIR::CodeBlock.block_cache_count'] ||= 0
|
32
|
+
count += 1
|
33
|
+
if count >= 1000
|
34
|
+
cache.clear
|
35
|
+
count = 0
|
36
|
+
end
|
37
|
+
Thread.current[:'ASIR::CodeBlock.block_cache_count'] = count
|
38
|
+
cache
|
39
|
+
end
|
40
|
+
|
41
|
+
# Uses ruby2ruby, if loaded.
|
42
|
+
def self.block_to_code block
|
43
|
+
(block_cache[block.object_id] ||=
|
44
|
+
[ block.respond_to?(:to_ruby) && block.to_ruby, block ]).
|
45
|
+
first
|
46
|
+
end
|
47
|
+
|
48
|
+
# Calls eval.
|
49
|
+
# May be unsafe.
|
50
|
+
def self.code_to_block code
|
51
|
+
(block_cache[code.dup.freeze] ||=
|
52
|
+
[ eval(@block_code), code ]).
|
53
|
+
first
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module ASIR
|
2
|
+
# !SLIDE
|
3
|
+
# Code More
|
4
|
+
#
|
5
|
+
# Help encode/decode and resolve receiver class.
|
6
|
+
module CodeMore
|
7
|
+
include ObjectResolving # resolve_object()
|
8
|
+
include CodeBlock # encode_block!, decode_block!
|
9
|
+
|
10
|
+
def encode_more!
|
11
|
+
obj = encode_block! # may self.dup
|
12
|
+
unless ::String === @receiver_class
|
13
|
+
obj ||= self.dup # dont dup twice.
|
14
|
+
obj.receiver = @receiver.name if ::Module === @receiver
|
15
|
+
obj.receiver_class = @receiver_class.name
|
16
|
+
if resp = obj.result and resp.message == self
|
17
|
+
resp.message = obj
|
18
|
+
end
|
19
|
+
end
|
20
|
+
obj || self
|
21
|
+
end
|
22
|
+
|
23
|
+
def decode_more!
|
24
|
+
decode_block!
|
25
|
+
if ::String === @receiver_class
|
26
|
+
@receiver_class = resolve_object(@receiver_class)
|
27
|
+
@receiver = resolve_object(@receiver) if ::Module === @receiver_class
|
28
|
+
unless @receiver_class === @receiver
|
29
|
+
raise Error, "receiver #{@receiver.class.name} is not a #{@receiver_class}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
# Mixin for Result.
|
36
|
+
module Result
|
37
|
+
def encode_more!
|
38
|
+
@message = @message.encode_more! if @message
|
39
|
+
self
|
40
|
+
end
|
41
|
+
|
42
|
+
def decode_more!
|
43
|
+
@message = @message.decode_more! if @message
|
44
|
+
self
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
# !SLIDE END
|
49
|
+
end
|
50
|
+
|
data/lib/asir/coder.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
module ASIR
|
2
|
+
# !SLIDE
|
3
|
+
# Coder
|
4
|
+
#
|
5
|
+
# Define encoding and decoding for Messages and Results along a Transport.
|
6
|
+
class Coder
|
7
|
+
include Log, Initialization
|
8
|
+
|
9
|
+
def encode obj
|
10
|
+
_encode obj
|
11
|
+
end
|
12
|
+
|
13
|
+
def decode obj
|
14
|
+
obj and _decode obj
|
15
|
+
end
|
16
|
+
|
17
|
+
# Coder subclasses:
|
18
|
+
def _subclass_responsibility *args
|
19
|
+
raise "subclass responsibility"
|
20
|
+
end
|
21
|
+
alias :_encode :_subclass_responsibility
|
22
|
+
alias :_decode :_subclass_responsibility
|
23
|
+
end
|
24
|
+
# !SLIDE END
|
25
|
+
end
|
26
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'asir'
|
2
|
+
|
3
|
+
require 'base64'
|
4
|
+
|
5
|
+
module ASIR
|
6
|
+
class Coder
|
7
|
+
class Base64 < self
|
8
|
+
def _encode obj
|
9
|
+
raise TypeError unless String === obj
|
10
|
+
::Base64.encode64(obj)
|
11
|
+
end
|
12
|
+
def _decode obj
|
13
|
+
raise TypeError unless String === obj
|
14
|
+
::Base64.decode64(obj)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module ASIR
|
2
|
+
class Coder
|
3
|
+
# !SLIDE
|
4
|
+
# Chain Coder
|
5
|
+
# Chain multiple Coders as one.
|
6
|
+
#
|
7
|
+
# @@@ text
|
8
|
+
# message --> | e1 | --> | e2 | --> | eN | -->
|
9
|
+
# result <-- | d1 | <-- | d2 | <-- | dN | <--
|
10
|
+
# @@@
|
11
|
+
class Chain < self
|
12
|
+
attr_accessor :encoders
|
13
|
+
|
14
|
+
def _encode obj
|
15
|
+
encoders.each do | e |
|
16
|
+
obj = e.dup.encode(obj)
|
17
|
+
end
|
18
|
+
obj
|
19
|
+
end
|
20
|
+
|
21
|
+
def _decode obj
|
22
|
+
encoders.reverse_each do | e |
|
23
|
+
obj = e.dup.decode(obj)
|
24
|
+
end
|
25
|
+
obj
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'asir/coder'
|
2
|
+
|
3
|
+
module ASIR
|
4
|
+
class Coder
|
5
|
+
# !SLIDE
|
6
|
+
# Identity Coder
|
7
|
+
# Perform no encode/decode.
|
8
|
+
class Identity < self
|
9
|
+
def _encode obj
|
10
|
+
obj
|
11
|
+
end
|
12
|
+
|
13
|
+
def _decode obj
|
14
|
+
obj
|
15
|
+
end
|
16
|
+
|
17
|
+
# Completely stateless.
|
18
|
+
def dup; self; end
|
19
|
+
end
|
20
|
+
# !SLIDE END
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'asir'
|
2
|
+
|
3
|
+
module ASIR
|
4
|
+
class Coder
|
5
|
+
# !SLIDE
|
6
|
+
# JSON Coder
|
7
|
+
#
|
8
|
+
# Note: Symbols are not handled.
|
9
|
+
# The actual JSON expression is wrapped with an Array.
|
10
|
+
class JSON < self
|
11
|
+
def _encode obj
|
12
|
+
[ obj ].to_json
|
13
|
+
end
|
14
|
+
|
15
|
+
def _decode obj
|
16
|
+
parser = ::JSON.parser.new(obj)
|
17
|
+
ary = parser.parse
|
18
|
+
ary.first
|
19
|
+
end
|
20
|
+
end
|
21
|
+
# !SLIDE END
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
if RUBY_PLATFORM =~ /java/
|
26
|
+
require 'json'
|
27
|
+
else
|
28
|
+
require 'json/ext'
|
29
|
+
end
|
30
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module ASIR
|
2
|
+
class Coder
|
3
|
+
# !SLIDE
|
4
|
+
# Marshal Coder
|
5
|
+
# Use Ruby Marshal for encode/decode.
|
6
|
+
class Marshal < self
|
7
|
+
def _encode obj
|
8
|
+
::Marshal.dump(obj)
|
9
|
+
end
|
10
|
+
|
11
|
+
def _decode obj
|
12
|
+
::Marshal.load(obj)
|
13
|
+
end
|
14
|
+
end # class
|
15
|
+
# !SLIDE END
|
16
|
+
end # class
|
17
|
+
end # module
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'asir/coder'
|
2
|
+
|
3
|
+
module ASIR
|
4
|
+
class Coder
|
5
|
+
# !SLIDE
|
6
|
+
# Null Coder
|
7
|
+
# Always encode/decode as nil.
|
8
|
+
class Null < self
|
9
|
+
def _encode obj
|
10
|
+
nil
|
11
|
+
end
|
12
|
+
|
13
|
+
def _decode obj
|
14
|
+
nil
|
15
|
+
end
|
16
|
+
|
17
|
+
# Completely stateless.
|
18
|
+
def dup; self; end
|
19
|
+
end
|
20
|
+
# !SLIDE END
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'asir'
|
2
|
+
|
3
|
+
module ASIR
|
4
|
+
class Coder
|
5
|
+
# !SLIDE
|
6
|
+
# Proc Coder
|
7
|
+
# Generic Proc-based coder.
|
8
|
+
class Proc < self
|
9
|
+
# Procs that take one argument.
|
10
|
+
attr_accessor :encoder, :decoder
|
11
|
+
|
12
|
+
def _encode obj
|
13
|
+
@encoder.call(obj)
|
14
|
+
end
|
15
|
+
def _decode obj
|
16
|
+
@decoder.call(obj)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
# !SLIDE END
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'digest/sha1'
|
2
|
+
|
3
|
+
module ASIR
|
4
|
+
class Coder
|
5
|
+
# !SLIDE
|
6
|
+
# Sign Coder
|
7
|
+
#
|
8
|
+
# Sign payload during encode, check signature during decode.
|
9
|
+
#
|
10
|
+
# Signature is the digest of secret + payload.
|
11
|
+
#
|
12
|
+
# Encode payload as Hash containing the digest function name, signature and payload.
|
13
|
+
# Decode and validate Hash containing the digest function name, signature and payload.
|
14
|
+
#
|
15
|
+
class Sign < self
|
16
|
+
attr_accessor :secret, :function
|
17
|
+
|
18
|
+
def _encode obj
|
19
|
+
payload = obj.to_s
|
20
|
+
{ :function => function,
|
21
|
+
:signature => ::Digest.const_get(function).
|
22
|
+
new.hexdigest(secret + payload),
|
23
|
+
:payload => payload }
|
24
|
+
end
|
25
|
+
|
26
|
+
def _decode obj
|
27
|
+
raise SignatureError, "expected Hash, given #{obj.class}" unless Hash === obj
|
28
|
+
payload = obj[:payload]
|
29
|
+
raise SignatureError, "signature invalid" unless obj == _encode(payload)
|
30
|
+
payload
|
31
|
+
end
|
32
|
+
|
33
|
+
# !SLIDE
|
34
|
+
# Sign Coder Support
|
35
|
+
|
36
|
+
# Signature Error.
|
37
|
+
class SignatureError < Error; end
|
38
|
+
|
39
|
+
def initialize_before_opts
|
40
|
+
@function = :SHA1
|
41
|
+
end
|
42
|
+
# !SLIDE END
|
43
|
+
end
|
44
|
+
# !SLIDE END
|
45
|
+
end # class
|
46
|
+
end # class
|
47
|
+
|
48
|
+
|