rachinations 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +6 -0
- data/.travis.yml +8 -0
- data/.yardopts +1 -0
- data/Gemfile +19 -0
- data/Gemfile.lock +93 -0
- data/LICENSE.txt +21 -0
- data/README.md +18 -0
- data/Rakefile +16 -0
- data/lib/rachinations.rb +49 -0
- data/lib/rachinations/domain/diagrams/diagram.rb +167 -0
- data/lib/rachinations/domain/diagrams/non_deterministic_diagram.rb +16 -0
- data/lib/rachinations/domain/diagrams/verbose_diagram.rb +9 -0
- data/lib/rachinations/domain/edge_collection.rb +12 -0
- data/lib/rachinations/domain/edges/edge.rb +159 -0
- data/lib/rachinations/domain/edges/random_edge.rb +4 -0
- data/lib/rachinations/domain/exceptions/bad_options.rb +3 -0
- data/lib/rachinations/domain/exceptions/no_elements_found.rb +2 -0
- data/lib/rachinations/domain/exceptions/no_elements_matching_condition_error.rb +2 -0
- data/lib/rachinations/domain/exceptions/no_elements_of_given_type.rb +3 -0
- data/lib/rachinations/domain/exceptions/unsupported_type_error.rb +2 -0
- data/lib/rachinations/domain/modules/common/hash_init.rb +88 -0
- data/lib/rachinations/domain/modules/common/invariant.rb +17 -0
- data/lib/rachinations/domain/modules/diagrams/verbose.rb +30 -0
- data/lib/rachinations/domain/node_collection.rb +30 -0
- data/lib/rachinations/domain/nodes/converter.rb +276 -0
- data/lib/rachinations/domain/nodes/gate.rb +6 -0
- data/lib/rachinations/domain/nodes/node.rb +166 -0
- data/lib/rachinations/domain/nodes/pool.rb +267 -0
- data/lib/rachinations/domain/nodes/resourceful_node.rb +96 -0
- data/lib/rachinations/domain/nodes/resourceless_node.rb +11 -0
- data/lib/rachinations/domain/nodes/sink.rb +17 -0
- data/lib/rachinations/domain/nodes/source.rb +161 -0
- data/lib/rachinations/domain/nodes/trader.rb +6 -0
- data/lib/rachinations/domain/resource_bag.rb +131 -0
- data/lib/rachinations/domain/resources/token.rb +51 -0
- data/lib/rachinations/domain/strategies/strategy.rb +5 -0
- data/lib/rachinations/domain/strategies/valid_types.rb +69 -0
- data/lib/rachinations/dsl/dsl.rb +63 -0
- data/lib/rachinations/extras/fifo.rb +27 -0
- data/lib/rachinations/version.rb +3 -0
- data/machinations_diagrams/apenas_bonito.xml +22 -0
- data/machinations_diagrams/behavior_converter.xml +53 -0
- data/machinations_diagrams/behavior_converter_fim.xml +10 -0
- data/machinations_diagrams/canon/README.md +8 -0
- data/machinations_diagrams/canon/converters_differences.xml +27 -0
- data/machinations_diagrams/canon/converters_differences2.xml +34 -0
- data/machinations_diagrams/canon/converters_similarities.xml +63 -0
- data/machinations_diagrams/canon/converters_similarities2.xml +21 -0
- data/machinations_diagrams/canon/nodes_and_edges_differences.xml +15 -0
- data/machinations_diagrams/canon/nodes_and_edges_similarities.xml +20 -0
- data/machinations_diagrams/deterministic_example.xml +46 -0
- data/machinations_diagrams/economies_of_scale.xml +32 -0
- data/machinations_diagrams/feature_ou_bug.xml +5 -0
- data/machinations_diagrams/loop_in_trigger.xml +21 -0
- data/machinations_diagrams/naficadevendo.xml +12 -0
- data/machinations_diagrams/noreporting_equivalent.xml +22 -0
- data/machinations_diagrams/pull_all_example.xml +7 -0
- data/machinations_diagrams/sketch_of_memory.xml +41 -0
- data/machinations_diagrams/software_engineering_process 2.xml +130 -0
- data/machinations_diagrams/software_engineering_process v3.xml +168 -0
- data/machinations_diagrams/software_engineering_process v4.xml +192 -0
- data/machinations_diagrams/software_engineering_process.xml +65 -0
- data/machinations_diagrams/software_engineering_process_with_rework_after_test.xml +195 -0
- data/machinations_diagrams/startup_marketing.xml +35 -0
- data/machinations_diagrams/triggers_allow_multiple_stages_at_same_round.xml +19 -0
- data/machinations_diagrams/um_de_cada_vez_vs_todos_de_uma_vez.xml +20 -0
- data/rachinations.gemspec +35 -0
- data/testing/features/step_definitions/step_definitions.rb +11 -0
- data/testing/simulations/modelo1.rb +20 -0
- data/testing/simulations/modelo2.rb +49 -0
- data/testing/simulations/noreporting.rb +51 -0
- data/testing/simulations/sequencial.rb +19 -0
- data/testing/simulations/sobonito.rb +28 -0
- data/testing/simulations/sobonitowhile.rb +28 -0
- data/testing/simulations/whatIwish1.rb +20 -0
- data/testing/spec/canon/converter_spec.rb +33 -0
- data/testing/spec/canon/pool_spec.rb +68 -0
- data/testing/spec/conditions_spec.rb +10 -0
- data/testing/spec/converter_spec.rb +223 -0
- data/testing/spec/diagram_spec.rb +671 -0
- data/testing/spec/edge_spec.rb +256 -0
- data/testing/spec/hash_init_spec.rb +59 -0
- data/testing/spec/node_spec.rb +31 -0
- data/testing/spec/non_deterministic_diagram_spec.rb +112 -0
- data/testing/spec/pool_spec.rb +233 -0
- data/testing/spec/source_spec.rb +132 -0
- data/testing/spec/spec_helper.rb +34 -0
- data/testing/spec/xexeo_spec.rb +193 -0
- metadata +283 -0
@@ -0,0 +1,159 @@
|
|
1
|
+
require_relative '../strategies/valid_types'
|
2
|
+
require_relative '../../domain/exceptions/no_elements_found'
|
3
|
+
|
4
|
+
class Edge
|
5
|
+
|
6
|
+
attr_reader :from, :to, :name, :label, :types
|
7
|
+
|
8
|
+
|
9
|
+
def initialize(hsh)
|
10
|
+
|
11
|
+
@name = hsh.fetch(:name)
|
12
|
+
|
13
|
+
@from = hsh.fetch(:from)
|
14
|
+
|
15
|
+
@to = hsh.fetch(:to)
|
16
|
+
|
17
|
+
#setting default values if needed.
|
18
|
+
hsh = defaults.merge hsh
|
19
|
+
|
20
|
+
@label = hsh.fetch(:label)
|
21
|
+
@types = hsh.fetch(:types)
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
# Simulates a ping!, but no resources get actually
|
26
|
+
# moved.
|
27
|
+
#
|
28
|
+
# @param [Boolean] require_all whether to require that the maximum
|
29
|
+
# number of Resources allowed (as per this Edge's label) be
|
30
|
+
# able to pass in order to return true.
|
31
|
+
#
|
32
|
+
# @return [Boolean] true in case a ping! on this Edge
|
33
|
+
# would return true. False otherwise.
|
34
|
+
def test_ping?(require_all=false)
|
35
|
+
return false if from.disabled? || to.disabled?
|
36
|
+
|
37
|
+
condition = strategy.condition
|
38
|
+
|
39
|
+
available_resources = from.resource_count(&condition)
|
40
|
+
|
41
|
+
if available_resources == 0
|
42
|
+
false
|
43
|
+
elsif available_resources >= label
|
44
|
+
true
|
45
|
+
elsif available_resources < label && require_all
|
46
|
+
false
|
47
|
+
else
|
48
|
+
# only some resources are able to pass but it's not require_all
|
49
|
+
true
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
# the code is the code but sometimes it helps to specify what action
|
55
|
+
# we are talking about so as to make code more understandable
|
56
|
+
alias_method :test_pull?, :test_ping?
|
57
|
+
alias_method :test_push?, :test_ping?
|
58
|
+
|
59
|
+
def supports?(type)
|
60
|
+
types.empty? || types.include?(type)
|
61
|
+
end
|
62
|
+
|
63
|
+
alias_method :support?, :supports?
|
64
|
+
|
65
|
+
def enabled?
|
66
|
+
true
|
67
|
+
end
|
68
|
+
|
69
|
+
def disabled?
|
70
|
+
not enabled?
|
71
|
+
end
|
72
|
+
|
73
|
+
def untyped?
|
74
|
+
types.empty?
|
75
|
+
end
|
76
|
+
|
77
|
+
def typed?
|
78
|
+
not untyped?
|
79
|
+
end
|
80
|
+
|
81
|
+
def from?(obj)
|
82
|
+
from.equal?(obj)
|
83
|
+
end
|
84
|
+
|
85
|
+
def to?(obj)
|
86
|
+
to.equal?(obj)
|
87
|
+
end
|
88
|
+
|
89
|
+
# Returns a block which will be later used by the calling node to search
|
90
|
+
# for a suitable resource.
|
91
|
+
#
|
92
|
+
# @return [Proc] a condition block
|
93
|
+
def push_expression
|
94
|
+
strategy.push_condition
|
95
|
+
end
|
96
|
+
|
97
|
+
# Returns a block which will be later used as a parameter
|
98
|
+
# to method pull!.
|
99
|
+
#
|
100
|
+
# @return [Proc] a condition block
|
101
|
+
def pull_expression
|
102
|
+
strategy.pull_condition
|
103
|
+
end
|
104
|
+
|
105
|
+
# Takes a resource and puts it into the node at the other
|
106
|
+
# end of this Edge.
|
107
|
+
#
|
108
|
+
# @raise [RuntimeError] in case the receiving node or this Edge
|
109
|
+
# won't accept the resource sent.
|
110
|
+
# @param res the resource to send.
|
111
|
+
def push!(res)
|
112
|
+
raise RuntimeError.new "This Edge does not support type: #{res.type}" unless supports?(res.type)
|
113
|
+
|
114
|
+
begin
|
115
|
+
to.put_resource!(res,self)
|
116
|
+
rescue => e
|
117
|
+
# just to make it clear that it bubbles
|
118
|
+
raise RuntimeError.new(e.message+" => "+'Push failed')
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# Tries to take a resource matching given block
|
123
|
+
# from the node at the other end.
|
124
|
+
#
|
125
|
+
# @param [Proc] blk block that will define what resource the other node
|
126
|
+
# should send.
|
127
|
+
# @raise [RuntimeError] in case the other node could provide no resources
|
128
|
+
# that satisfy this condition block.
|
129
|
+
# @return a resource that satisfies the given block.
|
130
|
+
def pull!(&blk)
|
131
|
+
begin
|
132
|
+
res=from.take_resource!(&blk)
|
133
|
+
rescue => e
|
134
|
+
# just to make it clear that it bubbles
|
135
|
+
raise RuntimeError.new("Pull failed")
|
136
|
+
else
|
137
|
+
res
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
|
142
|
+
def to_s
|
143
|
+
"Edge '#{@name}', from '#{from.name}' to '#{to.name}'"
|
144
|
+
end
|
145
|
+
|
146
|
+
private
|
147
|
+
|
148
|
+
def strategy
|
149
|
+
ValidTypes.new(from, self, to)
|
150
|
+
end
|
151
|
+
|
152
|
+
def defaults
|
153
|
+
{
|
154
|
+
:label => 1,
|
155
|
+
:types => []
|
156
|
+
}
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
#use this for classes you want to be able to instantiate
|
2
|
+
#using a hashful of parameters
|
3
|
+
module HashInit
|
4
|
+
|
5
|
+
class ::Array
|
6
|
+
|
7
|
+
def include_option? opt
|
8
|
+
|
9
|
+
each do |el|
|
10
|
+
if el == opt
|
11
|
+
return true
|
12
|
+
elsif el.is_a?(Hash) && el.has_key?(opt)
|
13
|
+
return true
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
false
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize(hsh={})
|
23
|
+
|
24
|
+
raise BadOptions.new 'This class requires a hash as parameter to its constructor.' if !hsh.is_a?(Hash)
|
25
|
+
|
26
|
+
check_options!(hsh)
|
27
|
+
hsh=set_defaults(hsh)
|
28
|
+
end
|
29
|
+
|
30
|
+
def check_options!(hsh)
|
31
|
+
|
32
|
+
#watch out for unknown options - might be typos!
|
33
|
+
hsh.each_pair do |key, value|
|
34
|
+
|
35
|
+
if !options.include_option?(key) && aliases_for(key).none?{|ali| options.include_option?(ali) }
|
36
|
+
raise BadOptions.new "Unknown option in parameter hash: :#{key} "
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
#make sure all required ones are there
|
41
|
+
|
42
|
+
options.each do |el|
|
43
|
+
if el.is_a? Hash
|
44
|
+
|
45
|
+
#we know for sure it's got only one key and one value
|
46
|
+
k = el.keys[0]
|
47
|
+
v = el.values[0]
|
48
|
+
|
49
|
+
if v == :required
|
50
|
+
|
51
|
+
if !hsh.has_key?(k) && hsh.keys.all?{|opt| aliases_for(opt).none?{|ali| ali == k } }
|
52
|
+
raise BadOptions.new "Required option #{k} was not found in parameter hash."
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
def aliases_for(opt)
|
62
|
+
aliases.select{|k,v| k == opt }.values
|
63
|
+
end
|
64
|
+
|
65
|
+
def set_defaults(hsh)
|
66
|
+
#in case the user hasn't passed full parameters to the constructor
|
67
|
+
defaults.merge hsh
|
68
|
+
end
|
69
|
+
|
70
|
+
# This method should be implemented by each client to indicate the options accepted.
|
71
|
+
#@return [Array] An array of accepted options.
|
72
|
+
#@example Many client Classes specify a :name option:
|
73
|
+
# def options
|
74
|
+
# [:name]
|
75
|
+
# end
|
76
|
+
def options
|
77
|
+
[]
|
78
|
+
end
|
79
|
+
|
80
|
+
def defaults
|
81
|
+
{}
|
82
|
+
end
|
83
|
+
|
84
|
+
def aliases
|
85
|
+
{}
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Invariant
|
2
|
+
|
3
|
+
#used to insert assertions into code
|
4
|
+
#remember that assertions should not be used to control program flow!!! assert things that should ALWAYS be false.
|
5
|
+
|
6
|
+
class AssertionError < RuntimeError
|
7
|
+
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
|
12
|
+
def invariant &block
|
13
|
+
raise AssertionError unless yield
|
14
|
+
end
|
15
|
+
|
16
|
+
alias_method :inv, :invariant
|
17
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Verbose
|
2
|
+
|
3
|
+
|
4
|
+
def before_run
|
5
|
+
print "\033[1;32m===== INITIAL STATE =====\e[00m\n\n"
|
6
|
+
|
7
|
+
puts self
|
8
|
+
end
|
9
|
+
|
10
|
+
def after_run
|
11
|
+
print "\033[1;32m====== FINAL STATE ======\e[00m\n\n"
|
12
|
+
|
13
|
+
puts self
|
14
|
+
|
15
|
+
print "\033[1;31m========== END ==========\e[00m\n\n"
|
16
|
+
end
|
17
|
+
|
18
|
+
def before_round(round_no)
|
19
|
+
print "======= ROUND #{round_no} =======\n\n"
|
20
|
+
end
|
21
|
+
|
22
|
+
def after_round (round_no)
|
23
|
+
puts self
|
24
|
+
end
|
25
|
+
|
26
|
+
def sanity_check_message
|
27
|
+
print "\033[1;31m= SAFEGUARD CONDITION REACHED - ABORTING EXECUTION =\e[00m\n\n"
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
class NodeCollection
|
4
|
+
extend Forwardable
|
5
|
+
|
6
|
+
def_delegators :@array, :[], :<<, :each, :push, :map, :select, :detect , :reduce, :shuffle, :sample
|
7
|
+
|
8
|
+
|
9
|
+
def initialize(init_array=nil)
|
10
|
+
|
11
|
+
if init_array.nil?
|
12
|
+
@array = []
|
13
|
+
else
|
14
|
+
@array = init_array
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def passive
|
19
|
+
@array.select{|el| el.passive? }
|
20
|
+
end
|
21
|
+
|
22
|
+
def automatic
|
23
|
+
@array.select{|el| el.automatic? }
|
24
|
+
end
|
25
|
+
|
26
|
+
def detect_by_name(name)
|
27
|
+
@array.detect{|el| el.name === name }
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,276 @@
|
|
1
|
+
require_relative '../../domain/nodes/node'
|
2
|
+
require_relative '../../domain/nodes/resourceless_node'
|
3
|
+
|
4
|
+
class Converter < ResourcefulNode
|
5
|
+
|
6
|
+
# VEJA O DIAGRAMA BEHAVIOR CONVERTER, inclusive com o equivalente dele
|
7
|
+
|
8
|
+
# um converter, ao ser ativado, deve tirar de um lugar e passar para outro
|
9
|
+
# nada fica guardado em um converter no sentido do pool, porém algumas coisas podem ficar
|
10
|
+
# temporariamente anotadas nele porque não foi completado ainda o & ou todos os recursos necessários
|
11
|
+
|
12
|
+
# um converter pode ser ativado de 3 maneiras
|
13
|
+
# por si mesmo
|
14
|
+
# quando um no que empurra empurra algo para ele
|
15
|
+
# quando um no que puxa puxa dele
|
16
|
+
|
17
|
+
# O tipo do conversor define o tipo de saida padrao
|
18
|
+
# porem o tipo do edge tem vantagem na definicao da saida
|
19
|
+
# O exemplo com 3 saidas mostra isso
|
20
|
+
|
21
|
+
def initialize(hsh={})
|
22
|
+
check_options!(hsh)
|
23
|
+
hsh = set_defaults(hsh)
|
24
|
+
@name = hsh.fetch(:name)
|
25
|
+
@mode = hsh.fetch(:mode)
|
26
|
+
@types = hsh.fetch(:types)
|
27
|
+
@activation = hsh.fetch(:activation)
|
28
|
+
|
29
|
+
# each edge may have contributed with some resources at any given time
|
30
|
+
@resources_contributed = init_resources
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
# Activates this Converter. It will try to pull from
|
35
|
+
# incoming nodes and, if successful, will push into
|
36
|
+
# outgoing nodes.
|
37
|
+
def trigger!
|
38
|
+
|
39
|
+
if all?
|
40
|
+
|
41
|
+
if incoming_edges.all? { |edge| edge.test_pull?(true) } && outgoing_edges.all? { |edge| edge.test_push?(true) }
|
42
|
+
pull_all!
|
43
|
+
push_all!
|
44
|
+
else
|
45
|
+
# does not trigger
|
46
|
+
end
|
47
|
+
|
48
|
+
elsif any?
|
49
|
+
|
50
|
+
pull_any!
|
51
|
+
|
52
|
+
if in_conditions_met?
|
53
|
+
if outgoing_edges.all? { |edge| edge.test_push?(true) }
|
54
|
+
push_all!
|
55
|
+
pop_stored_resources!
|
56
|
+
end # converters are always push_all
|
57
|
+
end # conditions weren't met this turn
|
58
|
+
|
59
|
+
else
|
60
|
+
raise ArgumentError.new "Unsupported mode :#{mode}"
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
# Puts a Resource into a Converter. The Edge may be used
|
66
|
+
# as index for internal states and the Resource may be
|
67
|
+
# used in case not all edge conditions have been met
|
68
|
+
# (only applicable when in pull_any mode).
|
69
|
+
#
|
70
|
+
def put_resource!(res, edge=nil)
|
71
|
+
inv { !edge.nil? }
|
72
|
+
if all?
|
73
|
+
if incoming_edges.all? { |e| e.test_push? }
|
74
|
+
push_all!
|
75
|
+
end
|
76
|
+
elsif any?
|
77
|
+
add_to_contributed_resources!(res, edge)
|
78
|
+
if in_conditions_met?
|
79
|
+
push_all!
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
# An override for the original method. The reason for this is
|
86
|
+
# that, when an Edge is attached to a Converter after it's been
|
87
|
+
# created, a key for it (frozen) needs to be created.
|
88
|
+
#
|
89
|
+
# @param [Edge] edge
|
90
|
+
def attach_edge!(edge)
|
91
|
+
#TODO use argument (edge) on the call to super and make sure tests still pass
|
92
|
+
# that way it's clearer that it is being passed on to super
|
93
|
+
super
|
94
|
+
resources_contributed.store(edge, Fifo.new)
|
95
|
+
self
|
96
|
+
end
|
97
|
+
|
98
|
+
def take_resource!(type=nil, &blk)
|
99
|
+
if incoming_edges.shuffle.all? { |edge| edge.test_pull? }
|
100
|
+
pull_all!
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
def resource_count(type=nil,&block)
|
106
|
+
return Float::INFINITY
|
107
|
+
end
|
108
|
+
|
109
|
+
def to_s
|
110
|
+
"Converter '#{@name}'"
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
# This method is used to test whether a Converter
|
116
|
+
# has had the conditions for incoming edges met.
|
117
|
+
#
|
118
|
+
# @return [Boolean] true if conditions for pull_any
|
119
|
+
# have all been met, false otherwise
|
120
|
+
def in_conditions_met?
|
121
|
+
|
122
|
+
edges = incoming_edges
|
123
|
+
|
124
|
+
incoming_edges
|
125
|
+
.all? { |edge| resources_contributed.keys.include?(edge) && resources_contributed.fetch(edge).length >= edge.label }
|
126
|
+
end
|
127
|
+
|
128
|
+
# This removes from the internal store just enough
|
129
|
+
# resources to accomplish one push_all (only applicable when in pull_any mode)
|
130
|
+
def pop_stored_resources!
|
131
|
+
#TODO
|
132
|
+
end
|
133
|
+
|
134
|
+
|
135
|
+
attr_accessor :resources_contributed
|
136
|
+
|
137
|
+
def pull_any!
|
138
|
+
incoming_edges
|
139
|
+
.shuffle
|
140
|
+
.each do |edge|
|
141
|
+
begin
|
142
|
+
blk = edge.pull_expression
|
143
|
+
rescue RuntimeError => ex
|
144
|
+
# Could not get a block for one Edge, but this is pull_any so I'll go ahead.
|
145
|
+
next #other edges might still be able to serve me.
|
146
|
+
end
|
147
|
+
|
148
|
+
edge.label.times do
|
149
|
+
begin
|
150
|
+
res = edge.pull!(&blk)
|
151
|
+
rescue RuntimeError => ex
|
152
|
+
# Let's try another Edge, perhaps?
|
153
|
+
break
|
154
|
+
end
|
155
|
+
|
156
|
+
# right here we would add the returned resource to the store,
|
157
|
+
# but with converters we dont store the resources; we just
|
158
|
+
# record that this edge has contributed one resource:
|
159
|
+
add_to_contributed_resources!(res, edge)
|
160
|
+
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
|
167
|
+
def pull_all!
|
168
|
+
|
169
|
+
incoming_edges
|
170
|
+
.shuffle
|
171
|
+
.each do |edge|
|
172
|
+
begin
|
173
|
+
blk = edge.pull_expression
|
174
|
+
rescue RuntimeError => ex
|
175
|
+
raise RuntimeError.new "One edge failed to provide an expression; the whole operation failed."
|
176
|
+
end
|
177
|
+
|
178
|
+
edge.label.times do
|
179
|
+
begin
|
180
|
+
res = edge.pull!(&blk)
|
181
|
+
res=nil # we do not store the results
|
182
|
+
rescue RuntimeError => ex
|
183
|
+
raise RuntimeError.new "One edge failed to pull; the whole operation failed."
|
184
|
+
end
|
185
|
+
|
186
|
+
end
|
187
|
+
|
188
|
+
end
|
189
|
+
|
190
|
+
end
|
191
|
+
|
192
|
+
def push_all!
|
193
|
+
outgoing_edges
|
194
|
+
.shuffle
|
195
|
+
.each do |edge|
|
196
|
+
begin
|
197
|
+
exp = edge.push_expression
|
198
|
+
rescue RuntimeError => ex
|
199
|
+
raise RuntimeError.new "One edge failed to provide an expression; the whole operation failed."
|
200
|
+
end
|
201
|
+
|
202
|
+
edge.label.times do
|
203
|
+
|
204
|
+
begin
|
205
|
+
res = make_resource(&exp)
|
206
|
+
rescue RuntimeError => ex
|
207
|
+
raise RuntimeError.new "This Converter cannot provide any suitable Resource."
|
208
|
+
end
|
209
|
+
|
210
|
+
begin
|
211
|
+
edge.push!(res)
|
212
|
+
rescue RuntimeError => e
|
213
|
+
raise RuntimeError.new e.message+" SO "+"One push over an edge failed; the whole operation failed."
|
214
|
+
end
|
215
|
+
|
216
|
+
end
|
217
|
+
|
218
|
+
end
|
219
|
+
|
220
|
+
|
221
|
+
end
|
222
|
+
|
223
|
+
def push_any!
|
224
|
+
raise NotImplementedError.new "Converters cannot push_any. Only push_all."
|
225
|
+
end
|
226
|
+
|
227
|
+
# Try to produce a Resource matching given condition.
|
228
|
+
#
|
229
|
+
# @return [Token] a token resource or any of its subtypes
|
230
|
+
# @raise [RuntimeError] in case this Converter cannot produce any
|
231
|
+
# Resource that matches the condition.
|
232
|
+
def make_resource(&condition)
|
233
|
+
|
234
|
+
if untyped?
|
235
|
+
res = Token.new
|
236
|
+
if condition.match_resource?(res)
|
237
|
+
res
|
238
|
+
else
|
239
|
+
raise RuntimeError.new "Failed to make Resource matching given conditions."
|
240
|
+
end
|
241
|
+
else
|
242
|
+
types.shuffle.each do |type|
|
243
|
+
res = type.new
|
244
|
+
if condition.match_resource?(res)
|
245
|
+
return res
|
246
|
+
end
|
247
|
+
end
|
248
|
+
raise RuntimeError.new "Failed to make Resource matching given conditions."
|
249
|
+
end
|
250
|
+
|
251
|
+
end
|
252
|
+
|
253
|
+
# A Converter may receive its needed resources across turns
|
254
|
+
# so there must be a way to keep count of which edges have already
|
255
|
+
# 'given their contribution' to this Converter.
|
256
|
+
def add_to_contributed_resources!(resource, edge)
|
257
|
+
resources_contributed.fetch(edge).put!(resource)
|
258
|
+
end
|
259
|
+
|
260
|
+
def init_resources
|
261
|
+
edges.reduce(Hash.new) { |hash, edge| hash.store(edge.object_id, Fifo.new) }
|
262
|
+
end
|
263
|
+
|
264
|
+
def options
|
265
|
+
[{name: :required}, :diagram, :mode, :activation, :types]
|
266
|
+
end
|
267
|
+
|
268
|
+
def defaults
|
269
|
+
{
|
270
|
+
mode: :pull_any,
|
271
|
+
activation: :passive,
|
272
|
+
types: []
|
273
|
+
}
|
274
|
+
end
|
275
|
+
|
276
|
+
end
|