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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -16
  3. data/Gemfile.lock +2 -0
  4. data/lib/rachinations/domain/diagrams/diagram.rb +46 -14
  5. data/lib/rachinations/domain/edges/edge.rb +34 -20
  6. data/lib/rachinations/domain/modules/common/hash_init.rb +2 -1
  7. data/lib/rachinations/domain/modules/common/invariant.rb +2 -2
  8. data/lib/rachinations/domain/modules/common/refiners/number_modifiers.rb +22 -0
  9. data/lib/rachinations/domain/nodes/converter.rb +5 -5
  10. data/lib/rachinations/domain/nodes/gate.rb +77 -0
  11. data/lib/rachinations/domain/nodes/node.rb +69 -36
  12. data/lib/rachinations/domain/nodes/pool.rb +121 -74
  13. data/lib/rachinations/domain/nodes/resourceful_node.rb +0 -1
  14. data/lib/rachinations/domain/nodes/sink.rb +3 -0
  15. data/lib/rachinations/domain/nodes/source.rb +3 -2
  16. data/lib/rachinations/domain/resource_bag.rb +3 -4
  17. data/lib/rachinations/dsl/bad_dsl.rb +2 -0
  18. data/lib/rachinations/dsl/bootstrap.rb +59 -0
  19. data/lib/rachinations/dsl/diagram_shorthand_methods.rb +107 -0
  20. data/lib/rachinations/dsl/helpers/parser.rb +170 -0
  21. data/lib/rachinations/extras/constant_hash.rb +25 -0
  22. data/lib/rachinations/extras/fifo.rb +25 -19
  23. data/lib/rachinations/helpers/edge_helper.rb +40 -0
  24. data/lib/rachinations/utils/string_helper.rb +7 -0
  25. data/lib/rachinations/version.rb +1 -1
  26. data/lib/rachinations.rb +13 -5
  27. data/rachinations.gemspec +3 -2
  28. data/testing/simulations/modelo1.rb +1 -1
  29. data/testing/simulations/sequencial.rb +1 -1
  30. data/testing/simulations/sobonito.rb +1 -1
  31. data/testing/simulations/sobonitowhile.rb +1 -1
  32. data/testing/simulations/whatIwish1.rb +2 -2
  33. data/testing/spec/canon/conditions_spec.rb +3 -4
  34. data/testing/spec/converter_spec.rb +3 -4
  35. data/testing/spec/diagram_spec.rb +293 -238
  36. data/testing/spec/edge_spec.rb +28 -14
  37. data/testing/spec/gate_spec.rb +34 -0
  38. data/testing/spec/pool_spec.rb +8 -10
  39. data/testing/spec/release1/dsl_spec.rb +204 -0
  40. data/testing/spec/spec_helper.rb +1 -0
  41. data/testing/spec/xexeo_spec.rb +39 -40
  42. metadata +30 -8
  43. data/lib/rachinations/domain/edges/random_edge.rb +0 -4
  44. data/lib/rachinations/dsl/dsl.rb +0 -63
  45. data/testing/spec/canon/trigger_spec.rb +0 -128
@@ -4,12 +4,14 @@ require_relative '../../domain/resources/token'
4
4
  require_relative '../resource_bag'
5
5
  require_relative '../../domain/exceptions/no_elements_matching_condition_error'
6
6
  require_relative '../../domain/modules/common/refiners/proc_convenience_methods'
7
-
7
+ require_relative '../../../../lib/rachinations/helpers/edge_helper'
8
8
 
9
9
  using ProcConvenienceMethods
10
10
 
11
11
  class Pool < ResourcefulNode
12
12
 
13
+ Helper = Helpers::EdgeHelper
14
+
13
15
  def initialize(hsh={})
14
16
 
15
17
  check_options!(hsh)
@@ -17,7 +19,8 @@ class Pool < ResourcefulNode
17
19
 
18
20
  @resources = get_initial_resources(params[:initial_value])
19
21
 
20
- @types = get_types(params[:initial_value], params[:types])
22
+ @types = get_types(initial_value: params[:initial_value],
23
+ given_types: params[:types])
21
24
 
