metacosm 0.1.3 → 0.1.4
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 +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
|