chione 0.3.0 → 0.4.0

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.
@@ -3,6 +3,7 @@
3
3
  require_relative '../spec_helper'
4
4
 
5
5
  require 'chione/archetype'
6
+ require 'chione/aspect'
6
7
  require 'chione/component'
7
8
  require 'chione/entity'
8
9
  require 'chione/world'
@@ -57,6 +58,19 @@ describe Chione::Archetype do
57
58
  end
58
59
 
59
60
 
61
+ it "can be constructed from the Components specified in an Aspect" do
62
+ aspect = Chione::Aspect.with_all_of( location_component, tags_component )
63
+ archetype = described_class.from_aspect( aspect )
64
+
65
+ expect( archetype ).to be_a( Module )
66
+ expect( archetype.from_aspect ).to eq( aspect )
67
+
68
+ entity = archetype.construct_for( world )
69
+
70
+ expect( aspect ).to match( entity )
71
+ end
72
+
73
+
60
74
  it "is still loadable as an `Assemblage`" do
61
75
  expect {
62
76
  expect( Chione::Assemblage ).to equal( described_class )
@@ -4,20 +4,28 @@ require_relative '../spec_helper'
4
4
 
5
5
  require 'chione/aspect'
6
6
  require 'chione/component'
7
+ require 'chione/fixtures'
7
8
 
8
9
 
9
10
  describe Chione::Aspect do
10
11
 
12
+ before( :all ) do
13
+ Chione::Fixtures.load( :entities )
14
+ end
15
+
16
+
11
17
  let( :location ) do
12
18
  Class.new( Chione::Component ) do
13
19
  field :x, default: 0
14
20
  field :y, default: 0
21
+ def self::name; "Location"; end
15
22
  end
16
23
  end
17
24
 
18
25
  let( :tags ) do
19
26
  Class.new( Chione::Component ) do
20
27
  field :tags, default: []
28
+ def self::name; "Tags"; end
21
29
  end
22
30
  end
23
31
 
@@ -27,9 +35,29 @@ describe Chione::Aspect do
27
35
  field :shade, default: 0
28
36
  field :value, default: 0
29
37
  field :opacity, default: 0
38
+ def self::name; "Color"; end
30
39
  end
31
40
  end
32
41
 
42
+ let( :world ) { Chione::World.new }
43
+
44
+ let( :entity ) { Chione::Fixtures.entity(world) }
45
+ let( :entity1 ) do
46
+ entity.with_components( location ).instance
47
+ end
48
+ let( :entity2 ) do
49
+ entity.with_components( location, color ).instance
50
+ end
51
+ let( :entity3 ) do
52
+ entity.with_components( location, color, tags ).instance
53
+ end
54
+ let( :entity4 ) do
55
+ entity.with_components( color, tags ).instance
56
+ end
57
+ let( :entity5 ) do
58
+ entity.with_components( tags ).instance
59
+ end
60
+
33
61
 
34
62
  it "doesn't have any default criteria" do
35
63
  aspect = described_class.new
@@ -139,5 +167,67 @@ describe Chione::Aspect do
139
167
  expect( aspect.none_of ).to include( tags, location )
140
168
  end
141
169
 
170
+
171
+ it "can be inverted"
172
+
173
+
174
+ describe "entity-matching" do
175
+
176
+ let( :all_entities ) {[ entity1, entity2, entity3, entity4, entity5 ]}
177
+
178
+
179
+ it "can find the matching subset of values given a Hash keyed by Components" do
180
+ entities = all_entities()
181
+
182
+ aspect = described_class.with_all_of( color, location ).and_none_of( tags )
183
+ result = aspect.matching_entities( world.entities_by_component )
184
+
185
+ expect( result ).to contain_exactly( entity2.id )
186
+ end
187
+
188
+
189
+ it "always matches an individual entity if it's empty" do
190
+ aspect = described_class.new
191
+
192
+ expect( aspect ).to match( entity1 )
193
+ expect( aspect ).to match( entity2 )
194
+ expect( aspect ).to match( entity3 )
195
+ expect( aspect ).to match( entity4 )
196
+ expect( aspect ).to match( entity5 )
197
+ end
198
+
199
+
200
+ it "matches an individual entity if its components meet the criteria" do
201
+ aspect = described_class.with_all_of( color, location ).and_none_of( tags )
202
+
203
+ expect( aspect ).to_not match( entity1 )
204
+ expect( aspect ).to match( entity2 )
205
+ expect( aspect ).to_not match( entity3 )
206
+ expect( aspect ).to_not match( entity4 )
207
+ expect( aspect ).to_not match( entity5 )
208
+
209
+ aspect = described_class.with_one_of( tags, color )
210
+
211
+ expect( aspect ).to_not match( entity1 )
212
+ expect( aspect ).to match( entity2 )
213
+ expect( aspect ).to match( entity3 )
214
+ expect( aspect ).to match( entity4 )
215
+ expect( aspect ).to match( entity5 )
216
+ end
217
+
218
+
219
+ it "matches a component hash if it meets the criteria" do
220
+ aspect = described_class.with_all_of( color, location ).and_none_of( tags )
221
+
222
+ expect( aspect ).to_not match( entity1.components )
223
+ expect( aspect ).to match( entity2.components )
224
+ expect( aspect ).to_not match( entity3.components )
225
+ expect( aspect ).to_not match( entity4.components )
226
+ expect( aspect ).to_not match( entity5.components )
227
+ end
228
+
229
+ end
230
+
231
+
142
232
  end
143
233
 
@@ -12,6 +12,7 @@ describe Chione::Component do
12
12
  let( :component_subclass ) do
13
13
  Class.new( described_class )
14
14
  end
15
+ let( :entity_id ) { '73BA6CB5-50CB-4EA4-9941-5EBF17D5D379' }
15
16
 
16
17
 
17
18
  it "can declare fields" do
@@ -27,6 +28,19 @@ describe Chione::Component do
27
28
  end
28
29
 
29
30
 
31
+ it "includes a description of its fields in its inspection output" do
32
+ component_subclass.field( :name )
33
+ component_subclass.field( :x )
34
+ component_subclass.field( :y )
35
+
36
+ instance = component_subclass.new
37
+
38
+ expect( instance.inspect ).to match( /\bname:/ )
39
+ expect( instance.inspect ).to match( /\bx:/ )
40
+ expect( instance.inspect ).to match( /\by:/ )
41
+ end
42
+
43
+
30
44
  it "can declare fields with default values" do
31
45
  component_subclass.field( :x, default: 0 )
32
46
  component_subclass.field( :y, default: 18 )
@@ -37,6 +51,17 @@ describe Chione::Component do
37
51
  end
38
52
 
39
53
 
54
+ it "can declare fields with a block for validation or pre-processing" do
55
+ component_subclass.field( :coordinates, default: [0, 0] ) do |vals|
56
+ { x: Integer(vals[0]), y: Integer(vals[1]) }
57
+ end
58
+
59
+ instance = component_subclass.new( coordinates: [88, 19] )
60
+ expect( instance.coordinates[:x] ).to eq( 88 )
61
+ expect( instance.coordinates[:y] ).to eq( 19 )
62
+ end
63
+
64
+
40
65
  it "uses a dup of the default if it's not an immediate object" do
41
66
  component_subclass.field( :things, default: [] )
42
67
 
@@ -59,6 +84,18 @@ describe Chione::Component do
59
84
  expect( instance2.oid ).to eq( 121212 )
60
85
  end
61
86
 
87
+
88
+ it "can be created with an entity ID" do
89
+ component_subclass.field( :x )
90
+ component_subclass.field( :y )
91
+
92
+ instance = component_subclass.new( entity_id, x: 1, y: 18 )
93
+
94
+ expect( instance.entity_id ).to eq( entity_id )
95
+ expect( instance.x ).to eq( 1 )
96
+ expect( instance.y ).to eq( 18 )
97
+ end
98
+
62
99
  end
63
100
 
64
101
  end
@@ -98,51 +98,44 @@ describe Chione::Entity do
98
98
  end
99
99
 
100
100
 
101
- it "lets components be fetched from it" do
102
- entity.add_component( location_component )
103
- entity.add_component( tags_component )
101
+ it "can have components with init arguments added to it" do
102
+ entity.add_component( location_component, x: 18, y: 51 )
103
+ entity.add_component( tags_component, tags: ['trace', 'volatile'] )
104
104
 
105
- expect(
106
- entity.find_component( location_component )
107
- ).to eq( entity.components[location_component] )
105
+ expect( entity.get_component(location_component).x ).to eq( 18 )
106
+ expect( entity.get_component(location_component).y ).to eq( 51 )
107
+ expect( entity.get_component(tags_component).tags ).to contain_exactly('trace', 'volatile')
108
108
  end
109
109
 
110
110
 
111
- it "supports backward-compatible component-fetcher method" do
111
+ it "lets components be fetched for it" do
112
112
  entity.add_component( location_component )
113
113
  entity.add_component( tags_component )
114
114
 
115
115
  expect(
116
116
  entity.get_component( location_component )
117
- ).to eq( entity.components[location_component] )
117
+ ).to be_an_instance_of( location_component )
118
118
  end