22
25
  #reference to the underlying diagram
23
26
  @diagram = params[:diagram]
@@ -47,16 +50,25 @@ class Pool < ResourcefulNode
47
50
 
48
51
  pull_any!
49
52
 
50
- end
53
+ elsif push? && all?
54
+
55
+ push_all!
56
+
57
+ elsif pull? && all?
58
+
59
+ pull_all!
51
60
 
61
+ else
62
+
63
+ end
52
64
  end
53
65
  end
54
66
 
55
- def resource_count(type=nil, &block)
67
+ def resource_count(type: nil, expr: nil)
56
68
 
57
- raise ArgumentError.new('Please provide either a type or a block, but not both.') if block_given? && !type.nil?
69
+ raise ArgumentError.new('Please provide either a type or a block, but not both.') if !expr.nil? && !type.nil?
58
70
 
59
- if type.nil? && !block_given?
71
+ if type.nil? && expr.nil?
60
72
  resources.count_where { |r| r.unlocked? }
61
73
  elsif type.is_a?(Class) && type <= Token
62
74
 
@@ -67,12 +79,12 @@ class Pool < ResourcefulNode
67
79
  else
68
80
  raise UnsupportedTypeError.new "Unsupported type: #{type.name}"
69
81
  end
70
- elsif block_given?
82
+ elsif !expr.nil?
71
83
 
72
84
  # client doesn't need to know about locked vs unlocked resources
73
- unlock_condition = Proc.new { |r| r.unlocked? }
85
+ unlock_condition = proc { |r| r.unlocked? }
74
86
 
75
- resources.count_where { |r| unlock_condition.match?(r) && block.match?(r) }
87
+ resources.count_where { |r| unlock_condition.match?(r) && expr.match?(r) }
76
88
 
77
89
  else
78
90
  raise ArgumentError.new("Wrong parameter types passed to #{__callee__}")
@@ -117,16 +129,16 @@ class Pool < ResourcefulNode
117
129
  end
118
130
  end
119
131
 
120
- def take_resource!(&expression)
132
+ def take_resource!(expression=nil)
121
133
 
122
- unless block_given?
134
+ if expression.nil?
123
135
  # if no conditions given, then anything goes.
124
- expression = Proc.new{ |res| true }
136
+ expression = Proc.new { |res| true }
125
137
  end
126
138
 
127
139
  raise RuntimeError.new unless resources.count_where(&expression) > 0
128
140
 
129
- res=remove_resource! &expression
141
+ res=remove_resource! expression
130
142
 
131
143
  fire_triggers!
132
144
 
@@ -135,7 +147,7 @@ class Pool < ResourcefulNode
135
147
  end
136
148
 
137
149
  def to_s
138
- "Pool '#{@name}': #{resources} "
150
+ "Pool '#{@name}': #{resources} "
139
151
  end
140
152
 
141
153
  # TODO this smells. where is this used? can i do without it?
@@ -156,25 +168,6 @@ class Pool < ResourcefulNode
156
168
 
157
169
  end
158
170
 
159
- # TODO document this or else refactor it out
160
- def get_types(initial_value, given_types)
161
- inv { !self.instance_variable_defined?(:@types) }
162
-
163
- if initial_value.is_a?(Fixnum) && given_types.empty?
164
- # nothing to do
165
- elsif initial_value == 0 && !given_types.empty?
166
- # nothing to do
167
- elsif initial_value.is_a?(Hash)
168
- initial_value.each_key { |type| given_types.push(type) }
169
- else
170
- raise ArgumentError.new
171
- end
172
-
173
- given_types.uniq
174
-
175
- end
176
-
177
-
178
171
  def types
179
172
  @types
180
173
  end
@@ -183,16 +176,28 @@ class Pool < ResourcefulNode
183
176
 
184
177
  attr_reader :resources
185
178
 
186
- def remove_resource!(&expression)
179
+ # Tries to remove a Resource that matches given expression from this node.
180
+ # @param [Proc] expression the expression to match against Resources
181
+ # @raise [RuntimeError] In case it was not possible to remove a Resource
182
+ def remove_resource!(expression)
187
183
 
