metacosm 0.1.4 → 0.1.5
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/lib/metacosm.rb +3 -116
- data/lib/metacosm/model.rb +68 -0
- data/lib/metacosm/simulation.rb +50 -0
- data/lib/metacosm/support/spec_harness.rb +21 -0
- data/lib/metacosm/version.rb +1 -1
- data/spec/metacosm_spec.rb +17 -6
- data/spec/spec_helper.rb +1 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 26c2c480e58c0185a99b3a80b500591da5ecdc90
|
4
|
+
data.tar.gz: a6b9ceed179df3805c430fa33297da1c85bd834d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b763778df53c6c589127402ab24a432c1c56dc91769bad50951191e4a0042eee173767f6a2de90b2a182d8e02750b88dbc8fbef509d2edccdc124ac8b491fc8c
|
7
|
+
data.tar.gz: cc70b16c39fd0332363b45020a201ce1db30be50416c8cba0ac0bca6d9ac0a4f36f7c5e693ecce1317ca31634c3b8fca7b885e63632028e759136bcc0d43a6aa
|
data/lib/metacosm.rb
CHANGED
@@ -1,75 +1,11 @@
|
|
1
1
|
require 'passive_record'
|
2
2
|
require 'frappuccino'
|
3
|
+
|
3
4
|
require 'metacosm/version'
|
5
|
+
require 'metacosm/model'
|
6
|
+
require 'metacosm/simulation'
|
4
7
|
|
5
8
|
module Metacosm
|
6
|
-
class Model
|
7
|
-
include PassiveRecord
|
8
|
-
after_create :register_observer, :emit_creation_event
|
9
|
-
after_update :emit_updation_event
|
10
|
-
|
11
|
-
private
|
12
|
-
def register_observer
|
13
|
-
Simulation.current.watch(self)
|
14
|
-
end
|
15
|
-
|
16
|
-
def emit_creation_event
|
17
|
-
emit(creation_event) if created_event_class
|
18
|
-
end
|
19
|
-
|
20
|
-
def emit_updation_event
|
21
|
-
emit(updation_event) if updated_event_class
|
22
|
-
end
|
23
|
-
|
24
|
-
def attributes_with_external_id
|
25
|
-
attrs = to_h
|
26
|
-
if attrs.key?(:id)
|
27
|
-
new_id_key = self.class.name.split('::').last.underscore + "_id"
|
28
|
-
attrs[new_id_key.to_sym] = attrs.delete(:id)
|
29
|
-
end
|
30
|
-
attrs
|
31
|
-
end
|
32
|
-
|
33
|
-
# trim down extenralized attrs for evt
|
34
|
-
def attributes_for_event(klass)
|
35
|
-
# assume evts attrs are attr_accessible?
|
36
|
-
keys_to_keep = klass.instance_methods.find_all do |method|
|
37
|
-
method != :== &&
|
38
|
-
method != :! &&
|
39
|
-
klass.instance_methods.include?(:"#{method}=")
|
40
|
-
end
|
41
|
-
|
42
|
-
attributes_with_external_id.
|
43
|
-
delete_if {|k,v| !keys_to_keep.include?(k) }
|
44
|
-
end
|
45
|
-
|
46
|
-
def assemble_event(klass, addl_attrs={})
|
47
|
-
klass.create(attributes_for_event(klass).merge(addl_attrs))
|
48
|
-
end
|
49
|
-
|
50
|
-
def creation_event
|
51
|
-
assemble_event created_event_class
|
52
|
-
end
|
53
|
-
|
54
|
-
def updation_event
|
55
|
-
assemble_event updated_event_class
|
56
|
-
end
|
57
|
-
|
58
|
-
def created_event_class
|
59
|
-
created_event_name = self.class.name + "CreatedEvent"
|
60
|
-
Object.const_get(created_event_name) rescue nil
|
61
|
-
end
|
62
|
-
|
63
|
-
def updated_event_class
|
64
|
-
updated_event_name = self.class.name + "UpdatedEvent"
|
65
|
-
Object.const_get(updated_event_name) rescue nil
|
66
|
-
end
|
67
|
-
|
68
|
-
def blacklisted_attribute_names
|
69
|
-
[ :@observer_peers ]
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
9
|
class View
|
74
10
|
include PassiveRecord
|
75
11
|
end
|
@@ -103,53 +39,4 @@ module Metacosm
|
|
103
39
|
self.simulation.apply(command)
|
104
40
|
end
|
105
41
|
end
|
106
|
-
|
107
|
-
class Simulation
|
108
|
-
def watch(model)
|
109
|
-
Frappuccino::Stream.new(model).on_value(&method(:receive))
|
110
|
-
end
|
111
|
-
|
112
|
-
def apply(command)
|
113
|
-
handler_for(command).handle(command.attrs)
|
114
|
-
end
|
115
|
-
|
116
|
-
def receive(event, record: true)
|
117
|
-
events.push(event) if record
|
118
|
-
|
119
|
-
listener = listener_for(event)
|
120
|
-
if event.attrs.any?
|
121
|
-
listener.receive(event.attrs)
|
122
|
-
else
|
123
|
-
listener.receive
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
def events
|
128
|
-
@events ||= []
|
129
|
-
end
|
130
|
-
|
131
|
-
def self.current
|
132
|
-
@current ||= new
|
133
|
-
end
|
134
|
-
|
135
|
-
def clear!
|
136
|
-
@events = []
|
137
|
-
end
|
138
|
-
|
139
|
-
protected
|
140
|
-
def handler_for(command)
|
141
|
-
@handlers ||= {}
|
142
|
-
@handlers[command] ||= Object.const_get(command.class.name.split('::').last + "Handler").new
|
143
|
-
end
|
144
|
-
|
145
|
-
def listener_for(event)
|
146
|
-
@listeners ||= {}
|
147
|
-
@listeners[event] ||= construct_listener_for(event)
|
148
|
-
end
|
149
|
-
|
150
|
-
def construct_listener_for(event)
|
151
|
-
listener = Object.const_get(event.class.name.split('::').last + "Listener").new(self)
|
152
|
-
listener
|
153
|
-
end
|
154
|
-
end
|
155
42
|
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Metacosm
|
2
|
+
class Model
|
3
|
+
include PassiveRecord
|
4
|
+
after_create :register_observer, :emit_creation_event
|
5
|
+
after_update :emit_updation_event
|
6
|
+
|
7
|
+
private
|
8
|
+
def register_observer
|
9
|
+
Simulation.current.watch(self)
|
10
|
+
end
|
11
|
+
|
12
|
+
def emit_creation_event
|
13
|
+
emit(creation_event) if created_event_class
|
14
|
+
end
|
15
|
+
|
16
|
+
def emit_updation_event
|
17
|
+
emit(updation_event) if updated_event_class
|
18
|
+
end
|
19
|
+
|
20
|
+
def attributes_with_external_id
|
21
|
+
attrs = to_h
|
22
|
+
if attrs.key?(:id)
|
23
|
+
new_id_key = self.class.name.split('::').last.underscore + "_id"
|
24
|
+
attrs[new_id_key.to_sym] = attrs.delete(:id)
|
25
|
+
end
|
26
|
+
attrs
|
27
|
+
end
|
28
|
+
|
29
|
+
# trim down extenralized attrs for evt
|
30
|
+
def attributes_for_event(klass)
|
31
|
+
# assume evts attrs are attr_accessible?
|
32
|
+
keys_to_keep = klass.instance_methods.find_all do |method|
|
33
|
+
method != :== &&
|
34
|
+
method != :! &&
|
35
|
+
klass.instance_methods.include?(:"#{method}=")
|
36
|
+
end
|
37
|
+
|
38
|
+
attributes_with_external_id.
|
39
|
+
delete_if {|k,v| !keys_to_keep.include?(k) }
|
40
|
+
end
|
41
|
+
|
42
|
+
def assemble_event(klass, addl_attrs={})
|
43
|
+
klass.create(attributes_for_event(klass).merge(addl_attrs))
|
44
|
+
end
|
45
|
+
|
46
|
+
def creation_event
|
47
|
+
assemble_event created_event_class
|
48
|
+
end
|
49
|
+
|
50
|
+
def updation_event
|
51
|
+
assemble_event updated_event_class
|
52
|
+
end
|
53
|
+
|
54
|
+
def created_event_class
|
55
|
+
created_event_name = self.class.name + "CreatedEvent"
|
56
|
+
Object.const_get(created_event_name) rescue nil
|
57
|
+
end
|
58
|
+
|
59
|
+
def updated_event_class
|
60
|
+
updated_event_name = self.class.name + "UpdatedEvent"
|
61
|
+
Object.const_get(updated_event_name) rescue nil
|
62
|
+
end
|
63
|
+
|
64
|
+
def blacklisted_attribute_names
|
65
|
+
[ :@observer_peers ]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Metacosm
|
2
|
+
class Simulation
|
3
|
+
def watch(model)
|
4
|
+
Frappuccino::Stream.new(model).on_value(&method(:receive))
|
5
|
+
end
|
6
|
+
|
7
|
+
def apply(command)
|
8
|
+
handler_for(command).handle(command.attrs)
|
9
|
+
end
|
10
|
+
|
11
|
+
def receive(event, record: true)
|
12
|
+
events.push(event) if record
|
13
|
+
|
14
|
+
listener = listener_for(event)
|
15
|
+
if event.attrs.any?
|
16
|
+
listener.receive(event.attrs)
|
17
|
+
else
|
18
|
+
listener.receive
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def events
|
23
|
+
@events ||= []
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.current
|
27
|
+
@current ||= new
|
28
|
+
end
|
29
|
+
|
30
|
+
def clear!
|
31
|
+
@events = []
|
32
|
+
end
|
33
|
+
|
34
|
+
protected
|
35
|
+
def handler_for(command)
|
36
|
+
@handlers ||= {}
|
37
|
+
@handlers[command] ||= Object.const_get(command.class.name.split('::').last + "Handler").new
|
38
|
+
end
|
39
|
+
|
40
|
+
def listener_for(event)
|
41
|
+
@listeners ||= {}
|
42
|
+
@listeners[event] ||= construct_listener_for(event)
|
43
|
+
end
|
44
|
+
|
45
|
+
def construct_listener_for(event)
|
46
|
+
listener = Object.const_get(event.class.name.split('::').last + "Listener").new(self)
|
47
|
+
listener
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
RSpec::Matchers.define :trigger_event do |event|
|
2
|
+
match do |command|
|
3
|
+
# PassiveRecord.drop_all
|
4
|
+
Simulation.current.clear!
|
5
|
+
Simulation.current.apply(command)
|
6
|
+
expect(Simulation.current.events).to include(event)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
RSpec::Matchers.define :trigger_events do |*events|
|
11
|
+
match do |command|
|
12
|
+
# PassiveRecord.drop_all
|
13
|
+
Simulation.current.clear!
|
14
|
+
|
15
|
+
Simulation.current.apply(command)
|
16
|
+
|
17
|
+
events.each do |event|
|
18
|
+
expect(Simulation.current.events).to include(event)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/metacosm/version.rb
CHANGED
data/spec/metacosm_spec.rb
CHANGED
@@ -12,11 +12,16 @@ describe "a simple simulation (fizzbuzz)" do
|
|
12
12
|
)
|
13
13
|
end
|
14
14
|
|
15
|
+
let(:counter_incremented) do
|
16
|
+
CounterIncrementedEvent.create(
|
17
|
+
counter_id: model.id, value: 1
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
15
21
|
it 'should run the linearized test for the README' do
|
16
22
|
sim = Simulation.current
|
17
23
|
counter_model = Counter.create
|
18
24
|
counter_view = CounterView.find_by(counter_id: counter_model.id)
|
19
|
-
|
20
25
|
expect(counter_view.value).to eq(0) # => 0
|
21
26
|
|
22
27
|
increment_counter_command = IncrementCounterCommand.create(
|
@@ -41,7 +46,7 @@ describe "a simple simulation (fizzbuzz)" do
|
|
41
46
|
CounterIncrementedEvent,
|
42
47
|
BuzzEvent,
|
43
48
|
CounterIncrementedEvent])
|
44
|
-
end
|
49
|
+
end
|
45
50
|
|
46
51
|
context "one command once" do
|
47
52
|
before { simulation.apply(increment_counter) }
|
@@ -66,6 +71,12 @@ describe "a simple simulation (fizzbuzz)" do
|
|
66
71
|
end
|
67
72
|
end
|
68
73
|
|
74
|
+
context "one command once (spec harness style)" do
|
75
|
+
before { model }
|
76
|
+
subject(:command) { increment_counter }
|
77
|
+
it { is_expected.to trigger_event(counter_incremented) }
|
78
|
+
end
|
79
|
+
|
69
80
|
context "one command ten times" do
|
70
81
|
it 'is expected to play fizz buzz' do
|
71
82
|
expect {
|
@@ -96,12 +107,12 @@ describe "a simple simulation (fizzbuzz)" do
|
|
96
107
|
counter_value_query.execute(counter_id: model.id)
|
97
108
|
end
|
98
109
|
|
99
|
-
it { is_expected.to eq(n) }
|
110
|
+
it { is_expected.to eq(n) }
|
100
111
|
end
|
101
112
|
end
|
102
113
|
|
103
114
|
context 'with concurrent command sources' do
|
104
|
-
let(:m) {
|
115
|
+
let(:m) { 5 }
|
105
116
|
let(:threads) {
|
106
117
|
ts = []
|
107
118
|
m.times do
|
@@ -145,8 +156,8 @@ describe "a more complex simulation (village)" do
|
|
145
156
|
|
146
157
|
describe "#apply" do
|
147
158
|
context 'create and populate villages' do
|
148
|
-
let(:person_id)
|
149
|
-
let(:village_id)
|
159
|
+
let(:person_id) { 'person_id' }
|
160
|
+
let(:village_id) { 'village_id' }
|
150
161
|
let(:village_name) { 'Oakville Ridge' }
|
151
162
|
|
152
163
|
let(:people_per_village) { 10 }
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: metacosm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joseph Weissman
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-02-
|
11
|
+
date: 2016-02-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: passive_record
|
@@ -162,6 +162,9 @@ files:
|
|
162
162
|
- features/step_definitions/metacosm_steps.rb
|
163
163
|
- gemspec.yml
|
164
164
|
- lib/metacosm.rb
|
165
|
+
- lib/metacosm/model.rb
|
166
|
+
- lib/metacosm/simulation.rb
|
167
|
+
- lib/metacosm/support/spec_harness.rb
|
165
168
|
- lib/metacosm/support/test_harness.rb
|
166
169
|
- lib/metacosm/version.rb
|
167
170
|
- metacosm.gemspec
|