119
119
 
120
120
 
121
- it "lets one of a list of components be fetched from it" do
121
+ it "can have components removed from it" do
122
122
  entity.add_component( location_component )
123
123
  entity.add_component( tags_component )
124
124
 
125
- expect(
126
- entity.find_component( bounding_box_component, location_component )
127
- ).to eq( entity.components[location_component] )
128
- end
129
-
125
+ entity.remove_component( tags_component )
130
126
 
131
- it "raises a KeyError if it doesn't have a fetched component" do
132
- entity.add_component( tags_component )
133
-
134
- expect {
135
- entity.find_component( location_component )
136
- }.to raise_error( KeyError, /#{entity.id} doesn't have/i )
127
+ expect( entity ).to have_component( location_component )
128
+ expect( entity ).to_not have_component( tags_component )
137
129
  end
138
130
 
139
131
 
140
- it "raises a KeyError if it doesn't have any of several fetched components" do
132
+ it "returns nil when fetching a component the entity doesn't have" do
133
+ entity.add_component( location_component )
141
134
  entity.add_component( tags_component )
142
135
 
143
- expect {
144
- entity.find_component( location_component, bounding_box_component )
145
- }.to raise_error( KeyError, /#{entity.id} doesn't have any of/i )
136
+ expect(
137
+ entity.get_component( bounding_box_component )
138
+ ).to be_nil
146
139
  end
147
140
 
148
141
  end
@@ -0,0 +1,135 @@
1
+ #!/usr/bin/env rspec -cfd
2
+
3
+ require_relative '../spec_helper'
4
+
5
+ require 'chione/iterating_system'
6
+ require 'chione/fixtures'
7
+
8
+
9
+ describe Chione::IteratingSystem do
10
+
11
+ before( :all ) do
12
+ Chione::Fixtures.load( :entities )
13
+ @component_classes = Chione::Component.derivatives.dup
14
+ end
15
+ before( :each ) do
16
+ Chione::Component.derivatives.clear
17
+ end
18
+ after( :all ) do
19
+ Chione::Component.derivatives.replace( @component_classes )
20
+ end
21
+
22
+
23
+ let( :subclass ) { Class.new(described_class) }
24
+ let( :world ) { Chione::World.new }
25
+ let( :location_component ) do
26
+ klass = Class.new( Chione::Component ) do
27
+ def self::name; "Location"; end
28
+ field :x, default: 0
29
+ field :y, default: 0
30
+ end
31
+ Chione::Component.derivatives['location'] = klass
32
+ klass
33
+ end
34
+
35
+ let( :tags_component ) do
36
+ klass = Class.new( Chione::Component ) do
37
+ def self::name; "Tags"; end
38
+ field :tags, default: []
39
+ end
40
+ Chione::Component.derivatives['tags'] = klass
41
+ klass
42
+ end
43
+
44
+ let( :bounding_box_component ) do
45
+ klass = Class.new( Chione::Component ) do
46
+ def self::name; "BoundingBox"; end
47
+ field :width, default: 1
48
+ field :height, default: 1
49
+ field :depth, default: 1
50
+ end
51
+ Chione::Component.derivatives['bounding_box'] = klass
52
+ klass
53
+ end
54
+
55
+
56
+ describe "instances" do
57
+
58
+ let( :instance ) { subclass.new(world) }
59
+
60
+
61
+ it "requires an override for the #process method" do
62
+ expect( instance ).to respond_to( :process )
63
+ expect {
64
+ instance.process( :default, 'entity-id', {} )
65
+ }.to raise_error( NotImplementedError, /#process/i )
66
+ end
67
+
68
+
69
+ describe "with a process method" do
70
+
71
+ let( :entity ) { Chione::Fixtures.entity(world) }
72
+
73
+
74
+ let( :subclass ) do
75
+ cls = super()
76
+ cls.class_exec(location_component, bounding_box_component) do |location, bbox|
77
+ aspect :default, all_of: [location, bbox]
78
+ aspect :emphemeral, all_of: [location], none_of: bbox
79
+
80
+ def initialize( * )
81
+ super
82
+ @calls = []
83
+ end
84
+
85
+ attr_reader :calls
86
+
87
+ def process( aspect_name, entity_id, components )
88
+ @calls << [ aspect_name, entity_id, components ]
89
+ end
90
+ end
91
+
92
+ cls
93
+ end
94
+
95
+
96
+ it "gets called for each entity which matches an aspect on each tick" do
97
+ entities_with_location = entity.with_components( location_component ).
98
+ generator.take( 5 ) # :ephemeral
99
+ entities_with_bb = entity.with_components( bounding_box_component ).
100
+ generator.take( 5 ) # -
101
+ entities_with_both = entity.
102
+ with_components( location_component, bounding_box_component ).
103
+ generator.take( 5 ) # :default
104
+
105
+ sys = world.add_system( subclass )
106
+ sys.start
107
+
108
+ world.publish( 'timing', 0.5, 1 )
109
+ world.publish_deferred_events
110
+
111
+ both_components = a_hash_including( location_component, bounding_box_component )
112
+ only_location_component = a_hash_including( location_component )
113
+
114
+ expect( sys.calls.length ).to eq( 10 )
115
+ expect( sys.calls ).to contain_exactly(
116
+ [:default, entities_with_both[0].id, both_components ],
117
+ [:default, entities_with_both[1].id, both_components ],
118
+ [:default, entities_with_both[2].id, both_components ],
119
+ [:default, entities_with_both[3].id, both_components ],
120
+ [:default, entities_with_both[4].id, both_components ],
121
+ [:emphemeral, entities_with_location[0].id, only_location_component ],
122
+ [:emphemeral, entities_with_location[1].id, only_location_component ],
123
+ [:emphemeral, entities_with_location[2].id, only_location_component ],
124
+ [:emphemeral, entities_with_location[3].id, only_location_component ],
125
+ [:emphemeral, entities_with_location[4].id, only_location_component ]
126
+ )
127
+ end
128
+
129
+ end
130
+
131
+ end
132
+
133
+
134
+ end
135
+
@@ -3,10 +3,16 @@
3
3
  require_relative '../spec_helper'
4
4
 
5
5
  require 'chione/system'
6
+ require 'chione/fixtures'
6
7
 
7
8
 
8
9
  describe Chione::System do
9
10
 
11
+ before( :all ) do
12
+ Chione::Fixtures.load( :entities )
13
+ end
14
+
15
+
10
16
  let( :location_component ) do
11
17
  Class.new( Chione::Component ) do
12
18
  field :x, default: 0
@@ -30,40 +36,218 @@ describe Chione::System do
30
36
  describe "subclass" do
31
37
 
32
38
  let( :subclass ) do
33
- Class.new(described_class)
39
+ Class.new( described_class ) do
40
+ def initialize( * )
41
+ super
42
+ @calls = []
43
+ end
44
+ attr_reader :calls
45
+
46
+ def inserted( *args )
47
+ self.calls << [ __method__, args ]
48
+ super
49
+ end
50
+ def removed( *args )
51
+ self.calls << [ __method__, args ]
52
+ super
53
+ end
54
+ end
34
55
  end
35
56
  let( :world ) { Chione::World.new }
36
57
 
37
58
 
38
- it "has a default Aspect which matches all entities" do
39
- expect( subclass.aspect ).to be_empty
40
- end
59
+ describe "aspects" do
60
+
61
+ it "has a default Aspect which matches all entities" do
62
+ expect( subclass.aspects ).to be_empty
63
+ expect( subclass.aspects[:default] ).to be_empty
64
+ end
65
+
66
+
67
+ it "can declare components for its default aspect" do
68
+ subclass.aspect :default,
69
+ all_of: volition_component,
70
+ one_of: [ tags_component, location_component ]
71
+
72
+ expect( subclass.aspects.keys ).to contain_exactly( :default )
73
+ expect( subclass.aspects[:default].all_of ).to include( volition_component )
74
+ expect( subclass.aspects[:default].one_of ).
75
+ to include( tags_component, location_component )
76
+ end
77
+
78
+
79
+ it "can declare a named aspect" do
80
+ subclass.aspect( :with_location, all_of: location_component )
81
+
82
+ expect( subclass.aspects.keys ).to contain_exactly( :with_location )
83
+ expect( subclass.aspects[:with_location].all_of ).
84
+ to contain_exactly( location_component )
85
+ end
86
+
87
+
88
+ it "can declare a named aspect using simplified syntax" do
89
+ subclass.aspect( :with_location, location_component )
90
+
91
+ expect( subclass.aspects.keys ).to contain_exactly( :with_location )
92
+ expect( subclass.aspects[:with_location].all_of ).
93
+ to contain_exactly( location_component )
94
+ end
95
+
96
+
97
+ it "can declare more than one named aspect" do
98
+ subclass.aspect( :with_location, all_of: location_component )
99
+ subclass.aspect( :self_movable, all_of: [location_component, volition_component] )
100
+
101
+ expect( subclass.aspects.keys ).to contain_exactly( :with_location, :self_movable )
102
+ expect( subclass.aspects[:with_location].all_of ).to contain_exactly( location_component )
103
+ expect( subclass.aspects[:self_movable].all_of ).
104
+ to contain_exactly( location_component, volition_component )
105
+ end
106
+
107
+
108
+ it "keeps an explicitly-defined default aspect event if other named ones are added" do
109
+ subclass.aspect( :default, all_of: location_component )
110
+ subclass.aspect( :self_movable, all_of: [location_component, volition_component] )
111
+
112
+ expect( subclass.aspects.keys ).to contain_exactly( :default, :self_movable )
113
+ expect( subclass.aspects[:default].all_of ).to contain_exactly( location_component )
114
+ expect( subclass.aspects[:self_movable].all_of ).
115
+ to contain_exactly( location_component, volition_component )
116
+ end
117
+
41
118
 
119
+ it "is notified when adding a component causes an entity to start matching one of its aspects" do
120
+ subclass.aspect( :with_location, all_of: location_component )
121
+ subclass.aspect( :self_movable, all_of: [location_component, volition_component] )
122
+
123
+ entity = world.create_entity
124
+
125
+ sys = world.add_system( subclass )
126
+ sys.start
127
+
128
+ world.add_component_to( entity, location_component )
129
+ world.publish_deferred_events
130
+
131
+ expect( sys.calls.length ).to eq( 1 )
132
+ expect( sys.calls ).to include([
133
+ :inserted, [
134
+ :with_location,
135
+ entity.id,
136
+ {location_component => an_instance_of(location_component)}
137
+ ]
138
+ ])
139
+
140
+ world.add_component_to( entity, volition_component )
141
+ world.publish_deferred_events
142
+
143
+ expect( sys.calls.length ).to eq( 2 )
144
+ expect( sys.calls ).to include([
145
+ :inserted, [
146
+ :self_movable,
147
+ entity.id,
148
+ {
149
+ location_component => an_instance_of(location_component),
150
+ volition_component => an_instance_of(volition_component)
151
+ }
152
+ ]
153
+ ])
154
+ end
42
155
 
43
- it "can declare components for its aspect" do
44
- subclass.aspect all_of: volition_component,
45
- one_of: [ tags_component, location_component ]
46
156
 
47
- expect( subclass.aspect ).to_not be_empty
48
- expect( subclass.aspect.all_of ).to include( volition_component )
49
- expect( subclass.aspect.one_of ).to include( tags_component, location_component )
157
+ it "is notified when removing a component causes an entity to no longer match one of its aspects" do
158
+ subclass.aspect( :with_location, all_of: location_component )
159
+ subclass.aspect( :self_movable, all_of: [location_component, volition_component] )
160
+
161
+ entity = world.create_entity
162
+ world.add_component_to( entity, location_component )
163
+ world.add_component_to( entity, volition_component )
164
+ world.publish_deferred_events
165
+
166
+ sys = world.add_system( subclass )
167
+ sys.start
168
+
169
+ world.remove_component_from( entity, volition_component )
170
+ world.publish_deferred_events
171
+
172
+ expect( sys.calls.length ).to eq( 1 )
173
+ expect( sys.calls ).to include([
174
+ :removed, [
175
+ :self_movable,
176
+ entity.id,
177
+ {location_component => an_instance_of(location_component)}
178
+ ]
179
+ ])
180
+
181
+ world.remove_component_from( entity, location_component )
182
+ world.publish_deferred_events
183
+
184
+ expect( sys.calls.length ).to eq( 2 )
185
+ expect( sys.calls ).to include([
186
+ :removed, [
187
+ :with_location,
188
+ entity.id,
189
+ {}
190
+ ]
191
+ ])
192
+ end
193
+
50
194
  end
51
195
 
52
196
 
53
- it "can declare required components for its aspect via shorthand syntax" do
54
- subclass.for_entities_that_have( volition_component )
197
+ describe "event handlers" do
198
+
199
+ it "has no event handlers by default" do
200
+ expect( subclass.event_handlers ).to be_empty
201
+ end
202
+
203
+
204
+ it "can register a handler method for an event" do
205
+ subclass.on( 'entity/created' ) do |*|
206
+ # no-op
207
+ end
208
+
209
+ expect( subclass.event_handlers ).
210
+ to include( ['entity/created', 'on_entity_created_event'] )
211
+ expect( subclass.instance_methods ).to include( :on_entity_created_event )
212
+ end
213
+
214
+
215
+ it "provides a convenience declaration for the timing event" do
216
+ subclass.every_tick do |*|
217
+ # no-op
218
+ end
219
+
220
+ expect( subclass.event_handlers ).
221
+ to include( ['timing', 'on_timing_event'] )
222
+ expect( subclass.instance_methods ).to include( :on_timing_event )
223
+ end
224
+
225
+
226
+ it "unwraps arguments to `every_tick` callbacks" do
227
+ received_delta = received_tick = nil
228
+ subclass.every_tick do |delta, tick|
229
+ received_delta = delta
230
+ received_tick = tick
231
+ end
232
+
233
+ instance = subclass.new( world )
234
+ instance.on_timing_event( 'timing', [0.016666666666666666, 0] )
235
+
236
+ expect( received_delta ).to be_within( 0.00001 ).of( 0.016666666666666666 )
237
+ expect( received_tick ).to eq( 0 )
238
+ end
55
239
 
56
- expect( subclass.aspect ).to_not be_empty
57
- expect( subclass.aspect.all_of ).to include( volition_component )
58
240
  end
59
241
 
60
242
 
61
243
  describe "instance" do
62
244
 
63
245
  let( :subclass ) do
64
- subclass = super()
65
- subclass.aspect all_of: volition_component,
66
- one_of: [ tags_component, location_component ]
246
+ subclass = Class.new( described_class )
247
+ subclass.aspect( :default,
248
+ all_of: volition_component,
249
+ one_of: [tags_component, location_component] )
250
+ subclass.aspect( :tagged, all_of: tags_component )
67
251
  subclass
68
252
  end
69
253
 
@@ -72,20 +256,33 @@ describe Chione::System do
72
256
  end
73
257
 
74
258
 
75
- it "can enumerate the entities from the world that match its aspect" do
76
- ent1 = world.create_entity
77
- ent1.add_component( volition_component )
78
- ent1.add_component( tags_component )
259
+ before( :each ) do
260
+ @ent1 = world.create_entity
261
+ @ent1.add_component( volition_component )
262
+ @ent1.add_component( tags_component )
79
263
 
80
- ent2 = world.create_entity
81
- ent2.add_component( volition_component )
82
- ent2.add_component( location_component )
264
+ @ent2 = world.create_entity
265
+ @ent2.add_component( volition_component )
266
+ @ent2.add_component( location_component )
83
267
 
84
- ent3 = world.create_entity
85
- ent3.add_component( volition_component )
268
+ @ent3 = world.create_entity
269
+ @ent3.add_component( volition_component )
86
270
 
271
+ @ent4 = world.create_entity
272
+ @ent4.add_component( location_component )
273
+ @ent4.add_component( tags_component )
274
+ end
275
+
276
+
277
+ it "can enumerate the entities from the world that match its default aspect" do
87
278
  expect( instance.entities ).to be_a( Enumerator )
88
- expect( instance.entities ).to contain_exactly( ent1, ent2 )
279
+ expect( instance.entities ).to contain_exactly( @ent1.id, @ent2.id )
280
+ end
281
+
282
+
283
+ it "can enumerate the entities from the world that match one of its named aspects" do
284
+ expect( instance.entities(:tagged) ).to be_a( Enumerator )
285
+ expect( instance.entities(:tagged) ).to contain_exactly( @ent1.id, @ent4.id )
89
286
  end
90
287
 
91
288