serf 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +21 -0
- data/.travis.yml +7 -0
- data/Gemfile +20 -26
- data/Guardfile +16 -0
- data/NOTICE.txt +1 -1
- data/README.md +223 -207
- data/Rakefile +3 -18
- data/lib/serf/builder.rb +31 -136
- data/lib/serf/errors/policy_failure.rb +10 -0
- data/lib/serf/middleware/error_handler.rb +53 -0
- data/lib/serf/middleware/parcel_freezer.rb +36 -0
- data/lib/serf/middleware/parcel_masher.rb +39 -0
- data/lib/serf/middleware/policy_checker.rb +31 -0
- data/lib/serf/middleware/uuid_tagger.rb +13 -11
- data/lib/serf/parcel_builder.rb +30 -0
- data/lib/serf/serfer.rb +27 -66
- data/lib/serf/util/error_handling.rb +13 -36
- data/lib/serf/util/protected_call.rb +2 -2
- data/lib/serf/util/uuidable.rb +14 -38
- data/lib/serf/version.rb +1 -1
- data/schemas/{caught_exception_event.json → serf/events/caught_error.json} +4 -7
- data/serf.gemspec +22 -101
- data/spec/serf/builder_spec.rb +44 -0
- data/spec/serf/errors/policy_failure_spec.rb +11 -0
- data/spec/serf/middleware/error_handler_spec.rb +48 -0
- data/spec/serf/middleware/parcel_freezer_spec.rb +20 -0
- data/spec/serf/middleware/parcel_masher_spec.rb +30 -0
- data/spec/serf/middleware/policy_checker_spec.rb +70 -0
- data/spec/serf/middleware/uuid_tagger_spec.rb +32 -0
- data/spec/serf/parcel_builder_spec.rb +46 -0
- data/spec/serf/serfer_spec.rb +61 -0
- data/spec/serf/util/error_handling_spec.rb +35 -0
- data/spec/serf/util/null_object_spec.rb +26 -0
- data/spec/serf/util/options_extraction_spec.rb +62 -0
- data/spec/serf/util/protected_call_spec.rb +33 -0
- data/spec/serf/util/uuidable_spec.rb +56 -0
- data/spec/serf_spec.rb +1 -4
- data/spec/spec_helper.rb +3 -0
- data/spec/support/error_handling_wrapper.rb +5 -0
- data/spec/support/factories.rb +32 -0
- data/spec/support/failing_policy.rb +9 -0
- data/spec/support/json_schema_tester.rb +14 -0
- data/spec/support/options_extraction_wrapper.rb +10 -0
- data/spec/support/passing_policy.rb +7 -0
- data/spec/support/protected_call_wrapper.rb +5 -0
- metadata +81 -131
- data/.document +0 -5
- data/.rspec +0 -1
- data/Gemfile.lock +0 -58
- data/docs/thread_pools.txt +0 -16
- data/lib/serf/command.rb +0 -79
- data/lib/serf/error.rb +0 -11
- data/lib/serf/errors/not_found.rb +0 -8
- data/lib/serf/middleware/girl_friday_async.rb +0 -39
- data/lib/serf/middleware/masherize.rb +0 -25
- data/lib/serf/routing/regexp_matcher.rb +0 -35
- data/lib/serf/routing/route.rb +0 -35
- data/lib/serf/routing/route_set.rb +0 -64
- data/schemas/message_accepted_event.json +0 -14
data/lib/serf/builder.rb
CHANGED
@@ -1,166 +1,61 @@
|
|
1
|
-
require 'serf/
|
2
|
-
require 'serf/
|
1
|
+
require 'serf/middleware/error_handler'
|
2
|
+
require 'serf/middleware/parcel_freezer'
|
3
|
+
require 'serf/middleware/parcel_masher'
|
4
|
+
require 'serf/middleware/policy_checker'
|
5
|
+
require 'serf/middleware/uuid_tagger'
|
3
6
|
require 'serf/serfer'
|
4
|
-
require 'serf/util/null_object'
|
5
7
|
require 'serf/util/options_extraction'
|
6
8
|
|
7
9
|
module Serf
|
8
10
|
|
9
|
-
##
|
10
|
-
# A Serf Builder that processes the SerfUp DSL to build a rack-like
|
11
|
-
# app to handlers that process received messages. This builder is
|
12
|
-
# implemented based on code from Rack::Builder.
|
13
|
-
#
|
14
|
-
# builder = Serf::Builder.parse_file 'examples/config.su'
|
15
|
-
# builder.to_app
|
16
|
-
#
|
17
|
-
# or
|
18
|
-
#
|
19
|
-
# builder = Serf::Builder.new do
|
20
|
-
# ... A SerfUp Config block here.
|
21
|
-
# end
|
22
|
-
# builder.to_app
|
23
|
-
#
|
24
11
|
class Builder
|
25
12
|
include Serf::Util::OptionsExtraction
|
26
13
|
|
27
|
-
attr_reader :serfer_factory
|
28
|
-
attr_reader :route_set_factory
|
29
|
-
attr_reader :route_factory
|
30
|
-
|
31
|
-
def self.parse_file(config)
|
32
|
-
cfgfile = ::File.read(config)
|
33
|
-
builder = eval "Serf::Builder.new {\n" + cfgfile + "\n}",
|
34
|
-
TOPLEVEL_BINDING, config
|
35
|
-
return builder
|
36
|
-
end
|
37
|
-
|
38
|
-
def self.app(*args, &block)
|
39
|
-
new(*args, &block).to_app
|
40
|
-
end
|
41
|
-
|
42
14
|
def initialize(*args, &block)
|
43
15
|
extract_options! args
|
44
16
|
|
45
|
-
|
46
|
-
@serfer_factory = opts :serfer_factory, Serf::Serfer
|
47
|
-
@route_set_factory = opts :route_set_factory, Serf::Routing::RouteSet
|
48
|
-
@route_factory = opts :route_factory, Serf::Routing::Route
|
49
|
-
|
50
|
-
# List of middleware to be executed (non-built form)
|
17
|
+
@run = opts :interactor
|
51
18
|
@use = []
|
19
|
+
@policy_chain = opts :policy_chain, []
|
52
20
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
@default_policies = []
|
59
|
-
|
60
|
-
# The current matcher
|
61
|
-
@matcher = nil
|
62
|
-
|
63
|
-
# Current policies to be run (PRE-built)
|
64
|
-
@policies = []
|
65
|
-
|
66
|
-
# configure based on a given block.
|
67
|
-
instance_eval(&block) if block_given?
|
21
|
+
if block_given?
|
22
|
+
instance_eval(&block)
|
23
|
+
else
|
24
|
+
use_defaults
|
25
|
+
end
|
68
26
|
end
|
69
27
|
|
70
28
|
##
|
71
|
-
#
|
72
|
-
#
|
73
|
-
#
|
29
|
+
# Set a default chain of the following:
|
30
|
+
#
|
31
|
+
# use Serf::Middleware::ParcelMasher
|
32
|
+
# use Serf::Middleware::UuidTagger
|
33
|
+
# use Serf::Middleware::ParcelFreezer
|
34
|
+
# use Serf::Middleware::ErrorHandler
|
35
|
+
# use Serf::Middleware::PolicyChecker, @policy_chain
|
36
|
+
# use Serf::Serfer
|
74
37
|
#
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
38
|
+
def use_defaults
|
39
|
+
use Serf::Middleware::ParcelMasher
|
40
|
+
use Serf::Middleware::UuidTagger
|
41
|
+
use Serf::Middleware::ParcelFreezer
|
42
|
+
use Serf::Middleware::ErrorHandler
|
43
|
+
use Serf::Middleware::PolicyChecker, policy_chain: @policy_chain
|
44
|
+
use Serf::Serfer
|
80
45
|
end
|
81
46
|
|
82
|
-
##
|
83
|
-
# Append a rack-like middleware
|
84
|
-
#
|
85
|
-
# @param the middleware class
|
86
|
-
# @param *args the arguments to pass to middleware.new
|
87
|
-
# @param &block the block to pass to middleware.new
|
88
47
|
def use(middleware, *args, &block)
|
89
48
|
@use << proc { |app| middleware.new(app, *args, &block) }
|
90
49
|
end
|
91
50
|
|
92
|
-
|
93
|
-
|
94
|
-
#
|
95
|
-
# @param policy the policy factory to append
|
96
|
-
# @param *args the arguments to pass to the factory
|
97
|
-
# @param &block the block to pass to the factory
|
98
|
-
def policy(policy, *args, &block)
|
99
|
-
@policies << proc { policy.build(*args, &block) }
|
51
|
+
def run(interactor)
|
52
|
+
@run = interactor
|
100
53
|
end
|
101
54
|
|
102
|
-
def response_channel(channel); @response_channel = channel; end
|
103
|
-
def error_channel(channel); @error_channel = channel; end
|
104
|
-
def logger(logger); @logger = logger; end
|
105
|
-
|
106
|
-
##
|
107
|
-
# DSL Method to change our current context to use the given matcher.
|
108
|
-
#
|
109
|
-
def match(matcher)
|
110
|
-
@matcher = matcher
|
111
|
-
@policies = []
|
112
|
-
end
|
113
|
-
|
114
|
-
##
|
115
|
-
# @param command_factory the factory to invoke (in #to_app)
|
116
|
-
# @param *args the rest of the args to pass to command_factory#build method
|
117
|
-
# @param &block the block to pass to command_factory#build method
|
118
|
-
def run(command_factory, *args, &block)
|
119
|
-
raise 'No matcher defined yet' unless @matcher
|
120
|
-
# Create a local duplicate of the matcher and policies "snapshotted"
|
121
|
-
# at the time this method is called... so that snapshot is consistent
|
122
|
-
# for when the proc is called.
|
123
|
-
matcher = @matcher.dup
|
124
|
-
policies = @policies.dup
|
125
|
-
|
126
|
-
# This proc will be called in to_app when we actually go ahead and
|
127
|
-
# instantiate all the objects. By this point, route_set and
|
128
|
-
# default_policies passed to this proc will be ready, built.
|
129
|
-
@runs << proc { |route_set, default_policies|
|
130
|
-
route_set.add(
|
131
|
-
matcher,
|
132
|
-
route_factory.build(
|
133
|
-
command: command_factory.build(*args, &block),
|
134
|
-
policies: (policies.size > 0 ?
|
135
|
-
policies.map{ |p| p.call } :
|
136
|
-
default_policies)))
|
137
|
-
}
|
138
|
-
end
|
139
|
-
|
140
|
-
##
|
141
|
-
# Create our app.
|
142
|
-
#
|
143
55
|
def to_app
|
144
|
-
|
145
|
-
route_set = route_set_factory.build
|
146
|
-
# Build the default policies to be used if routes did not specify any.
|
147
|
-
default_policies = @default_policies.map{ |p| p.call }
|
148
|
-
# Add each route to the route_set
|
149
|
-
for run in @runs
|
150
|
-
run.call route_set, default_policies
|
151
|
-
end
|
152
|
-
# Create our serfer class
|
153
|
-
app = serfer_factory.build(
|
154
|
-
route_set: route_set,
|
155
|
-
response_channel: (@response_channel || Serf::Util::NullObject.new),
|
156
|
-
error_channel: (@error_channel || Serf::Util::NullObject.new),
|
157
|
-
logger: (@logger || Serf::Util::NullObject.new))
|
158
|
-
|
159
|
-
# We're going to inject middleware here.
|
160
|
-
app = @use.reverse.inject(app) { |a,e| e[a] } if @use.size > 0
|
161
|
-
|
162
|
-
return app
|
56
|
+
@use.reverse.inject(@run) { |a,e| e[a] }
|
163
57
|
end
|
164
58
|
|
165
59
|
end
|
60
|
+
|
166
61
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'hashie'
|
2
|
+
|
3
|
+
require 'serf/parcel_builder'
|
4
|
+
require 'serf/util/error_handling'
|
5
|
+
require 'serf/util/uuidable'
|
6
|
+
|
7
|
+
module Serf
|
8
|
+
module Middleware
|
9
|
+
|
10
|
+
##
|
11
|
+
# Middleware to catch raised exceptions and return an error parcel
|
12
|
+
# instead.
|
13
|
+
#
|
14
|
+
class ErrorHandler
|
15
|
+
include Serf::Util::ErrorHandling
|
16
|
+
include Serf::Util::OptionsExtraction
|
17
|
+
|
18
|
+
attr_reader :app
|
19
|
+
attr_reader :parcel_builder
|
20
|
+
attr_reader :uuidable
|
21
|
+
|
22
|
+
##
|
23
|
+
# @param app the app
|
24
|
+
#
|
25
|
+
def initialize(app, *args)
|
26
|
+
extract_options! args
|
27
|
+
@app = app
|
28
|
+
|
29
|
+
# Tunable knobs
|
30
|
+
@parcel_builder = opts(:parcel_builder) { Serf::ParcelBuilder.new }
|
31
|
+
@uuidable = opts(:uuidable) { Serf::Util::Uuidable.new }
|
32
|
+
end
|
33
|
+
|
34
|
+
def call(parcel)
|
35
|
+
# Attempt to execute the app, catching errors
|
36
|
+
response_parcel, error_message = with_error_handling do
|
37
|
+
app.call parcel
|
38
|
+
end
|
39
|
+
|
40
|
+
# Return on success
|
41
|
+
return response_parcel if response_parcel
|
42
|
+
|
43
|
+
# We got an error message instead, so build out the headers
|
44
|
+
# and return the parcel.
|
45
|
+
error_headers = uuidable.create_uuids parcel[:headers]
|
46
|
+
error_headers[:kind] = 'serf/events/caught_error'
|
47
|
+
return parcel_builder.build error_headers, error_message
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'ice_nine'
|
2
|
+
|
3
|
+
require 'serf/util/options_extraction'
|
4
|
+
|
5
|
+
module Serf
|
6
|
+
module Middleware
|
7
|
+
|
8
|
+
##
|
9
|
+
# Middleware to add uuids to the headers of the parcel hash.
|
10
|
+
#
|
11
|
+
class ParcelFreezer
|
12
|
+
include Serf::Util::OptionsExtraction
|
13
|
+
|
14
|
+
attr_reader :app
|
15
|
+
attr_reader :freezer
|
16
|
+
|
17
|
+
##
|
18
|
+
# @param app the app
|
19
|
+
#
|
20
|
+
def initialize(app, *args)
|
21
|
+
extract_options! args
|
22
|
+
@app = app
|
23
|
+
@freezer = opts :freezer, IceNine
|
24
|
+
end
|
25
|
+
|
26
|
+
##
|
27
|
+
# Chains the call, but deep freezes the parcel.
|
28
|
+
def call(parcel)
|
29
|
+
freezer.deep_freeze parcel
|
30
|
+
app.call parcel
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'hashie'
|
2
|
+
|
3
|
+
require 'serf/util/options_extraction'
|
4
|
+
|
5
|
+
module Serf
|
6
|
+
module Middleware
|
7
|
+
|
8
|
+
##
|
9
|
+
# Middleware to add uuids to the headers of the parcel hash.
|
10
|
+
#
|
11
|
+
class ParcelMasher
|
12
|
+
include Serf::Util::OptionsExtraction
|
13
|
+
|
14
|
+
attr_reader :app
|
15
|
+
attr_reader :masher_class
|
16
|
+
|
17
|
+
##
|
18
|
+
# @param app the app
|
19
|
+
#
|
20
|
+
def initialize(app, *args)
|
21
|
+
extract_options! args
|
22
|
+
@app = app
|
23
|
+
@masher_class = opts :masher_class, Hashie::Mash
|
24
|
+
end
|
25
|
+
|
26
|
+
##
|
27
|
+
# Coerces the parcel into a Hashie::Mash, makes sure that
|
28
|
+
# the headers and message are set, and then passes it along the chain.
|
29
|
+
def call(parcel)
|
30
|
+
mashed_parcel = masher_class.new parcel
|
31
|
+
mashed_parcel[:headers] ||= {}
|
32
|
+
mashed_parcel[:message] ||= {}
|
33
|
+
app.call mashed_parcel
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'serf/util/options_extraction'
|
2
|
+
|
3
|
+
module Serf
|
4
|
+
module Middleware
|
5
|
+
|
6
|
+
class PolicyChecker
|
7
|
+
include Serf::Util::OptionsExtraction
|
8
|
+
|
9
|
+
attr_reader :app
|
10
|
+
attr_reader :policy_chain
|
11
|
+
|
12
|
+
def initialize(app, *args)
|
13
|
+
extract_options! args
|
14
|
+
@app = app
|
15
|
+
@policy_chain = opts :policy_chain, []
|
16
|
+
end
|
17
|
+
|
18
|
+
##
|
19
|
+
# Iterates the policy chain and does a check for each policy.
|
20
|
+
# Assumes that policies will raise errors on any policy failure.
|
21
|
+
def call(parcel)
|
22
|
+
policy_chain.each do |policy|
|
23
|
+
policy.check! parcel
|
24
|
+
end
|
25
|
+
app.call parcel
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -1,36 +1,38 @@
|
|
1
|
+
require 'hashie'
|
2
|
+
|
3
|
+
require 'serf/util/options_extraction'
|
1
4
|
require 'serf/util/uuidable'
|
2
5
|
|
3
6
|
module Serf
|
4
7
|
module Middleware
|
5
8
|
|
6
9
|
##
|
7
|
-
# Middleware to add
|
8
|
-
# of the env hash. But it won't overwrite the uuid field
|
9
|
-
# if the incoming request already has it.
|
10
|
+
# Middleware to add uuids to the headers of the parcel hash.
|
10
11
|
#
|
11
12
|
class UuidTagger
|
12
13
|
include Serf::Util::OptionsExtraction
|
13
14
|
|
15
|
+
attr_reader :app
|
14
16
|
attr_reader :uuidable
|
15
17
|
|
16
18
|
##
|
17
19
|
# @param app the app
|
18
|
-
# @options opts [String] :field the ENV field to set with a UUID.
|
19
20
|
#
|
20
21
|
def initialize(app, *args)
|
21
22
|
extract_options! args
|
22
23
|
@app = app
|
23
|
-
@uuidable = opts
|
24
|
+
@uuidable = opts(:uuidable) { Serf::Util::Uuidable.new }
|
24
25
|
end
|
25
26
|
|
26
|
-
def call(
|
27
|
-
|
28
|
-
|
27
|
+
def call(parcel)
|
28
|
+
# Makes sure our parcel has headers
|
29
|
+
parcel[:headers] ||= {}
|
29
30
|
|
30
|
-
|
31
|
-
|
31
|
+
# Tag headers with a UUID unless it already has one
|
32
|
+
parcel[:headers][:uuid] ||= uuidable.create_coded_uuid
|
32
33
|
|
33
|
-
|
34
|
+
# Pass on the given parcel with newly annotated headers
|
35
|
+
app.call parcel
|
34
36
|
end
|
35
37
|
|
36
38
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'hashie'
|
2
|
+
|
3
|
+
require 'serf/util/options_extraction'
|
4
|
+
|
5
|
+
module Serf
|
6
|
+
|
7
|
+
##
|
8
|
+
# Builds Parcels as Hashie::Mash objects with headers and messages.
|
9
|
+
#
|
10
|
+
class ParcelBuilder
|
11
|
+
include Serf::Util::OptionsExtraction
|
12
|
+
|
13
|
+
attr_reader :mash_class
|
14
|
+
|
15
|
+
def initialize(*args)
|
16
|
+
extract_options! args
|
17
|
+
|
18
|
+
@mash_class = opts :mash_class, Hashie::Mash
|
19
|
+
end
|
20
|
+
|
21
|
+
def build(headers=nil, message=nil)
|
22
|
+
# We want to make sure that our headers and message are Mashes.
|
23
|
+
headers = mash_class.new(headers) unless headers.kind_of? mash_class
|
24
|
+
message = mash_class.new(message) unless message.kind_of? mash_class
|
25
|
+
mash_class.new headers: headers, message: message
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
data/lib/serf/serfer.rb
CHANGED
@@ -1,87 +1,48 @@
|
|
1
1
|
require 'hashie'
|
2
2
|
|
3
|
-
require 'serf/
|
4
|
-
require 'serf/
|
5
|
-
require 'serf/util/
|
6
|
-
require 'serf/util/null_object'
|
3
|
+
require 'serf/parcel_builder'
|
4
|
+
require 'serf/util/options_extraction'
|
5
|
+
require 'serf/util/uuidable'
|
7
6
|
|
8
7
|
module Serf
|
9
8
|
|
10
9
|
##
|
11
|
-
# Class to drive the
|
12
|
-
#
|
10
|
+
# Class to drive the Interactor execution.
|
11
|
+
#
|
13
12
|
class Serfer
|
14
|
-
include Serf::Util::
|
13
|
+
include Serf::Util::OptionsExtraction
|
15
14
|
|
16
|
-
attr_reader :
|
17
|
-
attr_reader :
|
18
|
-
attr_reader :
|
19
|
-
attr_reader :logger
|
15
|
+
attr_reader :interactor
|
16
|
+
attr_reader :parcel_builder
|
17
|
+
attr_reader :uuidable
|
20
18
|
|
21
|
-
def initialize(*args)
|
19
|
+
def initialize(interactor, *args)
|
22
20
|
extract_options! args
|
23
21
|
|
24
|
-
|
25
|
-
@
|
26
|
-
|
27
|
-
|
22
|
+
# How to and when to handle requests
|
23
|
+
@interactor = interactor
|
24
|
+
|
25
|
+
# Tunable knobs
|
26
|
+
@parcel_builder = opts(:parcel_builder) { Serf::ParcelBuilder.new }
|
27
|
+
@uuidable = opts(:uuidable) { Serf::Util::Uuidable.new }
|
28
28
|
end
|
29
29
|
|
30
30
|
##
|
31
|
-
# Rack-like call to run
|
31
|
+
# Rack-like call to run the Interactor's use-case.
|
32
32
|
#
|
33
|
-
def call(
|
34
|
-
|
35
|
-
|
36
|
-
# We normalize by making the request a Hashie Mash
|
37
|
-
message = Hashie::Mash.new env.message
|
38
|
-
context = Hashie::Mash.new env.context
|
39
|
-
|
40
|
-
# Resolve the routes that we want to run
|
41
|
-
routes = route_set.resolve message, context
|
42
|
-
|
43
|
-
# We raise an error if no routes were found.
|
44
|
-
raise Serf::Errors::NotFound unless routes.size > 0
|
33
|
+
def call(parcel)
|
34
|
+
headers = parcel[:headers]
|
35
|
+
message = parcel[:message]
|
45
36
|
|
46
|
-
#
|
47
|
-
|
48
|
-
# 1. Check request+context with the policies (RAISE)
|
49
|
-
# 2. Execute command (RETURNS Hash)
|
50
|
-
ok, res = with_error_handling(
|
51
|
-
message: message,
|
52
|
-
options: context) do
|
53
|
-
route.check_policies! message, context
|
54
|
-
route.execute! message, context
|
55
|
-
end
|
56
|
-
# Return the run_results as result of this block.
|
57
|
-
res
|
58
|
-
}.flatten.select { |r| r }
|
59
|
-
push_results results, context
|
60
|
-
return results
|
61
|
-
rescue => e
|
62
|
-
e.extend(Serf::Error)
|
63
|
-
raise e
|
64
|
-
end
|
65
|
-
|
66
|
-
def self.build(*args, &block)
|
67
|
-
new *args, &block
|
68
|
-
end
|
37
|
+
# 1. Execute interactor
|
38
|
+
response_message, response_kind = interactor.call message
|
69
39
|
|
70
|
-
|
40
|
+
# 2. Create the response headers
|
41
|
+
response_headers = uuidable.create_uuids headers
|
42
|
+
response_headers[:kind] = response_kind
|
71
43
|
|
72
|
-
|
73
|
-
|
74
|
-
# Any error in pushing individual messages will result in
|
75
|
-
# a log event and an error channel event.
|
76
|
-
def push_results(results, context)
|
77
|
-
results.each do |result|
|
78
|
-
with_error_handling(result) do
|
79
|
-
response_channel.push(
|
80
|
-
message: result,
|
81
|
-
context: context)
|
82
|
-
end
|
83
|
-
end
|
84
|
-
return nil
|
44
|
+
# 3. Return the response headers and message as a parcel
|
45
|
+
return parcel_builder.build response_headers, response_message
|
85
46
|
end
|
86
47
|
|
87
48
|
end
|
@@ -1,7 +1,3 @@
|
|
1
|
-
require 'active_support/core_ext/string/inflections'
|
2
|
-
|
3
|
-
require 'serf/util/null_object'
|
4
|
-
require 'serf/util/options_extraction'
|
5
1
|
require 'serf/util/protected_call'
|
6
2
|
|
7
3
|
module Serf
|
@@ -9,53 +5,34 @@ module Util
|
|
9
5
|
|
10
6
|
##
|
11
7
|
# Helper module to rescues exceptions from executing blocks of
|
12
|
-
# code, and then
|
8
|
+
# code, and then converts the exception to an "Error Message".
|
13
9
|
#
|
14
|
-
# Including classes may have the following instance variables
|
15
|
-
# to override the default values:
|
16
|
-
# * @logger - ::Serf::Util::NullObject.new
|
17
|
-
# * @error_channel - ::Serf::Util::NullObject.new
|
18
10
|
module ErrorHandling
|
19
|
-
include Serf::Util::OptionsExtraction
|
20
11
|
include Serf::Util::ProtectedCall
|
21
12
|
|
22
13
|
##
|
23
14
|
# A block wrapper to handle errors when executing a block.
|
24
15
|
#
|
25
|
-
def with_error_handling(
|
26
|
-
|
27
|
-
return
|
16
|
+
def with_error_handling(*args, &block)
|
17
|
+
results, err = pcall *args, &block
|
18
|
+
return results, handle_error(err)
|
28
19
|
end
|
29
20
|
|
30
21
|
##
|
31
22
|
# Including classes may override this method to do alternate error
|
32
|
-
# handling. By default, this method will create a new
|
33
|
-
# event and publish it to the error channel. This method will also
|
34
|
-
# log the exception itself to the logger.
|
23
|
+
# handling. By default, this method will create a new error event message.
|
35
24
|
#
|
36
|
-
def handle_error(e
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
error: e.class.to_s
|
25
|
+
def handle_error(e)
|
26
|
+
# no error was passed, so do nothing.
|
27
|
+
return nil unless e
|
28
|
+
|
29
|
+
# Return a simple error event message
|
30
|
+
return {
|
31
|
+
error: e.class.to_s,
|
43
32
|
message: e.message,
|
33
|
+
process_env: ENV.to_hash,
|
44
34
|
backtrace: e.backtrace.join("\n")
|
45
35
|
}
|
46
|
-
|
47
|
-
# log the error to our logger
|
48
|
-
logger.error e
|
49
|
-
|
50
|
-
# log the error event to our error channel.
|
51
|
-
begin
|
52
|
-
error_channel.push error_event
|
53
|
-
rescue => e1
|
54
|
-
logger.error e1
|
55
|
-
end
|
56
|
-
|
57
|
-
# We're done, so just return this error.
|
58
|
-
return error_event
|
59
36
|
end
|
60
37
|
|
61
38
|
end
|
@@ -24,9 +24,9 @@ module Util
|
|
24
24
|
# @return boolean success and the block's (or caught exception) results.
|
25
25
|
#
|
26
26
|
def pcall(*args)
|
27
|
-
return
|
27
|
+
return yield(*args), nil
|
28
28
|
rescue => e
|
29
|
-
return
|
29
|
+
return nil, e
|
30
30
|
end
|
31
31
|
alias_method :protected_call, :pcall
|
32
32
|
|