188
184
  # client doesn't need to know about locked vs unlocked resources
189
185
  unlocked_expression = Proc.new { |r| r.unlocked? }
190
186
 
191
- res=resources.get_where { |r| unlocked_expression.call(r) && expression.call(r) }
187
+ begin
188
+ # the original expression plus the expression that specifies that only unlocked
189
+ # resources may be retrieved
190
+ res=resources.get_where { |r| unlocked_expression.call(r) && expression.call(r) }
191
+ rescue ArgumentError
192
+ raise RuntimeError, 'wrong arguments'
193
+ rescue NoElementsMatchingConditionError
194
+ raise RuntimeError, 'no elements matching'
195
+ else
196
+ @resources_removed[res.class] += 1
197
+ res
198
+ end
192
199
 
193
- @resources_removed[res.class] += 1
194
200
 
195
- res
196
201
  end
197
202
 
198
203
  def add_resource!(res)
@@ -201,43 +206,22 @@ class Pool < ResourcefulNode
201
206
 
202
207
  end
203
208
 
204
- def options
205
- [:conditions, :name, :activation, :mode, :types, :initial_value, :diagram]
206
- end
207
-
208
- def defaults
209
- {
210
- activation: :passive,
211
- mode: :pull_any,
212
- types: [],
213
- initial_value: 0
214
- }
215
- end
216
-
217
- def aliases
218
- {:initial_values => :initial_value}
219
- end
220
-
221
209
  def push_any!
222
210
 
223
211
  outgoing_edges.shuffle.each do |edge|
224
- begin
225
- blk = edge.push_expression
226
- rescue => ex
227
- # Could not get a block for one Edge, but this is push_any so I'll go ahead.
228
- next #other edges might still be able to serve me.
229
- end
212
+
213
+ blk = edge.push_expression
230
214
 
231
215
  edge.label.times do
232
216
  begin
233
- res = remove_resource!(&blk)
234
- rescue => ex
235
- # Failed to remove this resource. Let's try another Edge, perhaps?
217
+ res = remove_resource!(blk)
218
+ rescue RuntimeError
219
+ # Failed to remove this resource. Let's try another Edge, perhaps?
236
220
  break
221
+ else
222
+ edge.push!(res)
237
223
  end
238
224
 
239
- edge.push!(res)
240
-
241
225
  end
242
226
 
243
227
  end
@@ -245,26 +229,89 @@ class Pool < ResourcefulNode
245
229
 
246
230
  def pull_any!
247
231
  incoming_edges.shuffle.each do |edge|
248
- begin
249
- blk = edge.pull_expression
250
- rescue RuntimeError => ex
251
- # Could not get a block for one Edge, but this is pull_any so I'll go ahead.
252
- next #other edges might still be able to serve me.
253
- end
232
+
233
+ blk = edge.pull_expression
254
234
 
255
235
  edge.label.times do
256
236
  begin
257
- res = edge.pull!(&blk)
258
- rescue RuntimeError => ex
237
+ res = edge.pull!(blk)
238
+ rescue RuntimeError
259
239
  # Let's try another Edge, perhaps?
260
240
  break
241
+ else
242
+ add_resource!(res.lock!)
261
243
  end
262
244
 
263
- add_resource!(res.lock!)
245
+ end
246
+
247
+ end
248
+ end
249
+
250
+ def pull_all!
251
+
252
+ enabled_incoming_edges = incoming_edges.select { |edge| edge.enabled? }
253
+
254
+ if enabled_incoming_edges.all? { |edge| edge.test_ping? }
255
+
256
+ enabled_incoming_edges.each do |edge|
257
+ blk = edge.pull_expression
258
+
259
+ edge.label.times do
260
+ res = edge.pull!(blk)
261
+
262
+ add_resource!(res.lock!)
263
+
264
+ end
264
265
 
265
266
  end
267
+ fire_triggers!
268
+ end
269
+
270
+ end
271
+
272
+ def push_all!
273
+
274
+ enabled_outgoing_edges = outgoing_edges.select { |edge| edge.enabled? }
275
+
276
+
277
+ if Helper.all_can_push?(edges, require_all: true)
278
+
279
+ enabled_outgoing_edges.each do |edge|
280
+
281
+ expression = edge.push_expression
282
+
283
+ edge.label.times do
284
+
285
+ res = remove_resource!(expression)
286
+
287
+ edge.push!(res)
288
+
289
+ end
266
290
 
