startback 0.11.5 → 0.12.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/startback/context/middleware.rb +5 -9
- data/lib/startback/context.rb +7 -0
- data/lib/startback/event/agent.rb +76 -0
- data/lib/startback/event/bus/bunny/async.rb +162 -0
- data/lib/startback/{bus → event/bus}/bunny.rb +0 -0
- data/lib/startback/event/bus/memory/async.rb +45 -0
- data/lib/startback/event/bus/memory/sync.rb +35 -0
- data/lib/startback/{bus → event/bus}/memory.rb +0 -0
- data/lib/startback/event/bus.rb +100 -0
- data/lib/startback/event/engine.rb +132 -56
- data/lib/startback/event/ext/context.rb +5 -0
- data/lib/startback/event/ext/operation.rb +13 -0
- data/lib/startback/event.rb +7 -7
- data/lib/startback/support/robustness.rb +0 -2
- data/lib/startback/version.rb +2 -2
- data/spec/spec_helper.rb +26 -1
- data/spec/unit/context/test_dup.rb +3 -7
- data/spec/unit/context/test_fork.rb +38 -0
- data/spec/unit/context/test_h_factory.rb +0 -20
- data/spec/unit/context/test_middleware.rb +6 -8
- data/spec/unit/event/bus/memory/test_async.rb +43 -0
- data/spec/unit/event/bus/memory/test_sync.rb +43 -0
- data/spec/unit/test_event.rb +12 -19
- data/spec/unit/web/test_catch_all.rb +1 -1
- metadata +13 -10
- data/lib/startback/bus/bunny/async.rb +0 -123
- data/lib/startback/bus/memory/async.rb +0 -40
- data/lib/startback/bus/memory/sync.rb +0 -30
- data/lib/startback/bus.rb +0 -94
- data/spec/unit/bus/memory/test_async.rb +0 -41
- data/spec/unit/bus/memory/test_sync.rb +0 -41
@@ -4,10 +4,10 @@ require 'startback'
|
|
4
4
|
module Startback
|
5
5
|
class Event
|
6
6
|
#
|
7
|
-
# This class
|
8
|
-
# It
|
9
|
-
#
|
10
|
-
#
|
7
|
+
# This class is the starting point of event handling in
|
8
|
+
# Startback. It holds a Bus instance to which emitters
|
9
|
+
# and listeners can connect, and the possibility for the
|
10
|
+
# the listening part to start an infinite loop (ServerEngine).
|
11
11
|
#
|
12
12
|
# The Engine automatically runs a Webrick small webapp
|
13
13
|
# with a /healthcheck webservice. The class can be extended
|
@@ -15,86 +15,162 @@ module Startback
|
|
15
15
|
# checks.
|
16
16
|
#
|
17
17
|
# This class goes hand in hand with the `startback:engine`
|
18
|
-
# docker image.
|
18
|
+
# docker image. It can be extended by subclasses to override
|
19
|
+
# the following methods:
|
19
20
|
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
# # engine.rb
|
26
|
-
# require 'startback/event/engine'
|
27
|
-
# Startback::Event::Engine.run
|
21
|
+
# - bus to use something else than a simple memory bus
|
22
|
+
# - on_health_check to check specific health conditions
|
23
|
+
# - create_agents to instantiate all listening agents
|
24
|
+
# (unless auto_create_agents is used)
|
28
25
|
#
|
29
26
|
class Engine
|
27
|
+
include Support::Robustness
|
30
28
|
|
31
29
|
DEFAULT_OPTIONS = {
|
32
|
-
|
33
|
-
|
34
|
-
|
30
|
+
|
31
|
+
# To be passed to ServerEngine
|
32
|
+
server_engine: {}
|
33
|
+
|
35
34
|
}
|
36
35
|
|
37
|
-
def initialize
|
38
|
-
|
36
|
+
def initialize(options = {}, context = Context.new)
|
37
|
+
@options = DEFAULT_OPTIONS.merge(options)
|
38
|
+
@context = context
|
39
|
+
@context.engine = self
|
40
|
+
end
|
41
|
+
attr_reader :options, :context
|
42
|
+
|
43
|
+
class << self
|
44
|
+
def auto_create_agents?
|
45
|
+
!!@auto_create_agents
|
46
|
+
end
|
47
|
+
|
48
|
+
# Register a base class which will be used to discover
|
49
|
+
# the agents to start when the engine is ran.
|
50
|
+
def auto_create_agents(base_class = nil)
|
51
|
+
@auto_create_agents ||= base_class
|
52
|
+
@auto_create_agents
|
53
|
+
end
|
39
54
|
end
|
40
55
|
|
56
|
+
# This method is executed on health check and can be
|
57
|
+
# overriden by subclasses to perform specific checks.
|
41
58
|
def on_health_check
|
42
59
|
"Ok"
|
43
60
|
end
|
44
61
|
|
62
|
+
def bus
|
63
|
+
::Startback::Event::Bus.new
|
64
|
+
end
|
65
|
+
|
66
|
+
def connect
|
67
|
+
log(:info, self, "Connecting to the bus now!")
|
68
|
+
bus.connect
|
69
|
+
end
|
70
|
+
|
45
71
|
def run(options = {})
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
se
|
72
|
+
connect
|
73
|
+
|
74
|
+
log(:info, self, "Running agents and server engine!")
|
75
|
+
create_agents
|
76
|
+
Runner.new(self, options[:server_engine] || {}).run
|
52
77
|
end
|
53
78
|
|
54
|
-
|
55
|
-
|
56
|
-
|
79
|
+
def create_agents
|
80
|
+
return unless parent = self.class.auto_create_agents
|
81
|
+
|
82
|
+
ObjectSpace
|
83
|
+
.each_object(Class)
|
84
|
+
.select { |klass| klass < parent }
|
85
|
+
.each { |klass| klass.new(self) }
|
86
|
+
end
|
87
|
+
|
88
|
+
def factor_event(event_data)
|
89
|
+
Event.json(event_data, context)
|
90
|
+
end
|
91
|
+
|
92
|
+
class Runner
|
93
|
+
|
94
|
+
DEFAULT_SERVER_ENGINE_OPTIONS = {
|
95
|
+
daemonize: false,
|
96
|
+
worker_type: 'process',
|
97
|
+
workers: 1
|
98
|
+
}
|
99
|
+
|
100
|
+
def initialize(engine, options = {})
|
101
|
+
raise ArgumentError if engine.nil?
|
102
|
+
|
103
|
+
@engine = engine
|
104
|
+
@options = DEFAULT_SERVER_ENGINE_OPTIONS.merge(options)
|
105
|
+
require 'serverengine'
|
57
106
|
end
|
107
|
+
attr_reader :engine, :options
|
58
108
|
|
59
|
-
def
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
run(health)
|
66
|
-
end
|
67
|
-
end
|
109
|
+
def run(options = {})
|
110
|
+
health = self.class.build_health_check(engine)
|
111
|
+
worker = self.class.build_worker(engine, health)
|
112
|
+
se = ServerEngine.create(nil, worker, options)
|
113
|
+
se.run
|
114
|
+
se
|
68
115
|
end
|
69
116
|
|
70
|
-
|
71
|
-
|
72
|
-
|
117
|
+
class << self
|
118
|
+
def run(*args, &bl)
|
119
|
+
new.run(*args, &bl)
|
120
|
+
end
|
73
121
|
|
74
|
-
|
75
|
-
|
122
|
+
def build_health_check(engine)
|
123
|
+
Rack::Builder.new do
|
124
|
+
map '/health-check' do
|
125
|
+
health = Startback::Web::HealthCheck.new {
|
126
|
+
engine.on_health_check
|
127
|
+
}
|
128
|
+
run(health)
|
129
|
+
end
|
76
130
|
end
|
131
|
+
end
|
77
132
|
|
78
|
-
|
79
|
-
|
80
|
-
|
133
|
+
def build_worker(engine, health)
|
134
|
+
Module.new do
|
135
|
+
include Support::Env
|
81
136
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
137
|
+
def initialize
|
138
|
+
@stop_flag = ServerEngine::BlockingFlag.new
|
139
|
+
end
|
140
|
+
|
141
|
+
define_method(:health) do
|
142
|
+
health
|
143
|
+
end
|
144
|
+
|
145
|
+
define_method(:engine) do
|
146
|
+
engine
|
88
147
|
end
|
89
|
-
end
|
90
148
|
|
91
|
-
|
92
|
-
|
93
|
-
|
149
|
+
def run
|
150
|
+
ran = false
|
151
|
+
until @stop_flag.set?
|
152
|
+
if ran
|
153
|
+
engine.send(:log, :warn, engine, "Restarting internal loop")
|
154
|
+
else
|
155
|
+
engine.send(:log, :info, engine, "Starting internal loop")
|
156
|
+
end
|
157
|
+
Rack::Handler::WEBrick.run(health, {
|
158
|
+
:Port => env('STARTBACK_ENGINE_PORT', '3000').to_i,
|
159
|
+
:Host => env('STARTBACK_ENGINE_LISTEN', '0.0.0.0')
|
160
|
+
})
|
161
|
+
ran = true
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def stop
|
166
|
+
engine.send(:log, :info, engine, "Stopping internal loop")
|
167
|
+
@stop_flag.set!
|
168
|
+
Rack::Handler::WEBrick.shutdown
|
169
|
+
end
|
94
170
|
end
|
95
171
|
end
|
96
|
-
end
|
97
|
-
end # class
|
172
|
+
end # class << self
|
173
|
+
end # class Runner
|
98
174
|
end # class Engine
|
99
175
|
end # class Event
|
100
176
|
end # module Startback
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Startback
|
2
|
+
class Operation
|
3
|
+
|
4
|
+
def self.emits(type, &bl)
|
5
|
+
after_call do
|
6
|
+
event_data = instance_exec(&bl)
|
7
|
+
event = type.new(type.to_s, event_data, context)
|
8
|
+
context.engine.bus.emit(event)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
end # class Operation
|
13
|
+
end # module Startback
|
data/lib/startback/event.rb
CHANGED
@@ -20,14 +20,11 @@ module Startback
|
|
20
20
|
end
|
21
21
|
attr_reader :context, :type, :data
|
22
22
|
|
23
|
-
def self.json(src,
|
23
|
+
def self.json(src, context)
|
24
24
|
parsed = JSON.parse(src)
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
world[:context_factory].call(parsed)
|
29
|
-
end
|
30
|
-
Event.new(parsed['type'], parsed['data'], context)
|
25
|
+
klass = Kernel.const_get(parsed['type'])
|
26
|
+
context = context.fork(parsed['context']) if context
|
27
|
+
klass.new(parsed['type'], parsed['data'], context)
|
31
28
|
end
|
32
29
|
|
33
30
|
def to_json(*args, &bl)
|
@@ -41,5 +38,8 @@ module Startback
|
|
41
38
|
|
42
39
|
end # class Event
|
43
40
|
end # module Startback
|
41
|
+
require_relative 'event/ext/context'
|
42
|
+
require_relative 'event/ext/operation'
|
44
43
|
require_relative 'event/agent'
|
44
|
+
require_relative 'event/bus'
|
45
45
|
require_relative 'event/engine'
|
data/lib/startback/version.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
2
2
|
require 'startback'
|
3
|
-
require 'startback/
|
3
|
+
require 'startback/event'
|
4
4
|
require 'startback/support/fake_logger'
|
5
5
|
require 'rack/test'
|
6
6
|
|
@@ -10,3 +10,28 @@ end
|
|
10
10
|
RSpec.configure do |c|
|
11
11
|
c.include SpecHelpers
|
12
12
|
end
|
13
|
+
|
14
|
+
class SubContext < Startback::Context
|
15
|
+
attr_accessor :foo
|
16
|
+
h_factory do |c,h|
|
17
|
+
c.foo = h["foo"]
|
18
|
+
end
|
19
|
+
h_dump do |h|
|
20
|
+
h.merge!("foo" => foo)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class SubContext
|
25
|
+
attr_accessor :bar
|
26
|
+
h_factory do |c,h|
|
27
|
+
c.bar = h["bar"]
|
28
|
+
end
|
29
|
+
h_dump do |h|
|
30
|
+
h.merge!("bar" => bar)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class User
|
35
|
+
class Changed < Startback::Event
|
36
|
+
end
|
37
|
+
end
|
@@ -3,12 +3,8 @@ require 'spec_helper'
|
|
3
3
|
module Startback
|
4
4
|
describe Context, "dup" do
|
5
5
|
|
6
|
-
class Subcontext < Context
|
7
|
-
attr_accessor :foo
|
8
|
-
end
|
9
|
-
|
10
6
|
let(:context) {
|
11
|
-
|
7
|
+
SubContext.new.tap{|s| s.foo = "bar" }
|
12
8
|
}
|
13
9
|
|
14
10
|
class ContextRelatedAbstraction
|
@@ -27,7 +23,7 @@ module Startback
|
|
27
23
|
expect(x).not_to be(context)
|
28
24
|
}
|
29
25
|
expect(seen).to be(got)
|
30
|
-
expect(got).to be_a(
|
26
|
+
expect(got).to be_a(SubContext)
|
31
27
|
expect(got).not_to be(context)
|
32
28
|
expect(got.foo).to eql("bar")
|
33
29
|
end
|
@@ -43,4 +39,4 @@ module Startback
|
|
43
39
|
end
|
44
40
|
|
45
41
|
end
|
46
|
-
end
|
42
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Startback
|
4
|
+
describe Context, "fork" do
|
5
|
+
|
6
|
+
it 'is a simple dup without args' do
|
7
|
+
context = SubContext.new
|
8
|
+
context.foo = ['hello']
|
9
|
+
|
10
|
+
forked = context.fork
|
11
|
+
puts "Forked: #{forked.inspect}"
|
12
|
+
expect(fork).not_to be(context)
|
13
|
+
expect(forked.foo).to eql(['hello'])
|
14
|
+
expect(forked.foo).to be(context.foo)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'yields the context if a block is provided' do
|
18
|
+
context = SubContext.new
|
19
|
+
|
20
|
+
seen = false
|
21
|
+
context.fork({ 'foo' => 'hello' }) do |forked|
|
22
|
+
expect(fork).not_to be(context)
|
23
|
+
expect(forked.foo).to eql('hello')
|
24
|
+
seen = true
|
25
|
+
end
|
26
|
+
expect(seen).to eql(true)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'uses the factory on the hash provided' do
|
30
|
+
context = SubContext.new
|
31
|
+
|
32
|
+
forked = context.fork({ 'foo' => 'hello' })
|
33
|
+
expect(fork).not_to be(context)
|
34
|
+
expect(forked.foo).to eql('hello')
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
@@ -7,26 +7,6 @@ module Startback
|
|
7
7
|
expect(Context.new.to_json).to eql("{}")
|
8
8
|
end
|
9
9
|
|
10
|
-
class SubContext < Context
|
11
|
-
attr_accessor :foo
|
12
|
-
h_factory do |c,h|
|
13
|
-
c.foo = h["foo"]
|
14
|
-
end
|
15
|
-
h_dump do |h|
|
16
|
-
h.merge!("foo" => foo)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
class SubContext
|
21
|
-
attr_accessor :bar
|
22
|
-
h_factory do |c,h|
|
23
|
-
c.bar = h["bar"]
|
24
|
-
end
|
25
|
-
h_dump do |h|
|
26
|
-
h.merge!("bar" => bar)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
10
|
it 'allows installing factories' do
|
31
11
|
expect(Context.h_factories).to be_empty
|
32
12
|
expect(SubContext.h_factories.size).to eql(2)
|
@@ -10,18 +10,18 @@ module Startback
|
|
10
10
|
include Rack::Test::Methods
|
11
11
|
|
12
12
|
def app
|
13
|
-
|
13
|
+
build_args = self.build_args
|
14
14
|
Rack::Builder.new do
|
15
|
-
use Middleware,
|
15
|
+
use Middleware, *build_args
|
16
16
|
run ->(env){
|
17
|
-
ctx = env[Startback::Context::Middleware::RACK_ENV_KEY]
|
17
|
+
ctx = env[Startback::Context::Middleware::RACK_ENV_KEY]
|
18
18
|
[200, {}, ctx.class.to_s]
|
19
19
|
}
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
-
context 'when used without
|
24
|
-
let(:
|
23
|
+
context 'when used without context' do
|
24
|
+
let(:build_args){ [] }
|
25
25
|
|
26
26
|
it 'sets the default context class' do
|
27
27
|
get '/'
|
@@ -31,9 +31,7 @@ module Startback
|
|
31
31
|
end
|
32
32
|
|
33
33
|
context 'when specifying the context class' do
|
34
|
-
let(:
|
35
|
-
context_class: MyContextSubClass
|
36
|
-
}}
|
34
|
+
let(:build_args){ [MyContextSubClass.new] }
|
37
35
|
|
38
36
|
it 'sets the default context class' do
|
39
37
|
get '/'
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
module Startback
|
3
|
+
class Event
|
4
|
+
describe Bus::Memory do
|
5
|
+
|
6
|
+
subject{
|
7
|
+
Bus::Memory::Async.new
|
8
|
+
}
|
9
|
+
|
10
|
+
it 'allows emiting an receiving' do
|
11
|
+
seen = nil
|
12
|
+
subject.listen("user_changed") do |evt|
|
13
|
+
seen = evt
|
14
|
+
end
|
15
|
+
subject.emit(Event.new("user_changed", {id: 12}))
|
16
|
+
expect(seen).to be_a(Event)
|
17
|
+
expect(seen.type).to eql("user_changed")
|
18
|
+
expect(seen.data.to_h).to eql({id: 12})
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'allows mixin Symbol vs. String for event type' do
|
22
|
+
seen = nil
|
23
|
+
subject.listen(:user_changed) do |evt|
|
24
|
+
seen = evt
|
25
|
+
end
|
26
|
+
subject.emit(Event.new(:user_changed, {id: 12}))
|
27
|
+
expect(seen).to be_a(Event)
|
28
|
+
expect(seen.type).to eql("user_changed")
|
29
|
+
expect(seen.data.to_h).to eql({id: 12})
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'does not raise errors synchronously' do
|
33
|
+
subject.listen("user_changed") do |evt|
|
34
|
+
raise "An error occured"
|
35
|
+
end
|
36
|
+
expect {
|
37
|
+
subject.emit(Event.new("user_changed", {id: 12}))
|
38
|
+
}.not_to raise_error
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
module Startback
|
3
|
+
class Event
|
4
|
+
describe Bus::Memory do
|
5
|
+
|
6
|
+
subject{
|
7
|
+
Bus::Memory::Sync.new
|
8
|
+
}
|
9
|
+
|
10
|
+
it 'allows emiting an receiving' do
|
11
|
+
seen = nil
|
12
|
+
subject.listen("user_changed") do |evt|
|
13
|
+
seen = evt
|
14
|
+
end
|
15
|
+
subject.emit(Event.new("user_changed", {id: 12}))
|
16
|
+
expect(seen).to be_a(Event)
|
17
|
+
expect(seen.type).to eql("user_changed")
|
18
|
+
expect(seen.data.to_h).to eql({id: 12})
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'allows mixin Symbol vs. String for event type' do
|
22
|
+
seen = nil
|
23
|
+
subject.listen(:user_changed) do |evt|
|
24
|
+
seen = evt
|
25
|
+
end
|
26
|
+
subject.emit(Event.new(:user_changed, {id: 12}))
|
27
|
+
expect(seen).to be_a(Event)
|
28
|
+
expect(seen.type).to eql("user_changed")
|
29
|
+
expect(seen.data.to_h).to eql({id: 12})
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'raises emit errors synchronously' do
|
33
|
+
subject.listen("user_changed") do |evt|
|
34
|
+
raise "An error occured"
|
35
|
+
end
|
36
|
+
expect {
|
37
|
+
subject.emit(Event.new("user_changed", {id: 12}))
|
38
|
+
}.to raise_error("An error occured")
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/spec/unit/test_event.rb
CHANGED
@@ -4,7 +4,7 @@ module Startback
|
|
4
4
|
describe Event do
|
5
5
|
|
6
6
|
subject{
|
7
|
-
Event.new("
|
7
|
+
Event.new("User::Changed", { "foo" => "bar" })
|
8
8
|
}
|
9
9
|
|
10
10
|
it 'presents an ostruct on top of its data' do
|
@@ -15,7 +15,7 @@ module Startback
|
|
15
15
|
|
16
16
|
JSON_SRC = <<-JSON.gsub(/\s+/, "")
|
17
17
|
{
|
18
|
-
"type": "
|
18
|
+
"type": "User::Changed",
|
19
19
|
"data": {
|
20
20
|
"foo": "bar"
|
21
21
|
}
|
@@ -27,10 +27,10 @@ module Startback
|
|
27
27
|
end
|
28
28
|
|
29
29
|
it 'has a to_json that dumps the context if any' do
|
30
|
-
evt = Event.new("
|
30
|
+
evt = Event.new("User::Changed", { "foo" => "bar" }, { "baz": "context" })
|
31
31
|
expect(evt.to_json).to eql(<<-JSON.gsub(/\s+/, ""))
|
32
32
|
{
|
33
|
-
"type": "
|
33
|
+
"type": "User::Changed",
|
34
34
|
"data": {
|
35
35
|
"foo": "bar"
|
36
36
|
},
|
@@ -43,26 +43,19 @@ module Startback
|
|
43
43
|
|
44
44
|
|
45
45
|
it 'has a json class method that works as expected' do
|
46
|
-
evt = Event.json(JSON_SRC)
|
46
|
+
evt = Event.json(JSON_SRC, nil)
|
47
47
|
expect(evt).to be_a(Event)
|
48
|
-
expect(evt.type).to eql("
|
48
|
+
expect(evt.type).to eql("User::Changed")
|
49
49
|
expect(evt.data).to eql(subject.data)
|
50
50
|
end
|
51
51
|
|
52
|
-
it 'accepts an explicit context
|
53
|
-
|
54
|
-
|
52
|
+
it 'accepts an explicit context as second argument' do
|
53
|
+
c = SubContext.new.tap{|x| x.foo = 'hello' }
|
54
|
+
evt = Event.json(JSON_SRC, c)
|
55
|
+
expect(evt.context).not_to be(c)
|
56
|
+
expect(evt.context).to be_a(SubContext)
|
57
|
+
expect(evt.context.foo).to eql('hello')
|
55
58
|
end
|
56
|
-
|
57
|
-
it 'accepts an context factory in the world' do
|
58
|
-
cf = ->(arg) {
|
59
|
-
expect(arg).to eql(JSON.parse(JSON_SRC))
|
60
|
-
12
|
61
|
-
}
|
62
|
-
evt = Event.json(JSON_SRC, context_factory: cf)
|
63
|
-
expect(evt.context).to eql(12)
|
64
|
-
end
|
65
|
-
|
66
59
|
end
|
67
60
|
|
68
61
|
end
|