startback 0.4.5 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/lib/startback/audit/trailer.rb +145 -0
  3. data/lib/startback/audit.rb +1 -0
  4. data/lib/startback/bus/bunny/async.rb +117 -0
  5. data/lib/startback/bus/bunny.rb +1 -0
  6. data/lib/startback/bus/memory/async.rb +40 -0
  7. data/lib/startback/bus/memory/sync.rb +30 -0
  8. data/lib/startback/bus/memory.rb +2 -0
  9. data/lib/startback/bus.rb +94 -0
  10. data/lib/startback/caching/entity_cache.rb +80 -0
  11. data/lib/startback/caching/store.rb +34 -0
  12. data/lib/startback/context/middleware.rb +1 -1
  13. data/lib/startback/context.rb +93 -4
  14. data/lib/startback/event.rb +43 -0
  15. data/lib/startback/operation.rb +39 -6
  16. data/lib/startback/support/fake_logger.rb +18 -0
  17. data/lib/startback/support/hooks.rb +48 -0
  18. data/lib/startback/support/operation_runner.rb +150 -0
  19. data/lib/startback/support/robustness.rb +153 -0
  20. data/lib/startback/support.rb +3 -0
  21. data/lib/startback/version.rb +2 -2
  22. data/lib/startback/web/api.rb +3 -4
  23. data/lib/startback/web/catch_all.rb +12 -5
  24. data/lib/startback/web/middleware.rb +13 -0
  25. data/lib/startback.rb +2 -0
  26. data/spec/spec_helper.rb +2 -0
  27. data/spec/unit/audit/test_trailer.rb +88 -0
  28. data/spec/unit/bus/memory/test_async.rb +41 -0
  29. data/spec/unit/bus/memory/test_sync.rb +41 -0
  30. data/spec/unit/caching/test_entity_cache.rb +109 -0
  31. data/spec/unit/context/test_abstraction_factory.rb +64 -0
  32. data/spec/unit/support/hooks/test_after_hook.rb +54 -0
  33. data/spec/unit/support/hooks/test_before_hook.rb +54 -0
  34. data/spec/unit/support/operation_runner/test_around_run.rb +157 -0
  35. data/spec/unit/support/operation_runner/test_before_after_call.rb +48 -0
  36. data/spec/unit/support/test_robusteness.rb +209 -0
  37. data/spec/unit/test_context.rb +51 -0
  38. data/spec/unit/test_event.rb +69 -0
  39. data/spec/unit/test_operation.rb +0 -3
  40. metadata +32 -4
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+ module Startback
3
+ describe Bus::Memory do
4
+
5
+ subject{
6
+ Bus::Memory::Async.new
7
+ }
8
+
9
+ it 'allows emiting an receiving' do
10
+ seen = nil
11
+ subject.listen("user_changed") do |evt|
12
+ seen = evt
13
+ end
14
+ subject.emit(Event.new("user_changed", {id: 12}))
15
+ expect(seen).to be_a(Event)
16
+ expect(seen.type).to eql("user_changed")
17
+ expect(seen.data.to_h).to eql({id: 12})
18
+ end
19
+
20
+ it 'allows mixin Symbol vs. String for event type' do
21
+ seen = nil
22
+ subject.listen(:user_changed) do |evt|
23
+ seen = evt
24
+ end
25
+ subject.emit(Event.new(:user_changed, {id: 12}))
26
+ expect(seen).to be_a(Event)
27
+ expect(seen.type).to eql("user_changed")
28
+ expect(seen.data.to_h).to eql({id: 12})
29
+ end
30
+
31
+ it 'does not raise errors synchronously' do
32
+ subject.listen("user_changed") do |evt|
33
+ raise "An error occured"
34
+ end
35
+ expect {
36
+ subject.emit(Event.new("user_changed", {id: 12}))
37
+ }.not_to raise_error
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+ module Startback
3
+ describe Bus::Memory do
4
+
5
+ subject{
6
+ Bus::Memory::Sync.new
7
+ }
8
+
9
+ it 'allows emiting an receiving' do
10
+ seen = nil
11
+ subject.listen("user_changed") do |evt|
12
+ seen = evt
13
+ end
14
+ subject.emit(Event.new("user_changed", {id: 12}))
15
+ expect(seen).to be_a(Event)
16
+ expect(seen.type).to eql("user_changed")
17
+ expect(seen.data.to_h).to eql({id: 12})
18
+ end
19
+
20
+ it 'allows mixin Symbol vs. String for event type' do
21
+ seen = nil
22
+ subject.listen(:user_changed) do |evt|
23
+ seen = evt
24
+ end
25
+ subject.emit(Event.new(:user_changed, {id: 12}))
26
+ expect(seen).to be_a(Event)
27
+ expect(seen.type).to eql("user_changed")
28
+ expect(seen.data.to_h).to eql({id: 12})
29
+ end
30
+
31
+ it 'raises emit errors synchronously' do
32
+ subject.listen("user_changed") do |evt|
33
+ raise "An error occured"
34
+ end
35
+ expect {
36
+ subject.emit(Event.new("user_changed", {id: 12}))
37
+ }.to raise_error("An error occured")
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,109 @@
1
+ require 'spec_helper'
2
+ require 'startback/caching/entity_cache'
3
+ require 'startback/caching/store'
4
+ module Startback
5
+ module Caching
6
+ describe EntityCache do
7
+
8
+ class BaseCache < EntityCache
9
+
10
+ def initialize(context = nil)
11
+ super(Store.new, context)
12
+ @called = 0
13
+ @last_key = nil
14
+ end
15
+ attr_reader :called, :last_key
16
+
17
+ protected
18
+
19
+ def full_key(key)
20
+ { k: key }
21
+ end
22
+
23
+ def load_raw_data(key)
24
+ @called += 1
25
+ @last_key = key
26
+ "a value"
27
+ end
28
+
29
+ end
30
+
31
+ class ShortCache < BaseCache
32
+ self.default_ttl = 1
33
+ end
34
+
35
+ class InvalidatingCache < BaseCache
36
+
37
+ protected
38
+
39
+ def valid?(key, value)
40
+ false
41
+ end
42
+
43
+ end
44
+
45
+ let(:cache) {
46
+ BaseCache.new
47
+ }
48
+
49
+ describe "default_ttl" do
50
+
51
+ it 'has a default ttl of one hour' do
52
+ expect(BaseCache.default_ttl).to eql(3600)
53
+ end
54
+
55
+ it 'allows overriding it' do
56
+ expect(ShortCache.default_ttl).to eql(1)
57
+ end
58
+
59
+ it 'is accessible as default_caching_options on the instance' do
60
+ expect(cache.send(:default_caching_options)).to eql({ttl: 3600})
61
+ end
62
+
63
+ end
64
+
65
+ describe "get" do
66
+
67
+ subject{
68
+ cache.get("a key")
69
+ }
70
+
71
+ it 'yields to load_raw_data only once with the short key' do
72
+ expect(subject).to eql("a value")
73
+ expect(subject).to eql("a value")
74
+ expect(cache.called).to eql(1)
75
+ expect(cache.last_key).to eql("a key")
76
+ end
77
+
78
+ end
79
+
80
+ describe "invalidate" do
81
+
82
+ it 'strips the key on the store, yielding a cache miss' do
83
+ expect(cache.get("a key")).to eql("a value")
84
+ cache.invalidate("a key")
85
+ expect(cache.get("a key")).to eql("a value")
86
+ expect(cache.called).to eql(2)
87
+ expect(cache.last_key).to eql("a key")
88
+ end
89
+
90
+ end
91
+
92
+ describe "valid? override" do
93
+
94
+ let(:cache) {
95
+ InvalidatingCache.new
96
+ }
97
+
98
+ it 'yields to load_raw_data only once with the extend key' do
99
+ expect(cache.get("a key")).to eql("a value")
100
+ expect(cache.get("a key")).to eql("a value")
101
+ expect(cache.called).to eql(2)
102
+ expect(cache.last_key).to eql("a key")
103
+ end
104
+
105
+ end
106
+
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,64 @@
1
+ require 'spec_helper'
2
+
3
+ module Startback
4
+ describe Context do
5
+
6
+ let(:context) {
7
+ Context.new
8
+ }
9
+
10
+ class ContextRelatedAbstraction
11
+
12
+ def initialize(context)
13
+ @context = context
14
+ end
15
+ attr_reader :context
16
+
17
+ end
18
+
19
+ class ContextRelatedAbstractionWithArgs
20
+
21
+ def initialize(arg1, arg2, context)
22
+ @arg1 = arg1
23
+ @arg2 = arg2
24
+ @context = context
25
+ end
26
+ attr_reader :arg1, :arg2, :context
27
+
28
+ end
29
+
30
+ it 'is a factory for other context-related abstractions' do
31
+ got = context.factor(ContextRelatedAbstraction)
32
+ expect(got).to be_a(ContextRelatedAbstraction)
33
+ expect(got.context).to be(context)
34
+
35
+ got2 = context.factor(ContextRelatedAbstraction)
36
+ expect(got2).to be(got)
37
+ end
38
+
39
+ it 'is takes cares of abstraction arguments' do
40
+ got = context.factor(ContextRelatedAbstractionWithArgs, 12, 14)
41
+ expect(got).to be_a(ContextRelatedAbstractionWithArgs)
42
+ expect(got.context).to be(context)
43
+ expect(got.arg1).to eql(12)
44
+ expect(got.arg2).to eql(14)
45
+ end
46
+
47
+ it 'is caches even in presence ofabstraction arguments' do
48
+ got = context.factor(ContextRelatedAbstractionWithArgs, 12, 14)
49
+ expect(got).to be_a(ContextRelatedAbstractionWithArgs)
50
+
51
+ got2 = context.factor(ContextRelatedAbstractionWithArgs, 12, 14)
52
+ expect(got2).to be(got)
53
+ end
54
+
55
+ it 'is distinguishes different abstraction arguments' do
56
+ got = context.factor(ContextRelatedAbstractionWithArgs, 12, 14)
57
+ expect(got).to be_a(ContextRelatedAbstractionWithArgs)
58
+
59
+ got2 = context.factor(ContextRelatedAbstractionWithArgs, 17, 14)
60
+ expect(got2).not_to be(got)
61
+ end
62
+
63
+ end
64
+ end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+ require 'singleton'
3
+ module Startback
4
+ module Support
5
+ describe Hooks, "after_xxx" do
6
+
7
+ class AfterHooked
8
+ include Hooks.new(:xxx)
9
+
10
+ def initialize
11
+ super
12
+ @after_called = false
13
+ end
14
+ attr_accessor :after_called
15
+
16
+ after_xxx do
17
+ self.after_called = true
18
+ end
19
+
20
+ end
21
+
22
+ class SubAfterHooked < AfterHooked
23
+
24
+ def initialize
25
+ super
26
+ @subafter_called = false
27
+ end
28
+ attr_accessor :subafter_called
29
+
30
+ after_xxx do
31
+ self.subafter_called = true
32
+ end
33
+
34
+ end
35
+
36
+ it 'works as expected' do
37
+ h = AfterHooked.new
38
+ expect(h.after_called).to eql(false)
39
+ h.after_xxx
40
+ expect(h.after_called).to eql(true)
41
+ end
42
+
43
+ it 'works as expected on subclass' do
44
+ h = SubAfterHooked.new
45
+ expect(h.after_called).to eql(false)
46
+ expect(h.subafter_called).to eql(false)
47
+ h.after_xxx
48
+ expect(h.after_called).to eql(true)
49
+ expect(h.subafter_called).to eql(true)
50
+ end
51
+
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+ require 'singleton'
3
+ module Startback
4
+ module Support
5
+ describe Hooks, "before_xxx" do
6
+
7
+ class BeforeHooked
8
+ include Hooks.new(:xxx)
9
+
10
+ def initialize
11
+ super
12
+ @before_called = false
13
+ end
14
+ attr_accessor :before_called
15
+
16
+ before_xxx do
17
+ self.before_called = true
18
+ end
19
+
20
+ end
21
+
22
+ class SubBeforeHooked < BeforeHooked
23
+
24
+ def initialize
25
+ super
26
+ @subbefore_called = false
27
+ end
28
+ attr_accessor :subbefore_called
29
+
30
+ before_xxx do
31
+ self.subbefore_called = true
32
+ end
33
+
34
+ end
35
+
36
+ it 'works as expected' do
37
+ h = BeforeHooked.new
38
+ expect(h.before_called).to eql(false)
39
+ h.before_xxx
40
+ expect(h.before_called).to eql(true)
41
+ end
42
+
43
+ it 'works as expected on subclass' do
44
+ h = SubBeforeHooked.new
45
+ expect(h.before_called).to eql(false)
46
+ expect(h.subbefore_called).to eql(false)
47
+ h.before_xxx
48
+ expect(h.before_called).to eql(true)
49
+ expect(h.subbefore_called).to eql(true)
50
+ end
51
+
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,157 @@
1
+ require 'spec_helper'
2
+ require 'singleton'
3
+ module Startback
4
+ module Support
5
+ describe OperationRunner, "around_run" do
6
+
7
+ class OperationTest < Startback::Operation
8
+
9
+ def call
10
+ { seen_hello: hello }
11
+ end
12
+
13
+ end
14
+
15
+ let(:op) {
16
+ OperationTest.new
17
+ }
18
+
19
+ context 'the simplest contract' do
20
+ class RunnerTest1
21
+ include OperationRunner
22
+
23
+ def operation_world(op)
24
+ { hello: "world"}
25
+ end
26
+ end
27
+
28
+ it 'lets run an operation with world bound' do
29
+ expect(RunnerTest1.new.run(op)).to eql({
30
+ seen_hello: "world"
31
+ })
32
+ end
33
+ end
34
+
35
+ context 'the around feature' do
36
+ class RunnerTest2
37
+ include OperationRunner
38
+
39
+ def initialize
40
+ @arounds = []
41
+ end
42
+ attr_reader :arounds
43
+
44
+ around_run do |o, then_block|
45
+ raise unless o.is_a?(OperationTest)
46
+ arounds << "hello"
47
+ then_block.call
48
+ end
49
+
50
+ around_run do |_, then_block|
51
+ arounds << "world"
52
+ then_block.call
53
+ end
54
+
55
+ def operation_world(op)
56
+ { hello: "world" }
57
+ end
58
+ end
59
+
60
+ it 'calls the around before the operation itself' do
61
+ test = RunnerTest2.new
62
+ got = test.run(op)
63
+ expect(test.arounds).to eql(["hello", "world"])
64
+ expect(got).to eql({
65
+ seen_hello: "world"
66
+ })
67
+ end
68
+ end
69
+
70
+ context 'the around feature with a class' do
71
+ class TransactionManager
72
+ include Singleton
73
+
74
+ def initialize
75
+ @called = false
76
+ end
77
+ attr_reader :called
78
+
79
+ def call(runner, op)
80
+ raise unless runner.is_a?(RunnerTest3)
81
+ raise unless op.is_a?(OperationTest)
82
+ @called = true
83
+ yield
84
+ end
85
+
86
+ end
87
+
88
+ class RunnerTest3
89
+ include OperationRunner
90
+ around_run TransactionManager.instance
91
+
92
+ def operation_world(op)
93
+ { hello: "world" }
94
+ end
95
+ end
96
+
97
+ it 'calls the proc with expected parameters' do
98
+ test = RunnerTest3.new
99
+ got = test.run(op)
100
+ expect(TransactionManager.instance.called).to eql(true)
101
+ expect(got).to eql({
102
+ seen_hello: "world"
103
+ })
104
+ end
105
+ end
106
+
107
+ context 'the around feature with a subclass' do
108
+ class RunnerTest4
109
+ include OperationRunner
110
+
111
+ def initialize
112
+ @called = false
113
+ end
114
+ attr_reader :called
115
+
116
+ around_run do |o,t|
117
+ raise unless o.is_a?(OperationTest)
118
+ @called = true
119
+ t.call
120
+ end
121
+ end
122
+
123
+ class RunnerTest5 < RunnerTest4
124
+
125
+ def initialize
126
+ super
127
+ @subcalled = false
128
+ end
129
+ attr_reader :subcalled
130
+
131
+ around_run do |o,t|
132
+ raise unless o.is_a?(OperationTest)
133
+ @subcalled = true
134
+ t.call
135
+ end
136
+
137
+ def operation_world(op)
138
+ { hello: "world" }
139
+ end
140
+ end
141
+
142
+ it 'executes all hooks' do
143
+ test = RunnerTest5.new
144
+ got = test.run(op)
145
+ expect(test.called).to be(true)
146
+ expect(test.subcalled).to be(true)
147
+ expect(got).to eql({
148
+ seen_hello: "world"
149
+ })
150
+ end
151
+
152
+ end
153
+
154
+ end # module OperationRunner
155
+ end # module Support
156
+ end # module Startback
157
+
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+ require 'singleton'
3
+ module Startback
4
+ module Support
5
+ describe OperationRunner, "before_call" do
6
+
7
+ class OperationTestBeforeCall < Operation
8
+
9
+ def initialize
10
+ before_called = false
11
+ end
12
+ attr_accessor :before_called
13
+
14
+ before_call do
15
+ self.before_called = true
16
+ end
17
+
18
+ def call
19
+ {
20
+ seen_hello: "world"
21
+ }
22
+ end
23
+
24
+ end
25
+
26
+ let(:op) {
27
+ OperationTestBeforeCall.new
28
+ }
29
+
30
+ class RunnerTest1
31
+ include OperationRunner
32
+
33
+ def operation_world(op)
34
+ { hello: "world" }
35
+ end
36
+ end
37
+
38
+ it 'runs before the around hooks' do
39
+ expect(RunnerTest1.new.run(op)).to eql({
40
+ seen_hello: "world"
41
+ })
42
+ expect(op.before_called).to eql(true)
43
+ end
44
+
45
+
46
+ end
47
+ end
48
+ end