nodus 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: fccb4aa7a5c05d1a6e694d147acda4a93ecd1ae6
4
+ data.tar.gz: f2c48d2b460f142e5b8f76da1ffa9aee104d2b90
5
+ SHA512:
6
+ metadata.gz: 219e857114015240b3821ea7562c0668920109e6134e4f1d8d16542336685b4c1ee37a0ab44007d685fdb6f1a07466b2001b88dce1ebe6241795bc4b99f1cd2b
7
+ data.tar.gz: 3dce47b28fe69d2531a5342594699a3f3cc52dee7daa63951c50cef8adb4f6dcf9a02a1cf5f5c8a4d96fd2db9617f4545a8057a01ceed86a446ad0eddbc6e861
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
@@ -0,0 +1 @@
1
+ nodus
@@ -0,0 +1 @@
1
+ rbx
data/Gemfile ADDED
@@ -0,0 +1,38 @@
1
+ source "http://rubygems.org"
2
+ # Dependencies required by gem
3
+ gem 'activesupport', '>= 4.1.0'
4
+
5
+ # gem 'rubinius-actor'
6
+
7
+ # Will use these two for any genetic programming nodes
8
+ # ----------------------------------------------------
9
+ # gem 'parser'
10
+ # gem 'unparser'
11
+
12
+ # For future development of persistent/decoupled/cached nodes
13
+ # ----------------------------------------------------
14
+ # gem 'sequel'
15
+ # gem 'sequel_pg', require: 'sequel'
16
+ # gem 'sequel_postgresql_triggers'
17
+
18
+ # For nodes that access external APIs
19
+ # ----------------------------------------------------
20
+ # gem 'excon'
21
+
22
+ # Dependencies for gem development
23
+ # (Includes everything needed to run rake, tests, features, etc.)
24
+ group :development do
25
+ gem 'bundler'
26
+ gem 'git', git: 'git@github.com:josephwecker/ruby-git.git'
27
+ gem 'jeweler', git: 'git@github.com:technicalpickles/jeweler.git'
28
+ gem 'brice'
29
+ gem 'ansi'
30
+ gem 'rdoc'
31
+
32
+ # Testing
33
+ gem 'ffaker'
34
+ gem 'randexp'
35
+ gem 'minitest', '>= 5.3'
36
+ gem 'minitest-reporters', '>= 1.0.1'
37
+ gem 'simplecov', '~> 0.7.1', require: false
38
+ end
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2014 Joseph Wecker
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,8 @@
1
+ - ( ) CLEAN UP extension and all of the minor utilities (proplists, flexhash, etc.)
2
+ - ( ) Put in better locations
3
+ - ( ) Separate out better
4
+ - ( ) Move and separate tests
5
+ - ( ) Possibly put things in their own gems as necessary- at least mark them as "gemable" for the future
6
+ - ( ) All internal cleanup etc. for stuff as we need
7
+ - ( ) Build in some basic profiling (at the ruby vm level) and benchmark tests. Start to see right away where
8
+ bottlenecks are...
@@ -0,0 +1,383 @@
1
+ Nodus
2
+ ============================================================================================================================
3
+
4
+ _(WARNING: EXPERIMENTAL)_
5
+
6
+ Description
7
+ ------------------------------------------------------------------------------------------------------------------------------
8
+
9
+ Framework for [parallel](http://en.wikipedia.org/wiki/Parallelization)
10
+ [data-flow](http://en.wikipedia.org/wiki/Dataflow) based applications.
11
+
12
+
13
+ It is influenced by, inspired by, and in many cases similar to:
14
+
15
+ * [Functional Reactive Programming](http://en.wikipedia.org/wiki/Functional_reactive_programming) (also
16
+ [here](http://www.reactivemanifesto.org/))
17
+ * [Kahn Process Networks](http://en.wikipedia.org/wiki/Kahn_process_networks)
18
+ * [Algorithmic Skeletons](http://en.wikipedia.org/wiki/Algorithmic_skeleton) (see also [here](https://github.com/ParaPhrase/skel))
19
+ * [Iteratee IO](http://okmij.org/ftp/Streams.html)
20
+ * [Railway Oriented Programming](http://www.slideshare.net/ScottWlaschin/railway-oriented-programming)
21
+ * [Erlang](http://www.erlang.org/)
22
+ * And of course the hundreds of other reinventions with names like
23
+ `/(((data|signal|packet)?(stream|flow)|pipe(line)?|(flow|event|signal|reactive)-(processing|programming|architecture|computing|language)/`
24
+ combined with the multitude of parallelization, concurrency, and clustering paradigms, etc. etc...
25
+
26
+
27
+ ### In More Detail ##################################
28
+
29
+
30
+ - [x] Guiding philosophies
31
+ - [x] Steady-state
32
+ - [x] Composable
33
+ - [x] Proportionate
34
+ - [ ] Real-time & simulation-time
35
+ - [ ] Resilient (failure modes as first class features, exception/failure paths, bulkheads, circuitbreakers, hibernation, ...)
36
+ - [ ] Simple single-stream sequential
37
+ - [ ] URI-like notation
38
+ - [ ] Token state accumulation
39
+ - [ ] Single-stream sequential with explicit sink
40
+ - [ ] Single-stream parallel
41
+ - [ ] Branches
42
+ - [ ] Forks
43
+ - [ ] Waypoints
44
+ - [ ] Pattern Branching
45
+ - [ ] Multi-stream
46
+ - [ ] Getting away with duck-typing
47
+
48
+
49
+
50
+ #### Guiding Assumptions
51
+
52
+ This library and associated commandline tools are most appropriate for these types of (overlapping and somewhat
53
+ redundant) problems & constraints:
54
+
55
+ | | |
56
+ | ------ | ---- |
57
+ | __Dataflow-Oriented__ | Problems where the easiest way to look at it is a (possibly branching) pipeline of operations on a stream of data. |
58
+ | __Steady-State__ | Where the processing nodes and overall application have upper bounds on their memory requirements in order to safely and reliably handle very long running streams. i.e., bounded online algorithms and [streaming-algorithms](http://en.wikipedia.org/wiki/Streaming_algorithm) |
59
+ | __Functional__ | Most of the generator & processing nodes are usually assumed to be side-effect-free and pure (at least given all previous inputs). |
60
+ | __Composable__ | Easy to make nodes out of combinations and networks of other nodes |
61
+ | __Proportionate__| Very easy and fast to do a simple pipeline (for example some simple functions that mutate an integer from within the console), but easily scales up to more complex production-ready solutions/projects. It strives to maintain the following inequality: `effort ≤ problem‐complexity`. |
62
+
63
+ It is additionally tuned for (but doesn't assume) problems with the following properties:
64
+
65
+
66
+ | | |
67
+ | ------ | ---- |
68
+ | __Parallel__ | For example, map-reduce type problems, or wherever strictly sequential simply isn't required. |
69
+ | __Ordered__ | When there is a natural ordering to the tokens. e.g., a time-series, or bytes from most IO streams; |
70
+ | __State-Accumulation__| Where the tokens can accumulate state and where processing nodes can look at previous process results relative to that token as they pass through the graph. As opposed to destructive changes to the token at each process or lack of tokens altogether (like simple functions). |
71
+ | __Multi-Stream__ | Potentially create secondary streams that are unsynchronized with the original stream; |
72
+ | __Coordinated__ | Dataflow type problems where there is a need to synchronize/coordinate multiple orthogonal streams; |
73
+ | __Decoupled__ | A way to cache intermediate results (e.g., half way through the pipeline) so that multiple stream processing applications can be simultaneously writing results and other unrelated processes reading a stream of results- sometimes in sync with when they are being written by the decoupled process. (some good examples to come). |
74
+ | __Daemonized__ | Where certain stream sources and graphs of processors and sinks should be managed as a single long-lived system process when desired. |
75
+ | __Simulations/Reruns__| Persistent caching nodes (nexus decouplers) etc. allow one to easily simulate reruns of past parts of a stream- possibly generating a new version of subsequently persisted results. |
76
+ | __Rewinding__ | Processing nodes that require (bounded) rewinding that propagates to other nodes. |
77
+
78
+
79
+ Use [Little's law](http://en.wikipedia.org/wiki/Little%27s_law) for queue bounds
80
+
81
+ #### _Example_: Single-Stream Sequential
82
+
83
+ +---+ +------+ +------+
84
+ | G | --> | f(x) | --> | g(x) | -->
85
+ +---+ +------+ +------+
86
+
87
+ * No sink specified at the end, so it simply outputs everything to STDOUT
88
+ * Generator types, from simplest to most complex:
89
+ 1. Scalar? Not very useful; does it make sense to allow it?
90
+ 2. Simpler proc/block/lambda than an enumerable? - instantiate via implicit counter generator with an offset which
91
+ uses the simple proc as the next block (so you could have a generator such as `->(x){Math.sin(x/100.0)}`)
92
+ 3. Any enumerable or even just an object with an `each` method (will be made lazy if it isn't already)
93
+ 4. Simple DSL
94
+ 5. State-machine class
95
+ 6. Decoupler
96
+ 7. Synchronized Decoupler
97
+
98
+
99
+ ---
100
+
101
+ Components
102
+ ------------------------------------------------------------------------------------------------------------------------------
103
+
104
+ ### Data #############################################
105
+
106
+ | Thing | Otherwise Known As | |
107
+ |-----------|--------------------|----------------------------------------------------------|
108
+ | Stream | Flow, Signal | Related ordered data representation- bundled into tokens (chunks). A gradually consumed state |
109
+ | Token | Packet, Event, Chunk, Element, Sample | Coherent instance of stream data being mutated and passing through the pipelines (potentially in parallel) - special values of EOF & Exceptionals |
110
+
111
+ #### Stream
112
+
113
+ Defined by:
114
+ * The eventual property distribution of its constituent tokens (possibly given as a shorthand name/"token-type"),
115
+ which, because the token's internal state ends up mirroring the operations that have occurred, means it is also a
116
+ way of describing the graph of process nodes that act on tokens within the stream (explicitly or implicitly- don't
117
+ know yet) (usually all or a subset of a nodus application specification);
118
+ * An origination node for that specific stream (not necessarily a standalone origination node like for an app)
119
+ * A unique session identifier (uuid) identifying this run-instance, possibly shared with other streams (or Nexus' /
120
+ Decouplers)
121
+ * Creates the initial instances of tokens, with a monotonic order indexed from the first token of the session. (Hence,
122
+ streams can be theoretically infinite into the future, but always have a finite well-defined past, at least in the
123
+ context of a session).
124
+ * Sometimes a version, which can affect caching, for example, or conflict resolution when a sink is permanently saving
125
+ state from a running stream, etc.
126
+ * No cycles. Cycle-like behavior is handled by creating another stream with a delay (for example).
127
+
128
+ **`Stream == token type == (the data accumulated in the token are related) & especially (same intervals/timing)`**
129
+
130
+ #### Token
131
+
132
+ * A chunk of data - the ordered atoms of a stream.
133
+ * Has a "type"/name that coincides with the stream
134
+ * Analogous to a rolling stone gathering moss. The token starts out very small, and accumulates more pieces of
135
+ state/data as it moves through the nodes, with it's most recent state being the most relevant for the next node.
136
+ * Has a tree-like internal view of the accumulated state that ends up mirroring the topology of the node network.
137
+
138
+ #### Session
139
+
140
+ * The context within which the runtime system executes the dataflows/streams.
141
+
142
+
143
+ ### Nodes ##############################################
144
+
145
+ A node may be a generator for one or more streams AND/OR a sink for one or more streams, AND/OR an operator on one or
146
+ more streams (accepting tokens from one or more upstream nodes and emitting them to one (or more?) downstream nodes.
147
+ Generally though most nodes will deal with only a single stream- and most of those will be processor nodes, sandwiched
148
+ between a generator at the beginning and a sink at the end.
149
+
150
+ | Aspect | Otherwise Known As | |
151
+ |------------|---------------------|---|
152
+ | Generator | Observable, Source, Origin, Enumerator, Producer, Start | Has one or more output streams that originate there & are not among its incoming streams (although the incoming streams may have participated in its creation). |
153
+ | Sink | Consumer, Iteratee, Fold, Reducer, End | folds over one or more input streams & emits nothing except a final result/stats if the done/EOF token is encountered. Usually in charge of any external side-effects. |
154
+ | Processor | Observable, Filter, Enumeratee, Operator, Function, Convolution-operation, Transformer | Receives a token from a stream and either passes it through or advances its state before outputting the same stream-type. |
155
+ | Junction | Whenever a node has input from more than one signal |
156
+
157
+ Most nodes are intra-stream nodes...
158
+
159
+ Parameterization (applying the specified or calculated parameters) only happens once, when the first token is about to
160
+ hit the node. Any state change after that needs to be handled manually.
161
+
162
+ (maybe nodes with no generators applied get default behavior if they are run standalone- integer sequence generator, for
163
+ example, or default to stdin, argf, etc...)
164
+
165
+ #### Intra-Stream Nodes
166
+
167
+ Operate on data within a single stream
168
+
169
+
170
+ | Type | Behavior |
171
+ |------------|----------|
172
+ | Pipe | Chains intra-stream nodes together to operate on a token sequentially |
173
+ | Branch | Sends same token down multiple parallel branches (w/ or w/o conditions, w/ or w/o automatic remerge (wait) (?), w/ or w/o infered token subselection). Some paths may be skipped/short-circuited due to conditions. |
174
+ | Tap | Observer, splice, tee ... Semantic sugar to specify a non-synchronizing branch off of a point in a stream from the perspective of the branch (as if it were a generator). Can also be dynamic and/or temporary. |
175
+ | (Cached) | Wrap around a node if it has referential transparency (given all previous input). Might not implement for a while |
176
+ | | |
177
+ | Process | (Map, Node, Function, ...) Simple function on latest token data |
178
+ | System | A specialized Process that interfaces with an external application (via stdin/stdout/stderr for the most part?) |
179
+ | External | Another specialized Process that interfaces with an external application, this time via some other IPC call/response mechanisms |
180
+ | View | (Select?) Changes what the next node will consider the "active data" for the token. |
181
+ | | |
182
+ | Recombine | (Wait, Merge, Synchronize, Waypoint) Named synchronization point that also causes a "view" to be the combination of all merged branches. timeout logic, subselection logic, etc. |
183
+ |
184
+
185
+ **NOTES:**
186
+
187
+ * Branch without remerge -> when first branch path finishes (or none are deemed applicable) parent node operation
188
+ continues forward- meaning the other data may or may not be available by the time it reaches a sink.
189
+ * (possibly named sync-points / stream entry/exit points as syntactical sugar to break up description of graph)
190
+ * Special `Wait` nodes might include logic that allows a "partial" token to go ahead depending on certain system
191
+ conditions etc.
192
+
193
+ #### Stream-level Nodes (Inter-stream)
194
+
195
+ * `Application`: Adhoc collection of process networks meant to be run by itself. Usually with a single generator and a
196
+ single final output sink- although that stuff can be inferred potentially. While it's meant to be standalone, other
197
+ nodes should still be allowed to use it when composing more.
198
+ * `Generators`: Shorthand for a node with zero input streams and one or more output streams. In a broader semantic
199
+ perspective a generator is a node that creates any new streams regardless of whether it has input streams (maybe we
200
+ should call those `Originators` or something instead?)
201
+ - [ ] **sequences**/counters
202
+ - [ ] deterministic
203
+ - [ ] integer
204
+ - [ ] monotonic
205
+ - [ ] sequence ... (e.g., from <http://oeis.org>)
206
+ - [ ] real
207
+ - [ ] ... (monotonic with offsets, scaling, ...)
208
+ - [ ] ... (e.g., complex?, digits of irrationals like pi, ...)
209
+ - [ ] stochastic (or deterministic with seed in some cases)
210
+ - [ ] rng ... (prng, high quality, ...)
211
+ - [ ] quasi-random ...
212
+ - [ ] distributions ...
213
+ - [ ] **clocks**/timers ... (including simulated timers? e.g., meaningful timing sequence without waiting the actual intervals between tokens...)
214
+ - [ ] **system** (although usually used as normal internal functions for the nodes instead of streams)
215
+ - [ ] state changes (e.g., network connectivity, service status, ...)
216
+ - [ ] ... (e.g., resource utilization, uptime, network load, ...)
217
+ - [ ] **interprocess**
218
+ - [ ] signals
219
+ - [ ] argv
220
+ - [ ] file
221
+ - [ ] simple/read
222
+ - [ ] followed (i.e., `tail -f`-like or even `-F`)
223
+ - [ ] database ... (via query or change-monitoring etc.)
224
+ - [ ] pipe
225
+ - [ ] stdin
226
+ - [ ] named
227
+ - [ ] unix pipe (via file descriptor)
228
+ - [ ] argf (combo stdin and/or file(s))
229
+ - [ ] message queue
230
+ - [ ] semaphore
231
+ - [ ] shared memory
232
+ - [ ] mmap
233
+ - [ ] unix socket
234
+ - [ ] net
235
+ - [ ] socket
236
+ - [ ] secure socket
237
+ - [ ] HTTP
238
+ - [ ] HTTPS
239
+ - [ ] ... (e.g., AMQP, Erlang-node, ...)
240
+ * `Projections` (one input stream, one different output stream with timing change & probably data change):
241
+ - **SeqDelay**: (buffer, drop(n), drop-while, fifo-queue) output stream is same as input tokens but delayed by specified number of steps (implied (n - 1)
242
+ delay when a cycle is specified or detected?)
243
+ - **TimeDelay**: introduce a system-time latency
244
+ - **Sink**: (fold, accumulator, aggregate, reduce, inject) special ending node that only outputs a result stream with a single
245
+ token that reports some reduced version of all input tokens. (can be specified with a single operator, e.g., `:+`
246
+ for sum).
247
+ - **Decoupler**: persists records
248
+ - *Future*:
249
+ - first
250
+ - include?
251
+ - any?
252
+ - count
253
+ - ... other enumerable & functional operators that act on the whole population.
254
+ - **Reject**: (filter) Filters out specified tokens
255
+ - **Select**: (grep, search) Selects specified tokens
256
+ - **Reactors**: Nodes that give an impulse when they encounter certain conditions on the input stream: specifically
257
+ for monitoring and reacting to certain conditions like queue lengths etc. (can be thought of as being implemented
258
+ via -->[tap]-->[select])
259
+ - **Mutate**: Timing of new stream is the same as input, but data has been mutated in a way that incompatible with the
260
+ incoming stream
261
+ - *Future*:
262
+ - pattern detection
263
+ - sample/alias (time-based chunking)
264
+ - sketch (in lieu of sort, for example)
265
+ - chunk
266
+ - collect-concat / flat-map
267
+ - cycle
268
+ - each-cons
269
+ - take, take-while, drop, drop-while, skip, skip-while (?)
270
+ - ... other enumerable-like projection functions
271
+ - ... sink-like enumerable functions but "so far"- e.g., 'current-max,' 'current-minmax', ...
272
+ - *DIFFICULT* (or impossible considering infinite streams and steady-state, but with some possible partial-solutions)
273
+ - reverse, sort, sort-by (although that could be some kind of sketch...)
274
+
275
+ * `Merge` junctions:
276
+ - **Mux**: when token arrives on any input stream an output token is generated with copies of the latest of all input
277
+ tokens along with how long it has been since those tokens (respectively) arrived.
278
+ - **Zip**: combines specified number of input tokens (at least one each) laterally and emits an output token. Note
279
+ that this can cause non-steady-state behavior (including even race conditions) if one of the streams bottlenecks and
280
+ the other stream builds up a huge backlog. (Need to at least throw an error when this seems like it's happening, and
281
+ ideally even have mechanisms for automatically dropping, etc.)
282
+ - **AggregateZip**: emits an output token when at least one of every input stream is received. Tokens received from
283
+ streams multiple times while waiting for the first token on one of the other streams are aggregated together (so the
284
+ final resulting token contains a vector for each incoming stream, with every vector containing at least one item and
285
+ at least one vector containing _only_ one item [the last stream to give an inbound token while the others were
286
+ building up]).
287
+ - **StreamFold**: has a kernel that updates state and/or outputs a token at any input receipt from any stream.
288
+ - *Future*
289
+ - concat (if it's ever actually needed- nodus isn't really meant for it...)
290
+
291
+ * `Scatter` junctions:
292
+ - **Switch**: output streams contain subsets of the input tokens where each token travels down a path (or more than
293
+ one?) determined by a case statement. (can be thought of as being implemented via -->[branch]--*>[filter])
294
+ - **StateSwitch**: input tokens get fed to output stream depending on the last given token from a different input
295
+ stream.
296
+
297
+ * Generic custom `Junction`s: Implementing a Junction node requires that it has to actively use actor-like semantics
298
+ (including timeouts and possibly polling multiple streams) to request tokens on the input streams. [implemented by the
299
+ fact that in reality it is waiting on all streams at all times, and then decides whether it applies to the given wait
300
+ condition].
301
+
302
+
303
+ ### Application / Runtime ################################
304
+
305
+
306
+ #### Nodus Application Specification
307
+
308
+ _(could have just as easily been named 'Stream Processing Graph' or 'Nodus Application Specification', ...)_
309
+
310
+ Technically a normal node that happens to:
311
+
312
+ * Have neither inputs nor (maybe?) outputs
313
+ * Contain one or more initial generating nodes
314
+ * (therefore) allows execution by the `nodus` execution engine
315
+
316
+ In practical terms, it:
317
+
318
+ * Creates a stream which can then potentially create other streams
319
+ * Ends with explicit or implicit sinks/end-points/reductions
320
+ * Is "executable" in a standalone fashion
321
+ * Is generally behave as a steady-state daemon processing a hypothetically infinite stream of tokens
322
+ * Is usually specified at least at the highest level in a ".node" file that also describes its high-level purpose
323
+ * (in other words) is the top-most specification of a nodus, an Application.
324
+ * Will leave side-effects / external actions for the sinks
325
+ * Will finish by having executed the external action, outputting a reduced/folded result (not ideal since it
326
+ implies batch-like single-run behavior rather than daemon-like), or persisting the final tokens (or a form
327
+ thereof) in a Decoupler (persistent store separate from caches described shortly).
328
+
329
+ #### File/spec hierarchy, auto-loading, paths, ... (node-language agnosticism?)
330
+
331
+
332
+
333
+ #### Nodus Execution Engine (`nodus` command)
334
+
335
+ * Parses/processes a nodus specification/application/standalone-process-graph
336
+ * Starts everything up for the nodus and (normally) daemonizes
337
+ * /etc/init.d-(etc.)-like daemon instance management
338
+ * Erlang-like server behavior, ideally (or maybe even actually running on a distributed OTP network...)
339
+ * Could eventually conceivably evolve into tools with higher levels of management/abstraction (for distributed
340
+ processing and cluster management, for example)
341
+
342
+ #### Application Groups
343
+
344
+ * Ability for nodus to specify which applications should be running autonomously all the time (system-level
345
+ daemonized), including restart policy and footprint monitoring vs. those that are experimental / dev / transient,
346
+ vs. those that are something in-between.
347
+
348
+
349
+
350
+
351
+ ---
352
+
353
+ Scratch
354
+ --------------------------------------------------------------------------------------------------
355
+
356
+
357
+ Token: any object instance but usually openstruct with hash-like access as well (so like HashWithIndifferentAccess) but with
358
+ WRITE-ONCE semantics! (throws error if the same process/node tries to write the same field a second time)
359
+
360
+ suggested behavior:
361
+ - lock-field-value-on-write
362
+ - unlock-all-field-values (for when a node starts its turn | possibly specify that this proc/node is one that can write to it)
363
+ - exception on rewrite of a field after it's locked
364
+ -
365
+
366
+ Contributing
367
+ -------------------------------------------------------------------------------------------------
368
+
369
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
370
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
371
+ * Fork the project.
372
+ * Start a feature/bugfix branch.
373
+ * Commit and push until you are happy with your contribution.
374
+ * Ensure that it is accompanied with tests.
375
+ * Please try not to mess with the Rakefile, version, or history etc. If you want to have your own version, or it is
376
+ otherwise necessary, then fine, but please isolate to its own commit so I can cherry-pick around it.
377
+
378
+
379
+
380
+ Copyright
381
+ -------------------------------------------------------------------------------------------------
382
+
383
+ Copyright (c) 2014 Joseph Wecker. MIT License. See LICENSE.txt for further details.