rachinations 0.0.1
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 +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
|