serf 0.1.0.alpha1 → 0.2.0.alpha1
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.
- 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
|