woyo-world 0.0.7 → 0.0.8
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/woyo/world/action.rb +37 -0
- data/lib/woyo/world/attributes.rb +56 -82
- data/lib/woyo/world/group.rb +49 -25
- data/lib/woyo/world/version.rb +1 -1
- data/lib/woyo/world/way.rb +0 -1
- data/lib/woyo/world/world.rb +1 -0
- data/lib/woyo/world/world_object.rb +2 -3
- data/spec/woyo/dsl/dsl_spec.rb +138 -23
- data/spec/woyo/world/action_spec.rb +94 -0
- data/spec/woyo/world/attributes_spec.rb +20 -15
- data/spec/woyo/world/item_spec.rb +21 -0
- data/spec/woyo/world/world_object_spec.rb +4 -2
- data/todo.txt +27 -132
- metadata +5 -5
- data/lib/woyo/world/actions.rb +0 -24
- data/spec/woyo/world/actions_spec.rb +0 -71
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8cbc8a5d94754ea504e7df2e4cee823c172bec1c
|
4
|
+
data.tar.gz: 3aa4c6addaf017db4ceddfcbba8c062341883444
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0170be24f57b4fe0fbbb0bfb576b63ef8e5d5be4e20c86ee81e639728ae003336b4f4e0d035a27795fb5780b15455b6e591bfa260331af0d13b8cc2ad834ba5d
|
7
|
+
data.tar.gz: fa95a9aebdd52812b0940d1db8c4f60de33a3212518a5140f09f815018a6b66bec8b345b9c80966146b31ea1e3b1c50af348ed17a906f91773279967dba9717e
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require_relative 'world_object'
|
2
|
+
|
3
|
+
module Woyo
|
4
|
+
|
5
|
+
class Action < WorldObject
|
6
|
+
|
7
|
+
def initialize_object
|
8
|
+
super
|
9
|
+
attribute :describe
|
10
|
+
exclusion :result
|
11
|
+
@proc = proc { nil }
|
12
|
+
end
|
13
|
+
|
14
|
+
def execute
|
15
|
+
proc_result = if @proc.arity < 1
|
16
|
+
@context.instance_eval &@proc
|
17
|
+
else
|
18
|
+
@context.instance_exec self, &@proc
|
19
|
+
end
|
20
|
+
true_members = result.members.select { |member| result[member] }
|
21
|
+
true_members = true_members[0] if true_members.count == 1
|
22
|
+
true_members = nil if true_members.empty?
|
23
|
+
{ result: true_members, describe: describe, execution: proc_result }
|
24
|
+
end
|
25
|
+
|
26
|
+
def execution &block
|
27
|
+
if block_given?
|
28
|
+
@proc = block
|
29
|
+
else
|
30
|
+
@proc
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
@@ -8,44 +8,61 @@ module Woyo
|
|
8
8
|
|
9
9
|
module Attributes
|
10
10
|
|
11
|
+
class AttributesHash < Hash
|
12
|
+
|
13
|
+
alias_method :names, :keys
|
14
|
+
alias_method :set, :[]=
|
15
|
+
|
16
|
+
attr_reader :listeners
|
17
|
+
|
18
|
+
def initialize
|
19
|
+
@listeners = {}
|
20
|
+
end
|
21
|
+
|
22
|
+
def add_attribute_listener attr, listener
|
23
|
+
@listeners[attr] = listener
|
24
|
+
end
|
25
|
+
|
26
|
+
def []= attr, value
|
27
|
+
old_value = self[attr]
|
28
|
+
super
|
29
|
+
if ( listener = @listeners[attr] ) && value != old_value
|
30
|
+
listener.notify attr, value
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
11
36
|
def attribute *attrs, &block
|
12
|
-
|
37
|
+
attributes *attrs, &block
|
13
38
|
end
|
14
39
|
|
15
40
|
def attributes *attrs, &block
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
def _attributes attrs, ivn:, &block
|
20
|
-
if instance_variable_defined? ivn
|
21
|
-
ivar = instance_variable_get ivn
|
22
|
-
else
|
23
|
-
ivar = instance_variable_set ivn, Woyo::Attributes::AttributesHash.new
|
24
|
-
end
|
25
|
-
return ivar if attrs.empty?
|
41
|
+
@attributes ||= Woyo::Attributes::AttributesHash.new
|
42
|
+
return @attributes if attrs.empty?
|
26
43
|
attrs.each do |attr|
|
27
44
|
case
|
28
45
|
when attr.kind_of?( Hash )
|
29
46
|
attr.each do |attr_sym,default|
|
30
|
-
define_attr_methods attr_sym, default
|
31
|
-
|
47
|
+
define_attr_methods attr_sym, default
|
48
|
+
@attributes[attr_sym] = send "#{attr_sym}_default"
|
32
49
|
end
|
33
50
|
when block
|
34
|
-
define_attr_methods attr, block
|
35
|
-
|
51
|
+
define_attr_methods attr, block
|
52
|
+
@attributes[attr] = send "#{attr}_default"
|
36
53
|
else
|
37
|
-
unless
|
38
|
-
define_attr_methods attr
|
39
|
-
|
54
|
+
unless @attributes.include? attr
|
55
|
+
define_attr_methods attr
|
56
|
+
@attributes[attr] = nil
|
40
57
|
end
|
41
58
|
end
|
42
59
|
end
|
43
60
|
end
|
44
61
|
|
45
|
-
def define_attr_methods
|
62
|
+
def define_attr_methods attr, default = nil
|
46
63
|
define_attr_default attr, default
|
47
|
-
define_attr_equals attr
|
48
|
-
define_attr attr
|
64
|
+
define_attr_equals attr
|
65
|
+
define_attr attr
|
49
66
|
if default == true || default == false # boolean convenience methods
|
50
67
|
define_attr? attr
|
51
68
|
define_attr! attr
|
@@ -58,26 +75,31 @@ module Attributes
|
|
58
75
|
end
|
59
76
|
end
|
60
77
|
|
61
|
-
def define_attr_equals
|
78
|
+
def define_attr_equals attr
|
62
79
|
define_singleton_method "#{attr}=" do |arg|
|
63
|
-
|
64
|
-
ivar[attr] = arg
|
80
|
+
@attributes[attr] = arg
|
65
81
|
end
|
66
82
|
end
|
67
83
|
|
68
|
-
def define_attr
|
84
|
+
def define_attr attr
|
69
85
|
define_singleton_method attr do |arg = nil|
|
70
|
-
|
71
|
-
return ivar[attr] = arg unless arg.nil?
|
86
|
+
return @attributes[attr] = arg unless arg.nil?
|
72
87
|
case
|
73
|
-
when
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
88
|
+
when @attributes[attr].kind_of?( Hash )
|
89
|
+
truthy_matches = @attributes[attr].collect do |name,value|
|
90
|
+
truthy = if @attributes[name].respond_to?( :call )
|
91
|
+
@attributes[name].arity == 0 ? @attributes[name].call : @attributes[name].call(self)
|
92
|
+
else
|
93
|
+
@attributes[name]
|
94
|
+
end
|
95
|
+
truthy ? value : nil
|
96
|
+
end.compact
|
97
|
+
truthy_matches = truthy_matches.count == 1 ? truthy_matches[0] : truthy_matches
|
98
|
+
return truthy_matches
|
99
|
+
when @attributes[attr].respond_to?( :call )
|
100
|
+
return @attributes[attr].arity == 0 ? @attributes[attr].call : @attributes[attr].call(self)
|
79
101
|
else
|
80
|
-
|
102
|
+
@attributes[attr]
|
81
103
|
end
|
82
104
|
end
|
83
105
|
end
|
@@ -102,54 +124,6 @@ module Attributes
|
|
102
124
|
send "#{attr}=", true
|
103
125
|
end
|
104
126
|
|
105
|
-
def groups
|
106
|
-
@groups
|
107
|
-
end
|
108
|
-
|
109
|
-
def group sym, *attrs
|
110
|
-
@groups ||= {}
|
111
|
-
grp = @groups[sym] ? @groups[sym] : ( @groups[sym] = Woyo::Attributes::Group.new attributes )
|
112
|
-
attributes *attrs
|
113
|
-
attrs.each do |attr|
|
114
|
-
if attr.kind_of? Hash
|
115
|
-
attr.each do |attr_sym,default_value|
|
116
|
-
grp << attr_sym
|
117
|
-
end
|
118
|
-
else
|
119
|
-
grp << attr
|
120
|
-
end
|
121
|
-
end
|
122
|
-
define_singleton_method sym do
|
123
|
-
@groups[sym]
|
124
|
-
end
|
125
|
-
grp
|
126
|
-
end
|
127
|
-
|
128
|
-
def exclusions
|
129
|
-
@exclusions
|
130
|
-
end
|
131
|
-
|
132
|
-
def exclusion sym, *attrs
|
133
|
-
@exclusions ||= {}
|
134
|
-
exc = @exclusions[sym] ? @exclusions[sym] : ( @exclusions[sym] = Woyo::Attributes::Exclusion.new attributes )
|
135
|
-
attributes *attrs
|
136
|
-
attrs.each do |attr|
|
137
|
-
define_attr? attr
|
138
|
-
define_attr! attr
|
139
|
-
if attr.kind_of? Hash
|
140
|
-
attr.each do |attr_sym,default_value|
|
141
|
-
exc << attr_sym
|
142
|
-
end
|
143
|
-
else
|
144
|
-
exc << attr
|
145
|
-
end
|
146
|
-
end
|
147
|
-
define_singleton_method sym do
|
148
|
-
@exclusions[sym]
|
149
|
-
end
|
150
|
-
exc
|
151
|
-
end
|
152
|
-
|
153
127
|
end
|
154
128
|
|
155
129
|
end
|
data/lib/woyo/world/group.rb
CHANGED
@@ -1,34 +1,10 @@
|
|
1
1
|
require 'forwardable'
|
2
|
+
require_relative 'attributes'
|
2
3
|
|
3
4
|
module Woyo
|
4
5
|
|
5
6
|
module Attributes
|
6
7
|
|
7
|
-
class AttributesHash < Hash
|
8
|
-
|
9
|
-
alias_method :names, :keys
|
10
|
-
alias_method :set, :[]=
|
11
|
-
|
12
|
-
attr_reader :listeners
|
13
|
-
|
14
|
-
def initialize
|
15
|
-
@listeners = {}
|
16
|
-
end
|
17
|
-
|
18
|
-
def add_attribute_listener attr, listener
|
19
|
-
@listeners[attr] = listener
|
20
|
-
end
|
21
|
-
|
22
|
-
def []= attr, value
|
23
|
-
old_value = self[attr]
|
24
|
-
super
|
25
|
-
if ( listener = @listeners[attr] ) && value != old_value
|
26
|
-
listener.notify attr, value
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
end
|
31
|
-
|
32
8
|
class Group
|
33
9
|
|
34
10
|
extend Forwardable
|
@@ -110,6 +86,54 @@ module Attributes
|
|
110
86
|
|
111
87
|
end
|
112
88
|
|
89
|
+
def groups
|
90
|
+
@groups
|
91
|
+
end
|
92
|
+
|
93
|
+
def group sym, *attrs
|
94
|
+
@groups ||= {}
|
95
|
+
grp = @groups[sym] ? @groups[sym] : ( @groups[sym] = Woyo::Attributes::Group.new attributes )
|
96
|
+
attributes *attrs
|
97
|
+
attrs.each do |attr|
|
98
|
+
if attr.kind_of? Hash
|
99
|
+
attr.each do |attr_sym,default_value|
|
100
|
+
grp << attr_sym
|
101
|
+
end
|
102
|
+
else
|
103
|
+
grp << attr
|
104
|
+
end
|
105
|
+
end
|
106
|
+
define_singleton_method sym do
|
107
|
+
@groups[sym]
|
108
|
+
end
|
109
|
+
grp
|
110
|
+
end
|
111
|
+
|
112
|
+
def exclusions
|
113
|
+
@exclusions
|
114
|
+
end
|
115
|
+
|
116
|
+
def exclusion sym, *attrs
|
117
|
+
@exclusions ||= {}
|
118
|
+
exc = @exclusions[sym] ? @exclusions[sym] : ( @exclusions[sym] = Woyo::Attributes::Exclusion.new attributes )
|
119
|
+
attributes *attrs
|
120
|
+
attrs.each do |attr|
|
121
|
+
define_attr? attr
|
122
|
+
define_attr! attr
|
123
|
+
if attr.kind_of? Hash
|
124
|
+
attr.each do |attr_sym,default_value|
|
125
|
+
exc << attr_sym
|
126
|
+
end
|
127
|
+
else
|
128
|
+
exc << attr
|
129
|
+
end
|
130
|
+
end
|
131
|
+
define_singleton_method sym do
|
132
|
+
@exclusions[sym]
|
133
|
+
end
|
134
|
+
exc
|
135
|
+
end
|
136
|
+
|
113
137
|
end
|
114
138
|
|
115
139
|
end
|
data/lib/woyo/world/version.rb
CHANGED
data/lib/woyo/world/way.rb
CHANGED
data/lib/woyo/world/world.rb
CHANGED
@@ -1,6 +1,4 @@
|
|
1
|
-
|
2
1
|
require_relative 'attributes'
|
3
|
-
require_relative 'actions'
|
4
2
|
require_relative 'evaluate'
|
5
3
|
|
6
4
|
module Woyo
|
@@ -8,12 +6,13 @@ module Woyo
|
|
8
6
|
class WorldObject
|
9
7
|
|
10
8
|
include Attributes
|
11
|
-
include Actions
|
12
9
|
include Evaluate
|
13
10
|
|
14
11
|
attr_reader :id, :context
|
15
12
|
attr_accessor :_test
|
16
13
|
|
14
|
+
children :action
|
15
|
+
|
17
16
|
def initialize id, context: nil, &block
|
18
17
|
@id = id.to_s.downcase.to_sym
|
19
18
|
@context = context
|
data/spec/woyo/dsl/dsl_spec.rb
CHANGED
@@ -219,9 +219,10 @@ describe 'DSL' do
|
|
219
219
|
|
220
220
|
head 'Items'
|
221
221
|
|
222
|
-
doc 'stuff here'
|
223
|
-
|
224
|
-
|
222
|
+
doc 'stuff here'
|
223
|
+
# do
|
224
|
+
# pending
|
225
|
+
# end
|
225
226
|
|
226
227
|
end
|
227
228
|
|
@@ -309,17 +310,20 @@ describe 'DSL' do
|
|
309
310
|
head 'Context'
|
310
311
|
text "Other objects may be referred to in different ways."
|
311
312
|
|
312
|
-
doc 'world'
|
313
|
-
|
314
|
-
|
313
|
+
doc 'world'
|
314
|
+
# do
|
315
|
+
# pending
|
316
|
+
# end
|
315
317
|
|
316
|
-
doc 'location'
|
317
|
-
|
318
|
-
|
318
|
+
doc 'location'
|
319
|
+
# do
|
320
|
+
# pending
|
321
|
+
# end
|
319
322
|
|
320
|
-
doc 'context'
|
321
|
-
|
322
|
-
|
323
|
+
doc 'context'
|
324
|
+
# do
|
325
|
+
# pending
|
326
|
+
# end
|
323
327
|
|
324
328
|
end
|
325
329
|
|
@@ -335,16 +339,124 @@ describe 'DSL' do
|
|
335
339
|
text "Actions change the state of the world, usually by changing the value of attributes on world objects"
|
336
340
|
|
337
341
|
doc "making changes" do
|
338
|
-
text "Actions may be defined for a world object, in this case, an item."
|
342
|
+
text "Actions may be defined for a world object, in this case, an item. Actions have an id, and optionally a name and description. The steps an action is to perform is contained within an 'execution' block, which executes in the context of the containing world object."
|
343
|
+
code pre: "world.evaluate do",
|
344
|
+
code: "location :here do
|
345
|
+
item :thing do
|
346
|
+
name 'Thing One'
|
347
|
+
description 'Rename thing'
|
348
|
+
action :rename do
|
349
|
+
execution do
|
350
|
+
name 'Thing Two'
|
351
|
+
end
|
352
|
+
end
|
353
|
+
end
|
354
|
+
end",
|
355
|
+
post: "end"
|
356
|
+
text "Initially the name is as defined"
|
357
|
+
code "location = world.location :here"
|
358
|
+
code "thing = location.item :thing" => "world.locations[:here].items[:thing]"
|
359
|
+
code "thing.name" => "'Thing One'"
|
360
|
+
text "Executing rename action changes the name."
|
361
|
+
code "thing.action(:rename).execute"
|
362
|
+
code "thing.name" => "'Thing Two'"
|
363
|
+
end
|
364
|
+
|
365
|
+
doc "describing the action " do
|
366
|
+
text "When an action is executed it returns information about the action, including a description of the action."
|
339
367
|
code pre: "world.evaluate do",
|
340
368
|
code: "location :here do
|
341
369
|
item :thing do
|
342
|
-
name 'Thing
|
343
|
-
action :
|
344
|
-
|
370
|
+
name 'Thing One'
|
371
|
+
action :rename do
|
372
|
+
description 'Rename thing'
|
373
|
+
describe 'Thing is renamed'
|
374
|
+
execution do
|
375
|
+
name 'Thing Two'
|
376
|
+
end
|
345
377
|
end
|
346
|
-
|
347
|
-
|
378
|
+
end
|
379
|
+
end",
|
380
|
+
post: "end"
|
381
|
+
text "Initially the name is as defined"
|
382
|
+
code "location = world.location :here"
|
383
|
+
code "thing = location.item :thing" => "world.locations[:here].items[:thing]"
|
384
|
+
code "thing.name" => "'Thing One'"
|
385
|
+
text "Executing the rename action returns a result hash."
|
386
|
+
code "result = thing.action(:rename).execute" => "{ result: nil, execution: 'Thing Two', describe: 'Thing is renamed' }"
|
387
|
+
text "The name was changed as expected"
|
388
|
+
code "thing.name" => "'Thing Two'"
|
389
|
+
text "The contents of the result hash are useful to an application such as Woyo::Server that interacts with the world."
|
390
|
+
text "Action execution may be described with a default text (as in this case), or the value of :result can determine the describing text (as we'll see in the next example)."
|
391
|
+
code "result[:describe]" => "'Thing is renamed'"
|
392
|
+
text "The actual value returned by the execution block is also provided, this may be any kind of object."
|
393
|
+
code "result[:execution]" => "'Thing Two'"
|
394
|
+
end
|
395
|
+
|
396
|
+
doc "one of many descriptions" do
|
397
|
+
text "An action may be described for different results. An exclusion group of results ensures that only one description will be chosen. Here possible results are :success and :failure."
|
398
|
+
code pre: "world.evaluate do",
|
399
|
+
code: "location :here do
|
400
|
+
item :thing do
|
401
|
+
name 'Thing One'
|
402
|
+
action :rename do
|
403
|
+
description 'Rename thing'
|
404
|
+
exclusion :result, :success, :failure
|
405
|
+
describe success: 'Thing is renamed',
|
406
|
+
failure: 'Not renamed'
|
407
|
+
execution do |this|
|
408
|
+
if name == 'Thing One'
|
409
|
+
name 'Thing Two'
|
410
|
+
this.success!
|
411
|
+
else
|
412
|
+
this.failure!
|
413
|
+
end
|
414
|
+
end
|
415
|
+
end
|
416
|
+
end
|
417
|
+
end",
|
418
|
+
post: "end"
|
419
|
+
text "Initially the name is as defined"
|
420
|
+
code "location = world.location :here"
|
421
|
+
code "thing = location.item :thing" => "world.locations[:here].items[:thing]"
|
422
|
+
code "thing.name" => "'Thing One'"
|
423
|
+
text "Executing the rename action once succeeds."
|
424
|
+
code "thing.action(:rename).execute" => "{ result: :success, describe: 'Thing is renamed', execution: true }"
|
425
|
+
text "The name was changed as expected"
|
426
|
+
code "thing.name" => "'Thing Two'"
|
427
|
+
text "Executing the rename action again fails."
|
428
|
+
code "thing.action(:rename).execute" => "{ result: :failure, describe: 'Not renamed', execution: true }"
|
429
|
+
text "The name is unchanged."
|
430
|
+
code "thing.name" => "'Thing Two'"
|
431
|
+
end
|
432
|
+
|
433
|
+
doc "some of many descriptions" do
|
434
|
+
text "An action may be described for results other than :success and :failure, and may return multiple results and descriptions. This is achieved by specifying a group :result containing expected results."
|
435
|
+
code pre: "world.evaluate do",
|
436
|
+
code: "location :here do
|
437
|
+
item :thing do
|
438
|
+
name 'Thing One'
|
439
|
+
action :rename do
|
440
|
+
description 'Rename thing'
|
441
|
+
group :result, :renamed, :special, :same
|
442
|
+
describe renamed: 'Thing is renamed',
|
443
|
+
special: 'Newly discovered thing',
|
444
|
+
same: 'Not renamed'
|
445
|
+
execution do |this|
|
446
|
+
case
|
447
|
+
when name == 'Thing One'
|
448
|
+
name 'Thing Two'
|
449
|
+
this.renamed = true
|
450
|
+
when name == 'Thing Two'
|
451
|
+
name 'Thing Three'
|
452
|
+
this.renamed = true
|
453
|
+
this.special = true
|
454
|
+
else
|
455
|
+
this.renamed = false
|
456
|
+
this.special = false
|
457
|
+
this.same = true
|
458
|
+
end
|
459
|
+
end
|
348
460
|
end
|
349
461
|
end
|
350
462
|
end",
|
@@ -352,13 +464,16 @@ describe 'DSL' do
|
|
352
464
|
text "Initially the name is as defined"
|
353
465
|
code "location = world.location :here"
|
354
466
|
code "thing = location.item :thing" => "world.locations[:here].items[:thing]"
|
355
|
-
code "thing.name" => "'Thing?'"
|
356
|
-
text "Invoking action 'one', changes the name."
|
357
|
-
code "thing.one"
|
358
467
|
code "thing.name" => "'Thing One'"
|
359
|
-
text "
|
360
|
-
code "thing.
|
468
|
+
text "Executing the rename action once."
|
469
|
+
code "thing.action(:rename).execute" => "{ result: :renamed, describe: 'Thing is renamed', execution: true }"
|
361
470
|
code "thing.name" => "'Thing Two'"
|
471
|
+
text "Executing the rename action again."
|
472
|
+
code "thing.action(:rename).execute" => "{ result: [ :renamed, :special ], describe: [ 'Thing is renamed', 'Newly discovered thing' ], execution: true }"
|
473
|
+
code "thing.name" => "'Thing Three'"
|
474
|
+
text "Executing the rename action one more time."
|
475
|
+
code "thing.action(:rename).execute" => "{ result: :same, describe: 'Not renamed', execution: true }"
|
476
|
+
code "thing.name" => "'Thing Three'"
|
362
477
|
end
|
363
478
|
|
364
479
|
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'woyo/world/action'
|
3
|
+
|
4
|
+
describe Woyo::Action do
|
5
|
+
|
6
|
+
let( :action ) { Woyo::Action.new :test }
|
7
|
+
|
8
|
+
context 'has exclusions' do
|
9
|
+
|
10
|
+
context ':result' do
|
11
|
+
|
12
|
+
it 'exists' do
|
13
|
+
expect(action.exclusions.names).to include :result
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'has no members' do
|
17
|
+
expect(action.result.members).to be_empty
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
context "execution may be defined" do
|
25
|
+
|
26
|
+
it "as a block" do
|
27
|
+
action.execution { :answer }
|
28
|
+
expect(action.execution).to be_instance_of Proc
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
context "may be executed" do
|
34
|
+
|
35
|
+
it 'by calling #execution directly' do
|
36
|
+
action.execution { :answer }
|
37
|
+
expect(action.execution.call).to eq :answer
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'calling #execution returns nil by default' do
|
41
|
+
expect(action.execution.call).to be_nil
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'by calling #execute wrapper' do
|
45
|
+
|
46
|
+
context 'with result exclusion' do
|
47
|
+
|
48
|
+
it 'returns result hash with single values for single truthy result' do
|
49
|
+
action.exclusion :result, :success, :failure
|
50
|
+
action.execution { |this| this.success! }
|
51
|
+
action.describe success: "Succeeded"
|
52
|
+
expect(action.execute).to eq( { result: :success, describe: "Succeeded", execution: true } )
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'returns result hash with empty results for empty result exclusion' do
|
56
|
+
action.describe "Empty"
|
57
|
+
expect(action.execute).to eq( { result: nil, describe: "Empty", execution: nil } )
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
context 'with result group' do
|
63
|
+
|
64
|
+
it 'returns result hash with single value for single truthy result' do
|
65
|
+
action.group :result, a: false, b: true, c: false
|
66
|
+
action.describe a: 'aaa', b: 'bbb', c: 'ccc'
|
67
|
+
expect(action.execute).to eq( { result: :b, describe: 'bbb', execution: nil } )
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'returns result hash with multiple values for multiple truthy results' do
|
71
|
+
action.group :result, a: true, b: false, c: true
|
72
|
+
action.describe a: 'aaa', b: 'bbb', c: 'ccc'
|
73
|
+
expect(action.execute).to eq( { result: [ :a, :c ], describe: [ 'aaa', 'ccc' ], execution: nil } )
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'returns result hash with empty results for empty result group' do
|
77
|
+
action.describe "Empty"
|
78
|
+
expect(action.execute).to eq( { result: nil, describe: "Empty", execution: nil } )
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'returns result hash with empty results for no truthy results' do
|
82
|
+
action.group :result, a: false, b: false, c: false
|
83
|
+
action.describe "Empty"
|
84
|
+
expect(action.execute).to eq( { result: nil, describe: "Empty", execution: nil } )
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
@@ -115,15 +115,13 @@ describe Woyo::Attributes do
|
|
115
115
|
expect(attr_test.attributes.names).to eq [ :attr1, :attr2, :attr3, :attr4 ]
|
116
116
|
end
|
117
117
|
|
118
|
-
it 'with a default value' do
|
118
|
+
it 'with a default value (non-hash)' do
|
119
119
|
attr_test = AttrTest.new
|
120
120
|
attr_test.attributes attr_with_array___default: [ 1, 2, 3 ]
|
121
|
-
attr_test.attributes attr_with_hash____default: { a: 1, b: 2, c: 3 }
|
122
121
|
attr_test.attributes attr_with_number__default: 12345
|
123
122
|
attr_test.attributes attr_with_string__default: "abcde"
|
124
123
|
attr_test.attributes attr_with_boolean_default: true
|
125
124
|
expect(attr_test.attr_with_array___default).to eq [ 1, 2, 3 ]
|
126
|
-
expect(attr_test.attr_with_hash____default).to eq ( { a: 1, b: 2, c: 3 } )
|
127
125
|
expect(attr_test.attr_with_number__default).to eq 12345
|
128
126
|
expect(attr_test.attr_with_string__default).to eq "abcde"
|
129
127
|
expect(attr_test.attr_with_boolean_default).to eq true
|
@@ -391,26 +389,33 @@ describe Woyo::Attributes do
|
|
391
389
|
let(:hat) { AttrTest.new }
|
392
390
|
|
393
391
|
before :each do
|
394
|
-
hat.attributes :reaction, :hot, :cold
|
392
|
+
hat.attributes :reaction, :hot, :warm, :cool, :cold
|
395
393
|
end
|
396
394
|
|
397
395
|
it 'accept a hash as value' do
|
398
|
-
expect { hat.reaction hot: 'Sweat', cold: 'Shiver' }.to_not raise_error
|
396
|
+
expect { hat.reaction hot: 'Sweat', warm: 'Relax', cool: 'Huddle', cold: 'Shiver', not_attr: 'Nothing' }.to_not raise_error
|
399
397
|
end
|
400
398
|
|
401
|
-
it 'return
|
402
|
-
hat.reaction hot: 'Sweat', cold: 'Shiver'
|
399
|
+
it 'return a list values of the multiple keys that are truthy attributes' do
|
400
|
+
hat.reaction hot: 'Sweat', warm: 'Relax', cool: 'Huddle', cold: 'Shiver'
|
401
|
+
expect(hat.reaction).to eq [ ]
|
403
402
|
hat.cold = true
|
404
|
-
|
405
|
-
hat.
|
406
|
-
|
403
|
+
hat.cool = proc { 'truthy' }
|
404
|
+
hat.warm = proc { nil } # falsey
|
405
|
+
hat.hot = false
|
406
|
+
expect(hat.reaction).to eq [ 'Huddle', 'Shiver' ]
|
407
407
|
end
|
408
408
|
|
409
|
-
it '
|
410
|
-
hat.reaction hot: 'Sweat', cold: 'Shiver'
|
411
|
-
hat.
|
412
|
-
hat.
|
413
|
-
expect(hat.reaction).to eq
|
409
|
+
it 'return a single value of a single key that is a truthy attribute' do
|
410
|
+
hat.reaction hot: 'Sweat', warm: 'Relax', cool: 'Huddle', cold: 'Shiver'
|
411
|
+
expect(hat.reaction).to eq [ ]
|
412
|
+
hat.warm = true
|
413
|
+
expect(hat.reaction).to eq 'Relax'
|
414
|
+
end
|
415
|
+
|
416
|
+
it 'returns empty list if no keys are truthy attributes' do
|
417
|
+
hat.reaction hot: 'Sweat', warm: 'Relax', cool: 'Huddle', cold: 'Shiver'
|
418
|
+
expect(hat.reaction).to eq [ ]
|
414
419
|
end
|
415
420
|
|
416
421
|
end
|
@@ -42,5 +42,26 @@ describe Woyo::Item do
|
|
42
42
|
|
43
43
|
end
|
44
44
|
|
45
|
+
context 'actions' do
|
46
|
+
|
47
|
+
let( :item ) do
|
48
|
+
item = Woyo::Item.new :item do
|
49
|
+
action( :action1 ) { :empty }
|
50
|
+
action( :action2 ) { :empty }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'are listed' do
|
55
|
+
expect(item.actions.count).to eq 2
|
56
|
+
expect(item.actions.keys).to eq [ :action1, :action2 ]
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'are accessible' do
|
60
|
+
expect(action = item.actions[:action1]).to be_instance_of Woyo::Action
|
61
|
+
expect(action.id).to eq :action1
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
45
66
|
end
|
46
67
|
|
@@ -62,10 +62,12 @@ describe Woyo::WorldObject do
|
|
62
62
|
it 'actions' do
|
63
63
|
wo = Woyo::WorldObject.new :thing do
|
64
64
|
action :time do
|
65
|
-
|
65
|
+
execution do
|
66
|
+
Time.now
|
67
|
+
end
|
66
68
|
end
|
67
69
|
end
|
68
|
-
expect(wo.time).to be < Time.now
|
70
|
+
expect(wo.action(:time).execution.call).to be < Time.now
|
69
71
|
end
|
70
72
|
|
71
73
|
end
|
data/todo.txt
CHANGED
@@ -1,40 +1,24 @@
|
|
1
1
|
|
2
|
-
|
3
|
-
|
4
|
-
#
|
5
|
-
|
2
|
+
World featutes...
|
3
|
+
|
4
|
+
# actions
|
5
|
+
|
6
|
+
action.execute #=> {
|
7
|
+
result: :symbol, # from result exclusion
|
8
|
+
describe: 'text' # first (only) matching symbol
|
9
|
+
execution: return_value_from_execution_block
|
10
|
+
}
|
11
|
+
|
12
|
+
action.execute #=> {
|
13
|
+
result: [ :symbol1, :symbol2, :symbol3 ] # from result group
|
14
|
+
describe: [ 'text1', 'text2', 'text3' ] # all true/truthy members
|
15
|
+
execution: return_value_from_execution_block
|
16
|
+
}
|
17
|
+
|
6
18
|
# attributes
|
7
19
|
|
8
|
-
|
9
|
-
|
10
|
-
this will fix the problem of WorldObject#id being nil when Attributes is prepended and Attributes#initialize is run first
|
11
|
-
|
12
|
-
boolean attributes
|
13
|
-
|
14
|
-
ok detected by class level definition boolean default value
|
15
|
-
|
16
|
-
attribute open: true
|
17
|
-
attribute dark: false
|
18
|
-
|
19
|
-
ok after defintion, set boolean attribute values...
|
20
|
-
|
21
|
-
open = true
|
22
|
-
open true
|
23
|
-
open! # ... open true
|
24
|
-
|
25
|
-
ok query boolean attribute values... ?
|
26
|
-
|
27
|
-
dark
|
28
|
-
dark? # ... dark ? true : false
|
29
|
-
is? :dark # ... def is? attr ; attributes[attr] ? true : false ; end
|
30
|
-
|
31
|
-
ok additional class level define (and detect?) boolean attribute values...
|
32
|
-
|
33
|
-
is :open
|
34
|
-
is :dark
|
35
|
-
is :warm, :bright
|
36
|
-
|
37
|
-
group...
|
20
|
+
attribute returns whole attribute hash
|
21
|
+
attribute.render returns array of values with truthy keys
|
38
22
|
|
39
23
|
group weapons: [ :sword, :knife, :rock, :fists ] # hash - key will be group name, value an array of symbols for members
|
40
24
|
group :weapons, [ :sword, :knife, :rock, :fists ] # symbol will be group name, array of symbols for members
|
@@ -43,45 +27,11 @@ ok group :weapons, :sword, :knife, :rock, :fists # symbols, first will b
|
|
43
27
|
group stats: [ speed: 10, charisma: 20, strength: 30 ] # defaults like attributes (these are attributes)
|
44
28
|
ok group :stats, speed: 10, charisma: 20, strength: 30 # ditto
|
45
29
|
|
46
|
-
ok
|
47
|
-
|
48
|
-
|
49
|
-
world_object.weapons.names #=> weapons.keys
|
50
|
-
world_object.weapons.values #=> weapons.values
|
51
|
-
|
52
|
-
ok group is not an enum, it has no value itself
|
53
|
-
ok attribute methods for members in parent
|
54
|
-
|
55
|
-
world_object.sword
|
56
|
-
world_object.knife
|
57
|
-
|
58
|
-
no is group a namespace ? - optional ... attribute methods in group
|
59
|
-
|
60
|
-
world_object.weapons.sword
|
61
|
-
world_object.weapons.knife
|
62
|
-
|
63
|
-
ok access attributes via group hash
|
64
|
-
|
65
|
-
world_object.weapons[:sword] #=> rusty_sword
|
66
|
-
world_object.weapons[:sword] = sharp_sword
|
67
|
-
|
68
|
-
ok exclusive group (booleans)...
|
69
|
-
|
70
|
-
ok exclusive_group passable: , :open, :close
|
71
|
-
exclusive_group light: [ :dark, :dim, :bright ]
|
72
|
-
exclusive_group temperature: [ :cold, :cool, :warm, :hot ]
|
73
|
-
|
74
|
-
ok same as regular group +...
|
75
|
-
|
76
|
-
ok first member will be true, the rest false
|
77
|
-
ok additional convenience methods for members...
|
78
|
-
|
79
|
-
world_object.open?
|
80
|
-
world_object.close?
|
81
|
-
world_object.open!
|
82
|
-
world_object.close!
|
30
|
+
ok exclusion passable: , :open, :close
|
31
|
+
exclusion light: [ :dark, :dim, :bright ]
|
32
|
+
exclusion temperature: [ :cold, :cool, :warm, :hot ]
|
83
33
|
|
84
|
-
# descriptions
|
34
|
+
# descriptions (an attribute)
|
85
35
|
|
86
36
|
displayed everytime a location is visited...
|
87
37
|
|
@@ -136,18 +86,8 @@ ok additional convenience methods for members...
|
|
136
86
|
|
137
87
|
# location
|
138
88
|
|
139
|
-
locations do NOT contain locations
|
140
|
-
locations are a graph, linked by ways
|
141
|
-
|
142
|
-
location :here do
|
143
|
-
name 'Home'
|
144
|
-
description 'Cosy'
|
145
|
-
end
|
146
|
-
|
147
89
|
location :here, name: 'Home', description: 'cosy'
|
148
90
|
|
149
|
-
mass assignment...
|
150
|
-
|
151
91
|
locations do
|
152
92
|
home name: 'Home', description: 'Cosy'
|
153
93
|
yard name: 'Garden', description: 'Green'
|
@@ -156,39 +96,8 @@ ok additional convenience methods for members...
|
|
156
96
|
|
157
97
|
# way
|
158
98
|
|
159
|
-
a way leads one-way to a location
|
160
|
-
|
161
|
-
way :out do
|
162
|
-
name 'Door'
|
163
|
-
description 'Big strong'
|
164
|
-
to :garden
|
165
|
-
end
|
166
|
-
|
167
|
-
way can describe being open closed
|
168
|
-
|
169
|
-
way :out do
|
170
|
-
name 'Door'
|
171
|
-
to :garden
|
172
|
-
description 'Wooden door'
|
173
|
-
description :open => 'An open wooden door leading to sunlit garden',
|
174
|
-
:closed => 'A closed solid wooden door'
|
175
|
-
end
|
176
|
-
|
177
|
-
way can describe going (open) or not (closed)
|
178
|
-
|
179
|
-
way :out do
|
180
|
-
name 'Door'
|
181
|
-
to :garden
|
182
|
-
go :open => 'Stepping into the warm sunlight',
|
183
|
-
:closed => 'The solid wooden door remains stubbornly closed'
|
184
|
-
end
|
185
|
-
|
186
99
|
way :door, to: :other_location, description: 'Big'
|
187
100
|
|
188
|
-
is openable, default open
|
189
|
-
|
190
|
-
mass assignment...
|
191
|
-
|
192
101
|
ways do
|
193
102
|
east to: :china, name: 'slow boat'
|
194
103
|
west to: :wild_west, name: 'fast horse'
|
@@ -196,17 +105,13 @@ ok additional convenience methods for members...
|
|
196
105
|
down to: :pit, name: 'hole'
|
197
106
|
end
|
198
107
|
|
199
|
-
|
200
|
-
- north,south,east,west,
|
108
|
+
absolute directions
|
109
|
+
- north, south, east, west, high, low
|
201
110
|
|
202
|
-
|
203
|
-
|
204
|
-
items may be in a location
|
111
|
+
relative directions
|
112
|
+
- forward, back, left, right, up, down
|
205
113
|
|
206
|
-
|
207
|
-
name 'Thing'
|
208
|
-
description 'Useful object'
|
209
|
-
end
|
114
|
+
# items
|
210
115
|
|
211
116
|
item :id, name: 'Thing', description: "...", detail: "..."
|
212
117
|
|
@@ -223,14 +128,6 @@ ok additional convenience methods for members...
|
|
223
128
|
longer description when examined
|
224
129
|
detail "....."
|
225
130
|
|
226
|
-
have attributes
|
227
|
-
|
228
|
-
attributes :small, :light
|
229
|
-
|
230
|
-
may know their location ?
|
231
|
-
|
232
|
-
if item.location == :home
|
233
|
-
|
234
131
|
may know their relation to other items ?
|
235
132
|
|
236
133
|
if item.contains? :water
|
@@ -248,8 +145,6 @@ ok additional convenience methods for members...
|
|
248
145
|
|
249
146
|
end
|
250
147
|
|
251
|
-
# code for ... triggers, events, handlers, conditions, etc...
|
252
|
-
|
253
148
|
# can locations, items be subclassed in DSL ?
|
254
149
|
|
255
150
|
class Vase < Item
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: woyo-world
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gerard Fowley
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-08-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -81,7 +81,7 @@ files:
|
|
81
81
|
- README.md
|
82
82
|
- Rakefile
|
83
83
|
- lib/woyo/world.rb
|
84
|
-
- lib/woyo/world/
|
84
|
+
- lib/woyo/world/action.rb
|
85
85
|
- lib/woyo/world/attributes.rb
|
86
86
|
- lib/woyo/world/character.rb
|
87
87
|
- lib/woyo/world/evaluate.rb
|
@@ -97,7 +97,7 @@ files:
|
|
97
97
|
- spec/spec_doc_formatter.rb
|
98
98
|
- spec/spec_helper.rb
|
99
99
|
- spec/woyo/dsl/dsl_spec.rb
|
100
|
-
- spec/woyo/world/
|
100
|
+
- spec/woyo/world/action_spec.rb
|
101
101
|
- spec/woyo/world/attributes_spec.rb
|
102
102
|
- spec/woyo/world/character_spec.rb
|
103
103
|
- spec/woyo/world/evaluate_spec.rb
|
@@ -139,7 +139,7 @@ test_files:
|
|
139
139
|
- spec/spec_doc_formatter.rb
|
140
140
|
- spec/spec_helper.rb
|
141
141
|
- spec/woyo/dsl/dsl_spec.rb
|
142
|
-
- spec/woyo/world/
|
142
|
+
- spec/woyo/world/action_spec.rb
|
143
143
|
- spec/woyo/world/attributes_spec.rb
|
144
144
|
- spec/woyo/world/character_spec.rb
|
145
145
|
- spec/woyo/world/evaluate_spec.rb
|
data/lib/woyo/world/actions.rb
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
require_relative 'attributes'
|
2
|
-
|
3
|
-
module Woyo
|
4
|
-
|
5
|
-
module Actions
|
6
|
-
|
7
|
-
include Attributes
|
8
|
-
|
9
|
-
def action *acts, &block
|
10
|
-
send :_attributes, acts, ivn: '@actions', &block
|
11
|
-
end
|
12
|
-
|
13
|
-
def actions *acts, &block
|
14
|
-
send :_attributes, acts, ivn: '@actions', &block
|
15
|
-
end
|
16
|
-
|
17
|
-
def do act
|
18
|
-
send act
|
19
|
-
end
|
20
|
-
|
21
|
-
end
|
22
|
-
|
23
|
-
end
|
24
|
-
|
@@ -1,71 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
require 'woyo/world/actions'
|
3
|
-
|
4
|
-
describe Woyo::Actions do
|
5
|
-
|
6
|
-
context "may be listed" do
|
7
|
-
|
8
|
-
let( :at ) { class ActionTest ; include Woyo::Actions ; end.new }
|
9
|
-
|
10
|
-
it "intially empty" do
|
11
|
-
expect(at.actions).to be_instance_of Woyo::Attributes::AttributesHash
|
12
|
-
expect(at.actions).to be_empty
|
13
|
-
end
|
14
|
-
|
15
|
-
it "single action" do
|
16
|
-
at.action( :act1 ) { Time.now }
|
17
|
-
expect(at.actions.count).to eq 1
|
18
|
-
expect(at.actions[:act1]). to be_instance_of Proc
|
19
|
-
end
|
20
|
-
|
21
|
-
it "multiple actions" do
|
22
|
-
at.action( :act1 ) { Time.now }
|
23
|
-
at.action( :act2 ) { Time.now }
|
24
|
-
expect(at.actions.count).to eq 2
|
25
|
-
expect(at.actions[:act2]). to be_instance_of Proc
|
26
|
-
end
|
27
|
-
|
28
|
-
end
|
29
|
-
|
30
|
-
context "may be defined" do
|
31
|
-
|
32
|
-
let( :at ) { class ActionTest ; include Woyo::Actions ; end.new }
|
33
|
-
|
34
|
-
it "via a block" do
|
35
|
-
at.action( :sum ) { 1 + 2 }
|
36
|
-
expect(at.actions).to include :sum
|
37
|
-
expect(at.actions[:sum]).to be_instance_of Proc
|
38
|
-
end
|
39
|
-
|
40
|
-
it "via a proc" do
|
41
|
-
at.action sum: proc { 1 + 2 }
|
42
|
-
expect(at.actions).to include :sum
|
43
|
-
expect(at.actions[:sum]).to be_instance_of Proc
|
44
|
-
end
|
45
|
-
|
46
|
-
it "via a lambda" do
|
47
|
-
at.action sum: lambda { |this| 1 + 2 }
|
48
|
-
expect(at.actions).to include :sum
|
49
|
-
expect(at.actions[:sum]).to be_instance_of Proc
|
50
|
-
end
|
51
|
-
|
52
|
-
end
|
53
|
-
|
54
|
-
context "may be invoked" do
|
55
|
-
|
56
|
-
let( :at ) { class ActionTest ; include Woyo::Actions ; end.new }
|
57
|
-
|
58
|
-
it "by name" do
|
59
|
-
at.action( :sum ) { 1 + 2 }
|
60
|
-
expect(at.sum).to eq 3
|
61
|
-
end
|
62
|
-
|
63
|
-
it "via #do :action" do
|
64
|
-
at.action( :sum ) { 1 + 2 }
|
65
|
-
expect(at.do :sum).to eq 3
|
66
|
-
end
|
67
|
-
|
68
|
-
end
|
69
|
-
|
70
|
-
end
|
71
|
-
|