291
+ end
292
+ fire_triggers!
267
293
  end
294
+
295
+
296
+ end
297
+
298
+ def options
299
+ [:conditions, :name, :activation, :mode, :types, :initial_value, :diagram]
300
+ end
301
+
302
+ def defaults
303
+ {
304
+ activation: :passive,
305
+ mode: :pull_any,
306
+ types: [],
307
+ initial_value: 0
308
+ }
309
+ end
310
+
311
+ def aliases
312
+ {
313
+ :initial_values => :initial_value
314
+ }
268
315
  end
269
316
 
270
317
  end
@@ -3,7 +3,6 @@ require_relative '../../domain/resources/token'
3
3
  require_relative '../../domain/nodes/node'
4
4
  require_relative '../../domain/modules/common/refiners/proc_convenience_methods'
5
5
 
6
-
7
6
  using ProcConvenienceMethods
8
7
 
9
8
  class ResourcefulNode < Node
@@ -14,4 +14,7 @@ class Sink < Pool
14
14
  # do nothing
15
15
  end
16
16
 
17
+ def to_s
18
+ "Sink '#{@name}'\n\n"
19
+ end
17
20
  end
@@ -28,7 +28,7 @@ class Source < ResourcefulNode
28
28
  end
29
29
 
30
30
  def to_s
31
- "Source '#{@name}': \n"
31
+ "Source '#{@name}'\n\n"
32
32
  end
33
33
 
34
34
  def put_resource!;
@@ -98,7 +98,7 @@ class Source < ResourcefulNode
98
98
 
99
99
  #we'll need to change this if source starts accepting
100
100
  # more than a single type
101
- inv { types.size === 1 }
101
+ inv("Sources can only have a single type") { types.size === 1 }
102
102
 
103
103
  if type.nil?
104
104
  res = Token.new
@@ -144,6 +144,7 @@ class Source < ResourcefulNode
144
144
  end
145
145
 
146
146
  end
147
+ fire_triggers!
147
148
  end
148
149
 
149
150
  def options
@@ -82,16 +82,15 @@ class ResourceBag
82
82
  unlocked = count_where{|r| (r.is_type? klass) && (r.unlocked?) }
83
83
  locked = count_where{|r| (r.is_type? klass) && (r.locked?) }
84
84
 
85
- out += "\n #{name} -> #{unlocked} (#{locked}) \n\n"
85
+ out += "#{name} -> #{unlocked} (#{locked}) \n"
86
86
  end
87
87
 
88
88
  if classes.empty?
89
- "\n Empty\n\n"
89
+ "Empty\n\n"
90
90
  else
91
- out
91
+ out+"\n"
92
92
  end
93
93
 
94
-
95
94
  end
96
95
 
97
96
  # created so that I don't have to call count everytime just to see whether there's at least one element matching said condition
