startback 0.4.5 → 0.5.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.
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