leonidas 0.0.1
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/CHANGELOG +1 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +105 -0
- data/LICENSE +21 -0
- data/Manifest +63 -0
- data/README.md +213 -0
- data/Rakefile +42 -0
- data/assets/scripts/coffee/leonidas/client.coffee +15 -0
- data/assets/scripts/coffee/leonidas/commander.coffee +34 -0
- data/assets/scripts/coffee/leonidas/commands/command.coffee +9 -0
- data/assets/scripts/coffee/leonidas/commands/organizer.coffee +24 -0
- data/assets/scripts/coffee/leonidas/commands/processor.coffee +13 -0
- data/assets/scripts/coffee/leonidas/commands/stabilizer.coffee +13 -0
- data/assets/scripts/coffee/leonidas/commands/synchronizer.coffee +38 -0
- data/assets/scripts/js/lib/jquery.js +6 -0
- data/bin/leonidas.js +178 -0
- data/config/assets.rb +7 -0
- data/leonidas.gemspec +36 -0
- data/lib/leonidas.rb +14 -0
- data/lib/leonidas/app/app.rb +80 -0
- data/lib/leonidas/app/connection.rb +20 -0
- data/lib/leonidas/app/repository.rb +41 -0
- data/lib/leonidas/commands/aggregator.rb +31 -0
- data/lib/leonidas/commands/command.rb +24 -0
- data/lib/leonidas/commands/handler.rb +21 -0
- data/lib/leonidas/commands/processor.rb +30 -0
- data/lib/leonidas/dsl/configuration_expression.rb +17 -0
- data/lib/leonidas/memory_layer/memory_registry.rb +33 -0
- data/lib/leonidas/persistence_layer/persister.rb +54 -0
- data/lib/leonidas/persistence_layer/state_builder.rb +17 -0
- data/lib/leonidas/persistence_layer/state_loader.rb +22 -0
- data/lib/leonidas/routes/sync.rb +45 -0
- data/lib/leonidas/symbols.rb +17 -0
- data/spec/jasmine/jasmine.yml +44 -0
- data/spec/jasmine/runner.html +77 -0
- data/spec/jasmine/support/classes.coffee +16 -0
- data/spec/jasmine/support/helpers.coffee +22 -0
- data/spec/jasmine/support/mocks.coffee +19 -0
- data/spec/jasmine/support/objects.coffee +11 -0
- data/spec/jasmine/support/requirements.coffee +1 -0
- data/spec/jasmine/tests/client_spec.coffee +20 -0
- data/spec/jasmine/tests/commander_spec.coffee +69 -0
- data/spec/jasmine/tests/commands/command_spec.coffee +12 -0
- data/spec/jasmine/tests/commands/organizer_spec.coffee +70 -0
- data/spec/jasmine/tests/commands/processor_spec.coffee +22 -0
- data/spec/jasmine/tests/commands/stabilizer_spec.coffee +30 -0
- data/spec/jasmine/tests/commands/synchronizer_spec.coffee +72 -0
- data/spec/rspec/spec_helper.rb +4 -0
- data/spec/rspec/support/classes/app.rb +26 -0
- data/spec/rspec/support/classes/commands.rb +52 -0
- data/spec/rspec/support/classes/persistence.rb +56 -0
- data/spec/rspec/support/config.rb +3 -0
- data/spec/rspec/support/mocks.rb +15 -0
- data/spec/rspec/support/objects.rb +11 -0
- data/spec/rspec/unit/app/app_spec.rb +185 -0
- data/spec/rspec/unit/app/repository_spec.rb +114 -0
- data/spec/rspec/unit/commands/aggregator_spec.rb +103 -0
- data/spec/rspec/unit/commands/command.rb +17 -0
- data/spec/rspec/unit/commands/processor_spec.rb +30 -0
- data/spec/rspec/unit/dsl/configuration_expression_spec.rb +32 -0
- data/spec/rspec/unit/leonidas_spec.rb +26 -0
- data/spec/rspec/unit/memory_layer/memory_registry_spec.rb +85 -0
- data/spec/rspec/unit/persistence_layer/persister_spec.rb +84 -0
- data/spec/rspec/unit/persistence_layer/state_loader_spec.rb +29 -0
- metadata +166 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
globalize class IncrementHandler
|
|
2
|
+
|
|
3
|
+
constructor: (@state)->
|
|
4
|
+
|
|
5
|
+
handles: (command)-> command.name is "increment"
|
|
6
|
+
|
|
7
|
+
run: (command)-> @state.integer++
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
globalize class PopCharHandler
|
|
11
|
+
|
|
12
|
+
constructor: (@state)->
|
|
13
|
+
|
|
14
|
+
handles: (command)-> command.name is "pop-char"
|
|
15
|
+
|
|
16
|
+
run: (command)-> @state.string = @state.string.slice(0,-1)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
(exports ? this).globalize = (fxn, name=null)=>
|
|
2
|
+
functionName = if name is null then fxn.name else name
|
|
3
|
+
(exports ? this)[functionName] = fxn
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# MOCKING #
|
|
7
|
+
(exports ? this).mocks = {}
|
|
8
|
+
|
|
9
|
+
(exports ? this).addMock = (name, obj)=>
|
|
10
|
+
(exports ? this).mocks[name] = obj
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# RUNTIME PATCHING #
|
|
14
|
+
(exports ? this).patches = {}
|
|
15
|
+
|
|
16
|
+
(exports ? this).patch = (method, patch)=>
|
|
17
|
+
(exports ? this).patches[method.name] = method
|
|
18
|
+
method = patch
|
|
19
|
+
|
|
20
|
+
(exports ? this).restore = (method)=>
|
|
21
|
+
method = (exports ? this).patches[method.name]
|
|
22
|
+
delete (exports ? this).patches[method.name]
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
addMock "syncPushResponse",
|
|
2
|
+
success: true
|
|
3
|
+
message: 'commands received'
|
|
4
|
+
data: { }
|
|
5
|
+
|
|
6
|
+
addMock "syncPullResponse",
|
|
7
|
+
success: true
|
|
8
|
+
message: 'commands retrieved'
|
|
9
|
+
data:
|
|
10
|
+
commands: [
|
|
11
|
+
{ name: 'pop-char', data: { }, timestamp: 2 },
|
|
12
|
+
{ name: 'increment', data: { }, timestamp: 6 },
|
|
13
|
+
{ name: 'increment', data: { }, timestamp: 8 }
|
|
14
|
+
]
|
|
15
|
+
currentClients: [
|
|
16
|
+
{ id: "2345", lastUpdate: 2 },
|
|
17
|
+
{ id: "3456", lastUpdate: 8 }
|
|
18
|
+
]
|
|
19
|
+
stableTimestamp: 2
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Command = require 'leonidas/commands/command'
|
|
2
|
+
Client = require 'leonidas/client'
|
|
3
|
+
|
|
4
|
+
buildCommand = (timestamp, name="increment", data={})->
|
|
5
|
+
new Command(name, data, timestamp)
|
|
6
|
+
|
|
7
|
+
buildClient = ->
|
|
8
|
+
new Client("app 1", { integer: 1, string: "test" })
|
|
9
|
+
|
|
10
|
+
globalize(buildCommand, "buildCommand")
|
|
11
|
+
globalize(buildClient, "buildClient")
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require "lib/jquery"
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Client = require 'leonidas/client'
|
|
2
|
+
|
|
3
|
+
describe "Client", ->
|
|
4
|
+
client = null
|
|
5
|
+
|
|
6
|
+
beforeEach ->
|
|
7
|
+
client = new Client("app 1", { test: "test" })
|
|
8
|
+
client.activeState = { test: "different" }
|
|
9
|
+
|
|
10
|
+
describe "#revertState", ->
|
|
11
|
+
|
|
12
|
+
it "will revert the active state to the locked state", ->
|
|
13
|
+
client.revertState()
|
|
14
|
+
expect(client.activeState).toEqual { test: "test" }
|
|
15
|
+
|
|
16
|
+
describe "#lockState", ->
|
|
17
|
+
|
|
18
|
+
it "will lock the state to the active state", ->
|
|
19
|
+
client.lockState()
|
|
20
|
+
expect(client.lockedState).toEqual { test: "different" }
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
Organizer = require "leonidas/commands/organizer"
|
|
2
|
+
Processor = require "leonidas/commands/processor"
|
|
3
|
+
Stabilizer = require "leonidas/commands/stabilizer"
|
|
4
|
+
Synchronizer = require "leonidas/commands/synchronizer"
|
|
5
|
+
Commander = require 'leonidas/commander'
|
|
6
|
+
|
|
7
|
+
describe "Commander", ->
|
|
8
|
+
commander = null
|
|
9
|
+
client = null
|
|
10
|
+
organizer = null
|
|
11
|
+
synchronizer = null
|
|
12
|
+
|
|
13
|
+
beforeEach ->
|
|
14
|
+
client = buildClient()
|
|
15
|
+
organizer = new Organizer()
|
|
16
|
+
processor = new Processor([ new PopCharHandler(client.activeState) ])
|
|
17
|
+
stabilizer = new Stabilizer(client, organizer, processor)
|
|
18
|
+
synchronizer = new Synchronizer("http://mydomain.com/sync", client, organizer, stabilizer)
|
|
19
|
+
spyOn(synchronizer, "push")
|
|
20
|
+
spyOn(synchronizer, "pull")
|
|
21
|
+
commander = new Commander(organizer, processor, stabilizer, synchronizer)
|
|
22
|
+
|
|
23
|
+
describe "::default", ->
|
|
24
|
+
|
|
25
|
+
it "will return a default commander using the built in classes", ->
|
|
26
|
+
commander = Commander.default(client, [ new PopCharHandler("tim") ], "http://mydomain.com/sync")
|
|
27
|
+
expect(commander.constructor).toEqual Commander
|
|
28
|
+
|
|
29
|
+
describe "#startSync", ->
|
|
30
|
+
|
|
31
|
+
beforeEach ->
|
|
32
|
+
jasmine.Clock.useMock()
|
|
33
|
+
|
|
34
|
+
it "will set the synchronizer to begin pushing updates", ->
|
|
35
|
+
commander.startSync()
|
|
36
|
+
jasmine.Clock.tick(5500)
|
|
37
|
+
expect(synchronizer.push.calls.length).toEqual 5
|
|
38
|
+
|
|
39
|
+
it "will set the synchronizer to begin pulling updates", ->
|
|
40
|
+
commander.startSync()
|
|
41
|
+
jasmine.Clock.tick(11000)
|
|
42
|
+
expect(synchronizer.pull.calls.length).toEqual 2
|
|
43
|
+
|
|
44
|
+
describe "#stopSync", ->
|
|
45
|
+
|
|
46
|
+
beforeEach ->
|
|
47
|
+
jasmine.Clock.useMock()
|
|
48
|
+
|
|
49
|
+
it "will stop the synchronizer from pushing updates", ->
|
|
50
|
+
commander.startSync()
|
|
51
|
+
commander.stopSync()
|
|
52
|
+
jasmine.Clock.tick(10000)
|
|
53
|
+
expect(synchronizer.push).not.toHaveBeenCalled()
|
|
54
|
+
|
|
55
|
+
it "will stop the synchronizer from pulling updates", ->
|
|
56
|
+
commander.startSync()
|
|
57
|
+
commander.stopSync()
|
|
58
|
+
jasmine.Clock.tick(10000)
|
|
59
|
+
expect(synchronizer.pull).not.toHaveBeenCalled()
|
|
60
|
+
|
|
61
|
+
describe "#issueCommand", ->
|
|
62
|
+
|
|
63
|
+
it "will generate an unsynchronized command", ->
|
|
64
|
+
commander.issueCommand "pop-char", {}
|
|
65
|
+
expect(organizer.unsyncedCommands.length).toEqual 1
|
|
66
|
+
|
|
67
|
+
it "will run the command to update the local client state", ->
|
|
68
|
+
commander.issueCommand "pop-char", {}
|
|
69
|
+
expect(client.activeState.string).toEqual "tes"
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Command = require 'leonidas/commands/command'
|
|
2
|
+
|
|
3
|
+
describe "Command", ->
|
|
4
|
+
command = null
|
|
5
|
+
|
|
6
|
+
beforeEach ->
|
|
7
|
+
command = new Command("test", { testData: "test" }, 25)
|
|
8
|
+
|
|
9
|
+
describe "#toHash", ->
|
|
10
|
+
|
|
11
|
+
it "will return the command serialized as a hash", ->
|
|
12
|
+
expect(command.toHash()).toEqual { name: "test", data: { testData: "test" }, timestamp: 25 }
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
Organizer = require 'leonidas/commands/organizer'
|
|
2
|
+
|
|
3
|
+
describe "Organizer", ->
|
|
4
|
+
command1 = command2 = command3 = command4 = null
|
|
5
|
+
organizer = null
|
|
6
|
+
|
|
7
|
+
beforeEach ->
|
|
8
|
+
command1 = buildCommand(1)
|
|
9
|
+
command2 = buildCommand(2, "pop-char")
|
|
10
|
+
command3 = buildCommand(3, "pop-char")
|
|
11
|
+
command4 = buildCommand(4)
|
|
12
|
+
organizer = new Organizer()
|
|
13
|
+
|
|
14
|
+
describe "#addCommand", ->
|
|
15
|
+
|
|
16
|
+
it "will add unsynced commands", ->
|
|
17
|
+
organizer.addCommand command1
|
|
18
|
+
expect(organizer.unsyncedCommands).toEqual [ command1 ]
|
|
19
|
+
|
|
20
|
+
it "will add synced commands", ->
|
|
21
|
+
organizer.addCommand command1, false
|
|
22
|
+
expect(organizer.syncedCommands).toEqual [ command1 ]
|
|
23
|
+
|
|
24
|
+
describe "#addCommands", ->
|
|
25
|
+
|
|
26
|
+
it "will add multiple unsynced commands", ->
|
|
27
|
+
organizer.addCommands [ command1, command2 ]
|
|
28
|
+
expect(organizer.unsyncedCommands).toEqual [ command1, command2 ]
|
|
29
|
+
|
|
30
|
+
it "will add multiple synced commands", ->
|
|
31
|
+
organizer.addCommands [ command1, command2 ], false
|
|
32
|
+
expect(organizer.syncedCommands).toEqual [ command1, command2 ]
|
|
33
|
+
|
|
34
|
+
describe "#markAsSynced", ->
|
|
35
|
+
|
|
36
|
+
it "will add the requested commands to the syncedCommands list", ->
|
|
37
|
+
organizer.addCommands [ command1, command2, command3 ]
|
|
38
|
+
organizer.markAsSynced [ command1, command2 ]
|
|
39
|
+
expect(organizer.syncedCommands).toEqual [ command1, command2 ]
|
|
40
|
+
|
|
41
|
+
it "will remove the requested commands from the unsyncedCommands list", ->
|
|
42
|
+
organizer.addCommands [ command1, command2, command3 ]
|
|
43
|
+
organizer.markAsSynced [ command1, command2 ]
|
|
44
|
+
expect(organizer.unsyncedCommands).toEqual [ command3 ]
|
|
45
|
+
|
|
46
|
+
describe "#markAsInactive", ->
|
|
47
|
+
|
|
48
|
+
it "will add the requested commands to the inactiveCommands list", ->
|
|
49
|
+
organizer.addCommands [ command1, command2, command3 ]
|
|
50
|
+
organizer.markAsInactive [ command1, command2 ]
|
|
51
|
+
expect(organizer.inactiveCommands).toEqual [ command1, command2 ]
|
|
52
|
+
|
|
53
|
+
it "will remove requested commands from the syncedCommands list", ->
|
|
54
|
+
organizer.addCommands [ command1, command2, command3 ], false
|
|
55
|
+
organizer.markAsInactive [ command1, command2 ]
|
|
56
|
+
expect(organizer.syncedCommands).toEqual [ command3 ]
|
|
57
|
+
|
|
58
|
+
describe "#activeCommands", ->
|
|
59
|
+
|
|
60
|
+
it "will return a concatenated list of synced and unsynced commands", ->
|
|
61
|
+
organizer.addCommands [ command2, command4 ]
|
|
62
|
+
organizer.addCommands [ command1, command3 ], false
|
|
63
|
+
expect(organizer.activeCommands().length).toEqual 4
|
|
64
|
+
expect(command in organizer.activeCommands()).toBeTruthy() for command in organizer.unsyncedCommands
|
|
65
|
+
expect(command in organizer.activeCommands()).toBeTruthy() for command in organizer.syncedCommands
|
|
66
|
+
|
|
67
|
+
it "will sort the list of commands by timestamp", ->
|
|
68
|
+
organizer.addCommands [ command2, command4 ]
|
|
69
|
+
organizer.addCommands [ command1, command3 ], false
|
|
70
|
+
expect(organizer.activeCommands()).toEqual [ command1, command2, command3, command4 ]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Client = require 'leonidas/client'
|
|
2
|
+
Processor = require 'leonidas/commands/processor'
|
|
3
|
+
|
|
4
|
+
describe "Processor", ->
|
|
5
|
+
processor = null
|
|
6
|
+
client = null
|
|
7
|
+
|
|
8
|
+
beforeEach ->
|
|
9
|
+
client = buildClient()
|
|
10
|
+
processor = new Processor([ new IncrementHandler(client.activeState), new PopCharHandler(client.activeState)])
|
|
11
|
+
|
|
12
|
+
describe "#processCommand", ->
|
|
13
|
+
|
|
14
|
+
it "will run a command", ->
|
|
15
|
+
processor.processCommand buildCommand(1)
|
|
16
|
+
expect(client.activeState).toEqual { integer: 2, string: "test" }
|
|
17
|
+
|
|
18
|
+
describe "#processCommands", ->
|
|
19
|
+
|
|
20
|
+
it "will run multiple commands", ->
|
|
21
|
+
processor.processCommands [ buildCommand(1), buildCommand(2, "pop-char") ]
|
|
22
|
+
expect(client.activeState).toEqual { integer: 2, string: "tes" }
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
Client = require 'leonidas/client'
|
|
2
|
+
Organizer = require 'leonidas/commands/organizer'
|
|
3
|
+
Processor = require 'leonidas/commands/processor'
|
|
4
|
+
Stabilizer = require 'leonidas/commands/stabilizer'
|
|
5
|
+
|
|
6
|
+
describe "Stabilizer", ->
|
|
7
|
+
stabilizer = null
|
|
8
|
+
client = null
|
|
9
|
+
organizer = null
|
|
10
|
+
|
|
11
|
+
beforeEach ->
|
|
12
|
+
client = buildClient()
|
|
13
|
+
organizer = new Organizer()
|
|
14
|
+
organizer.addCommands [ buildCommand(1), buildCommand(2, "pop-char"), buildCommand(3, "pop-char"), buildCommand(4) ], false
|
|
15
|
+
processor = new Processor([ new IncrementHandler(client.activeState), new PopCharHandler(client.activeState)])
|
|
16
|
+
stabilizer = new Stabilizer(client, organizer, processor)
|
|
17
|
+
|
|
18
|
+
describe "#stabilize", ->
|
|
19
|
+
|
|
20
|
+
it "will update the locked state to the state at the given timestamp", ->
|
|
21
|
+
stabilizer.stabilize 2
|
|
22
|
+
expect(client.lockedState).toEqual { integer: 2, string: "tes" }
|
|
23
|
+
|
|
24
|
+
it "will deactivate the stable commands in the command organizer", ->
|
|
25
|
+
stabilizer.stabilize 2
|
|
26
|
+
expect(organizer.activeCommands()).toEqual [ buildCommand(3, "pop-char"), buildCommand(4) ]
|
|
27
|
+
|
|
28
|
+
it "will process the remaining active commands to leave the active state entirely current", ->
|
|
29
|
+
stabilizer.stabilize 2
|
|
30
|
+
expect(client.activeState).toEqual { integer: 3, string: "te" }
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
Organizer = require 'leonidas/commands/organizer'
|
|
2
|
+
Processor = require 'leonidas/commands/processor'
|
|
3
|
+
Stabilizer = require 'leonidas/commands/stabilizer'
|
|
4
|
+
Synchronizer = require 'leonidas/commands/synchronizer'
|
|
5
|
+
|
|
6
|
+
describe "Synchronizer", ->
|
|
7
|
+
command1 = command4 = command5 = command7 = null
|
|
8
|
+
client = null
|
|
9
|
+
organizer = null
|
|
10
|
+
synchronizer = null
|
|
11
|
+
|
|
12
|
+
beforeEach ->
|
|
13
|
+
client = buildClient()
|
|
14
|
+
organizer = new Organizer()
|
|
15
|
+
command1 = buildCommand(1)
|
|
16
|
+
command4 = buildCommand(4, "pop-char")
|
|
17
|
+
command5 = buildCommand(5, "pop-char")
|
|
18
|
+
command7 = buildCommand(7)
|
|
19
|
+
processor = new Processor([ new IncrementHandler(client.activeState), new PopCharHandler(client.activeState)])
|
|
20
|
+
stabilizer = new Stabilizer(client, organizer, processor)
|
|
21
|
+
synchronizer = new Synchronizer("http://mydomain.com/sync", client, organizer, stabilizer)
|
|
22
|
+
|
|
23
|
+
describe "#push", ->
|
|
24
|
+
|
|
25
|
+
beforeEach ->
|
|
26
|
+
organizer.addCommands [ command1, command4 ]
|
|
27
|
+
|
|
28
|
+
describe "when successful", ->
|
|
29
|
+
|
|
30
|
+
it "will mark the commands pushed as synced", ->
|
|
31
|
+
spyOn($,"ajax").andCallFake( (params)-> params.success(mocks.syncPushResponse))
|
|
32
|
+
synchronizer.push()
|
|
33
|
+
expect(organizer.syncedCommands).toEqual [ command1, command4 ]
|
|
34
|
+
expect(organizer.unsyncedCommands).toEqual [ ]
|
|
35
|
+
|
|
36
|
+
it "will not mark unsynced commands added since push was called as synced", ->
|
|
37
|
+
spyOn($,"ajax").andCallFake( (params)->
|
|
38
|
+
organizer.addCommands [ command5, command7 ]
|
|
39
|
+
params.success(mocks.syncPushResponse)
|
|
40
|
+
)
|
|
41
|
+
synchronizer.push()
|
|
42
|
+
expect(organizer.syncedCommands).toEqual [ command1, command4 ]
|
|
43
|
+
expect(organizer.unsyncedCommands).toEqual [ command5, command7 ]
|
|
44
|
+
|
|
45
|
+
describe "#pull", ->
|
|
46
|
+
|
|
47
|
+
describe "when successful", ->
|
|
48
|
+
|
|
49
|
+
beforeEach ->
|
|
50
|
+
organizer.addCommands [ command1, command4 ], false
|
|
51
|
+
spyOn($,"ajax").andCallFake( (params)-> params.success(mocks.syncPullResponse))
|
|
52
|
+
|
|
53
|
+
it "will update the list of external clients and their latest timestamps", ->
|
|
54
|
+
synchronizer.pull()
|
|
55
|
+
expect(synchronizer.externalClients).toEqual [ { id: "2345", lastUpdate: 2 }, { id: "3456", lastUpdate: 8 } ]
|
|
56
|
+
|
|
57
|
+
it "will add the list of received commands as synced commands", ->
|
|
58
|
+
synchronizer.pull()
|
|
59
|
+
expect(organizer.syncedCommands.length).toEqual 3
|
|
60
|
+
expect(organizer.syncedCommands[0].toHash()).toEqual { name: 'pop-char', data: { }, timestamp: 4 },
|
|
61
|
+
expect(organizer.syncedCommands[1].toHash()).toEqual { name: 'increment', data: { }, timestamp: 6 },
|
|
62
|
+
expect(organizer.syncedCommands[2].toHash()).toEqual { name: 'increment', data: { }, timestamp: 8 }
|
|
63
|
+
|
|
64
|
+
it "will lock to a new stable state", ->
|
|
65
|
+
synchronizer.pull()
|
|
66
|
+
expect(client.lockedState).toEqual { integer: 2, string: "tes" }
|
|
67
|
+
|
|
68
|
+
it "will deactivate stable commands", ->
|
|
69
|
+
synchronizer.pull()
|
|
70
|
+
expect(organizer.inactiveCommands.length).toEqual 2
|
|
71
|
+
expect(organizer.inactiveCommands[0].toHash()).toEqual { name: 'increment', data: { }, timestamp: 1 },
|
|
72
|
+
expect(organizer.inactiveCommands[1].toHash()).toEqual { name: 'pop-char', data: { }, timestamp: 2 },
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module TestClasses
|
|
2
|
+
|
|
3
|
+
class TestApp
|
|
4
|
+
include ::Leonidas::App::App
|
|
5
|
+
|
|
6
|
+
def initialize(name="app 1")
|
|
7
|
+
@name = name
|
|
8
|
+
@persist_state = false
|
|
9
|
+
@locked_state = { value: 0 }
|
|
10
|
+
@active_state = { value: 1 }
|
|
11
|
+
@connections = [ ]
|
|
12
|
+
@processor = ::Leonidas::Commands::Processor.new([ IncrementHandler.new(self), MultiplyHandler.new(self) ])
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def state=(val)
|
|
16
|
+
@locked_state = val.dup
|
|
17
|
+
@active_state = val.dup
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class TestRepositoryContainer
|
|
23
|
+
include ::Leonidas::App::AppRepository
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module TestClasses
|
|
2
|
+
|
|
3
|
+
class IncrementHandler
|
|
4
|
+
include ::Leonidas::Commands::Handler
|
|
5
|
+
|
|
6
|
+
def initialize(app)
|
|
7
|
+
@app = app
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def handles?(command)
|
|
11
|
+
command.name == "increment"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def run(command)
|
|
15
|
+
@app.current_state[:value] += command.data[:increment_by]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def persist(command)
|
|
19
|
+
TestClasses::PersistentState.value += command.data[:increment_by]
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
class MultiplyHandler
|
|
24
|
+
include ::Leonidas::Commands::Handler
|
|
25
|
+
|
|
26
|
+
def initialize(app)
|
|
27
|
+
@app = app
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def handles?(command)
|
|
31
|
+
command.name == "multiply"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def run(command)
|
|
35
|
+
@app.current_state[:value] *= command.data[:multiply_by]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def persist(command)
|
|
39
|
+
TestClasses::PersistentState.value *= command.data[:multiply_by]
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
class TestAggregator
|
|
44
|
+
include ::Leonidas::Commands::Aggregator
|
|
45
|
+
|
|
46
|
+
def initialize
|
|
47
|
+
@active_commands = [ ]
|
|
48
|
+
@inactive_commands = [ ]
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
end
|