@@ -0,0 +1,2 @@
1
+ class BadDSL < RuntimeError
2
+ end
@@ -0,0 +1,59 @@
1
+ require_relative '../../../lib/rachinations/domain/diagrams/diagram'
2
+ require_relative 'diagram_shorthand_methods'
3
+ require_relative 'bad_dsl'
4
+ require_relative 'helpers/parser'
5
+
6
+ # Bootstraping a diagram using the DiagramShorthandMethods
7
+ module DSL
8
+ module Bootstrap
9
+
10
+ def diagram(name='new_diagram', mode: :silent, &blk)
11
+
12
+ # cant verbose be a simple boolean instead?
13
+ if mode == :silent || mode == 'silent'
14
+ dia= Diagram.new(Parser.validate_name!(name))
15
+ elsif mode == :verbose || mode == 'verbose'
16
+ dia = VerboseDiagram.new(Parser.validate_name!(name))
17
+ else
18
+ raise BadDSL, "Unknown diagram mode: #{mode.to_s}"
19
+ end
20
+
21
+ # methods in the block get run as if they were called on
22
+ # the diagram (augmented by the DiagramShorthandMethods module) itself
23
+ dia.instance_eval(&blk)
24
+
25
+ dia
26
+
27
+ end
28
+
29
+
30
+ def non_deterministic_diagram(name, verbose=:silent, &blk)
31
+
32
+ dia=NonDeterministicDiagram.new(Parser.validate_name!(name))
33
+
34
+ # cant verbose be a simple boolean instead?
35
+ if verbose === :verbose
36
+ dia.extend(Verbose)
37
+ end
38
+
39
+ dia.instance_eval &blk
40
+
41
+ dia
42
+
43
+ end
44
+
45
+ # This is just a convenience method to create a proc in a way that's more
46
+ # intuitive for ends users.
47
+ # @example Create a proc, equivalent to proc{|x| x+1 }
48
+ # expr{|x| x+1 }
49
+ def expr(&blk)
50
+ if !block_given?
51
+ raise ArgumentError, "expected a block, but none was given"
52
+ else
53
+ Proc.new(&blk)
54
+ end
55
+ end
56
+
57
+ end
58
+
59
+ end
@@ -0,0 +1,107 @@
1
+ require_relative '../domain/diagrams/diagram'
2
+ require_relative '../domain/diagrams/verbose_diagram'
3
+ require_relative '../domain/diagrams/non_deterministic_diagram'
4
+ require_relative '../domain/modules/diagrams/verbose'
5
+ require_relative '../domain/modules/common/refiners/proc_convenience_methods'
6
+ require_relative './bad_dsl'
7
+ require_relative '../utils/string_helper'
8
+ require_relative 'helpers/parser'
9
+
10
+ module DSL
11
+
12
+ module DiagramShorthandMethods
13
+
14
+ using ProcConvenienceMethods
15
+
16
+ # if you can turn this into a refiner and still keep all tests passing,
17
+ # send a PR for me :smile:
18
+ class ::Diagram
19
+
20
+ alias_method :run, :run!
21
+
22
+ def pool(*args)
23
+
24
+ hash = Parser.parse_arguments(args)
25
+
26
+ add_node! Pool, hash
27
+
28
+ end
29
+
30
+ def source(*args)
31
+
32
+ hash = Parser.parse_arguments(args)
33
+
34
+ add_node! Source, hash
35
+
36
+ end
37
+
38
+ def sink(*args)
39
+
40
+ hash = Parser.parse_arguments(args)
41
+
42
+ add_node! Sink, hash
43
+
44
+ end
45
+
46
+ def converter(*args)
47
+
48
+ hash = Parser.parse_arguments(args)
49
+
50
+ add_node! Converter, hash
51
+
52
+ end
53
+
54
+ def trader(*args)
55
+
56
+ hash = Parser.parse_arguments(args)
57
+
58
+ add_node! Trader, hash
59
+
60
+ end
61
+
62
+ def gate(*args)
63
+ # gate is different because it doesn't take some arguments
64
+ hash = Parser.parse_gate_arguments(args)
65
+
66
+ add_node! Gate, hash
67
+
68
+ end
69
+
70
+ # methods to create edges
71
+ def edge(*args)
72
+
73
+ hash = Parser.parse_edge_arguments(args)
74
+
75
+ add_edge! Edge, hash
76
+
77
+ end
78
+
79
+
80
+ # so that I can easily access elements which have been given a name
81
+ # (mostly nodes and maybe edges too)
82
+ def method_missing(method_sym, *args, &block)
83
+ super if method_sym.to_s == 'anonymous' #these aren't actual node names
84
+
85
+ begin
86
+ #does a node with that name exist?
87
+ node=get_node(method_sym.to_s)
88
+ rescue RuntimeError => err_node
89
+ #what about an edge?
90
+ begin
91
+ edge = get_edge(method_sym.to_s)
92
+ rescue RuntimeError => err_edge
93
+ #no luck, sorry
94
+ super
95
+ else
96
+ edge
97
+ end
98
+ else
99
+ node
100
+ end
101
+ end
102
+
103
+ end
104
+
105
+ end
106
+
107
+ end