ceml 0.7.13 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Makefile +1 -1
- data/VERSION +1 -1
- data/ceml.gemspec +62 -50
- data/guide/guide.html +69 -14
- data/guide/guide.md +74 -15
- data/guide/guide.pdf +0 -0
- data/lib/ceml/driver.rb +0 -181
- data/lib/ceml/lang/basic_instruction.rb +49 -0
- data/lib/ceml/{casting_statement.rb → lang/casting_statement.rb} +19 -9
- data/lib/ceml/{instruction_statements.rb → lang/instruction_statements.rb} +5 -16
- data/lib/ceml/{script.rb → lang/script.rb} +53 -43
- data/lib/ceml/lang/tt/casting.rb +432 -0
- data/lib/ceml/lang/tt/casting.treetop +29 -0
- data/lib/ceml/lang/tt/instructions.rb +1130 -0
- data/lib/ceml/lang/tt/instructions.treetop +86 -0
- data/lib/ceml/lang/tt/lexer.rb +1804 -0
- data/lib/ceml/{tt → lang/tt}/lexer.treetop +70 -7
- data/lib/ceml/lang/tt/scripts.rb +647 -0
- data/lib/ceml/{tt → lang/tt}/scripts.treetop +2 -2
- data/lib/ceml/lang.rb +10 -0
- data/lib/ceml/models/audition.rb +65 -0
- data/lib/ceml/models/bundle.rb +64 -0
- data/lib/ceml/models/cast.rb +108 -0
- data/lib/ceml/models/castable.rb +81 -0
- data/lib/ceml/{incident.rb → models/incident.rb} +63 -15
- data/lib/ceml/models/incident_model.rb +100 -0
- data/lib/ceml/models/incident_role_slot.rb +16 -0
- data/lib/ceml/models/player.rb +80 -0
- data/lib/ceml/models/queue.rb +12 -0
- data/lib/ceml/models/waiting_room.rb +40 -0
- data/lib/ceml/models.rb +16 -0
- data/lib/ceml/processor.rb +162 -0
- data/lib/ceml.rb +7 -14
- data/test/askchain.ceml +6 -0
- data/test/compliment.ceml +4 -0
- data/test/dialogues/accept.ceml +24 -0
- data/test/dialogues/basic_seed.ceml +26 -0
- data/test/dialogues/jordan.ceml +121 -0
- data/test/helper.rb +44 -39
- data/test/jane.ceml +48 -0
- data/test/{test_casting.rb → lang/test_casting.rb} +5 -0
- data/test/lang/test_instructions.rb +42 -0
- data/test/{test_scripts.rb → lang/test_scripts.rb} +3 -2
- data/test/sync.ceml +6 -0
- data/test/test_castable.rb +20 -0
- data/test/test_dialogues.rb +58 -0
- data/test/test_incident.rb +64 -127
- metadata +54 -30
- data/.gitignore +0 -23
- data/lib/ceml/confluence.rb +0 -63
- data/lib/ceml/role.rb +0 -61
- data/lib/ceml/tt/casting.treetop +0 -65
- data/lib/ceml/tt/instructions.treetop +0 -91
- data/test/test_instructions.rb +0 -27
- data/test/test_release.rb +0 -78
data/Makefile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.8.0
|
data/ceml.gemspec
CHANGED
@@ -1,77 +1,89 @@
|
|
1
1
|
# Generated by jeweler
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{ceml}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.8.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Joe Edelman"]
|
12
|
-
s.date = %q{2011-
|
12
|
+
s.date = %q{2011-05-27}
|
13
13
|
s.description = %q{a language for coordinating real world events}
|
14
14
|
s.email = %q{joe@citizenlogistics.com}
|
15
15
|
s.extra_rdoc_files = [
|
16
16
|
"LICENSE",
|
17
|
-
|
17
|
+
"README.markdown"
|
18
18
|
]
|
19
19
|
s.files = [
|
20
20
|
".document",
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
21
|
+
"LICENSE",
|
22
|
+
"Makefile",
|
23
|
+
"README.markdown",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION",
|
26
|
+
"ceml.gemspec",
|
27
|
+
"editors/CEML.tmbundle/Syntaxes/ceml.tmLanguage",
|
28
|
+
"editors/CEML.tmbundle/info.plist",
|
29
|
+
"examples/breakfast-exchange.ceml",
|
30
|
+
"examples/citizen-investigation.ceml",
|
31
|
+
"examples/high-fives.ceml",
|
32
|
+
"guide/guide.html",
|
33
|
+
"guide/guide.md",
|
34
|
+
"guide/guide.pdf",
|
35
|
+
"lib/ceml.rb",
|
36
|
+
"lib/ceml/driver.rb",
|
37
|
+
"lib/ceml/lang.rb",
|
38
|
+
"lib/ceml/lang/basic_instruction.rb",
|
39
|
+
"lib/ceml/lang/casting_statement.rb",
|
40
|
+
"lib/ceml/lang/instruction_statements.rb",
|
41
|
+
"lib/ceml/lang/script.rb",
|
42
|
+
"lib/ceml/lang/tt/casting.rb",
|
43
|
+
"lib/ceml/lang/tt/casting.treetop",
|
44
|
+
"lib/ceml/lang/tt/instructions.rb",
|
45
|
+
"lib/ceml/lang/tt/instructions.treetop",
|
46
|
+
"lib/ceml/lang/tt/lexer.rb",
|
47
|
+
"lib/ceml/lang/tt/lexer.treetop",
|
48
|
+
"lib/ceml/lang/tt/scripts.rb",
|
49
|
+
"lib/ceml/lang/tt/scripts.treetop",
|
50
|
+
"lib/ceml/models.rb",
|
51
|
+
"lib/ceml/models/audition.rb",
|
52
|
+
"lib/ceml/models/bundle.rb",
|
53
|
+
"lib/ceml/models/cast.rb",
|
54
|
+
"lib/ceml/models/castable.rb",
|
55
|
+
"lib/ceml/models/incident.rb",
|
56
|
+
"lib/ceml/models/incident_model.rb",
|
57
|
+
"lib/ceml/models/incident_role_slot.rb",
|
58
|
+
"lib/ceml/models/player.rb",
|
59
|
+
"lib/ceml/models/queue.rb",
|
60
|
+
"lib/ceml/models/waiting_room.rb",
|
61
|
+
"lib/ceml/processor.rb",
|
62
|
+
"test/askchain.ceml",
|
63
|
+
"test/compliment.ceml",
|
64
|
+
"test/dialogues/accept.ceml",
|
65
|
+
"test/dialogues/basic_seed.ceml",
|
66
|
+
"test/dialogues/jordan.ceml",
|
67
|
+
"test/helper.rb",
|
68
|
+
"test/jane.ceml",
|
69
|
+
"test/lang/test_casting.rb",
|
70
|
+
"test/lang/test_instructions.rb",
|
71
|
+
"test/lang/test_scripts.rb",
|
72
|
+
"test/sync.ceml",
|
73
|
+
"test/test_castable.rb",
|
74
|
+
"test/test_dialogues.rb",
|
75
|
+
"test/test_incident.rb",
|
76
|
+
"try"
|
55
77
|
]
|
56
78
|
s.homepage = %q{http://github.com/citizenlogistics/ceml}
|
57
|
-
s.rdoc_options = ["--charset=UTF-8"]
|
58
79
|
s.require_paths = ["lib"]
|
59
|
-
s.rubygems_version = %q{1.
|
80
|
+
s.rubygems_version = %q{1.6.2}
|
60
81
|
s.summary = %q{a language for coordinating real world events}
|
61
|
-
s.test_files = [
|
62
|
-
"test/helper.rb",
|
63
|
-
"test/test_casting.rb",
|
64
|
-
"test/test_incident.rb",
|
65
|
-
"test/test_instructions.rb",
|
66
|
-
"test/test_release.rb",
|
67
|
-
"test/test_scripts.rb"
|
68
|
-
]
|
69
82
|
|
70
83
|
if s.respond_to? :specification_version then
|
71
|
-
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
72
84
|
s.specification_version = 3
|
73
85
|
|
74
|
-
if Gem::Version.new(Gem::
|
86
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
75
87
|
s.add_runtime_dependency(%q<treetop>, [">= 0"])
|
76
88
|
else
|
77
89
|
s.add_dependency(%q<treetop>, [">= 0"])
|
data/guide/guide.html
CHANGED
@@ -31,7 +31,7 @@ tell b: Someone nearby likes the color |a.x|. Find them.</code></pre>
|
|
31
31
|
|
32
32
|
<h2 id='commands'>Commands</h2>
|
33
33
|
|
34
|
-
<p>The above script only uses three CEML commands–<em>gather</em>, <em>ask</em>, and <em>tell</em>–but there aren’t many more commands to learn. Only
|
34
|
+
<p>The above script only uses three CEML commands–<em>gather</em>, <em>ask</em>, and <em>tell</em>–but there aren’t many more commands to learn. Only six more, actually: besides <em>gather</em>, the other ingredients commands are <em>await</em> and <em>nab</em>. Besides <em>ask</em> and <em>tell</em>, the other instructions commands are <em>assign</em>, <em>release</em>, <em>expect</em>, and <em>sync</em>.</p>
|
35
35
|
|
36
36
|
<p>As a sneak preview, here’s an example that uses the other commands:</p>
|
37
37
|
|
@@ -40,7 +40,7 @@ await 1 concerned patient
|
|
40
40
|
nab a doctor within 5 miles
|
41
41
|
ask patient re problem: What's wrong?
|
42
42
|
assign doctor to patient: Attend to patient's |problem|.
|
43
|
-
|
43
|
+
release doctor as responsive</code></pre>
|
44
44
|
|
45
45
|
<h2 id='texts'>Texts</h2>
|
46
46
|
|
@@ -70,7 +70,7 @@ tell both: thanks so much for participating!</code></pre>
|
|
70
70
|
|
71
71
|
<p>In the examples so far, the words like ‘enemy’, ‘reporter’, ‘guy’, ‘girl’, ‘a’, ‘b’, ‘signup’, ‘patient’, and ‘doctor’ are <em>role names</em>. Most of the time, a role name can be any word you like. They are just a placeholder to connect <em>casting commands</em> like <em>gather</em> and <em>await</em>, with <em>coordination commands</em> like <em>tell</em>.</p>
|
72
72
|
|
73
|
-
<p>Squads on Groundcrew, however, can define special meanings for certain roles. So on a particular squad, a ‘doctor’ might mean someone who’s been tagged/
|
73
|
+
<p>Squads on Groundcrew, however, can define special meanings for certain roles. So on a particular squad, a ‘doctor’ might mean someone who’s been tagged/released with the tag ‘doctor’, and a patient might mean anyone else.</p>
|
74
74
|
|
75
75
|
<p>Some role names are always special. For instance, if you use the role name ‘organizer’, it always means someone who’s an official organizer for that squad. And if you use the role names ‘both’, ‘all’, ‘each’, or ‘everyone’, it means that the instruction applies to everyone regardless of how they were <em>cast</em>.</p>
|
76
76
|
|
@@ -116,15 +116,38 @@ assign player to streetcorner:
|
|
116
116
|
|
117
117
|
<p>On iPhone or mobile web, the player will get a little map to direct them. There are several special <em>role names</em> which are used to indicate places rather than people. These include “streetcorner”, “park”, “field”, and “landmark”.</p>
|
118
118
|
|
119
|
+
<h2 id='expect'>Expect</h2>
|
120
|
+
|
121
|
+
<p>When you ask a question using <em>ask</em>, you may not want to let a player proceed unless they’ve answered in a way you understand or recognize. This can be accomplished using <em>expect</em>:</p>
|
122
|
+
|
123
|
+
<pre><code>ask player re path:
|
124
|
+
Do you prefer to the path to the left or the right?
|
125
|
+
expect /left|right/ from player:
|
126
|
+
Please say "left" or "right"!</code></pre>
|
127
|
+
|
128
|
+
<p>The expect statement, like the release statement described below, can be passed strings, certain keywords, or regular expressions.</p>
|
129
|
+
|
130
|
+
<h2 id='sync_up'>Sync up</h2>
|
131
|
+
|
132
|
+
<p>There also may be times when you want to sync up several players and make sure they have both completed assignments or answered questions before they each proceed with the script:</p>
|
133
|
+
|
134
|
+
<pre><code>assign redcoat:
|
135
|
+
March briskly towards Concord
|
136
|
+
assign minuteman:
|
137
|
+
Hide in the bushes
|
138
|
+
sync up redcoat and minuteman
|
139
|
+
tell both:
|
140
|
+
Prepare to be surprised.</code></pre>
|
141
|
+
|
119
142
|
<h2 id='choosing_players_await_gather_and_nab'>Choosing Players: Await, Gather, and Nab.</h2>
|
120
143
|
|
121
144
|
<p>So what is really the difference between <em>await</em>, <em>gather</em>, and <em>nab</em>, you may be asking? Or perhaps you have already figured it out.</p>
|
122
145
|
|
123
146
|
<p><em>Await</em> defines a kind of trigger–as soon as the conditions awaited for are met, the script will run.</p>
|
124
147
|
|
125
|
-
<pre><code>await 2-5
|
148
|
+
<pre><code>await 2-5 level=1 players within 50ft
|
126
149
|
assign: shout out "woo-hoo!"
|
127
|
-
|
150
|
+
release as level=2</code></pre>
|
128
151
|
|
129
152
|
<p>Unless there’s an await statement in your script, it will have to be executed manually by an organizer.</p>
|
130
153
|
|
@@ -156,27 +179,59 @@ assign:
|
|
156
179
|
|
157
180
|
<p>Another qualifier with a special meaning is <em>concerned</em>. This is applied to players who, because of their text messaging or their tweeting or their interaction with the iphone or web apps, seem like are confused or have an urgent question or issue.</p>
|
158
181
|
|
159
|
-
<p>The other qualifiers don’t mean anythings special, but they select only players who have been
|
182
|
+
<p>The other qualifiers don’t mean anythings special, but they select only players who have been released or tagged with that particular word. So “gather 5-20 level=1 users” will only invite users who have been tagged or released as level=1.</p>
|
183
|
+
|
184
|
+
<h2 id='finer_control_of_matching'>Finer control of matching</h2>
|
185
|
+
|
186
|
+
<p>The <em>await</em> keyword supports a variety of options that let you match players with more specificity. For instance, you may wish to only match players that text in within 10 minutes of one another:</p>
|
160
187
|
|
161
|
-
<
|
188
|
+
<pre><code>await 2 new players over 10 minutes</code></pre>
|
162
189
|
|
163
|
-
<p>
|
190
|
+
<p>Or players that have the same favorite color:</p>
|
191
|
+
|
192
|
+
<pre><code>await 2 new players with matching favorite_color</code></pre>
|
193
|
+
|
194
|
+
<p>Here’s two scripts that cooperate to put people in groups by favorite color:</p>
|
195
|
+
|
196
|
+
<pre><code>await new signup
|
197
|
+
ask signup re name: What's your name?
|
198
|
+
ask signup re favorite_color: What's your favorite color?
|
199
|
+
|
200
|
+
await 2 players with matching favorite_color
|
201
|
+
tell players: |buddy.name| also likes |favorite_color|.</code></pre>
|
202
|
+
|
203
|
+
<h2 id='releasing'>Releasing</h2>
|
204
|
+
|
205
|
+
<p>When a player has successfully completed their role in your script, you can add or change the tags associated with that player using the <em>release</em> command.</p>
|
164
206
|
|
165
207
|
<pre><code>await 3 new signups
|
166
208
|
assign:
|
167
209
|
You have all just signed up!
|
168
210
|
Take a photo of something you
|
169
211
|
love and share with one another.
|
170
|
-
|
212
|
+
release as photo_sharing</code></pre>
|
213
|
+
|
214
|
+
<p>You can also release a player early, subject to some conditions, by using <em>if</em> or <em>unless</em>:</p>
|
215
|
+
|
216
|
+
<pre><code>await 1 boss and 1 worker
|
217
|
+
ask worker:
|
218
|
+
Do you feel good about working today?
|
219
|
+
release worker unless yes
|
220
|
+
ask worker re skill:
|
221
|
+
What are you good at?
|
222
|
+
ask boss re job:
|
223
|
+
You have a worker with skill |skill|. What should they do?</code></pre>
|
224
|
+
|
225
|
+
<p>There are some special keywords you can use after if or unless. These include “yes”, “no”, “done”, “okay”. You can also use a string in quotes (“activate”), in which case their answer is compared to the string case insensitively and with whitespace trimmed. Or you can use a <a href='http://en.wikipedia.org/wiki/Regular_expression'>regular expression</a> surrounded by forward slashes (/^red|blue|green$/) for more exact matching.</p>
|
171
226
|
|
172
227
|
<h2 id='making_connections_between_scripts'>Making Connections between Scripts</h2>
|
173
228
|
|
174
|
-
<p><em>
|
229
|
+
<p><em>Release</em> and qualifications can be used to link scripts together. The release at the end of the first script can trigger the <em>await</em> statement in the next script. Here’s an example:</p>
|
175
230
|
|
176
|
-
<pre><code>await 20 photo_sharing users
|
231
|
+
<pre><code>await 20 stage=photo_sharing users
|
177
232
|
ask re opinion:
|
178
233
|
How did you like sharing photos just now?
|
179
|
-
|
234
|
+
release as stage=signed_up</code></pre>
|
180
235
|
|
181
236
|
<h2 id='congratulations'>Congratulations</h2>
|
182
237
|
|
@@ -199,10 +254,10 @@ assign:
|
|
199
254
|
sketch out a design for something you
|
200
255
|
want to build and take a photo
|
201
256
|
document as designer_photo
|
202
|
-
|
257
|
+
release designer_photo as unreviewed
|
203
258
|
|
204
259
|
await 6 reviewers and unreviewed designer_photo document
|
205
260
|
ask reviewers re opinion:
|
206
261
|
What do you think of this idea? |designer_photo|
|
207
|
-
|
262
|
+
release designer_photo as reviewed not unreviewed</code></pre>
|
208
263
|
</body></html>
|
data/guide/guide.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
Beginners Guide to CEML
|
2
2
|
=======================
|
3
3
|
|
4
|
-
Thanks for taking an interest in CEML, the Coordinated Event
|
4
|
+
Thanks for taking an interest in CEML, the Coordinated Event Messaging Language. Using CEML you'll be able to get people to do whatever you want, whether it's evacuating a submarine, finding a gym buddy, helping people nearby, or just throwing a great party.
|
5
5
|
|
6
6
|
A CEML _script_ is a recipe for action. Just like a recipe, it has three parts: a title, a list of ingredients, and then a section that says what to do with the ingredients. Here's an example:
|
7
7
|
|
@@ -26,7 +26,7 @@ Whether you write a script compactly with short names for roles and answers or l
|
|
26
26
|
Commands
|
27
27
|
--------
|
28
28
|
|
29
|
-
The above script only uses three CEML commands--*gather*, *ask*, and *tell*--but there aren't many more commands to learn. Only
|
29
|
+
The above script only uses three CEML commands--*gather*, *ask*, and *tell*--but there aren't many more commands to learn. Only six more, actually: besides *gather*, the other ingredients commands are *await* and *nab*. Besides *ask* and *tell*, the other instructions commands are *assign*, *release*, *expect*, and *sync*.
|
30
30
|
|
31
31
|
As a sneak preview, here's an example that uses the other commands:
|
32
32
|
|
@@ -35,7 +35,7 @@ As a sneak preview, here's an example that uses the other commands:
|
|
35
35
|
nab a doctor within 5 miles
|
36
36
|
ask patient re problem: What's wrong?
|
37
37
|
assign doctor to patient: Attend to patient's |problem|.
|
38
|
-
|
38
|
+
release doctor as responsive
|
39
39
|
|
40
40
|
Texts
|
41
41
|
-----
|
@@ -67,7 +67,7 @@ Roles
|
|
67
67
|
|
68
68
|
In the examples so far, the words like 'enemy', 'reporter', 'guy', 'girl', 'a', 'b', 'signup', 'patient', and 'doctor' are *role names*. Most of the time, a role name can be any word you like. They are just a placeholder to connect *casting commands* like *gather* and *await*, with *coordination commands* like *tell*.
|
69
69
|
|
70
|
-
Squads on Groundcrew, however, can define special meanings for certain roles. So on a particular squad, a 'doctor' might mean someone who's been tagged/
|
70
|
+
Squads on Groundcrew, however, can define special meanings for certain roles. So on a particular squad, a 'doctor' might mean someone who's been tagged/released with the tag 'doctor', and a patient might mean anyone else.
|
71
71
|
|
72
72
|
Some role names are always special. For instance, if you use the role name 'organizer', it always means someone who's an official organizer for that squad. And if you use the role names 'both', 'all', 'each', or 'everyone', it means that the instruction applies to everyone regardless of how they were *cast*.
|
73
73
|
|
@@ -115,6 +115,31 @@ A special form of *assign* will additionally direct the player to a person or pl
|
|
115
115
|
|
116
116
|
On iPhone or mobile web, the player will get a little map to direct them. There are several special *role names* which are used to indicate places rather than people. These include "streetcorner", "park", "field", and "landmark".
|
117
117
|
|
118
|
+
Expect
|
119
|
+
------
|
120
|
+
|
121
|
+
When you ask a question using *ask*, you may not want to let a player proceed unless they've answered in a way you understand or recognize. This can be accomplished using *expect*:
|
122
|
+
|
123
|
+
ask player re path:
|
124
|
+
Do you prefer to the path to the left or the right?
|
125
|
+
expect /left|right/ from player:
|
126
|
+
Please say "left" or "right"!
|
127
|
+
|
128
|
+
The expect statement, like the release statement described below, can be passed strings, certain keywords, or regular expressions.
|
129
|
+
|
130
|
+
Sync up
|
131
|
+
-------
|
132
|
+
|
133
|
+
There also may be times when you want to sync up several players and make sure they have both completed assignments or answered questions before they each proceed with the script:
|
134
|
+
|
135
|
+
assign redcoat:
|
136
|
+
March briskly towards Concord
|
137
|
+
assign minuteman:
|
138
|
+
Hide in the bushes
|
139
|
+
sync up redcoat and minuteman
|
140
|
+
tell both:
|
141
|
+
Prepare to be surprised.
|
142
|
+
|
118
143
|
Choosing Players: Await, Gather, and Nab.
|
119
144
|
----------------------------------------------
|
120
145
|
|
@@ -122,9 +147,9 @@ So what is really the difference between *await*, *gather*, and *nab*, you may b
|
|
122
147
|
|
123
148
|
*Await* defines a kind of trigger--as soon as the conditions awaited for are met, the script will run.
|
124
149
|
|
125
|
-
await 2-5
|
150
|
+
await 2-5 level=1 players within 50ft
|
126
151
|
assign: shout out "woo-hoo!"
|
127
|
-
|
152
|
+
release as level=2
|
128
153
|
|
129
154
|
Unless there's an await statement in your script, it will have to be executed manually by an organizer.
|
130
155
|
|
@@ -157,29 +182,63 @@ Some of these have special meanings: the qualifier *new* is automatically appli
|
|
157
182
|
|
158
183
|
Another qualifier with a special meaning is *concerned*. This is applied to players who, because of their text messaging or their tweeting or their interaction with the iphone or web apps, seem like are confused or have an urgent question or issue.
|
159
184
|
|
160
|
-
The other qualifiers don't mean anythings special, but they select only players who have been
|
185
|
+
The other qualifiers don't mean anythings special, but they select only players who have been released or tagged with that particular word. So "gather 5-20 level=1 users" will only invite users who have been tagged or released as level=1.
|
186
|
+
|
187
|
+
Finer control of matching
|
188
|
+
-------------------------
|
189
|
+
|
190
|
+
The *await* keyword supports a variety of options that let you match players with more specificity. For instance, you may wish to only match players that text in within 10 minutes of one another:
|
161
191
|
|
162
|
-
|
192
|
+
await 2 new players over 10 minutes
|
193
|
+
|
194
|
+
Or players that have the same favorite color:
|
195
|
+
|
196
|
+
await 2 new players with matching favorite_color
|
197
|
+
|
198
|
+
Here's two scripts that cooperate to put people in groups by favorite color:
|
199
|
+
|
200
|
+
await new signup
|
201
|
+
ask signup re name: What's your name?
|
202
|
+
ask signup re favorite_color: What's your favorite color?
|
203
|
+
|
204
|
+
await 2 players with matching favorite_color
|
205
|
+
tell players: |buddy.name| also likes |favorite_color|.
|
206
|
+
|
207
|
+
|
208
|
+
Releasing
|
163
209
|
----------
|
164
210
|
|
165
|
-
When a player has successfully completed their role in your script, you can add or change the tags associated with that player using the *
|
211
|
+
When a player has successfully completed their role in your script, you can add or change the tags associated with that player using the *release* command.
|
166
212
|
|
167
213
|
await 3 new signups
|
168
214
|
assign:
|
169
215
|
You have all just signed up!
|
170
216
|
Take a photo of something you
|
171
217
|
love and share with one another.
|
172
|
-
|
218
|
+
release as photo_sharing
|
219
|
+
|
220
|
+
You can also release a player early, subject to some conditions, by using *if* or *unless*:
|
221
|
+
|
222
|
+
await 1 boss and 1 worker
|
223
|
+
ask worker:
|
224
|
+
Do you feel good about working today?
|
225
|
+
release worker unless yes
|
226
|
+
ask worker re skill:
|
227
|
+
What are you good at?
|
228
|
+
ask boss re job:
|
229
|
+
You have a worker with skill |skill|. What should they do?
|
230
|
+
|
231
|
+
There are some special keywords you can use after if or unless. These include "yes", "no", "done", "okay". You can also use a string in quotes ("activate"), in which case their answer is compared to the string case insensitively and with whitespace trimmed. Or you can use a [regular expression](http://en.wikipedia.org/wiki/Regular_expression) surrounded by forward slashes (/^red|blue|green$/) for more exact matching.
|
173
232
|
|
174
233
|
Making Connections between Scripts
|
175
234
|
----------------------------------
|
176
235
|
|
177
|
-
*
|
236
|
+
*Release* and qualifications can be used to link scripts together. The release at the end of the first script can trigger the *await* statement in the next script. Here's an example:
|
178
237
|
|
179
|
-
await 20 photo_sharing users
|
238
|
+
await 20 stage=photo_sharing users
|
180
239
|
ask re opinion:
|
181
240
|
How did you like sharing photos just now?
|
182
|
-
|
241
|
+
release as stage=signed_up
|
183
242
|
|
184
243
|
Congratulations
|
185
244
|
---------------
|
@@ -206,9 +265,9 @@ The February or March 2011 version of CEML will support passing and curating not
|
|
206
265
|
sketch out a design for something you
|
207
266
|
want to build and take a photo
|
208
267
|
document as designer_photo
|
209
|
-
|
268
|
+
release designer_photo as unreviewed
|
210
269
|
|
211
270
|
await 6 reviewers and unreviewed designer_photo document
|
212
271
|
ask reviewers re opinion:
|
213
272
|
What do you think of this idea? |designer_photo|
|
214
|
-
|
273
|
+
release designer_photo as reviewed not unreviewed
|
data/guide/guide.pdf
CHANGED
Binary file
|
data/lib/ceml/driver.rb
CHANGED
@@ -1,32 +1,5 @@
|
|
1
|
-
require 'set'
|
2
|
-
|
3
|
-
module Kernel
|
4
|
-
def gen_code(size = 8)
|
5
|
-
rand(36**size).to_s(36)
|
6
|
-
end
|
7
|
-
end
|
8
|
-
|
9
1
|
module CEML
|
10
2
|
class Driver
|
11
|
-
|
12
|
-
PLAYERS = {}
|
13
|
-
INCIDENTS = {}
|
14
|
-
def with_incident(id, script = nil, metadata = {})
|
15
|
-
id ||= rand(36**10).to_s(36)
|
16
|
-
PLAYERS[id] ||= []
|
17
|
-
INCIDENTS[id] ||= CEML::Incident.new to_bytecode(script), id if script
|
18
|
-
raise "no incident #{id}" unless INCIDENTS[id]
|
19
|
-
yield INCIDENTS[id], PLAYERS[id], metadata if block_given?
|
20
|
-
|
21
|
-
PLAYERS[id].select{ |p| p[:released] }.each do |p|
|
22
|
-
release p
|
23
|
-
ping_all p[:script_collection_id], p
|
24
|
-
end
|
25
|
-
|
26
|
-
id
|
27
|
-
end
|
28
|
-
alias_method :start, :with_incident
|
29
|
-
|
30
3
|
def to_bytecode bytecode_or_script
|
31
4
|
case bytecode_or_script
|
32
5
|
when String; return CEML.parse(:script, bytecode_or_script).bytecode
|
@@ -35,159 +8,5 @@ module CEML
|
|
35
8
|
else return nil
|
36
9
|
end
|
37
10
|
end
|
38
|
-
|
39
|
-
def log(s)
|
40
|
-
# puts s
|
41
|
-
end
|
42
|
-
|
43
|
-
SCRIPTS = Hash.new{ |h,k| h[k] = [] }
|
44
|
-
def add_script script_collection_id, script
|
45
|
-
SCRIPTS[script_collection_id] << script
|
46
|
-
end
|
47
|
-
|
48
|
-
def ping_all script_collection_id, candidate, release = false
|
49
|
-
candidate[:script_collection_id] = script_collection_id
|
50
|
-
SCRIPTS[script_collection_id].each{ |s| ping script_collection_id, s.roles_to_cast, candidate, release }
|
51
|
-
end
|
52
|
-
|
53
|
-
def released_from_all script_collection_id, candidate
|
54
|
-
SCRIPTS[script_collection_id].each{ |s| release script_collection_id, s.roles_to_cast, candidate }
|
55
|
-
end
|
56
|
-
|
57
|
-
def release p
|
58
|
-
p[:tags] -= ['new']
|
59
|
-
if p[:released] =~ /^(\w+)=/
|
60
|
-
p[:tags].delete_if{ |t| t =~ /^#{$1}=/ }
|
61
|
-
end
|
62
|
-
p[:tags] += [p[:released]]
|
63
|
-
[:pc, :roles, :released].each{ |sym| p.delete(sym) }
|
64
|
-
(p[:matchables]||={}).update (p[:qs_answers]||{})
|
65
|
-
end
|
66
|
-
|
67
|
-
def launch id, script_collection_id, roleset, *cast
|
68
|
-
script = SCRIPTS[script_collection_id].select{ |s| s.roles_to_cast == roleset }.sort_by{ rand }.first
|
69
|
-
unless script
|
70
|
-
rolesets = SCRIPTS[script_collection_id].map(&:roles_to_cast)
|
71
|
-
raise "matching roleset not found: #{roleset.inspect} in #{rolesets.inspect}"
|
72
|
-
end
|
73
|
-
log "launching #{script.bytecode.inspect} with cast #{cast.inspect}"
|
74
|
-
push id, script.bytecode, *cast
|
75
|
-
end
|
76
|
-
|
77
|
-
|
78
|
-
LOCATIONS = Hash.new{ |h,k| h[k] = [] }
|
79
|
-
def with_confluences script_collection_id, roleset
|
80
|
-
yield LOCATIONS["#{script_collection_id}:#{roleset.hash}"]
|
81
|
-
end
|
82
|
-
|
83
|
-
def ping script_collection_id, roleset, candidate, involvement = :sticky
|
84
|
-
return unless roleset.any?{ |r| r.fits? candidate }
|
85
|
-
candidate[:ts] = CEML.clock
|
86
|
-
already_launched_with = nil
|
87
|
-
run_after = []
|
88
|
-
|
89
|
-
with_confluences script_collection_id, roleset do |confluences|
|
90
|
-
already_launched_with = nil
|
91
|
-
# live_with = confluences.select{ |c| c.live_with?(candidate) }
|
92
|
-
# if not live_with.empty?
|
93
|
-
# already_launched_with = live_with.first.incident_id
|
94
|
-
# live_with.each{ |c| c.rm candidate } if involvement != :sticky
|
95
|
-
# break if involvement != :released
|
96
|
-
# end
|
97
|
-
|
98
|
-
locs = confluences.group_by{ |l| l.stage_with_candidate(candidate) }
|
99
|
-
if locs[:joinable]
|
100
|
-
log "joining..."
|
101
|
-
first = locs[:joinable].shift
|
102
|
-
first.push candidate unless first.incident_id == already_launched_with
|
103
|
-
# JOIN THEM
|
104
|
-
run_after << [:push, first.incident_id, nil, candidate]
|
105
|
-
|
106
|
-
elsif locs[:launchable]
|
107
|
-
log "launching..."
|
108
|
-
first = locs[:launchable].shift
|
109
|
-
first.push candidate
|
110
|
-
cast = first.cast
|
111
|
-
first.incident_id = gen_code
|
112
|
-
(locs[:launchable] + (locs[:listable]||[])).each{ |l| l.rm *cast }
|
113
|
-
# LAUNCH
|
114
|
-
run_after << [:launch, first.incident_id, script_collection_id, roleset, *cast]
|
115
|
-
|
116
|
-
elsif locs[:listable]
|
117
|
-
log "listing..."
|
118
|
-
locs[:listable].each{ |l| l.push candidate }
|
119
|
-
|
120
|
-
else
|
121
|
-
c = Confluence.new(roleset)
|
122
|
-
log "start-listing..."
|
123
|
-
if c.stage_with_candidate(candidate) == :launchable
|
124
|
-
log "start-launching..."
|
125
|
-
c.push candidate
|
126
|
-
c.incident_id = gen_code
|
127
|
-
run_after << [:launch, c.incident_id, script_collection_id, roleset, candidate]
|
128
|
-
else
|
129
|
-
c.push candidate
|
130
|
-
end
|
131
|
-
confluences << c
|
132
|
-
end
|
133
|
-
confluences.delete_if(&:over?)
|
134
|
-
end
|
135
|
-
|
136
|
-
run_after.each do |cmd|
|
137
|
-
send(*cmd)
|
138
|
-
end
|
139
|
-
|
140
|
-
# if already_launched_with and involvement == :sticky
|
141
|
-
# puts "PUSHING INSTEAD"
|
142
|
-
# push already_launched_with, nil, candidate
|
143
|
-
# end
|
144
|
-
end
|
145
|
-
|
146
|
-
def push incident_id, script, *updated_players
|
147
|
-
with_incident incident_id, script do |incident, players, metadata|
|
148
|
-
subpost incident, players, metadata, *updated_players
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
def post incident_id, *updated_players
|
153
|
-
push incident_id, nil, *updated_players
|
154
|
-
end
|
155
|
-
|
156
|
-
alias_method :run, :post
|
157
|
-
|
158
|
-
def subpost incident, players, metadata, *updated_players
|
159
|
-
updated_players.each do |player|
|
160
|
-
player_id = player[:id]
|
161
|
-
player[:roles] = Set.new([*player[:roles] || []])
|
162
|
-
player[:roles] << :agents << :players << :both << :all << :each << :everyone
|
163
|
-
if existing_player = players.find{ |p| p[:id] == player_id }
|
164
|
-
existing_player[:roles] += player.delete :roles
|
165
|
-
existing_player.update player
|
166
|
-
else
|
167
|
-
players << player
|
168
|
-
end
|
169
|
-
end
|
170
|
-
incident.run(players) do |player, meth, what|
|
171
|
-
meth = "player_#{meth}"
|
172
|
-
log "[#{incident.id}] #{meth}: #{player[:id]} #{what.inspect}"
|
173
|
-
if respond_to? meth
|
174
|
-
metadata.update :player => player, :players => players, :id => incident.id
|
175
|
-
result = send(meth, metadata, what)
|
176
|
-
metadata.delete :player
|
177
|
-
metadata.delete :players
|
178
|
-
result
|
179
|
-
end
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
JUST_SAID = {}
|
184
|
-
def player_said(data, what)
|
185
|
-
JUST_SAID[data[:player][:id]] = what
|
186
|
-
puts "Said #{what.inspect}"
|
187
|
-
end
|
188
|
-
|
189
|
-
# def player_start(data, what)
|
190
|
-
# puts "STARTED #{data[:player].inspect}"
|
191
|
-
# end
|
192
11
|
end
|
193
12
|
end
|