metacosm 0.2.13 → 0.2.14
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.
- checksums.yaml +4 -4
- data/README.md +9 -166
- data/lib/metacosm/remote_simulation.rb +5 -3
- data/lib/metacosm/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 68f1b5dd4218a10775dfe6aae1afc0b4db1e295d
|
4
|
+
data.tar.gz: b6d7707b7b226b4d30a8f1d3a89e275aee2973b2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f6aaa059326435c46219c6205a21892b999980d9fc40d1108e1ec7ed47052206698098f94b8e7ce40c5a009002f38ce15c081337b2eb4f08a95b67d119e05d42
|
7
|
+
data.tar.gz: 8653b0c85c98fa87b1e09036ecc01e7e5ede20696b56e57ba660843951bba77c63ad7ce48ed150c494185ffb3a339514f72b18ed4983cff08fd12675c072a1e6
|
data/README.md
CHANGED
@@ -10,181 +10,24 @@
|
|
10
10
|
|
11
11
|
Metacosm is an awesome microframework for building reactive systems.
|
12
12
|
|
13
|
-
|
13
|
+
## Goals
|
14
14
|
|
15
|
-
|
16
|
-
|
15
|
+
Enable quick prototyping of command-query separated architectures, and empower development of event-sourced systems.
|
16
|
+
|
17
|
+
## Background
|
18
|
+
|
19
|
+
One core concept is that we use commands to update "write-only" models, which trigger events that update "read-only" view models that are used by queries.
|
17
20
|
|
18
21
|
Models only transform their state in response to commands, so their state can be reconstructed by replaying the stream of commands.
|
19
22
|
|
20
23
|
## Features
|
21
24
|
|
22
|
-
-
|
25
|
+
- Distributed simulations using Redis
|
23
26
|
|
24
27
|
## Examples
|
25
28
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
You may also want to [look at this repository](https://github.com/jweissman/gol) which implements Conway's game of life using metacosm and gosu.
|
30
|
-
|
31
|
-
````ruby
|
32
|
-
require 'metacosm'
|
33
|
-
include Metacosm
|
34
|
-
|
35
|
-
class Counter < Model
|
36
|
-
def initialize
|
37
|
-
@counter = 0
|
38
|
-
super
|
39
|
-
end
|
40
|
-
|
41
|
-
def fizz!
|
42
|
-
emit fizz
|
43
|
-
end
|
44
|
-
|
45
|
-
def buzz!
|
46
|
-
emit buzz
|
47
|
-
end
|
48
|
-
|
49
|
-
def increment!(inc)
|
50
|
-
@counter += inc
|
51
|
-
emit(counter_incremented)
|
52
|
-
end
|
53
|
-
|
54
|
-
protected
|
55
|
-
def fizz
|
56
|
-
FizzEvent.create
|
57
|
-
end
|
58
|
-
|
59
|
-
def buzz
|
60
|
-
BuzzEvent.create
|
61
|
-
end
|
62
|
-
|
63
|
-
def counter_incremented
|
64
|
-
CounterIncrementedEvent.create(
|
65
|
-
value: @counter,
|
66
|
-
counter_id: @id
|
67
|
-
)
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
class CounterView < View
|
72
|
-
attr_accessor :value, :counter_id
|
73
|
-
def update_value(new_value)
|
74
|
-
@value = new_value
|
75
|
-
self
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
class IncrementCounterCommand < Command
|
80
|
-
attr_accessor :increment, :counter_id
|
81
|
-
end
|
82
|
-
|
83
|
-
class IncrementCounterCommandHandler
|
84
|
-
def handle(increment:,counter_id:)
|
85
|
-
counter = Counter.find(counter_id)
|
86
|
-
counter.increment!(increment)
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
class CounterIncrementedEvent < Event
|
91
|
-
attr_accessor :value, :counter_id
|
92
|
-
end
|
93
|
-
|
94
|
-
class CounterIncrementedEventListener < EventListener
|
95
|
-
def receive(value:,counter_id:)
|
96
|
-
update_counter_view(counter_id, value)
|
97
|
-
|
98
|
-
fizz_buzz!(counter_id, value)
|
99
|
-
puts(value) unless fizz?(value) || buzz?(value)
|
100
|
-
end
|
101
|
-
|
102
|
-
def update_counter_view(counter_id, value)
|
103
|
-
counter_view = CounterView.where(counter_id: counter_id).first_or_create
|
104
|
-
counter_view.update value: value
|
105
|
-
end
|
106
|
-
|
107
|
-
private
|
108
|
-
def fizz_buzz!(counter_id, n)
|
109
|
-
fire(FizzCommand.create(counter_id: counter_id)) if fizz?(n)
|
110
|
-
fire(BuzzCommand.create(counter_id: counter_id)) if buzz?(n)
|
111
|
-
end
|
112
|
-
|
113
|
-
def fizz?(n); n % 3 == 0 end
|
114
|
-
def buzz?(n); n % 5 == 0 end
|
115
|
-
end
|
116
|
-
|
117
|
-
class FizzCommand < Command
|
118
|
-
attr_accessor :counter_id
|
119
|
-
end
|
120
|
-
|
121
|
-
class FizzCommandHandler
|
122
|
-
def handle(counter_id:)
|
123
|
-
counter = Counter.find(counter_id)
|
124
|
-
counter.fizz!
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
class BuzzCommand < Command
|
129
|
-
attr_accessor :counter_id
|
130
|
-
end
|
131
|
-
|
132
|
-
class BuzzCommandHandler
|
133
|
-
def handle(counter_id:)
|
134
|
-
counter = Counter.find(counter_id)
|
135
|
-
counter.buzz!
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
class FizzEvent < Event
|
140
|
-
end
|
141
|
-
|
142
|
-
class FizzEventListener < EventListener
|
143
|
-
def receive
|
144
|
-
puts "fizz"
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
class BuzzEvent < Event
|
149
|
-
end
|
150
|
-
|
151
|
-
class BuzzEventListener < EventListener
|
152
|
-
def receive
|
153
|
-
puts "buzz"
|
154
|
-
end
|
155
|
-
end
|
156
|
-
````
|
157
|
-
|
158
|
-
Given all this prelude we can run a fizzbuzz "simulation":
|
159
|
-
|
160
|
-
````ruby
|
161
|
-
sim = Simulation.current
|
162
|
-
counter_model = Counter.create
|
163
|
-
counter_view = CounterView.find_by(counter_id: counter_model.id)
|
164
|
-
|
165
|
-
counter_view.value # => 0
|
166
|
-
|
167
|
-
increment_counter_command = IncrementCounterCommand.create(
|
168
|
-
increment: 1, counter_id: counter_model.id
|
169
|
-
)
|
170
|
-
|
171
|
-
sim.apply(increment_counter_command)
|
172
|
-
|
173
|
-
counter_view.value # => 1
|
174
|
-
|
175
|
-
100.times { sim.apply(increment_counter_command) }
|
176
|
-
|
177
|
-
sim.events.take(10)
|
178
|
-
# => [CounterCreatedEvent (id: 1, counter_id: 1),
|
179
|
-
# CounterIncrementedEvent (id: 1, value: 1, counter_id: 1),
|
180
|
-
# CounterIncrementedEvent (id: 2, value: 2, counter_id: 1),
|
181
|
-
# CounterIncrementedEvent (id: 3, value: 3, counter_id: 1),
|
182
|
-
# FizzEvent (id: 1),
|
183
|
-
# CounterIncrementedEvent (id: 4, value: 4, counter_id: 1),
|
184
|
-
# CounterIncrementedEvent (id: 5, value: 5, counter_id: 1),
|
185
|
-
# BuzzEvent (id: 1),
|
186
|
-
# CounterIncrementedEvent (id: 6, value: 6, counter_id: 1)]
|
187
|
-
````
|
29
|
+
- [Game of Life](https://github.com/jweissman/gol), which implements Conway's game of life using metacosm and gosu
|
30
|
+
- [Socius](http://github.com/jweissman/socius), a civlike using the Redis integration to communicate with a game server running the sim
|
188
31
|
|
189
32
|
## Requirements
|
190
33
|
|
@@ -1,6 +1,8 @@
|
|
1
1
|
module Metacosm
|
2
2
|
class RemoteSimulation < Simulation
|
3
|
-
def initialize
|
3
|
+
def initialize(command_queue, event_stream)
|
4
|
+
@command_queue_name = command_queue
|
5
|
+
@event_stream_name = event_stream
|
4
6
|
setup_connection
|
5
7
|
end
|
6
8
|
|
@@ -11,14 +13,14 @@ module Metacosm
|
|
11
13
|
def fire(command)
|
12
14
|
command_dto = command.attrs.merge(handler_module: command.handler_module_name, handler_class_name: command.handler_class_name)
|
13
15
|
redis = redis_connection
|
14
|
-
redis.publish(
|
16
|
+
redis.publish(@command_queue_name, Marshal.dump(command_dto))
|
15
17
|
end
|
16
18
|
|
17
19
|
def setup_connection
|
18
20
|
@remote_listener_thread = Thread.new do
|
19
21
|
begin
|
20
22
|
redis = redis_connection
|
21
|
-
redis.subscribe(
|
23
|
+
redis.subscribe(@event_stream_name) do |on|
|
22
24
|
on.subscribe do |channel, subscriptions|
|
23
25
|
puts "Subscribed to remote simulation event stream ##{channel} (#{subscriptions} subscriptions)"
|
24
26
|
end
|
data/lib/metacosm/version.rb
CHANGED