serf 0.10.0 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- 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/.gitignore
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# Gemfile.lock because this is a library, not a deployable application.
|
2
|
+
Gemfile.lock
|
3
|
+
|
4
|
+
# rcov generated
|
5
|
+
coverage
|
6
|
+
|
7
|
+
# rdoc generated
|
8
|
+
rdoc
|
9
|
+
|
10
|
+
# yard generated
|
11
|
+
doc
|
12
|
+
.yardoc
|
13
|
+
|
14
|
+
# bundler config
|
15
|
+
.bundle
|
16
|
+
|
17
|
+
# bundler installs here if bundle path is set to "vendor"
|
18
|
+
vendor
|
19
|
+
|
20
|
+
# generated builds
|
21
|
+
pkg
|
data/.travis.yml
ADDED
data/Gemfile
CHANGED
@@ -1,35 +1,29 @@
|
|
1
1
|
source 'http://rubygems.org'
|
2
|
-
# Add dependencies required to use your gem here.
|
3
|
-
# Example:
|
4
|
-
# gem 'activesupport', '>= 2.3.5'
|
5
2
|
|
6
|
-
#
|
7
|
-
|
8
|
-
gem 'i18n', '>= 0.6.0' # For ActiveSupport
|
9
|
-
gem 'hashie', ">= 1.2.0"
|
10
|
-
# Used by Serf::Messages::*
|
11
|
-
gem 'uuidtools', '>= 2.1.2'
|
3
|
+
# Runtime dependencies specified in gemspec
|
4
|
+
gemspec
|
12
5
|
|
13
|
-
#
|
14
|
-
# Include everything needed to run rake, tests, features, etc.
|
6
|
+
# Development and Testing dependencies
|
15
7
|
group :development, :test do
|
16
|
-
gem
|
17
|
-
gem
|
18
|
-
gem "bundler", "~> 1.1.3"
|
19
|
-
gem "jeweler", "~> 1.8.3"
|
20
|
-
gem 'simplecov', '>= 0'
|
8
|
+
gem 'bundler'
|
9
|
+
gem 'rake'
|
21
10
|
|
22
|
-
#
|
23
|
-
gem '
|
11
|
+
# Requirements to run our tests and metrics and docs generation
|
12
|
+
gem 'fuubar'
|
13
|
+
gem 'guard'
|
14
|
+
gem 'guard-rspec'
|
15
|
+
gem 'rspec'
|
16
|
+
gem 'yard'
|
17
|
+
gem 'simplecov', require: false
|
24
18
|
|
25
|
-
#
|
26
|
-
|
27
|
-
gem 'msgpack', '>= 0.4.6'
|
28
|
-
#gem 'multi_json', '~> 1.0.3'
|
19
|
+
# Required to support testing
|
20
|
+
gem 'factory_girl'
|
29
21
|
|
30
|
-
#
|
22
|
+
# Required by Guard
|
23
|
+
gem 'rb-inotify', require: false
|
24
|
+
gem 'rb-fsevent', require: false
|
25
|
+
gem 'rb-fchange', require: false
|
31
26
|
|
32
|
-
#
|
33
|
-
gem '
|
34
|
-
gem 'girl_friday', '>= 0.9.7'
|
27
|
+
# Required by our Specs
|
28
|
+
gem 'json-schema'
|
35
29
|
end
|
data/Guardfile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
guard(
|
2
|
+
:rspec,
|
3
|
+
cli: '--format Fuubar --color',
|
4
|
+
all_on_start: true,
|
5
|
+
all_after_pass: false,
|
6
|
+
:version => 2) do
|
7
|
+
|
8
|
+
# Watch our specs
|
9
|
+
watch(%r{^spec/.+_spec\.rb$})
|
10
|
+
|
11
|
+
# Watch our lib directory, and run the matching spec
|
12
|
+
watch(%r{^lib/(.+)\.rb$}) { |m|
|
13
|
+
"spec/#{m[1]}_spec.rb"
|
14
|
+
}
|
15
|
+
|
16
|
+
end
|
data/NOTICE.txt
CHANGED
data/README.md
CHANGED
@@ -1,82 +1,193 @@
|
|
1
1
|
serf
|
2
2
|
====
|
3
3
|
|
4
|
-
|
5
|
-
messages (requests or events) to "Command" handlers.
|
4
|
+
Code your Interactors with policy protection.
|
6
5
|
|
7
|
-
|
8
|
-
|
9
|
-
the Command Query Responsibility Separation pattern.
|
6
|
+
Serf Links
|
7
|
+
----------
|
10
8
|
|
11
|
-
|
9
|
+
* Source: https://github.com/byu/serf
|
10
|
+
* Continuous Integration: https://travis-ci.org/byu/serf
|
11
|
+
* [![Build Status](https://secure.travis-ci.org/byu/serf.png)](http://travis-ci.org/byu/serf)
|
12
|
+
* RubyGems: http://rubygems.org/gems/serf
|
13
|
+
* RubyDocs: http://rubydoc.info/gems/serf
|
14
|
+
|
15
|
+
Interactors
|
16
|
+
-----------
|
17
|
+
|
18
|
+
The piece of work to be done. This takes in a request, represented
|
19
|
+
by a "Message", and returns an "Event" as its result. The Interactor
|
20
|
+
is the "Domain Controller" with respect to performing
|
21
|
+
Domain Layer business logic in coordinating and interacting with
|
22
|
+
the Domain Layer's Entities (Value Objects and Entity Gateways).
|
23
|
+
|
24
|
+
1. Include the "Serf::Interactor" module in your class.
|
25
|
+
2. Implement the 'call(message)' method.
|
26
|
+
3. Return the tuple: (message, kind)
|
27
|
+
a. Hashie::Mash is recommended for the message, nil is acceptable
|
28
|
+
b. The kind is the string representation of the message type,
|
29
|
+
It is optional.
|
30
|
+
|
31
|
+
Example:
|
32
|
+
|
33
|
+
require 'hashie'
|
34
|
+
|
35
|
+
class MyInteractor
|
36
|
+
|
37
|
+
def initialize(*contructor_params, &block)
|
38
|
+
# Do some validation here, or extra parameter setting with the args
|
39
|
+
@model = opts :model, MyModel
|
40
|
+
end
|
41
|
+
|
42
|
+
def call(message)
|
43
|
+
# Do something w/ the message and opts.
|
44
|
+
# Simple data structures for the Interactor's "Request".
|
45
|
+
|
46
|
+
item = @model.find message.model_id
|
47
|
+
|
48
|
+
# Make a simple data structure as the Interactor "Response".
|
49
|
+
response = Hashie::Mash.new
|
50
|
+
response.item = item
|
51
|
+
# Return the response 'kind' and the response data.
|
52
|
+
return response, 'my_app/events/did_something'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
Parcels
|
58
|
+
-------
|
59
|
+
|
60
|
+
A Parcel is just the package of Headers and Message. Serf's convention
|
61
|
+
represents requests and responses as (mostly) just Plain Old Hash Objects
|
62
|
+
(POHO as opposed to PORO) over the Boundaries (see Architecture Lost Years).
|
63
|
+
This simplifies marshalling over the network. It also gives us easier
|
64
|
+
semantics in defining Request and Responses without need of extra classes,
|
65
|
+
code, etc.
|
66
|
+
|
67
|
+
The Parcel in Ruby (Datastructure) is represented simply as:
|
68
|
+
|
69
|
+
* A 2 element Hash: { headers: headers, message: message}.
|
70
|
+
|
71
|
+
NOTE: Hashie::Mash is *Awesome*. (https://github.com/intridea/hashie)
|
72
|
+
NOTE: Serf passes the parcel as frozen Hashie::Mash instances
|
73
|
+
to Interactor' call method by default.
|
74
|
+
|
75
|
+
*Messages* are the representation of a Business Request or Business Event.
|
76
|
+
|
77
|
+
In the parcel, the message is the business data. It specifies what
|
78
|
+
business work needs to be done, or what business was done.
|
79
|
+
Everything that an Interactor needs to execute its Use Case SHOULD
|
80
|
+
be in the message.
|
81
|
+
|
82
|
+
RECOMMENDED: Use JSON Schema to validate the structure of a message.
|
83
|
+
https://github.com/hoxworth/json-schema
|
84
|
+
This can be implemented in the 'Policy' chain.
|
85
|
+
|
86
|
+
*Headers* are the processing meta data that is associated with a Message.
|
87
|
+
|
88
|
+
Headers provide information that would assist in processing, tracking
|
89
|
+
a Message. But does not provide business relevant information to
|
90
|
+
the Request or Event Message.
|
91
|
+
|
92
|
+
RECOMMENDED: Recommended to be placed in headers is the 'kind' field.
|
93
|
+
|
94
|
+
The "kind" field identifies the ontological meaning of the message, which
|
95
|
+
may be used to route messages over messaging channels to Interactors.
|
96
|
+
The convention is 'mymodule/requests/my_business_request' for Requests,
|
97
|
+
and 'mymodule/events/my_business_event' for Events.
|
98
|
+
|
99
|
+
Examples are:
|
100
|
+
* UUIDs to track request and events, providing a sequential order of
|
101
|
+
execution of commands. (Already Implemented by Serf).
|
102
|
+
* Current User that sent the request. For authentication and authorization.
|
103
|
+
* Host and Application Server that is processing this request.
|
104
|
+
|
105
|
+
Generally, the header information is populated only by the infrastructure
|
106
|
+
that hosts the Interactors. The Interactors themselves do not
|
107
|
+
return any headers in the response. The Interactors are tasked to provide
|
108
|
+
only business relevant data in the Event messages they return.
|
109
|
+
|
110
|
+
|
111
|
+
Policies
|
112
|
+
--------
|
113
|
+
|
114
|
+
Serf implements Policy Chains to validate, check the incoming Parcels before
|
115
|
+
actually executing Interactors.
|
116
|
+
|
117
|
+
Example Benefits:
|
118
|
+
* Authorization to execute Command.
|
119
|
+
* Validation of Message schema
|
120
|
+
|
121
|
+
Policies only need to implement a single method:
|
122
|
+
|
123
|
+
def check!(parcel)
|
124
|
+
raise 'Failure' # To fail the policy, raise an error.
|
125
|
+
end
|
126
|
+
|
127
|
+
RECOMMENDED: Use `Serf::Errors::PolicyFailure` error type.
|
128
|
+
|
129
|
+
|
130
|
+
References
|
12
131
|
==========
|
13
132
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
custom factories.
|
73
|
-
4. Handler methods SHOULD return zero or more messages.
|
74
|
-
a. Raised errors are caught and pushed to error channels.
|
75
|
-
b. Returned messages MUST be Hash based objects for Serialization.
|
76
|
-
5. Handler methods SHOULD handle catch their business logic exceptions and
|
77
|
-
return them as specialized messages that can be forwarded down error channels.
|
78
|
-
Uncaught exceptions that are then caught by Serf are pushed as
|
79
|
-
generic CaughtExceptionEvents, and are harder to deal with.
|
133
|
+
Keynote: Architecture the Lost Years, by Robert Martin
|
134
|
+
* http://confreaks.com/videos/759
|
135
|
+
* http://vimeo.com/43612849
|
136
|
+
|
137
|
+
Domain Driven Design by Eric Evans:
|
138
|
+
* http://books.google.com/books?id=7dlaMs0SECsC&dq=domain+driven+design
|
139
|
+
|
140
|
+
Patterns of Enterprise Application Architecture by Martin Fowler
|
141
|
+
* http://martinfowler.com/books/eip.html
|
142
|
+
* Command (Unit of Work) Pattern
|
143
|
+
* Event Sourcing
|
144
|
+
|
145
|
+
Enterprise Integration Patterns by Hohpe and Woolf
|
146
|
+
* http://www.eaipatterns.com/
|
147
|
+
|
148
|
+
DDD for Rails Developers Series:
|
149
|
+
* http://rubysource.com/ddd-for-rails-developers-part-1-layered-architecture/
|
150
|
+
* http://rubysource.com/ddd-for-rails-developers-part-2-entities-and-values/
|
151
|
+
* http://rubysource.com/ddd-for-rails-developers-part-3-aggregates/
|
152
|
+
|
153
|
+
DCI in Ruby
|
154
|
+
* Maybe use DCI to better manage business logic in Entities.
|
155
|
+
* http://mikepackdev.com/blog_posts/24-the-right-way-to-code-dci-in-ruby
|
156
|
+
* http://mikepackdev.com/blog_posts/35-dci-with-ruby-refinements
|
157
|
+
* http://nicksda.apotomo.de/2011/12/ruby-on-rest-2-representers-and-the-dci-pattern/
|
158
|
+
|
159
|
+
CQRS
|
160
|
+
* http://www.udidahan.com/2009/12/09/clarified-cqrs/
|
161
|
+
* http://elegantcode.com/2009/11/11/cqrs-la-greg-young/
|
162
|
+
* http://elegantcode.com/2009/11/20/cqrs-the-domain-events/
|
163
|
+
|
164
|
+
Life beyond Distributed Transactions: an Apostate’s Opinion by Pat Helland
|
165
|
+
* http://www.ics.uci.edu/~cs223/papers/cidr07p15.pdf
|
166
|
+
|
167
|
+
Building on Quicksand by Pat Helland
|
168
|
+
* http://arxiv.org/ftp/arxiv/papers/0909/0909.1788.pdf
|
169
|
+
|
170
|
+
The Domain Layer (from DDD):
|
171
|
+
|
172
|
+
1. Entities (Model Entities)- What your application is.
|
173
|
+
* Also remember Value Objects.
|
174
|
+
* How your Domain Model is structured, but NOT necessarily tied to the
|
175
|
+
underlying storage infrastructure.
|
176
|
+
* http://rubysource.com/ddd-for-rails-developers-part-2-entities-and-values/
|
177
|
+
2. Domain Controllers (Interactors) - What your application does.
|
178
|
+
* The business logic of coordinating different entities.
|
179
|
+
Different than a Rails controller.
|
180
|
+
* Keynote: Architecture The Lost Years
|
181
|
+
Robert Martin
|
182
|
+
Ruby Midwest 2011
|
183
|
+
http://confreaks.com/videos/759
|
184
|
+
* Your Rails Application is Missing a Domain Controller
|
185
|
+
Nicholas Henry
|
186
|
+
http://blog.firsthand.ca/2011/12/your-rails-application-is-missing.html
|
187
|
+
3. There is a balancing game of what business logic code lives in an
|
188
|
+
Entity vs a Domain Controller... Do what works for you.
|
189
|
+
But mostly follow "Use Cases" in Domain Controllers,
|
190
|
+
and "Application Agnostic Logic" in Entities.
|
80
191
|
|
81
192
|
|
82
193
|
Example
|
@@ -87,170 +198,75 @@ Example
|
|
87
198
|
require 'yell'
|
88
199
|
|
89
200
|
require 'serf/builder'
|
90
|
-
require 'serf/command'
|
91
|
-
require 'serf/middleware/uuid_tagger'
|
92
|
-
require 'serf/util/options_extraction'
|
93
201
|
|
94
202
|
# create a simple logger for this example
|
95
|
-
my_logger = Yell.new
|
96
|
-
l.level = :debug
|
97
|
-
l.adapter :datefile, 'my_production.log', :level => [:debug, :info, :warn]
|
98
|
-
l.adapter :datefile, 'my_error.log', :level => Yell.level.gte(:error)
|
99
|
-
end
|
100
|
-
|
101
|
-
# Helper class for this example to receive result or error messages
|
102
|
-
# and pipe it into our logger.
|
103
|
-
class MyChannel
|
104
|
-
def initialize(logger, error=false)
|
105
|
-
@logger = logger
|
106
|
-
@error = error
|
107
|
-
end
|
108
|
-
def push(message)
|
109
|
-
if @error
|
110
|
-
@logger.fatal "ERROR CHANNEL: #{message.to_json}"
|
111
|
-
else
|
112
|
-
@logger.debug "RESP CHANNEL: #{message.to_json}"
|
113
|
-
end
|
114
|
-
end
|
115
|
-
end
|
203
|
+
my_logger = Yell.new STDOUT
|
116
204
|
|
117
205
|
# my_lib/my_policy.rb
|
118
206
|
class MyPolicy
|
119
207
|
|
120
|
-
def check!(
|
121
|
-
raise '
|
122
|
-
end
|
123
|
-
|
124
|
-
def self.build(*args, &block)
|
125
|
-
new *args, &block
|
208
|
+
def check!(parcel)
|
209
|
+
raise 'Policy Error: User is nil' unless parcel.headers.user
|
126
210
|
end
|
127
211
|
|
128
212
|
end
|
129
213
|
|
130
|
-
# my_lib/
|
131
|
-
class
|
132
|
-
include Serf::Command
|
133
|
-
|
134
|
-
attr_reader :name
|
135
|
-
attr_reader :do_raise
|
214
|
+
# my_lib/my_interactor.rb
|
215
|
+
class MyInteractor
|
136
216
|
|
137
|
-
def
|
138
|
-
|
139
|
-
@name = opts! :name
|
140
|
-
@do_raise = opts :raises, false
|
141
|
-
end
|
142
|
-
|
143
|
-
def call(request, context)
|
144
|
-
# Just our name to sort things out
|
217
|
+
def call(message)
|
218
|
+
raise 'Error' if message.raise_an_error
|
145
219
|
|
146
|
-
|
220
|
+
# And return a message as result. Nil is valid response.
|
221
|
+
return { success: true }, 'my_lib/events/success_event'
|
147
222
|
|
148
|
-
#
|
149
|
-
#
|
150
|
-
return { kind: "#{name}_result", input: request }
|
223
|
+
# Optionally just return the message w/o a tagged kind
|
224
|
+
#return { success: true }
|
151
225
|
end
|
152
226
|
|
153
227
|
end
|
154
228
|
|
155
229
|
# Create a new builder for this serf app.
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
logger my_logger
|
168
|
-
|
169
|
-
# Here, we define a route.
|
170
|
-
# We are matching the kind for 'my_message', and we have the MyPolicy
|
171
|
-
# to filter for this route.
|
172
|
-
match 'my_message'
|
173
|
-
policy MyPolicy
|
174
|
-
run MyOverloadedCommand, name: 'my_message_command'
|
175
|
-
|
176
|
-
match 'other_message'
|
177
|
-
run MyOverloadedCommand, name: 'raises_error', raises: true
|
178
|
-
run MyOverloadedCommand, name: 'good_other_handler'
|
179
|
-
|
180
|
-
match /^events\/.*$/
|
181
|
-
run MyOverloadedCommand, name: 'regexp_matched_command'
|
182
|
-
end
|
183
|
-
app = builder.to_app
|
184
|
-
|
185
|
-
# This will submit a 'my_message' message (as a hash) to the Serf App.
|
186
|
-
# NOTE: We should get an error message pushed to the error channel
|
187
|
-
# because no 'data' field was put in my_message as required
|
188
|
-
# And the Result should have a CaughtExceptionEvent.
|
189
|
-
my_logger.info 'Call 1: Start'
|
190
|
-
results = app.call(
|
191
|
-
message: {
|
192
|
-
kind: 'my_message'
|
193
|
-
},
|
194
|
-
context: nil)
|
195
|
-
my_logger.info "Call 1: #{results.size} #{results.to_json}"
|
230
|
+
app = Serf::Builder.new(
|
231
|
+
interactor: MyInteractor.new,
|
232
|
+
policy_chain: [
|
233
|
+
MyPolicy.new
|
234
|
+
]).to_app
|
235
|
+
|
236
|
+
# This will submit a 'my_message' message (as a hash) to Serfer.
|
237
|
+
# Missing data field will raise an error within the interactor, which
|
238
|
+
# will be caught by the serfer.
|
239
|
+
results = app.call nil
|
240
|
+
my_logger.info "Call 1: #{results.to_json}"
|
196
241
|
|
197
242
|
# Here is good result
|
198
|
-
my_logger.info 'Call 2: Start'
|
199
243
|
results = app.call(
|
200
|
-
|
201
|
-
|
202
|
-
data: '2'
|
244
|
+
headers: {
|
245
|
+
user: 'user_info_1'
|
203
246
|
},
|
204
|
-
context: nil)
|
205
|
-
my_logger.info "Call 2: #{results.size} #{results.to_json}"
|
206
|
-
|
207
|
-
# We should get two event messages in the results because we
|
208
|
-
# mounted two commands to the other_message kind.
|
209
|
-
my_logger.info 'Call 3: Start'
|
210
|
-
results = app.call(
|
211
247
|
message: {
|
212
|
-
|
213
|
-
|
214
|
-
},
|
215
|
-
context: nil)
|
216
|
-
my_logger.info "Call 3: #{results.size} #{results.to_json}"
|
248
|
+
})
|
249
|
+
my_logger.info "Call 2: #{results.to_json}"
|
217
250
|
|
218
|
-
#
|
219
|
-
my_logger.info 'Call 4: Start'
|
251
|
+
# Here get an error that was raised from the interactor
|
220
252
|
results = app.call(
|
221
|
-
|
222
|
-
|
223
|
-
data: '4'
|
253
|
+
headers: {
|
254
|
+
user: 'user_info_1'
|
224
255
|
},
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
# Here, we're going to submit a message that we don't handle.
|
230
|
-
# By default, an exception will be raised.
|
231
|
-
my_logger.info 'Call 5: Start'
|
232
|
-
app.call(
|
233
|
-
message: {
|
234
|
-
kind: 'unhandled_message_kind'
|
235
|
-
},
|
236
|
-
context: nil)
|
237
|
-
my_logger.fatal 'OOOPS: Should not get here'
|
238
|
-
rescue => e
|
239
|
-
my_logger.info "Call 5: Caught in main: #{e.inspect}"
|
240
|
-
end
|
241
|
-
|
256
|
+
message: {
|
257
|
+
raise_an_error: true
|
258
|
+
})
|
259
|
+
my_logger.info "Call 3: #{results.to_json}"
|
242
260
|
|
243
|
-
Contributing to serf
|
244
|
-
====================
|
245
261
|
|
246
|
-
|
247
|
-
|
248
|
-
* Fork the project
|
249
|
-
* Start a feature/bugfix branch
|
250
|
-
* Commit and push until you are happy with your contribution
|
251
|
-
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
252
|
-
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
262
|
+
Contributing
|
263
|
+
============
|
253
264
|
|
265
|
+
1. Fork it
|
266
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
267
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
268
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
269
|
+
5. Create new Pull Request
|
254
270
|
|
255
271
|
Copyright
|
256
272
|
=========
|
data/Rakefile
CHANGED
@@ -1,7 +1,8 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
#!/usr/bin/env rake
|
3
2
|
require 'rubygems'
|
4
3
|
require 'bundler'
|
4
|
+
require "bundler/gem_tasks"
|
5
|
+
|
5
6
|
begin
|
6
7
|
Bundler.setup(:default, :development)
|
7
8
|
rescue Bundler::BundlerError => e
|
@@ -11,22 +12,6 @@ rescue Bundler::BundlerError => e
|
|
11
12
|
end
|
12
13
|
require 'rake'
|
13
14
|
|
14
|
-
require 'jeweler'
|
15
|
-
require './lib/serf/version.rb'
|
16
|
-
Jeweler::Tasks.new do |gem|
|
17
|
-
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
18
|
-
gem.name = "serf"
|
19
|
-
gem.homepage = "http://github.com/byu/serf"
|
20
|
-
gem.license = "Apache 2.0"
|
21
|
-
gem.summary = %Q{Event-Driven SOA with CQRS}
|
22
|
-
gem.description = %Q{Event-Driven SOA with CQRS}
|
23
|
-
gem.email = "benjaminlyu@gmail.com"
|
24
|
-
gem.authors = ["Benjamin Yu"]
|
25
|
-
gem.version = Serf::Version::STRING
|
26
|
-
# dependencies defined in Gemfile
|
27
|
-
end
|
28
|
-
Jeweler::RubygemsDotOrgTasks.new
|
29
|
-
|
30
15
|
require 'rspec/core'
|
31
16
|
require 'rspec/core/rake_task'
|
32
17
|
RSpec::Core::RakeTask.new(:spec) do |spec|
|