metacosm 0.1.3 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +55 -26
- data/gemspec.yml +2 -2
- data/lib/metacosm.rb +13 -17
- data/lib/metacosm/support/test_harness.rb +104 -0
- data/lib/metacosm/version.rb +1 -1
- data/spec/metacosm_spec.rb +31 -0
- data/spec/spec_helper.rb +2 -106
- data/spec/support/fizz_buzz.rb +12 -8
- data/spec/support/village.rb +0 -1
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 052e0d6b334fd7502342b5facd9cc6925a466df1
|
4
|
+
data.tar.gz: e00f5d01027be65e8f91ff04c8b48665d664eeea
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4087a92a6eee8aa00cfb280ac6dd2cdba64bdef2b1fda60e29ae5c31d26ebff3a9b2a66c8bcd3c21e3bd5f4b7214c6c3ef2b2007ef897c36ff3710b5cfff5dd2
|
7
|
+
data.tar.gz: b008d3c6409925ad9f1d601079dd2805b42fd5709b0245a512e1c183d194f8a8032d5407a465592f909e19359512c2734006efbcd26b3953d5f9a65db7fc8f21
|
data/README.md
CHANGED
@@ -50,16 +50,16 @@ A Fizzbuzz implementation contrived enough to show off many of the features of t
|
|
50
50
|
|
51
51
|
protected
|
52
52
|
def fizz
|
53
|
-
FizzEvent.create
|
53
|
+
FizzEvent.create
|
54
54
|
end
|
55
55
|
|
56
56
|
def buzz
|
57
|
-
BuzzEvent.create
|
57
|
+
BuzzEvent.create
|
58
58
|
end
|
59
59
|
|
60
60
|
def counter_incremented
|
61
61
|
CounterIncrementedEvent.create(
|
62
|
-
value: @counter,
|
62
|
+
value: @counter,
|
63
63
|
counter_id: @id
|
64
64
|
)
|
65
65
|
end
|
@@ -73,13 +73,14 @@ A Fizzbuzz implementation contrived enough to show off many of the features of t
|
|
73
73
|
end
|
74
74
|
end
|
75
75
|
|
76
|
-
class IncrementCounterCommand <
|
76
|
+
class IncrementCounterCommand < Command
|
77
|
+
attr_accessor :increment, :counter_id
|
77
78
|
end
|
78
79
|
|
79
80
|
class IncrementCounterCommandHandler
|
80
|
-
def handle(
|
81
|
-
counter = Counter.
|
82
|
-
counter.increment!(
|
81
|
+
def handle(increment:,counter_id:)
|
82
|
+
counter = Counter.find(counter_id)
|
83
|
+
counter.increment!(increment)
|
83
84
|
end
|
84
85
|
end
|
85
86
|
|
@@ -97,61 +98,89 @@ A Fizzbuzz implementation contrived enough to show off many of the features of t
|
|
97
98
|
|
98
99
|
def update_counter_view(counter_id, value)
|
99
100
|
counter_view = CounterView.where(counter_id: counter_id).first_or_create
|
100
|
-
counter_view.value
|
101
|
+
counter_view.update value: value
|
101
102
|
end
|
102
103
|
|
103
104
|
private
|
104
105
|
def fizz_buzz!(counter_id, n)
|
105
|
-
fire(FizzCommand.
|
106
|
-
fire(BuzzCommand.
|
106
|
+
fire(FizzCommand.create(counter_id: counter_id)) if fizz?(n)
|
107
|
+
fire(BuzzCommand.create(counter_id: counter_id)) if buzz?(n)
|
107
108
|
end
|
108
109
|
|
109
110
|
def fizz?(n); n % 3 == 0 end
|
110
111
|
def buzz?(n); n % 5 == 0 end
|
111
112
|
end
|
112
113
|
|
113
|
-
class FizzCommand <
|
114
|
+
class FizzCommand < Command
|
115
|
+
attr_accessor :counter_id
|
116
|
+
end
|
117
|
+
|
114
118
|
class FizzCommandHandler
|
115
|
-
def handle(
|
116
|
-
counter = Counter.
|
119
|
+
def handle(counter_id:)
|
120
|
+
counter = Counter.find(counter_id)
|
117
121
|
counter.fizz!
|
118
122
|
end
|
119
123
|
end
|
120
124
|
|
121
|
-
class BuzzCommand <
|
125
|
+
class BuzzCommand < Command
|
126
|
+
attr_accessor :counter_id
|
127
|
+
end
|
128
|
+
|
122
129
|
class BuzzCommandHandler
|
123
|
-
def handle(
|
124
|
-
counter = Counter.
|
130
|
+
def handle(counter_id:)
|
131
|
+
counter = Counter.find(counter_id)
|
125
132
|
counter.buzz!
|
126
133
|
end
|
127
134
|
end
|
128
135
|
|
129
136
|
class FizzEvent < Event
|
130
|
-
attr_accessor :value, :counter_id
|
131
137
|
end
|
132
138
|
|
133
139
|
class FizzEventListener < EventListener
|
134
|
-
def receive
|
140
|
+
def receive
|
135
141
|
puts "fizz"
|
136
142
|
end
|
137
143
|
end
|
138
144
|
|
139
145
|
class BuzzEvent < Event
|
140
|
-
attr_accessor :value, :counter_id
|
141
146
|
end
|
142
147
|
|
143
148
|
class BuzzEventListener < EventListener
|
144
|
-
def receive
|
149
|
+
def receive
|
145
150
|
puts "buzz"
|
146
151
|
end
|
147
152
|
end
|
153
|
+
````
|
154
|
+
|
155
|
+
Given all this prelude we can run a fizzbuzz "simulation":
|
156
|
+
|
157
|
+
````ruby
|
158
|
+
sim = Simulation.current
|
159
|
+
counter_model = Counter.create
|
160
|
+
counter_view = CounterView.find_by(counter_id: counter_model.id)
|
148
161
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
162
|
+
counter_view.value # => 0
|
163
|
+
|
164
|
+
increment_counter_command = IncrementCounterCommand.create(
|
165
|
+
increment: 1, counter_id: counter_model.id
|
166
|
+
)
|
167
|
+
|
168
|
+
sim.apply(increment_counter_command)
|
169
|
+
|
170
|
+
counter_view.value # => 1
|
171
|
+
|
172
|
+
100.times { sim.apply(increment_counter_command) }
|
173
|
+
|
174
|
+
sim.events.take(10)
|
175
|
+
# => [CounterCreatedEvent (id: 1, counter_id: 1),
|
176
|
+
# CounterIncrementedEvent (id: 1, value: 1, counter_id: 1),
|
177
|
+
# CounterIncrementedEvent (id: 2, value: 2, counter_id: 1),
|
178
|
+
# CounterIncrementedEvent (id: 3, value: 3, counter_id: 1),
|
179
|
+
# FizzEvent (id: 1),
|
180
|
+
# CounterIncrementedEvent (id: 4, value: 4, counter_id: 1),
|
181
|
+
# CounterIncrementedEvent (id: 5, value: 5, counter_id: 1),
|
182
|
+
# BuzzEvent (id: 1),
|
183
|
+
# CounterIncrementedEvent (id: 6, value: 6, counter_id: 1)]
|
155
184
|
````
|
156
185
|
|
157
186
|
## Requirements
|
data/gemspec.yml
CHANGED
@@ -6,8 +6,8 @@ authors: Joseph Weissman
|
|
6
6
|
email: jweissman1986@gmail.com
|
7
7
|
homepage: https://rubygems.org/gems/metacosm
|
8
8
|
dependencies:
|
9
|
-
passive_record: ~> 0.
|
10
|
-
frappuccino: ~> 0.3
|
9
|
+
passive_record: ~> 0.3
|
10
|
+
frappuccino: ~> 0.3
|
11
11
|
development_dependencies:
|
12
12
|
bundler: ~> 1.10
|
13
13
|
codeclimate-test-reporter: ~> 0.1
|
data/lib/metacosm.rb
CHANGED
@@ -6,16 +6,9 @@ module Metacosm
|
|
6
6
|
class Model
|
7
7
|
include PassiveRecord
|
8
8
|
after_create :register_observer, :emit_creation_event
|
9
|
+
after_update :emit_updation_event
|
9
10
|
|
10
|
-
|
11
|
-
attrs.each do |k,v|
|
12
|
-
send("#{k}=",v)
|
13
|
-
end
|
14
|
-
|
15
|
-
emit(updation_event(attrs)) if updated_event_class
|
16
|
-
end
|
17
|
-
|
18
|
-
protected
|
11
|
+
private
|
19
12
|
def register_observer
|
20
13
|
Simulation.current.watch(self)
|
21
14
|
end
|
@@ -24,6 +17,10 @@ module Metacosm
|
|
24
17
|
emit(creation_event) if created_event_class
|
25
18
|
end
|
26
19
|
|
20
|
+
def emit_updation_event
|
21
|
+
emit(updation_event) if updated_event_class
|
22
|
+
end
|
23
|
+
|
27
24
|
def attributes_with_external_id
|
28
25
|
attrs = to_h
|
29
26
|
if attrs.key?(:id)
|
@@ -34,7 +31,7 @@ module Metacosm
|
|
34
31
|
end
|
35
32
|
|
36
33
|
# trim down extenralized attrs for evt
|
37
|
-
def attributes_for_event(klass
|
34
|
+
def attributes_for_event(klass)
|
38
35
|
# assume evts attrs are attr_accessible?
|
39
36
|
keys_to_keep = klass.instance_methods.find_all do |method|
|
40
37
|
method != :== &&
|
@@ -43,8 +40,7 @@ module Metacosm
|
|
43
40
|
end
|
44
41
|
|
45
42
|
attributes_with_external_id.
|
46
|
-
delete_if {|k,v| !keys_to_keep.include?(k) }
|
47
|
-
merge(additional_attrs)
|
43
|
+
delete_if {|k,v| !keys_to_keep.include?(k) }
|
48
44
|
end
|
49
45
|
|
50
46
|
def assemble_event(klass, addl_attrs={})
|
@@ -52,7 +48,11 @@ module Metacosm
|
|
52
48
|
end
|
53
49
|
|
54
50
|
def creation_event
|
55
|
-
assemble_event
|
51
|
+
assemble_event created_event_class
|
52
|
+
end
|
53
|
+
|
54
|
+
def updation_event
|
55
|
+
assemble_event updated_event_class
|
56
56
|
end
|
57
57
|
|
58
58
|
def created_event_class
|
@@ -60,10 +60,6 @@ module Metacosm
|
|
60
60
|
Object.const_get(created_event_name) rescue nil
|
61
61
|
end
|
62
62
|
|
63
|
-
def updation_event(changed_attrs={})
|
64
|
-
assemble_event(updated_event_class, changed_attrs)
|
65
|
-
end
|
66
|
-
|
67
63
|
def updated_event_class
|
68
64
|
updated_event_name = self.class.name + "UpdatedEvent"
|
69
65
|
Object.const_get(updated_event_name) rescue nil
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module Metacosm
|
2
|
+
module TestHarness
|
3
|
+
class GivenWhenThen < Struct.new(:given_events,:when_command,:then_event_class)
|
4
|
+
include RSpec::Matchers
|
5
|
+
|
6
|
+
def when(*commands)
|
7
|
+
@when_commands ||= []
|
8
|
+
commands.each do |command|
|
9
|
+
@when_commands.push command
|
10
|
+
end
|
11
|
+
self
|
12
|
+
end
|
13
|
+
|
14
|
+
def expect_events(evts)
|
15
|
+
@then_events = evts
|
16
|
+
verify!
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
def expect_query(query, to_find:)
|
21
|
+
@query = query
|
22
|
+
@expected_query_results = to_find
|
23
|
+
verify!
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
protected
|
28
|
+
|
29
|
+
def verify!
|
30
|
+
clean_slate!
|
31
|
+
receive_events!
|
32
|
+
fire_commands!
|
33
|
+
|
34
|
+
validate_events!
|
35
|
+
validate_query!
|
36
|
+
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def clean_slate!
|
43
|
+
PassiveRecord.drop_all
|
44
|
+
Simulation.current.clear!
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
def receive_events!
|
49
|
+
unless self.given_events.nil?
|
50
|
+
self.given_events.each do |evt|
|
51
|
+
sim.receive(evt, record: false)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
self
|
55
|
+
end
|
56
|
+
|
57
|
+
def fire_commands!
|
58
|
+
unless @when_commands.nil?
|
59
|
+
@when_commands.each do |cmd|
|
60
|
+
sim.apply(cmd)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
self
|
64
|
+
end
|
65
|
+
|
66
|
+
def validate_events!
|
67
|
+
if @then_event_class
|
68
|
+
expect(@then_event_class).to eq(sim.events.last.class)
|
69
|
+
end
|
70
|
+
|
71
|
+
if @then_events
|
72
|
+
expect(@then_events).to match_array(sim.events)
|
73
|
+
end
|
74
|
+
|
75
|
+
if @then_event_attrs
|
76
|
+
@then_event_attrs.each do |k,v|
|
77
|
+
expect(sim.events.last.send(k)).to eq(v)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
self
|
82
|
+
end
|
83
|
+
|
84
|
+
def validate_query!
|
85
|
+
if @query
|
86
|
+
expect(@query.execute).to eq(@expected_query_results)
|
87
|
+
end
|
88
|
+
self
|
89
|
+
end
|
90
|
+
|
91
|
+
def sim
|
92
|
+
@sim ||= Simulation.current
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def given_no_activity
|
97
|
+
GivenWhenThen.new
|
98
|
+
end
|
99
|
+
|
100
|
+
def given_events(events)
|
101
|
+
GivenWhenThen.new(events)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
data/lib/metacosm/version.rb
CHANGED
data/spec/metacosm_spec.rb
CHANGED
@@ -12,6 +12,37 @@ describe "a simple simulation (fizzbuzz)" do
|
|
12
12
|
)
|
13
13
|
end
|
14
14
|
|
15
|
+
it 'should run the linearized test for the README' do
|
16
|
+
sim = Simulation.current
|
17
|
+
counter_model = Counter.create
|
18
|
+
counter_view = CounterView.find_by(counter_id: counter_model.id)
|
19
|
+
|
20
|
+
expect(counter_view.value).to eq(0) # => 0
|
21
|
+
|
22
|
+
increment_counter_command = IncrementCounterCommand.create(
|
23
|
+
increment: 1, counter_id: counter_model.id
|
24
|
+
)
|
25
|
+
|
26
|
+
sim.apply(increment_counter_command)
|
27
|
+
|
28
|
+
# model is updated which triggers view changes
|
29
|
+
expect(counter_view.value).to eq(1) # => 1
|
30
|
+
|
31
|
+
100.times { sim.apply(increment_counter_command) }
|
32
|
+
|
33
|
+
expect(sim.events.take(10).map(&:class)).to eq(
|
34
|
+
[CounterCreatedEvent,
|
35
|
+
CounterCreatedEvent,
|
36
|
+
CounterIncrementedEvent,
|
37
|
+
CounterIncrementedEvent,
|
38
|
+
CounterIncrementedEvent,
|
39
|
+
FizzEvent,
|
40
|
+
CounterIncrementedEvent,
|
41
|
+
CounterIncrementedEvent,
|
42
|
+
BuzzEvent,
|
43
|
+
CounterIncrementedEvent])
|
44
|
+
end
|
45
|
+
|
15
46
|
context "one command once" do
|
16
47
|
before { simulation.apply(increment_counter) }
|
17
48
|
|
data/spec/spec_helper.rb
CHANGED
@@ -4,117 +4,13 @@ require 'rspec/its'
|
|
4
4
|
require 'pry'
|
5
5
|
require 'ostruct'
|
6
6
|
require 'metacosm'
|
7
|
+
require 'metacosm/support/test_harness'
|
7
8
|
|
8
9
|
include Metacosm
|
9
10
|
|
10
11
|
require 'support/fizz_buzz'
|
11
12
|
require 'support/village'
|
12
13
|
|
13
|
-
class GivenWhenThen < Struct.new(:given_events,:when_command,:then_event_class)
|
14
|
-
include RSpec::Matchers
|
15
|
-
|
16
|
-
def when(*commands)
|
17
|
-
@when_commands ||= []
|
18
|
-
commands.each do |command|
|
19
|
-
@when_commands.push command
|
20
|
-
end
|
21
|
-
self
|
22
|
-
end
|
23
|
-
|
24
|
-
def expect_events(evts)
|
25
|
-
@then_events = evts
|
26
|
-
verify!
|
27
|
-
self
|
28
|
-
end
|
29
|
-
|
30
|
-
def expect_query(query, to_find:)
|
31
|
-
@query = query
|
32
|
-
@expected_query_results = to_find
|
33
|
-
verify!
|
34
|
-
self
|
35
|
-
end
|
36
|
-
|
37
|
-
protected
|
38
|
-
|
39
|
-
def verify!
|
40
|
-
clean_slate!
|
41
|
-
receive_events!
|
42
|
-
fire_commands!
|
43
|
-
|
44
|
-
validate_events!
|
45
|
-
validate_query!
|
46
|
-
|
47
|
-
self
|
48
|
-
end
|
49
|
-
|
50
|
-
private
|
51
|
-
|
52
|
-
def clean_slate!
|
53
|
-
PassiveRecord.drop_all
|
54
|
-
Simulation.current.clear!
|
55
|
-
self
|
56
|
-
end
|
57
|
-
|
58
|
-
def receive_events!
|
59
|
-
unless self.given_events.nil?
|
60
|
-
self.given_events.each do |evt|
|
61
|
-
sim.receive(evt, record: false)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
self
|
65
|
-
end
|
66
|
-
|
67
|
-
def fire_commands!
|
68
|
-
unless @when_commands.nil?
|
69
|
-
@when_commands.each do |cmd|
|
70
|
-
sim.apply(cmd)
|
71
|
-
end
|
72
|
-
end
|
73
|
-
self
|
74
|
-
end
|
75
|
-
|
76
|
-
def validate_events!
|
77
|
-
if @then_event_class
|
78
|
-
expect(@then_event_class).to eq(sim.events.last.class)
|
79
|
-
end
|
80
|
-
|
81
|
-
if @then_events
|
82
|
-
expect(@then_events).to match_array(sim.events)
|
83
|
-
end
|
84
|
-
|
85
|
-
if @then_event_attrs
|
86
|
-
@then_event_attrs.each do |k,v|
|
87
|
-
expect(sim.events.last.send(k)).to eq(v)
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
self
|
92
|
-
end
|
93
|
-
|
94
|
-
def validate_query!
|
95
|
-
if @query
|
96
|
-
expect(@query.execute).to eq(@expected_query_results)
|
97
|
-
end
|
98
|
-
self
|
99
|
-
end
|
100
|
-
|
101
|
-
def sim
|
102
|
-
@sim ||= Simulation.current
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
module Metacosm
|
107
|
-
module SpecHelpers
|
108
|
-
def given_no_activity
|
109
|
-
GivenWhenThen.new
|
110
|
-
end
|
111
|
-
|
112
|
-
def given_events(events)
|
113
|
-
GivenWhenThen.new(events)
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
14
|
RSpec.configure do |c|
|
119
|
-
c.include Metacosm::
|
15
|
+
c.include Metacosm::TestHarness
|
120
16
|
end
|
data/spec/support/fizz_buzz.rb
CHANGED
@@ -36,9 +36,15 @@ end
|
|
36
36
|
|
37
37
|
class CounterView < View
|
38
38
|
attr_accessor :value, :counter_id
|
39
|
-
|
40
|
-
|
41
|
-
|
39
|
+
end
|
40
|
+
|
41
|
+
class CounterCreatedEvent < Event
|
42
|
+
attr_accessor :counter_id #, :value
|
43
|
+
end
|
44
|
+
|
45
|
+
class CounterCreatedEventListener < EventListener
|
46
|
+
def receive(counter_id:)
|
47
|
+
CounterView.create(counter_id: counter_id, value: 0)
|
42
48
|
end
|
43
49
|
end
|
44
50
|
|
@@ -67,7 +73,7 @@ class CounterIncrementedEventListener < EventListener
|
|
67
73
|
|
68
74
|
def update_counter_view(counter_id, value)
|
69
75
|
counter_view = CounterView.where(counter_id: counter_id).first_or_create
|
70
|
-
counter_view.value
|
76
|
+
counter_view.update value: value
|
71
77
|
end
|
72
78
|
|
73
79
|
private
|
@@ -102,8 +108,7 @@ class BuzzCommandHandler
|
|
102
108
|
end
|
103
109
|
end
|
104
110
|
|
105
|
-
class FizzEvent < Event
|
106
|
-
end
|
111
|
+
class FizzEvent < Event; end
|
107
112
|
|
108
113
|
class FizzEventListener < EventListener
|
109
114
|
def receive
|
@@ -111,8 +116,7 @@ class FizzEventListener < EventListener
|
|
111
116
|
end
|
112
117
|
end
|
113
118
|
|
114
|
-
class BuzzEvent < Event
|
115
|
-
end
|
119
|
+
class BuzzEvent < Event; end
|
116
120
|
|
117
121
|
class BuzzEventListener < EventListener
|
118
122
|
def receive
|
data/spec/support/village.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.4
|
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-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: passive_record
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '0.
|
19
|
+
version: '0.3'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '0.
|
26
|
+
version: '0.3'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: frappuccino
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -162,6 +162,7 @@ files:
|
|
162
162
|
- features/step_definitions/metacosm_steps.rb
|
163
163
|
- gemspec.yml
|
164
164
|
- lib/metacosm.rb
|
165
|
+
- lib/metacosm/support/test_harness.rb
|
165
166
|
- lib/metacosm/version.rb
|
166
167
|
- metacosm.gemspec
|
167
168
|
- schema.png
|