serf 0.1.0.alpha1 → 0.2.0.alpha1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +5 -9
- data/Gemfile.lock +9 -20
- data/README.md +1 -59
- data/Rakefile +1 -1
- data/lib/serf/builder.rb +55 -52
- data/lib/serf/handler.rb +67 -0
- data/lib/serf/message.rb +44 -0
- data/lib/serf/serfer.rb +78 -0
- data/lib/serf/{null_object.rb → util/null_object.rb} +2 -0
- data/lib/serf/version.rb +1 -1
- data/lib/serf.rb +0 -17
- data/serf.gemspec +16 -40
- metadata +35 -107
- data/bin/serfup +0 -9
- data/examples/config.su +0 -58
- data/lib/serf/emitters/redis_emitter.rb +0 -23
- data/lib/serf/middleware/celluloid_runner.rb +0 -45
- data/lib/serf/middleware/em_runner.rb +0 -32
- data/lib/serf/middleware/kind_mapper.rb +0 -48
- data/lib/serf/receivers/msgpack_receiver.rb +0 -55
- data/lib/serf/receivers/redis_pubsub_receiver.rb +0 -38
data/Gemfile
CHANGED
@@ -3,14 +3,9 @@ source 'http://rubygems.org'
|
|
3
3
|
# Example:
|
4
4
|
# gem 'activesupport', '>= 2.3.5'
|
5
5
|
|
6
|
-
gem '
|
6
|
+
gem 'activemodel', '~> 3.1.3'
|
7
|
+
gem 'activesupport', '~> 3.1.3'
|
7
8
|
gem 'eventmachine', '~> 0.12.10'
|
8
|
-
gem 'i18n', '~> 0.6.0'
|
9
|
-
gem 'msgpack', '~> 0.4.6'
|
10
|
-
gem 'msgpack-rpc', '~> 0.4.5'
|
11
|
-
gem 'multi_json', '~> 1.0.3'
|
12
|
-
gem 'rack', '~> 1.3.5'
|
13
|
-
gem 'redis', '~> 2.2.2'
|
14
9
|
|
15
10
|
# Add dependencies to develop your gem here.
|
16
11
|
# Include everything needed to run rake, tests, features, etc.
|
@@ -22,6 +17,7 @@ group :development, :test do
|
|
22
17
|
gem 'rcov', '>= 0'
|
23
18
|
|
24
19
|
# Soft Dependencies
|
25
|
-
gem 'log4r', '~> 1.1.9'
|
26
|
-
gem '
|
20
|
+
#gem 'log4r', '~> 1.1.9'
|
21
|
+
gem 'msgpack', '~> 0.4.6'
|
22
|
+
#gem 'multi_json', '~> 1.0.3'
|
27
23
|
end
|
data/Gemfile.lock
CHANGED
@@ -1,30 +1,25 @@
|
|
1
1
|
GEM
|
2
2
|
remote: http://rubygems.org/
|
3
3
|
specs:
|
4
|
-
|
4
|
+
activemodel (3.1.3)
|
5
|
+
activesupport (= 3.1.3)
|
6
|
+
builder (~> 3.0.0)
|
7
|
+
i18n (~> 0.6)
|
8
|
+
activesupport (3.1.3)
|
5
9
|
multi_json (~> 1.0)
|
6
|
-
|
10
|
+
builder (3.0.0)
|
7
11
|
diff-lcs (1.1.3)
|
8
12
|
eventmachine (0.12.10)
|
9
13
|
git (1.2.5)
|
10
14
|
i18n (0.6.0)
|
11
|
-
iobuffer (1.0.0)
|
12
15
|
jeweler (1.6.4)
|
13
16
|
bundler (~> 1.0)
|
14
17
|
git (>= 1.2.5)
|
15
18
|
rake
|
16
|
-
log4r (1.1.9)
|
17
19
|
msgpack (0.4.6)
|
18
|
-
|
19
|
-
msgpack (>= 0.4.4)
|
20
|
-
rev (>= 0.3.0)
|
21
|
-
multi_json (1.0.3)
|
22
|
-
rack (1.3.5)
|
20
|
+
multi_json (1.0.4)
|
23
21
|
rake (0.9.2.2)
|
24
22
|
rcov (0.9.11)
|
25
|
-
redis (2.2.2)
|
26
|
-
rev (0.3.2)
|
27
|
-
iobuffer (>= 0.1.3)
|
28
23
|
rspec (2.3.0)
|
29
24
|
rspec-core (~> 2.3.0)
|
30
25
|
rspec-expectations (~> 2.3.0)
|
@@ -39,18 +34,12 @@ PLATFORMS
|
|
39
34
|
ruby
|
40
35
|
|
41
36
|
DEPENDENCIES
|
42
|
-
|
37
|
+
activemodel (~> 3.1.3)
|
38
|
+
activesupport (~> 3.1.3)
|
43
39
|
bundler (~> 1.0.0)
|
44
|
-
celluloid (~> 0.5.0)
|
45
40
|
eventmachine (~> 0.12.10)
|
46
|
-
i18n (~> 0.6.0)
|
47
41
|
jeweler (~> 1.6.4)
|
48
|
-
log4r (~> 1.1.9)
|
49
42
|
msgpack (~> 0.4.6)
|
50
|
-
msgpack-rpc (~> 0.4.5)
|
51
|
-
multi_json (~> 1.0.3)
|
52
|
-
rack (~> 1.3.5)
|
53
43
|
rcov
|
54
|
-
redis (~> 2.2.2)
|
55
44
|
rspec (~> 2.3.0)
|
56
45
|
yard (~> 0.6.0)
|
data/README.md
CHANGED
@@ -5,64 +5,9 @@ Serf is a library that scaffolds distributed systems that are architected using
|
|
5
5
|
Event-Driven Service Oriented Architecture design in combinations with
|
6
6
|
the Command Query Responsibility Separation pattern.
|
7
7
|
|
8
|
-
Fundamentally, serf is a server process that receives commands and
|
9
|
-
emits events. The actual business logic is set up using `Serf's Up`
|
10
|
-
configuration files similar to rackup files.
|
11
|
-
|
12
|
-
An application developer writes Handler code that knows how to
|
13
|
-
process commands and events. This handler code is wired up in the
|
14
|
-
afore mentioned serfup file.
|
15
|
-
|
16
|
-
Handler code is essentally an object that responds to 'call' as with
|
17
|
-
other Rack based applications. The primary difference is that
|
18
|
-
the 'env' passed to the Handler object is NOT rack env conformant.
|
19
|
-
|
20
|
-
The message passed to the Handler objects' call methods are a
|
21
|
-
hash, which has a `kind` keyed element whose value is the type
|
22
|
-
of command (or event). A Handler SHOULD verify that this value is
|
23
|
-
what it expects in case of misrouting due to bad serfup configuration.
|
24
|
-
|
25
|
-
The serf library provides a very basic event service bus using
|
26
|
-
Redis' pubsub capabilities. We hope to implement more advanced topologies
|
27
|
-
using ZeroMQ in the future.
|
28
|
-
|
29
|
-
View the `examples/config.su` file for an example of a serfup configuration.
|
30
|
-
|
31
|
-
Besides Handlers, we have `Receivers` that expose points of entry for
|
32
|
-
command and event messages. We implement two types:
|
33
|
-
|
34
|
-
1. RedisPubsubReceiver - For pubsub event messages.
|
35
|
-
2. MsgpackReceiver - For msgpack rpc receipt of command messages.
|
36
|
-
|
37
8
|
Middleware
|
38
9
|
==========
|
39
10
|
|
40
|
-
Serf::Middleware::EmRunner
|
41
|
-
--------------------------
|
42
|
-
|
43
|
-
The EmRunner middleware is a mechanism to take a received message
|
44
|
-
(from RedisPubsubReceiver or MsgpackReceiver) and have the subsequent
|
45
|
-
app chain to be processed in the EventMachine deferred thread pool.
|
46
|
-
This is handy for async messages that come in through the PubSub
|
47
|
-
channel. And it is helpful in the CQRS model of processing when
|
48
|
-
accepting commands through Msgpack RPC.
|
49
|
-
|
50
|
-
Serf::Middleware::CelluloidRunner
|
51
|
-
---------------------------------
|
52
|
-
|
53
|
-
`celluloid` is a soft dependency.
|
54
|
-
|
55
|
-
But this middleware functions the same as the EmRunner, but with
|
56
|
-
actors and fibers instead.
|
57
|
-
|
58
|
-
NOTE: The limitation of this is that only 1 actor will be generated
|
59
|
-
per 'use Serf::Middleware::CelluloidRunner' definition. Thus messages
|
60
|
-
to any handler within a single 'group' will be processed serially.
|
61
|
-
|
62
|
-
This might be helpful in special cases where you want non-blocking
|
63
|
-
receipt of messages but also want ordered processing based on
|
64
|
-
received messages.
|
65
|
-
|
66
11
|
Airbrake
|
67
12
|
--------
|
68
13
|
|
@@ -76,10 +21,7 @@ You can use Airbrake's middleware to catch exceptions.
|
|
76
21
|
config.api_key = 'my_api_key'
|
77
22
|
end
|
78
23
|
|
79
|
-
|
80
|
-
use Airbrake::Rack
|
81
|
-
handle 'my_command', MyCommand.new
|
82
|
-
end
|
24
|
+
use Airbrake::Rack
|
83
25
|
|
84
26
|
|
85
27
|
Contributing to serf
|
data/Rakefile
CHANGED
@@ -17,7 +17,7 @@ Jeweler::Tasks.new do |gem|
|
|
17
17
|
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
18
18
|
gem.name = "serf"
|
19
19
|
gem.homepage = "http://github.com/byu/serf"
|
20
|
-
gem.license = "
|
20
|
+
gem.license = "Apache 2.0"
|
21
21
|
gem.summary = %Q{Event-Driven SOA with CQRS}
|
22
22
|
gem.description = %Q{Event-Driven SOA with CQRS}
|
23
23
|
gem.email = "benjaminlyu@gmail.com"
|
data/lib/serf/builder.rb
CHANGED
@@ -1,22 +1,21 @@
|
|
1
|
-
require '
|
1
|
+
require 'serf/serfer'
|
2
2
|
|
3
3
|
module Serf
|
4
4
|
|
5
5
|
##
|
6
|
-
# A Serf Builder that processes the SerfUp DSL to
|
7
|
-
#
|
6
|
+
# A Serf Builder that processes the SerfUp DSL to build a rack-like
|
7
|
+
# app to route and process received messages. This builder is
|
8
|
+
# implemented with lots of code from Rack::Builder.
|
8
9
|
#
|
9
10
|
# builder = Serf::Builder.parse_file 'examples/config.su'
|
10
|
-
# builder.
|
11
|
-
# EventMachine::run # Most likely do this because of EmRunner middleware.
|
11
|
+
# builder.to_app
|
12
12
|
#
|
13
13
|
# or
|
14
14
|
#
|
15
15
|
# builder = Serf::Builder.new do
|
16
16
|
# ... A SerfUp Config block here. See the examples/config.su
|
17
17
|
# end
|
18
|
-
# builder.
|
19
|
-
# EventMachine::run
|
18
|
+
# builder.to_app
|
20
19
|
#
|
21
20
|
class Builder
|
22
21
|
def self.parse_file(config)
|
@@ -26,71 +25,75 @@ module Serf
|
|
26
25
|
return builder
|
27
26
|
end
|
28
27
|
|
29
|
-
def initialize(&block)
|
30
|
-
@
|
31
|
-
@
|
32
|
-
@
|
33
|
-
|
34
|
-
|
28
|
+
def initialize(options={}, &block)
|
29
|
+
@use = []
|
30
|
+
@manifest = {}
|
31
|
+
@config = {}
|
32
|
+
@not_found = options[:not_found]
|
33
|
+
@serfer_class = options.fetch(:serfer_class) { ::Serf::Serfer }
|
34
|
+
@serfer_options = options[:serfer_options] || {}
|
35
35
|
instance_eval(&block) if block_given?
|
36
36
|
end
|
37
37
|
|
38
|
-
def
|
39
|
-
|
40
|
-
@current_group_name = name
|
41
|
-
instance_eval(&block) if block_given?
|
42
|
-
commit_current_group
|
43
|
-
reset_current_group
|
38
|
+
def self.app(default_app=nil, &block)
|
39
|
+
self.new(default_app, &block).to_app
|
44
40
|
end
|
45
41
|
|
46
42
|
def use(middleware, *args, &block)
|
47
43
|
@use << proc { |app| middleware.new(app, *args, &block) }
|
48
44
|
end
|
49
45
|
|
50
|
-
def
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
end
|
46
|
+
def register(manifest)
|
47
|
+
@manifest.merge! manifest
|
48
|
+
end
|
49
|
+
|
50
|
+
def config(handler, *args, &block)
|
51
|
+
@config[handler] = [args, block]
|
57
52
|
end
|
58
53
|
|
59
54
|
def not_found(app)
|
60
|
-
raise 'not_found already declared for this group' if @not_found
|
61
55
|
@not_found = app
|
62
56
|
end
|
63
57
|
|
64
|
-
def
|
65
|
-
app =
|
66
|
-
@
|
67
|
-
end
|
68
|
-
|
69
|
-
def run
|
70
|
-
raise 'Already run' if @already_run
|
71
|
-
@already_run = true
|
72
|
-
@receivers.each do |receiver|
|
73
|
-
Thread.new {
|
74
|
-
receiver.run
|
75
|
-
}
|
76
|
-
end
|
58
|
+
def to_app
|
59
|
+
app = generate_routes
|
60
|
+
return @use.reverse.inject(app) { |a,e| e[a] }
|
77
61
|
end
|
78
62
|
|
79
63
|
private
|
80
64
|
|
81
|
-
def
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
65
|
+
def generate_routes
|
66
|
+
kinds = {}
|
67
|
+
handlers = {}
|
68
|
+
async_handlers = {}
|
69
|
+
|
70
|
+
@manifest.each do |kind, options|
|
71
|
+
# Instantiate our handler with any possible configuration.
|
72
|
+
handler_str = options.fetch(:handler)
|
73
|
+
handler_class = handler_str.camelize.constantize
|
74
|
+
args, block = @config.fetch(handler_str) { [[], nil] }
|
75
|
+
handler = handler_class.new *args, &block
|
76
|
+
|
77
|
+
# Then put it into the proper map of handlers for either
|
78
|
+
# synchronous or asynchronous processing.
|
79
|
+
async = options.fetch(:async) { true }
|
80
|
+
(async ? async_handlers : handlers)[kind] = handler
|
81
|
+
|
82
|
+
# Get the implementing message serialization class.
|
83
|
+
# For a given message kind, we may have a different (or nil)
|
84
|
+
# implementing class. If nil, then we're not going to try to
|
85
|
+
# create a message class to validate before passing to handler.
|
86
|
+
message_class = options.fetch(:message_class) { kind }
|
87
|
+
kinds[kind] = message_class && message_class.camelize.constantize
|
88
|
+
end
|
87
89
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
90
|
+
# We create the serfer class to handle all the messages.
|
91
|
+
return @serfer_class.new(
|
92
|
+
@serfer_options.merge(
|
93
|
+
kinds: kinds,
|
94
|
+
handlers: handlers,
|
95
|
+
async_handlers: async_handlers,
|
96
|
+
not_found: @not_found))
|
94
97
|
end
|
95
98
|
|
96
99
|
end
|
data/lib/serf/handler.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
require 'active_support/core_ext/class/attribute'
|
3
|
+
require 'active_support/core_ext/hash/keys'
|
4
|
+
require 'active_support/core_ext/object/blank'
|
5
|
+
require 'active_support/ordered_options'
|
6
|
+
|
7
|
+
module Serf
|
8
|
+
|
9
|
+
module Handler
|
10
|
+
extend ActiveSupport::Concern
|
11
|
+
|
12
|
+
included do
|
13
|
+
# In the class that includes this module, we're going to
|
14
|
+
# create an inheritable class attribute that will store
|
15
|
+
# our mappings between messages and the methods to call.
|
16
|
+
class_attribute :serf_actions
|
17
|
+
send(
|
18
|
+
'serf_actions=',
|
19
|
+
ActiveSupport::InheritableOptions.new)
|
20
|
+
|
21
|
+
def self.inherited(kls) #:nodoc:
|
22
|
+
super
|
23
|
+
# Sets the current subclass class attribute to be an
|
24
|
+
# inheritable copy of the superclass options.
|
25
|
+
kls.send(
|
26
|
+
'serf_actions=',
|
27
|
+
self.serf_actions.inheritable_copy)
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
module InstanceMethods
|
33
|
+
|
34
|
+
##
|
35
|
+
# Rack-like call. It receives an environment hash, which we
|
36
|
+
# assume is a message.
|
37
|
+
#
|
38
|
+
def call(env={})
|
39
|
+
# Just to stringify the environment keys
|
40
|
+
env = env.dup.stringify_keys
|
41
|
+
# Make sure a kind was set, and that we can handle it.
|
42
|
+
message_kind = env['kind']
|
43
|
+
raise ArgumentError, 'No "kind" in call env' if message_kind.blank?
|
44
|
+
method = self.class.serf_actions[message_kind]
|
45
|
+
raise ArgumentError, "#{message_kind} not found" if method.blank?
|
46
|
+
# Now execute the method with the environment parameters
|
47
|
+
self.send method, env
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
module ClassMethods
|
52
|
+
|
53
|
+
##
|
54
|
+
# registers a method to handle the receipt of a message type.
|
55
|
+
#
|
56
|
+
def receives(message_kind, options={})
|
57
|
+
raise ArgumentError, 'Blank message_kind' if message_kind.blank?
|
58
|
+
exposed_method = options[:with]
|
59
|
+
raise ArgumentError, 'Missing "with" option' if exposed_method.blank?
|
60
|
+
self.serf_actions[message_kind] = exposed_method
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
data/lib/serf/message.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'active_model'
|
2
|
+
require 'active_support/concern'
|
3
|
+
require 'active_support/core_ext/class/attribute'
|
4
|
+
require 'active_support/core_ext/string/inflections'
|
5
|
+
|
6
|
+
module Serf
|
7
|
+
|
8
|
+
##
|
9
|
+
# A module to represent a message that we're transporting over
|
10
|
+
# the wire. This is mainly for commands and events in ED-SOA.
|
11
|
+
# Optional, but useful for validations, etc.
|
12
|
+
#
|
13
|
+
module Message
|
14
|
+
extend ActiveSupport::Concern
|
15
|
+
include ActiveModel::Serialization
|
16
|
+
include ActiveModel::Serializers::JSON
|
17
|
+
include ActiveModel::Validations
|
18
|
+
|
19
|
+
included do
|
20
|
+
class_attribute :kind
|
21
|
+
send 'kind=', self.to_s.tableize.singularize
|
22
|
+
end
|
23
|
+
|
24
|
+
module InstanceMethods
|
25
|
+
|
26
|
+
def attributes
|
27
|
+
{
|
28
|
+
'kind' => kind
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
def kind
|
33
|
+
self.class.kind
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_msgpack
|
37
|
+
attributes.to_msgpack
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
data/lib/serf/serfer.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'eventmachine'
|
2
|
+
|
3
|
+
require 'serf/util/null_object'
|
4
|
+
|
5
|
+
module Serf
|
6
|
+
|
7
|
+
##
|
8
|
+
# The Serfer is a rack app endpoint that:
|
9
|
+
class Serfer
|
10
|
+
|
11
|
+
def initialize(options={})
|
12
|
+
# Options for handling the requests
|
13
|
+
@kinds = options[:kinds] || {}
|
14
|
+
@handlers = options[:handlers] || {}
|
15
|
+
@async_handlers = options[:async_handlers] || {}
|
16
|
+
@not_found = options[:not_found]
|
17
|
+
|
18
|
+
# Other processing aspects
|
19
|
+
@em = options.fetch(:event_machine) { ::EM }
|
20
|
+
@logger = options.fetch(:logger) { ::Serf::Util::NullObject.new }
|
21
|
+
end
|
22
|
+
|
23
|
+
##
|
24
|
+
# Rack-like call to handle a message
|
25
|
+
#
|
26
|
+
def call(env)
|
27
|
+
kind = env['kind']
|
28
|
+
|
29
|
+
# Do a message_class validation if we have it listed.
|
30
|
+
# And use the message attributes instead of raw env when passing
|
31
|
+
# to message handler.
|
32
|
+
message_class = @kinds[kind]
|
33
|
+
if message_class
|
34
|
+
message = message_class.new env
|
35
|
+
raise message.errors.full_messages.join('. ') unless message.valid?
|
36
|
+
params = message.attributes
|
37
|
+
else
|
38
|
+
params = env.stringify_keys
|
39
|
+
end
|
40
|
+
|
41
|
+
# Run an asynchronous handler if we have it.
|
42
|
+
handler = @async_handlers[kind]
|
43
|
+
if handler
|
44
|
+
@em.defer(proc do
|
45
|
+
begin
|
46
|
+
handler.call params
|
47
|
+
rescue => e
|
48
|
+
@logger.error e
|
49
|
+
end
|
50
|
+
end)
|
51
|
+
return [
|
52
|
+
202,
|
53
|
+
{
|
54
|
+
'Content-Type' => 'text/plain'
|
55
|
+
},
|
56
|
+
['Accepted']
|
57
|
+
]
|
58
|
+
end
|
59
|
+
|
60
|
+
# Run a synchronous handler if we have it.
|
61
|
+
handler = @handlers[kind]
|
62
|
+
return handler.call(params) if handler
|
63
|
+
|
64
|
+
# run a not found
|
65
|
+
return @not_found.call(params) if @not_found
|
66
|
+
|
67
|
+
# we can't handle this kind.
|
68
|
+
return [
|
69
|
+
404,
|
70
|
+
{
|
71
|
+
'Content-Type' => 'text/plain',
|
72
|
+
'X-Cascade' => 'pass'
|
73
|
+
},
|
74
|
+
['Not Found']
|
75
|
+
]
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/lib/serf/version.rb
CHANGED
data/lib/serf.rb
CHANGED
@@ -1,21 +1,4 @@
|
|
1
1
|
module Serf
|
2
|
-
|
3
|
-
autoload :Builder, 'serf/builder'
|
4
|
-
autoload :NullObject, 'serf/null_object'
|
5
|
-
|
6
|
-
# Emitters
|
7
|
-
autoload :RedisEmitter, 'serf/emitters/redis_emitter'
|
8
|
-
|
9
|
-
# Receivers
|
10
|
-
autoload :MsgpackReceiver, 'serf/receivers/msgpack_receiver'
|
11
|
-
autoload :RedisPubsubReceiver, 'serf/receivers/redis_pubsub_receiver'
|
12
|
-
|
13
|
-
module Middleware
|
14
|
-
autoload :CelluloidRunner, 'serf/middleware/celluloid_runner'
|
15
|
-
autoload :EmRunner, 'serf/middleware/em_runner'
|
16
|
-
autoload :KindMapper, 'serf/middleware/kind_mapper'
|
17
|
-
end
|
18
|
-
|
19
2
|
end
|
20
3
|
|
21
4
|
require 'serf/version'
|
data/serf.gemspec
CHANGED
@@ -5,14 +5,13 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "serf"
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.2.0.alpha1"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Benjamin Yu"]
|
12
|
-
s.date = "2011-
|
12
|
+
s.date = "2011-12-12"
|
13
13
|
s.description = "Event-Driven SOA with CQRS"
|
14
14
|
s.email = "benjaminlyu@gmail.com"
|
15
|
-
s.executables = ["serfup"]
|
16
15
|
s.extra_rdoc_files = [
|
17
16
|
"LICENSE.txt",
|
18
17
|
"README.md"
|
@@ -26,24 +25,19 @@ Gem::Specification.new do |s|
|
|
26
25
|
"NOTICE.txt",
|
27
26
|
"README.md",
|
28
27
|
"Rakefile",
|
29
|
-
"bin/serfup",
|
30
|
-
"examples/config.su",
|
31
28
|
"lib/serf.rb",
|
32
29
|
"lib/serf/builder.rb",
|
33
|
-
"lib/serf/
|
34
|
-
"lib/serf/
|
35
|
-
"lib/serf/
|
36
|
-
"lib/serf/
|
37
|
-
"lib/serf/null_object.rb",
|
38
|
-
"lib/serf/receivers/msgpack_receiver.rb",
|
39
|
-
"lib/serf/receivers/redis_pubsub_receiver.rb",
|
30
|
+
"lib/serf/handler.rb",
|
31
|
+
"lib/serf/message.rb",
|
32
|
+
"lib/serf/serfer.rb",
|
33
|
+
"lib/serf/util/null_object.rb",
|
40
34
|
"lib/serf/version.rb",
|
41
35
|
"serf.gemspec",
|
42
36
|
"spec/serf_spec.rb",
|
43
37
|
"spec/spec_helper.rb"
|
44
38
|
]
|
45
39
|
s.homepage = "http://github.com/byu/serf"
|
46
|
-
s.licenses = ["
|
40
|
+
s.licenses = ["Apache 2.0"]
|
47
41
|
s.require_paths = ["lib"]
|
48
42
|
s.rubygems_version = "1.8.10"
|
49
43
|
s.summary = "Event-Driven SOA with CQRS"
|
@@ -52,54 +46,36 @@ Gem::Specification.new do |s|
|
|
52
46
|
s.specification_version = 3
|
53
47
|
|
54
48
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
55
|
-
s.add_runtime_dependency(%q<
|
49
|
+
s.add_runtime_dependency(%q<activemodel>, ["~> 3.1.3"])
|
50
|
+
s.add_runtime_dependency(%q<activesupport>, ["~> 3.1.3"])
|
56
51
|
s.add_runtime_dependency(%q<eventmachine>, ["~> 0.12.10"])
|
57
|
-
s.add_runtime_dependency(%q<i18n>, ["~> 0.6.0"])
|
58
|
-
s.add_runtime_dependency(%q<msgpack>, ["~> 0.4.6"])
|
59
|
-
s.add_runtime_dependency(%q<msgpack-rpc>, ["~> 0.4.5"])
|
60
|
-
s.add_runtime_dependency(%q<multi_json>, ["~> 1.0.3"])
|
61
|
-
s.add_runtime_dependency(%q<rack>, ["~> 1.3.5"])
|
62
|
-
s.add_runtime_dependency(%q<redis>, ["~> 2.2.2"])
|
63
52
|
s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
|
64
53
|
s.add_development_dependency(%q<yard>, ["~> 0.6.0"])
|
65
54
|
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
66
55
|
s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
|
67
56
|
s.add_development_dependency(%q<rcov>, [">= 0"])
|
68
|
-
s.add_development_dependency(%q<
|
69
|
-
s.add_development_dependency(%q<celluloid>, ["~> 0.5.0"])
|
57
|
+
s.add_development_dependency(%q<msgpack>, ["~> 0.4.6"])
|
70
58
|
else
|
71
|
-
s.add_dependency(%q<
|
59
|
+
s.add_dependency(%q<activemodel>, ["~> 3.1.3"])
|
60
|
+
s.add_dependency(%q<activesupport>, ["~> 3.1.3"])
|
72
61
|
s.add_dependency(%q<eventmachine>, ["~> 0.12.10"])
|
73
|
-
s.add_dependency(%q<i18n>, ["~> 0.6.0"])
|
74
|
-
s.add_dependency(%q<msgpack>, ["~> 0.4.6"])
|
75
|
-
s.add_dependency(%q<msgpack-rpc>, ["~> 0.4.5"])
|
76
|
-
s.add_dependency(%q<multi_json>, ["~> 1.0.3"])
|
77
|
-
s.add_dependency(%q<rack>, ["~> 1.3.5"])
|
78
|
-
s.add_dependency(%q<redis>, ["~> 2.2.2"])
|
79
62
|
s.add_dependency(%q<rspec>, ["~> 2.3.0"])
|
80
63
|
s.add_dependency(%q<yard>, ["~> 0.6.0"])
|
81
64
|
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
82
65
|
s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
|
83
66
|
s.add_dependency(%q<rcov>, [">= 0"])
|
84
|
-
s.add_dependency(%q<
|
85
|
-
s.add_dependency(%q<celluloid>, ["~> 0.5.0"])
|
67
|
+
s.add_dependency(%q<msgpack>, ["~> 0.4.6"])
|
86
68
|
end
|
87
69
|
else
|
88
|
-
s.add_dependency(%q<
|
70
|
+
s.add_dependency(%q<activemodel>, ["~> 3.1.3"])
|
71
|
+
s.add_dependency(%q<activesupport>, ["~> 3.1.3"])
|
89
72
|
s.add_dependency(%q<eventmachine>, ["~> 0.12.10"])
|
90
|
-
s.add_dependency(%q<i18n>, ["~> 0.6.0"])
|
91
|
-
s.add_dependency(%q<msgpack>, ["~> 0.4.6"])
|
92
|
-
s.add_dependency(%q<msgpack-rpc>, ["~> 0.4.5"])
|
93
|
-
s.add_dependency(%q<multi_json>, ["~> 1.0.3"])
|
94
|
-
s.add_dependency(%q<rack>, ["~> 1.3.5"])
|
95
|
-
s.add_dependency(%q<redis>, ["~> 2.2.2"])
|
96
73
|
s.add_dependency(%q<rspec>, ["~> 2.3.0"])
|
97
74
|
s.add_dependency(%q<yard>, ["~> 0.6.0"])
|
98
75
|
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
99
76
|
s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
|
100
77
|
s.add_dependency(%q<rcov>, [">= 0"])
|
101
|
-
s.add_dependency(%q<
|
102
|
-
s.add_dependency(%q<celluloid>, ["~> 0.5.0"])
|
78
|
+
s.add_dependency(%q<msgpack>, ["~> 0.4.6"])
|
103
79
|
end
|
104
80
|
end
|
105
81
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: serf
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0.alpha1
|
5
5
|
prerelease: 6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,99 +9,44 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-
|
12
|
+
date: 2011-12-12 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
|
-
name:
|
16
|
-
requirement: &
|
17
|
-
none: false
|
18
|
-
requirements:
|
19
|
-
- - ~>
|
20
|
-
- !ruby/object:Gem::Version
|
21
|
-
version: 3.1.1
|
22
|
-
type: :runtime
|
23
|
-
prerelease: false
|
24
|
-
version_requirements: *70284262969520
|
25
|
-
- !ruby/object:Gem::Dependency
|
26
|
-
name: eventmachine
|
27
|
-
requirement: &70284262967940 !ruby/object:Gem::Requirement
|
15
|
+
name: activemodel
|
16
|
+
requirement: &70254700994980 !ruby/object:Gem::Requirement
|
28
17
|
none: false
|
29
18
|
requirements:
|
30
19
|
- - ~>
|
31
20
|
- !ruby/object:Gem::Version
|
32
|
-
version:
|
33
|
-
type: :runtime
|
34
|
-
prerelease: false
|
35
|
-
version_requirements: *70284262967940
|
36
|
-
- !ruby/object:Gem::Dependency
|
37
|
-
name: i18n
|
38
|
-
requirement: &70284262966820 !ruby/object:Gem::Requirement
|
39
|
-
none: false
|
40
|
-
requirements:
|
41
|
-
- - ~>
|
42
|
-
- !ruby/object:Gem::Version
|
43
|
-
version: 0.6.0
|
44
|
-
type: :runtime
|
45
|
-
prerelease: false
|
46
|
-
version_requirements: *70284262966820
|
47
|
-
- !ruby/object:Gem::Dependency
|
48
|
-
name: msgpack
|
49
|
-
requirement: &70284262965600 !ruby/object:Gem::Requirement
|
50
|
-
none: false
|
51
|
-
requirements:
|
52
|
-
- - ~>
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: 0.4.6
|
21
|
+
version: 3.1.3
|
55
22
|
type: :runtime
|
56
23
|
prerelease: false
|
57
|
-
version_requirements: *
|
24
|
+
version_requirements: *70254700994980
|
58
25
|
- !ruby/object:Gem::Dependency
|
59
|
-
name:
|
60
|
-
requirement: &
|
61
|
-
none: false
|
62
|
-
requirements:
|
63
|
-
- - ~>
|
64
|
-
- !ruby/object:Gem::Version
|
65
|
-
version: 0.4.5
|
66
|
-
type: :runtime
|
67
|
-
prerelease: false
|
68
|
-
version_requirements: *70284262964540
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: multi_json
|
71
|
-
requirement: &70284262963360 !ruby/object:Gem::Requirement
|
72
|
-
none: false
|
73
|
-
requirements:
|
74
|
-
- - ~>
|
75
|
-
- !ruby/object:Gem::Version
|
76
|
-
version: 1.0.3
|
77
|
-
type: :runtime
|
78
|
-
prerelease: false
|
79
|
-
version_requirements: *70284262963360
|
80
|
-
- !ruby/object:Gem::Dependency
|
81
|
-
name: rack
|
82
|
-
requirement: &70284262962220 !ruby/object:Gem::Requirement
|
26
|
+
name: activesupport
|
27
|
+
requirement: &70254700994500 !ruby/object:Gem::Requirement
|
83
28
|
none: false
|
84
29
|
requirements:
|
85
30
|
- - ~>
|
86
31
|
- !ruby/object:Gem::Version
|
87
|
-
version: 1.3
|
32
|
+
version: 3.1.3
|
88
33
|
type: :runtime
|
89
34
|
prerelease: false
|
90
|
-
version_requirements: *
|
35
|
+
version_requirements: *70254700994500
|
91
36
|
- !ruby/object:Gem::Dependency
|
92
|
-
name:
|
93
|
-
requirement: &
|
37
|
+
name: eventmachine
|
38
|
+
requirement: &70254700994020 !ruby/object:Gem::Requirement
|
94
39
|
none: false
|
95
40
|
requirements:
|
96
41
|
- - ~>
|
97
42
|
- !ruby/object:Gem::Version
|
98
|
-
version:
|
43
|
+
version: 0.12.10
|
99
44
|
type: :runtime
|
100
45
|
prerelease: false
|
101
|
-
version_requirements: *
|
46
|
+
version_requirements: *70254700994020
|
102
47
|
- !ruby/object:Gem::Dependency
|
103
48
|
name: rspec
|
104
|
-
requirement: &
|
49
|
+
requirement: &70254700993540 !ruby/object:Gem::Requirement
|
105
50
|
none: false
|
106
51
|
requirements:
|
107
52
|
- - ~>
|
@@ -109,10 +54,10 @@ dependencies:
|
|
109
54
|
version: 2.3.0
|
110
55
|
type: :development
|
111
56
|
prerelease: false
|
112
|
-
version_requirements: *
|
57
|
+
version_requirements: *70254700993540
|
113
58
|
- !ruby/object:Gem::Dependency
|
114
59
|
name: yard
|
115
|
-
requirement: &
|
60
|
+
requirement: &70254700993060 !ruby/object:Gem::Requirement
|
116
61
|
none: false
|
117
62
|
requirements:
|
118
63
|
- - ~>
|
@@ -120,10 +65,10 @@ dependencies:
|
|
120
65
|
version: 0.6.0
|
121
66
|
type: :development
|
122
67
|
prerelease: false
|
123
|
-
version_requirements: *
|
68
|
+
version_requirements: *70254700993060
|
124
69
|
- !ruby/object:Gem::Dependency
|
125
70
|
name: bundler
|
126
|
-
requirement: &
|
71
|
+
requirement: &70254700992580 !ruby/object:Gem::Requirement
|
127
72
|
none: false
|
128
73
|
requirements:
|
129
74
|
- - ~>
|
@@ -131,10 +76,10 @@ dependencies:
|
|
131
76
|
version: 1.0.0
|
132
77
|
type: :development
|
133
78
|
prerelease: false
|
134
|
-
version_requirements: *
|
79
|
+
version_requirements: *70254700992580
|
135
80
|
- !ruby/object:Gem::Dependency
|
136
81
|
name: jeweler
|
137
|
-
requirement: &
|
82
|
+
requirement: &70254700992100 !ruby/object:Gem::Requirement
|
138
83
|
none: false
|
139
84
|
requirements:
|
140
85
|
- - ~>
|
@@ -142,10 +87,10 @@ dependencies:
|
|
142
87
|
version: 1.6.4
|
143
88
|
type: :development
|
144
89
|
prerelease: false
|
145
|
-
version_requirements: *
|
90
|
+
version_requirements: *70254700992100
|
146
91
|
- !ruby/object:Gem::Dependency
|
147
92
|
name: rcov
|
148
|
-
requirement: &
|
93
|
+
requirement: &70254700991540 !ruby/object:Gem::Requirement
|
149
94
|
none: false
|
150
95
|
requirements:
|
151
96
|
- - ! '>='
|
@@ -153,33 +98,21 @@ dependencies:
|
|
153
98
|
version: '0'
|
154
99
|
type: :development
|
155
100
|
prerelease: false
|
156
|
-
version_requirements: *
|
101
|
+
version_requirements: *70254700991540
|
157
102
|
- !ruby/object:Gem::Dependency
|
158
|
-
name:
|
159
|
-
requirement: &
|
160
|
-
none: false
|
161
|
-
requirements:
|
162
|
-
- - ~>
|
163
|
-
- !ruby/object:Gem::Version
|
164
|
-
version: 1.1.9
|
165
|
-
type: :development
|
166
|
-
prerelease: false
|
167
|
-
version_requirements: *70284262910880
|
168
|
-
- !ruby/object:Gem::Dependency
|
169
|
-
name: celluloid
|
170
|
-
requirement: &70284262909900 !ruby/object:Gem::Requirement
|
103
|
+
name: msgpack
|
104
|
+
requirement: &70254700991060 !ruby/object:Gem::Requirement
|
171
105
|
none: false
|
172
106
|
requirements:
|
173
107
|
- - ~>
|
174
108
|
- !ruby/object:Gem::Version
|
175
|
-
version: 0.
|
109
|
+
version: 0.4.6
|
176
110
|
type: :development
|
177
111
|
prerelease: false
|
178
|
-
version_requirements: *
|
112
|
+
version_requirements: *70254700991060
|
179
113
|
description: Event-Driven SOA with CQRS
|
180
114
|
email: benjaminlyu@gmail.com
|
181
|
-
executables:
|
182
|
-
- serfup
|
115
|
+
executables: []
|
183
116
|
extensions: []
|
184
117
|
extra_rdoc_files:
|
185
118
|
- LICENSE.txt
|
@@ -193,24 +126,19 @@ files:
|
|
193
126
|
- NOTICE.txt
|
194
127
|
- README.md
|
195
128
|
- Rakefile
|
196
|
-
- bin/serfup
|
197
|
-
- examples/config.su
|
198
129
|
- lib/serf.rb
|
199
130
|
- lib/serf/builder.rb
|
200
|
-
- lib/serf/
|
201
|
-
- lib/serf/
|
202
|
-
- lib/serf/
|
203
|
-
- lib/serf/
|
204
|
-
- lib/serf/null_object.rb
|
205
|
-
- lib/serf/receivers/msgpack_receiver.rb
|
206
|
-
- lib/serf/receivers/redis_pubsub_receiver.rb
|
131
|
+
- lib/serf/handler.rb
|
132
|
+
- lib/serf/message.rb
|
133
|
+
- lib/serf/serfer.rb
|
134
|
+
- lib/serf/util/null_object.rb
|
207
135
|
- lib/serf/version.rb
|
208
136
|
- serf.gemspec
|
209
137
|
- spec/serf_spec.rb
|
210
138
|
- spec/spec_helper.rb
|
211
139
|
homepage: http://github.com/byu/serf
|
212
140
|
licenses:
|
213
|
-
-
|
141
|
+
- Apache 2.0
|
214
142
|
post_install_message:
|
215
143
|
rdoc_options: []
|
216
144
|
require_paths:
|
@@ -223,7 +151,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
223
151
|
version: '0'
|
224
152
|
segments:
|
225
153
|
- 0
|
226
|
-
hash:
|
154
|
+
hash: 1721782691913874176
|
227
155
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
228
156
|
none: false
|
229
157
|
requirements:
|
data/bin/serfup
DELETED
data/examples/config.su
DELETED
@@ -1,58 +0,0 @@
|
|
1
|
-
# A Serf's Up file
|
2
|
-
|
3
|
-
require 'log4r'
|
4
|
-
|
5
|
-
# Set up a general logger for the app
|
6
|
-
logger = Log4r::Logger.new 'my_logger'
|
7
|
-
logger.outputters = Log4r::FileOutputter.new(
|
8
|
-
'fileOutputter',
|
9
|
-
filename: 'console.txt')
|
10
|
-
|
11
|
-
# Define a single emitter w/ default redis connections
|
12
|
-
emitter = Serf::RedisEmitter.new
|
13
|
-
|
14
|
-
# Define a group of events handlers
|
15
|
-
group :events do
|
16
|
-
# We use the EmRunner middleware so the handlers get run in the
|
17
|
-
# EventMachine deferred thread pool.
|
18
|
-
use Serf::Middleware::EmRunner, logger: logger
|
19
|
-
|
20
|
-
# Define a handler for the post_rated_event kind of message.
|
21
|
-
handle 'post_rated_event', proc { |env|
|
22
|
-
logger.info("I'm in post_rated_event #{env.inspect}")
|
23
|
-
[200, {}, '']
|
24
|
-
}
|
25
|
-
|
26
|
-
# Define what happens if a message received has a 'kind' attribute that
|
27
|
-
# is not defined in this group.
|
28
|
-
not_found(proc { |env|
|
29
|
-
logger.info("event not found #{env.inspect}")
|
30
|
-
[200, {},'']
|
31
|
-
})
|
32
|
-
end
|
33
|
-
|
34
|
-
# Define a group of handlers that'll be handled by the MsgPackReceiver
|
35
|
-
group :commands do
|
36
|
-
# We use the EmRunner middleware so the handlers get run in the
|
37
|
-
# EventMachine deferred thread pool.
|
38
|
-
use Serf::Middleware::EmRunner, logger: logger
|
39
|
-
|
40
|
-
# Handle the 'post_rating_request' command message.
|
41
|
-
handle 'post_rating_request', proc { |env|
|
42
|
-
logger.info("I'm in post_rating_request #{env.inspect}")
|
43
|
-
emitter.emit(
|
44
|
-
kind: 'post_rated_event',
|
45
|
-
rated_data: env.to_s)
|
46
|
-
[200, {},'']
|
47
|
-
}
|
48
|
-
not_found(proc { |env|
|
49
|
-
logger.info("command not found #{env.inspect}")
|
50
|
-
[200, {},'']
|
51
|
-
})
|
52
|
-
end
|
53
|
-
|
54
|
-
# Bind our receivers and handler groups
|
55
|
-
bind :events, Serf::RedisPubsubReceiver
|
56
|
-
bind :commands, Serf::MsgpackReceiver, :host => '0.0.0.0', :post => 18800
|
57
|
-
|
58
|
-
|
@@ -1,23 +0,0 @@
|
|
1
|
-
require 'multi_json'
|
2
|
-
require 'redis'
|
3
|
-
|
4
|
-
module Serf
|
5
|
-
|
6
|
-
##
|
7
|
-
# Emits messages/events over a redis pubsub channel.
|
8
|
-
#
|
9
|
-
class RedisEmitter
|
10
|
-
DEFAULT_CHANNEL = 'serf_pubsub_channel'
|
11
|
-
|
12
|
-
def initialize(options={})
|
13
|
-
@redis = options.fetch(:redis) { Redis.connect }
|
14
|
-
@channel = options.fetch(:channel) { DEFAULT_CHANNEL }
|
15
|
-
end
|
16
|
-
|
17
|
-
def emit(message)
|
18
|
-
encoded_message = MultiJson.encode message
|
19
|
-
@redis.publish @channel, encoded_message
|
20
|
-
end
|
21
|
-
|
22
|
-
end
|
23
|
-
end
|
@@ -1,45 +0,0 @@
|
|
1
|
-
require 'celluloid'
|
2
|
-
|
3
|
-
module Serf
|
4
|
-
module Middleware
|
5
|
-
|
6
|
-
##
|
7
|
-
# Spins off a received message to be run async by a celluloid actor.
|
8
|
-
#
|
9
|
-
class CelluloidRunner
|
10
|
-
|
11
|
-
def initialize(app, options={})
|
12
|
-
actor_class = options.fetch(:actor_class) { CelluloidRunnerActor }
|
13
|
-
@logger = options.fetch(:logger) { ::Serf::NullObject.new }
|
14
|
-
@actor = actor_class.new app, logger: @logger
|
15
|
-
end
|
16
|
-
|
17
|
-
def call(message)
|
18
|
-
@actor.call! message
|
19
|
-
return [
|
20
|
-
202,
|
21
|
-
{
|
22
|
-
'Content-Type' => 'text/plain'
|
23
|
-
},
|
24
|
-
['Accepted']
|
25
|
-
]
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
class CelluloidRunnerActor
|
30
|
-
include Celluloid
|
31
|
-
|
32
|
-
def initialize(app, options={})
|
33
|
-
@app = app
|
34
|
-
@logger = options.fetch(:logger) { ::Serf::NullObject.new }
|
35
|
-
end
|
36
|
-
|
37
|
-
def call(message)
|
38
|
-
@app.call message
|
39
|
-
rescue => e
|
40
|
-
@logger.error e
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
end
|
45
|
-
end
|
@@ -1,32 +0,0 @@
|
|
1
|
-
require 'eventmachine'
|
2
|
-
|
3
|
-
module Serf
|
4
|
-
module Middleware
|
5
|
-
|
6
|
-
class EmRunner
|
7
|
-
def initialize(app, options={})
|
8
|
-
@em = options.fetch(:event_machine) { EM }
|
9
|
-
@app = app
|
10
|
-
@logger = options.fetch(:logger) { ::Serf::NullObject.new }
|
11
|
-
end
|
12
|
-
|
13
|
-
def call(env)
|
14
|
-
@em.defer(proc do
|
15
|
-
begin
|
16
|
-
@app.call env
|
17
|
-
rescue => e
|
18
|
-
@logger.error e
|
19
|
-
end
|
20
|
-
end)
|
21
|
-
return [
|
22
|
-
202,
|
23
|
-
{
|
24
|
-
'Content-Type' => 'text/plain'
|
25
|
-
},
|
26
|
-
['Accepted']
|
27
|
-
]
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
end
|
32
|
-
end
|
@@ -1,48 +0,0 @@
|
|
1
|
-
module Serf
|
2
|
-
module Middleware
|
3
|
-
|
4
|
-
##
|
5
|
-
# Our "router" app that will call the actual registered handler object
|
6
|
-
# with the received message based on the 'kind' of message received.
|
7
|
-
#
|
8
|
-
# This means we look into the 'env' of the call and match the
|
9
|
-
# env['kind'] value to see if we have it registered.
|
10
|
-
# If found, we pass to the proper handler, else we run a not_found
|
11
|
-
# handler. Sans a registered not_found handler, we just return a
|
12
|
-
# 404 message. Note that if we used any Async middleware (i.e.
|
13
|
-
# EmRunner or CelluloidRunner), the calling client (using Msgpack RPC)
|
14
|
-
# will not see the 404 or not found handler results. The Async middleware
|
15
|
-
# will have returned a 202 Accepted result.
|
16
|
-
#
|
17
|
-
# Developers SHOULD implement alternate mechanisms of error handling
|
18
|
-
# and logging. Even possibly implementing a 404 handler that
|
19
|
-
# broadcasts such a not found error event message.
|
20
|
-
#
|
21
|
-
class KindMapper
|
22
|
-
|
23
|
-
def initialize(options={})
|
24
|
-
@map = options.fetch(:map) { {} }
|
25
|
-
@not_found = options[:not_found]
|
26
|
-
end
|
27
|
-
|
28
|
-
def call(env)
|
29
|
-
kind = env['kind']
|
30
|
-
if kind && @map.has_key?(kind)
|
31
|
-
return @map[kind].call env
|
32
|
-
elsif @not_found
|
33
|
-
return @not_found.call env
|
34
|
-
end
|
35
|
-
return [
|
36
|
-
404,
|
37
|
-
{
|
38
|
-
'Content-Type' => 'text/plain',
|
39
|
-
'X-Cascade' => 'pass'
|
40
|
-
},
|
41
|
-
['Not Found']
|
42
|
-
]
|
43
|
-
end
|
44
|
-
|
45
|
-
end
|
46
|
-
|
47
|
-
end
|
48
|
-
end
|
@@ -1,55 +0,0 @@
|
|
1
|
-
require 'active_support/core_ext/hash'
|
2
|
-
require 'msgpack/rpc'
|
3
|
-
|
4
|
-
module Serf
|
5
|
-
|
6
|
-
##
|
7
|
-
# A MsgpackRpc handler that is just here to act as a protective
|
8
|
-
# facade to only expose the 'call' rpc method to MsgpackRpc clients.
|
9
|
-
#
|
10
|
-
class MsgpackHandler
|
11
|
-
def initialize(app, options={})
|
12
|
-
@app = app
|
13
|
-
@logger = options.fetch(:logger) { ::Serf::NullObject.new }
|
14
|
-
end
|
15
|
-
|
16
|
-
def call(env)
|
17
|
-
@app.call env.stringify_keys
|
18
|
-
rescue => e
|
19
|
-
@logger = options.fetch(:logger) { ::Serf::NullObject.new }
|
20
|
-
# We reraise this error so it's passed on through to the remote client.
|
21
|
-
raise e
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
##
|
26
|
-
# Defines a Msgpack RPC Server to run to receive messages.
|
27
|
-
#
|
28
|
-
class MsgpackReceiver
|
29
|
-
DEFAULT_SERVER_TRANSPORT_CLASS = MessagePack::RPC::TCPServerTransport
|
30
|
-
DEFAULT_ADDRESS_CLASS = MessagePack::RPC::Address
|
31
|
-
|
32
|
-
def initialize(app, options={})
|
33
|
-
@handler = options.fetch(:handler) { MsgpackHandler.new(app) }
|
34
|
-
|
35
|
-
@listener = options.fetch(:listener) {
|
36
|
-
host = options.fetch(:host) { '0.0.0.0' }
|
37
|
-
port = options.fetch(:port) { 18800 }
|
38
|
-
address = DEFAULT_ADDRESS_CLASS.new host, port
|
39
|
-
DEFAULT_SERVER_TRANSPORT_CLASS.new address
|
40
|
-
}
|
41
|
-
|
42
|
-
@rpc_class = options.fetch(:rpc_class) { MessagePack::RPC::Server }
|
43
|
-
end
|
44
|
-
|
45
|
-
##
|
46
|
-
# Runs, doesn't return.
|
47
|
-
def run
|
48
|
-
svr = @rpc_class.new
|
49
|
-
svr.listen @listener, @handler
|
50
|
-
svr.run
|
51
|
-
end
|
52
|
-
|
53
|
-
end
|
54
|
-
|
55
|
-
end
|
@@ -1,38 +0,0 @@
|
|
1
|
-
require 'active_support/core_ext/hash'
|
2
|
-
require 'multi_json'
|
3
|
-
require "redis"
|
4
|
-
|
5
|
-
module Serf
|
6
|
-
|
7
|
-
##
|
8
|
-
# Defines a receiver that listens for messages from a subscribed
|
9
|
-
# Redis pubsub channel.
|
10
|
-
#
|
11
|
-
class RedisPubsubReceiver
|
12
|
-
|
13
|
-
def initialize(app, options={})
|
14
|
-
@app = app
|
15
|
-
|
16
|
-
@redis = options.fetch(:redis) { Redis.connect }
|
17
|
-
@channel = options.fetch(:channel) { 'serf_pubsub_channel' }
|
18
|
-
@logger = options.fetch(:logger) { ::Serf::NullObject.new }
|
19
|
-
end
|
20
|
-
|
21
|
-
##
|
22
|
-
# Runs, doesn't return.
|
23
|
-
def run
|
24
|
-
@redis.subscribe(@channel) do |on|
|
25
|
-
on.message do |channel, message|
|
26
|
-
begin
|
27
|
-
decoded_message = MultiJson.decode message
|
28
|
-
raise 'Received non-hash JSON' unless decoded_message.kind_of? Hash
|
29
|
-
@app.call decoded_message.stringify_keys
|
30
|
-
rescue => e
|
31
|
-
@logger.error e
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
end
|
38
|
-
end
|