rachinations 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile +1 -16
- data/Gemfile.lock +2 -0
- data/lib/rachinations/domain/diagrams/diagram.rb +46 -14
- data/lib/rachinations/domain/edges/edge.rb +34 -20
- data/lib/rachinations/domain/modules/common/hash_init.rb +2 -1
- data/lib/rachinations/domain/modules/common/invariant.rb +2 -2
- data/lib/rachinations/domain/modules/common/refiners/number_modifiers.rb +22 -0
- data/lib/rachinations/domain/nodes/converter.rb +5 -5
- data/lib/rachinations/domain/nodes/gate.rb +77 -0
- data/lib/rachinations/domain/nodes/node.rb +69 -36
- data/lib/rachinations/domain/nodes/pool.rb +121 -74
- data/lib/rachinations/domain/nodes/resourceful_node.rb +0 -1
- data/lib/rachinations/domain/nodes/sink.rb +3 -0
- data/lib/rachinations/domain/nodes/source.rb +3 -2
- data/lib/rachinations/domain/resource_bag.rb +3 -4
- data/lib/rachinations/dsl/bad_dsl.rb +2 -0
- data/lib/rachinations/dsl/bootstrap.rb +59 -0
- data/lib/rachinations/dsl/diagram_shorthand_methods.rb +107 -0
- data/lib/rachinations/dsl/helpers/parser.rb +170 -0
- data/lib/rachinations/extras/constant_hash.rb +25 -0
- data/lib/rachinations/extras/fifo.rb +25 -19
- data/lib/rachinations/helpers/edge_helper.rb +40 -0
- data/lib/rachinations/utils/string_helper.rb +7 -0
- data/lib/rachinations/version.rb +1 -1
- data/lib/rachinations.rb +13 -5
- data/rachinations.gemspec +3 -2
- data/testing/simulations/modelo1.rb +1 -1
- data/testing/simulations/sequencial.rb +1 -1
- data/testing/simulations/sobonito.rb +1 -1
- data/testing/simulations/sobonitowhile.rb +1 -1
- data/testing/simulations/whatIwish1.rb +2 -2
- data/testing/spec/canon/conditions_spec.rb +3 -4
- data/testing/spec/converter_spec.rb +3 -4
- data/testing/spec/diagram_spec.rb +293 -238
- data/testing/spec/edge_spec.rb +28 -14
- data/testing/spec/gate_spec.rb +34 -0
- data/testing/spec/pool_spec.rb +8 -10
- data/testing/spec/release1/dsl_spec.rb +204 -0
- data/testing/spec/spec_helper.rb +1 -0
- data/testing/spec/xexeo_spec.rb +39 -40
- metadata +30 -8
- data/lib/rachinations/domain/edges/random_edge.rb +0 -4
- data/lib/rachinations/dsl/dsl.rb +0 -63
- data/testing/spec/canon/trigger_spec.rb +0 -128
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b207a47240a7f2ac500c459b3ebf5a3eebbbee31
|
4
|
+
data.tar.gz: a2c6097eeb47737aab5f9b19f0f93221101911e5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3742c0dde3133a7776eaf8b7d08f134d7355806012f3f974952b46c0d32da999b4573eb75ca1aebfa7cbb7e29267d4ed9705935ed85cf9938793108139a5bc83
|
7
|
+
data.tar.gz: 2b90e71c41aad90e5858b9682d84be9a9ace1d7b45569d1956a1b3e512df16c33a3dee7b86e199a09b4c2f59a49f606ad685ddd36fa588e5802ed726f18752af
|
data/Gemfile
CHANGED
@@ -1,19 +1,4 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
|
3
3
|
# Specify your gem's dependencies in rachinations.gemspec
|
4
|
-
gemspec
|
5
|
-
|
6
|
-
|
7
|
-
# ruby '2.1.1'
|
8
|
-
#
|
9
|
-
# gem 'rake'
|
10
|
-
# gem 'activesupport','3.0.0'
|
11
|
-
# gem 'i18n'
|
12
|
-
# gem 'minitest'
|
13
|
-
# gem 'minitest-reporters'
|
14
|
-
# gem 'rspec'
|
15
|
-
# gem 'cucumber'
|
16
|
-
# gem 'coveralls', require: false
|
17
|
-
|
18
|
-
|
19
|
-
|
4
|
+
gemspec
|
data/Gemfile.lock
CHANGED
@@ -4,6 +4,7 @@ PATH
|
|
4
4
|
rachinations (0.0.3)
|
5
5
|
activesupport (= 3.0.0)
|
6
6
|
i18n (= 0.6.11)
|
7
|
+
weighted_distribution
|
7
8
|
|
8
9
|
GEM
|
9
10
|
remote: https://rubygems.org/
|
@@ -65,6 +66,7 @@ GEM
|
|
65
66
|
tins (~> 1.0)
|
66
67
|
thor (0.19.1)
|
67
68
|
tins (1.3.0)
|
69
|
+
weighted_distribution (1.0.0)
|
68
70
|
|
69
71
|
PLATFORMS
|
70
72
|
ruby
|
@@ -8,13 +8,14 @@ class Diagram
|
|
8
8
|
|
9
9
|
attr_accessor :name, :max_iterations, :nodes, :edges
|
10
10
|
|
11
|
-
def initialize(name)
|
11
|
+
def initialize(name='Anonymous diagram')
|
12
12
|
@nodes = NodeCollection.new
|
13
13
|
@edges = EdgeCollection.new
|
14
14
|
@name = name
|
15
15
|
@max_iterations = 999
|
16
16
|
end
|
17
17
|
|
18
|
+
|
18
19
|
def get_node(name)
|
19
20
|
|
20
21
|
nodes.each do |node|
|
@@ -26,12 +27,36 @@ class Diagram
|
|
26
27
|
raise RuntimeError, "Node with name='#{name}' not found."
|
27
28
|
end
|
28
29
|
|
30
|
+
def get_edge(name)
|
31
|
+
edges.each do |edge|
|
32
|
+
if edge.name == name
|
33
|
+
return edge
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
raise RuntimeError, "Edge with name='#{name}' not found."
|
38
|
+
end
|
39
|
+
|
29
40
|
def add_node!(node_klass, params)
|
30
41
|
|
31
42
|
params.store(:diagram, self)
|
32
43
|
|
44
|
+
#if there's a condition, return it, otherwise return default condition
|
45
|
+
condition = params.delete(:condition) { lambda { true } }
|
46
|
+
|
47
|
+
#similarly, if nodes are supposed to be triggered by another node
|
48
|
+
triggered_by = params.delete(:triggered_by) { nil }
|
49
|
+
|
33
50
|
node = node_klass.new(params)
|
34
51
|
|
52
|
+
node.attach_condition &condition
|
53
|
+
|
54
|
+
if !triggered_by.nil?
|
55
|
+
# ask the current class (diagram) to evaluate what node it is
|
56
|
+
triggerer = self.send(triggered_by.to_sym)
|
57
|
+
triggerer.attach_trigger(node)
|
58
|
+
end
|
59
|
+
|
35
60
|
nodes.push(node)
|
36
61
|
|
37
62
|
self
|
@@ -43,8 +68,8 @@ class Diagram
|
|
43
68
|
params.store(:diagram, self)
|
44
69
|
|
45
70
|
#we need to send the actual nodes, not their names
|
46
|
-
from = get_node(params.
|
47
|
-
to = get_node(params.
|
71
|
+
from = get_node(params.delete(:from))
|
72
|
+
to = get_node(params.delete(:to))
|
48
73
|
|
49
74
|
params.store(:from, from)
|
50
75
|
params.store(:to, to)
|
@@ -78,7 +103,7 @@ class Diagram
|
|
78
103
|
|
79
104
|
i=1
|
80
105
|
|
81
|
-
#if given condition block turned false, it's time to stop
|
106
|
+
# if given condition block turned false, it's time to stop
|
82
107
|
while yield i do
|
83
108
|
|
84
109
|
break unless sanity_check? i
|
@@ -106,7 +131,7 @@ class Diagram
|
|
106
131
|
def resource_count(klass=nil)
|
107
132
|
total=0
|
108
133
|
@nodes.each do |n|
|
109
|
-
total+=n.resource_count(klass)
|
134
|
+
total+=n.resource_count(type: klass)
|
110
135
|
end
|
111
136
|
total
|
112
137
|
end
|
@@ -128,7 +153,7 @@ class Diagram
|
|
128
153
|
|
129
154
|
def run_first_round!
|
130
155
|
|
131
|
-
enabled_nodes.select { |
|
156
|
+
enabled_nodes.select { |node| node.automatic? || node.start? }.shuffle.each { |node| node.trigger! }
|
132
157
|
|
133
158
|
commit_nodes!
|
134
159
|
|
@@ -136,7 +161,7 @@ class Diagram
|
|
136
161
|
|
137
162
|
def run_round!
|
138
163
|
|
139
|
-
enabled_nodes.select { |
|
164
|
+
enabled_nodes.select { |node| node.automatic? }.shuffle.each { |node| node.trigger! }
|
140
165
|
|
141
166
|
commit_nodes!
|
142
167
|
|
@@ -144,26 +169,33 @@ class Diagram
|
|
144
169
|
|
145
170
|
def commit_nodes!
|
146
171
|
#only after all nodes have run do we update the actual resources and changes, to be used in the next round.
|
147
|
-
nodes.shuffle.each { |
|
172
|
+
nodes.shuffle.each { |node| node.commit! }
|
173
|
+
|
148
174
|
end
|
149
175
|
|
150
176
|
def enabled_nodes
|
151
|
-
nodes.select{|
|
177
|
+
nodes.select { |node| node.enabled? }
|
178
|
+
|
152
179
|
end
|
153
180
|
|
154
181
|
#template method
|
155
|
-
def before_round(node_no)
|
182
|
+
def before_round(node_no)
|
183
|
+
end
|
156
184
|
|
157
185
|
#template method
|
158
|
-
def after_round(node_no)
|
186
|
+
def after_round(node_no)
|
187
|
+
end
|
159
188
|
|
160
189
|
#template method
|
161
|
-
def before_run;
|
190
|
+
def before_run;
|
191
|
+
end
|
162
192
|
|
163
193
|
#template method
|
164
|
-
def after_run;
|
194
|
+
def after_run;
|
195
|
+
end
|
165
196
|
|
166
197
|
#template method
|
167
|
-
def sanity_check_message;
|
198
|
+
def sanity_check_message;
|
199
|
+
end
|
168
200
|
|
169
201
|
end
|
@@ -1,24 +1,31 @@
|
|
1
1
|
require_relative '../strategies/valid_types'
|
2
2
|
require_relative '../../domain/exceptions/no_elements_found'
|
3
|
+
require_relative '../../../../lib/rachinations/domain/modules/common/hash_init'
|
4
|
+
require_relative '../../../../lib/rachinations/domain/modules/common/refiners/number_modifiers'
|
3
5
|
|
4
6
|
class Edge
|
7
|
+
include HashInit
|
8
|
+
using NumberModifiers
|
9
|
+
|
5
10
|
|
6
11
|
attr_reader :from, :to, :name, :label, :types
|
7
12
|
|
8
13
|
|
9
14
|
def initialize(hsh)
|
10
15
|
|
11
|
-
|
16
|
+
check_options!(hsh)
|
17
|
+
|
18
|
+
params = set_defaults(hsh)
|
19
|
+
|
20
|
+
@name = params.fetch(:name, 'anonymous')
|
12
21
|
|
13
|
-
@from =
|
22
|
+
@from = params.fetch(:from)
|
14
23
|
|
15
|
-
@to =
|
24
|
+
@to = params.fetch(:to)
|
16
25
|
|
17
|
-
|
18
|
-
hsh = defaults.merge hsh
|
26
|
+
@label = params.fetch(:label)
|
19
27
|
|
20
|
-
@
|
21
|
-
@types = hsh.fetch(:types)
|
28
|
+
@types = params.fetch(:types)
|
22
29
|
|
23
30
|
end
|
24
31
|
|
@@ -31,12 +38,12 @@ class Edge
|
|
31
38
|
#
|
32
39
|
# @return [Boolean] true in case a ping! on this Edge
|
33
40
|
# would return true. False otherwise.
|
34
|
-
def test_ping?(require_all
|
41
|
+
def test_ping?(require_all:false)
|
35
42
|
return false if from.disabled? || to.disabled?
|
36
43
|
|
37
44
|
condition = strategy.condition
|
38
45
|
|
39
|
-
available_resources = from.resource_count(
|
46
|
+
available_resources = from.resource_count(expr: condition)
|
40
47
|
|
41
48
|
if available_resources == 0
|
42
49
|
false
|
@@ -45,17 +52,19 @@ class Edge
|
|
45
52
|
elsif available_resources < label && require_all
|
46
53
|
false
|
47
54
|
else
|
48
|
-
# only some resources are able to pass but it's not require_all
|
55
|
+
# only some resources are able to pass but it's not require_all,
|
56
|
+
# so the ping takes place
|
49
57
|
true
|
50
58
|
end
|
51
59
|
|
52
60
|
end
|
53
61
|
|
54
|
-
# the code is the
|
62
|
+
# the code is the same but sometimes it helps to specify what action
|
55
63
|
# we are talking about so as to make code more understandable
|
56
64
|
alias_method :test_pull?, :test_ping?
|
57
65
|
alias_method :test_push?, :test_ping?
|
58
66
|
|
67
|
+
|
59
68
|
def supports?(type)
|
60
69
|
types.empty? || types.include?(type)
|
61
70
|
end
|
@@ -107,15 +116,14 @@ class Edge
|
|
107
116
|
#
|
108
117
|
# @raise [RuntimeError] in case the receiving node or this Edge
|
109
118
|
# won't accept the resource sent.
|
110
|
-
# @param res the resource to send.
|
119
|
+
# @param res [Token] the resource to send.
|
111
120
|
def push!(res)
|
112
|
-
raise RuntimeError
|
121
|
+
raise RuntimeError, "This Edge does not support type: #{res.type}" unless supports?(res.type)
|
113
122
|
|
114
123
|
begin
|
115
|
-
to.put_resource!(res,self)
|
116
|
-
rescue
|
117
|
-
|
118
|
-
raise RuntimeError.new(e.message+" => "+'Push failed')
|
124
|
+
to.put_resource!(res, self)
|
125
|
+
rescue UnsupportedTypeError
|
126
|
+
raise RuntimeError, "unsupported type"
|
119
127
|
end
|
120
128
|
end
|
121
129
|
|
@@ -126,10 +134,12 @@ class Edge
|
|
126
134
|
# should send.
|
127
135
|
# @raise [RuntimeError] in case the other node could provide no resources
|
128
136
|
# that satisfy this condition block.
|
129
|
-
# @return a
|
130
|
-
|
137
|
+
# @return [Token,nil] a Resource that satisfies the given block or nil,
|
138
|
+
# if the pull was not performed for some reason (e.g. it's probabilistic)
|
139
|
+
def pull!(blk)
|
140
|
+
|
131
141
|
begin
|
132
|
-
res=from.take_resource!(
|
142
|
+
res=from.take_resource!(blk)
|
133
143
|
rescue => e
|
134
144
|
# just to make it clear that it bubbles
|
135
145
|
raise RuntimeError.new("Pull failed")
|
@@ -156,4 +166,8 @@ class Edge
|
|
156
166
|
}
|
157
167
|
end
|
158
168
|
|
169
|
+
def options
|
170
|
+
[:name,:label,:types,:from,:to,:diagram]
|
171
|
+
end
|
172
|
+
|
159
173
|
end
|
@@ -33,8 +33,9 @@ module HashInit
|
|
33
33
|
hsh.each_pair do |key, value|
|
34
34
|
|
35
35
|
if !options.include_option?(key) && aliases_for(key).none?{|ali| options.include_option?(ali) }
|
36
|
-
raise BadOptions.new "
|
36
|
+
raise BadOptions.new "Class #{self.class}: unknown option in constructor: :#{key} (using HashInit)"
|
37
37
|
end
|
38
|
+
|
38
39
|
end
|
39
40
|
|
40
41
|
#make sure all required ones are there
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module NumberModifiers
|
2
|
+
|
3
|
+
|
4
|
+
# allow user to modify Numbers in order to express
|
5
|
+
# percentages ( 12.percent ) and also fractions, for
|
6
|
+
# example.
|
7
|
+
refine Fixnum do
|
8
|
+
|
9
|
+
def /(other)
|
10
|
+
fdiv(other)
|
11
|
+
end
|
12
|
+
|
13
|
+
# # but Fixnums are frozen by default so they can't change state.
|
14
|
+
# # so that i can know whether the user has explicitly called #percent
|
15
|
+
# # i would like to add an instance variable to this number
|
16
|
+
def percent
|
17
|
+
fdiv(100)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -41,7 +41,7 @@ class Converter < ResourcefulNode
|
|
41
41
|
|
42
42
|
if all?
|
43
43
|
|
44
|
-
if incoming_edges.all? { |edge| edge.test_pull?(true) } && outgoing_edges.all? { |edge| edge.test_push?(true) }
|
44
|
+
if incoming_edges.all? { |edge| edge.test_pull?(require_all: true) } && outgoing_edges.all? { |edge| edge.test_push?(require_all:true) }
|
45
45
|
pull_all!
|
46
46
|
push_all!
|
47
47
|
else
|
@@ -53,7 +53,7 @@ class Converter < ResourcefulNode
|
|
53
53
|
pull_any!
|
54
54
|
|
55
55
|
if in_conditions_met?
|
56
|
-
if outgoing_edges.all? { |edge| edge.test_push?(true) }
|
56
|
+
if outgoing_edges.all? { |edge| edge.test_push?(require_all: true) }
|
57
57
|
push_all!
|
58
58
|
clear_stored_resources!
|
59
59
|
end # converters are always push_all
|
@@ -94,7 +94,7 @@ class Converter < ResourcefulNode
|
|
94
94
|
#TODO use argument (edge) on the call to super and make sure tests still pass
|
95
95
|
# that way it's clearer that it is being passed on to super
|
96
96
|
super
|
97
|
-
resources_contributed.store(edge, Fifo.new)
|
97
|
+
resources_contributed.store(edge, Extras::Fifo.new)
|
98
98
|
self
|
99
99
|
end
|
100
100
|
|
@@ -153,7 +153,7 @@ class Converter < ResourcefulNode
|
|
153
153
|
|
154
154
|
edge.label.times do
|
155
155
|
begin
|
156
|
-
res = edge.pull!(
|
156
|
+
res = edge.pull!(blk)
|
157
157
|
rescue RuntimeError => ex
|
158
158
|
# Let's try another Edge, perhaps?
|
159
159
|
break
|
@@ -183,7 +183,7 @@ class Converter < ResourcefulNode
|
|
183
183
|
|
184
184
|
edge.label.times do
|
185
185
|
begin
|
186
|
-
res = edge.pull!(
|
186
|
+
res = edge.pull!(blk)
|
187
187
|
res=nil # we do not store the results
|
188
188
|
rescue RuntimeError => ex
|
189
189
|
raise RuntimeError.new "One edge failed to pull; the whole operation failed."
|
@@ -1,6 +1,83 @@
|
|
1
1
|
require_relative '../../domain/nodes/node'
|
2
2
|
require_relative '../../domain/nodes/resourceless_node'
|
3
3
|
|
4
|
+
require 'weighted_distribution'
|
5
|
+
|
4
6
|
class Gate < ResourcelessNode
|
5
7
|
|
8
|
+
|
9
|
+
def initialize(hsh={})
|
10
|
+
check_options!(hsh)
|
11
|
+
params = set_defaults(hsh)
|
12
|
+
|
13
|
+
@diagram = params[:diagram]
|
14
|
+
@name = params[:name]
|
15
|
+
@activation = params[:activation]
|
16
|
+
@mode = params[:mode]
|
17
|
+
@types = get_types(given_types: params[:types])
|
18
|
+
|
19
|
+
super(hsh)
|
20
|
+
end
|
21
|
+
|
22
|
+
def put_resource!(res,edge)
|
23
|
+
|
24
|
+
inv("Edges must be either all defaults or all probabilities") { outgoing_edges.all?{|e| e.label == 1 } || outgoing_edges.all?{|e| e.label.class == Float} }
|
25
|
+
inv("If probabilities given, their sum must not exceed 1"){ outgoing_edges.reduce(0){|acc,el| acc + el.label } <= 1 || !outgoing_edges.all?{|e| e.label.class == Float} }
|
26
|
+
|
27
|
+
maybe_edge = pick_one(outgoing_edges)
|
28
|
+
|
29
|
+
if(maybe_edge.nil?)
|
30
|
+
# do nothing - resource has 'vanished' - happens every other day
|
31
|
+
else
|
32
|
+
maybe_edge.push!(res)
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_s
|
38
|
+
"Gate '#{@name}'\n\n"
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def pick_one(edges)
|
44
|
+
|
45
|
+
if(edges.all?{|e| e.label == 1})
|
46
|
+
#edges is probably an enumerable but only arrays can be sample()'d
|
47
|
+
edges.to_a.sample
|
48
|
+
elsif(edges.all?{|e| e.label.class == Float})
|
49
|
+
#{edge=>weight} is the shape required by WeightedRandomizer
|
50
|
+
weights = edges.reduce(Hash.new){|acc,el| acc[el] = el.label; acc }
|
51
|
+
|
52
|
+
sum = edges.reduce(0){|acc,el|acc + el.label }
|
53
|
+
|
54
|
+
remaining = 1.0 - sum
|
55
|
+
|
56
|
+
#resource 'vanishes'
|
57
|
+
weights[nil] = remaining
|
58
|
+
|
59
|
+
edge_distribution = WeightedDistribution.new(weights)
|
60
|
+
edge_distribution.sample
|
61
|
+
else
|
62
|
+
raise RuntimeError.new('Invalid setup')
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
def options
|
68
|
+
[:name, :diagram, :activation, :mode, :types]
|
69
|
+
end
|
70
|
+
|
71
|
+
def aliases
|
72
|
+
{}
|
73
|
+
end
|
74
|
+
|
75
|
+
def defaults
|
76
|
+
{
|
77
|
+
activation: :passive,
|
78
|
+
mode: :push_any,
|
79
|
+
types: []
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
6
83
|
end
|
@@ -6,17 +6,36 @@ class Node
|
|
6
6
|
include Invariant
|
7
7
|
include HashInit
|
8
8
|
|
9
|
-
attr_reader :name
|
9
|
+
attr_reader :name, :types
|
10
10
|
|
11
|
+
# Tries to figure out this Node's types based upon what's passed as
|
12
|
+
# parameters. If types were given then just set those as this Node
|
13
|
+
# types. If initial_values were given then try to work out the types
|
14
|
+
# from those.
|
15
|
+
#
|
16
|
+
# @param initial_value [Hash,Fixnum] initial values for this node
|
17
|
+
# @param given_types [Array] provided types
|
18
|
+
# @return [Array] the computed types for this node
|
19
|
+
def get_types(initial_value: 0, given_types: [])
|
20
|
+
inv { !self.instance_variable_defined?(:@types) }
|
21
|
+
|
22
|
+
actual_types = given_types
|
23
|
+
|
24
|
+
if initial_value.is_a?(Fixnum) && given_types.empty?
|
25
|
+
# nothing to do
|
26
|
+
elsif initial_value == 0 && !given_types.empty?
|
27
|
+
# nothing to do
|
28
|
+
elsif initial_value.is_a?(Hash)
|
29
|
+
initial_value.each_key { |type| actual_types.push(type) }
|
30
|
+
else
|
31
|
+
raise ArgumentError.new
|
32
|
+
end
|
11
33
|
|
12
|
-
|
13
|
-
conditions.push(condition)
|
14
|
-
end
|
34
|
+
actual_types.uniq
|
15
35
|
|
16
|
-
def conditions
|
17
|
-
@conditions ||= Array.new
|
18
36
|
end
|
19
37
|
|
38
|
+
|
20
39
|
def edges
|
21
40
|
if @edges.is_a? Array
|
22
41
|
@edges
|
@@ -27,11 +46,11 @@ class Node
|
|
27
46
|
end
|
28
47
|
|
29
48
|
def incoming_edges
|
30
|
-
edges.select{|e| e.to == self}
|
49
|
+
edges.select { |e| e.to == self }
|
31
50
|
end
|
32
51
|
|
33
52
|
def outgoing_edges
|
34
|
-
edges.select{|e| e.from == self}
|
53
|
+
edges.select { |e| e.from == self }
|
35
54
|
end
|
36
55
|
|
37
56
|
def attach_edge(edge)
|
@@ -64,46 +83,60 @@ class Node
|
|
64
83
|
types.empty?
|
65
84
|
end
|
66
85
|
|
67
|
-
def
|
68
|
-
|
86
|
+
def attach_condition(&blk)
|
87
|
+
conditions.push(blk)
|
69
88
|
end
|
70
89
|
|
71
|
-
def
|
72
|
-
@
|
90
|
+
def conditions
|
91
|
+
@conditions = @conditions || Array.new
|
92
|
+
@conditions
|
73
93
|
end
|
74
94
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
95
|
+
|
96
|
+
def attach_trigger(target_node)
|
97
|
+
triggers.push(target_node)
|
98
|
+
end
|
99
|
+
|
100
|
+
def triggers
|
101
|
+
@triggers = @triggers || Array.new
|
102
|
+
@triggers
|
79
103
|
end
|
80
104
|
|
105
|
+
# def clear_triggers
|
106
|
+
# triggers.each do |t|
|
107
|
+
# t[2]=true
|
108
|
+
# end
|
109
|
+
# end
|
110
|
+
|
111
|
+
# Call trigger! on each node stored in self.triggers
|
112
|
+
#
|
81
113
|
def fire_triggers!
|
82
|
-
triggers.each do |
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
114
|
+
triggers.each do |node|
|
115
|
+
node.trigger!
|
116
|
+
# if (n[0].is_a? Proc) && n[2]
|
117
|
+
# if n[0].call
|
118
|
+
# n[2]=false
|
119
|
+
# n[1].trigger!
|
120
|
+
# end
|
121
|
+
# elsif n[0] && n[2]
|
122
|
+
# n[2]=false
|
123
|
+
# n[1].trigger!
|
124
|
+
# end
|
92
125
|
end
|
93
126
|
end
|
94
127
|
|
95
128
|
def enabled?
|
96
|
-
|
97
|
-
conditions.each do |
|
98
|
-
if
|
99
|
-
|
100
|
-
elsif
|
129
|
+
status=true
|
130
|
+
conditions.each do |condition|
|
131
|
+
if condition.is_a? Proc
|
132
|
+
status = (status && condition.call)
|
133
|
+
elsif condition === false
|
101
134
|
return false
|
102
|
-
elsif
|
135
|
+
elsif condition === true
|
103
136
|
# do nothing
|
104
137
|
end
|
105
138
|
end
|
106
|
-
|
139
|
+
status
|
107
140
|
end
|
108
141
|
|
109
142
|
def disabled?
|
@@ -111,8 +144,8 @@ class Node
|
|
111
144
|
end
|
112
145
|
|
113
146
|
def commit!
|
114
|
-
clear_triggers
|
115
|
-
self
|
147
|
+
# clear_triggers
|
148
|
+
#self
|
116
149
|
end
|
117
150
|
|
118
151
|
def pull?
|
@@ -159,7 +192,7 @@ class Node
|
|
159
192
|
# fire triggers.
|
160
193
|
#
|
161
194
|
# @raise [RuntimeError] in case this node won't take the resource
|
162
|
-
def put_resource!(res,edge=nil)
|
195
|
+
def put_resource!(res, edge=nil)
|
163
196
|
raise NotImplementedError, "Please update class #{self.class} to respond to: :#{__callee__}"
|
164
197
|
end
|
165
198
|
|