rachinations 0.0.7 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +3 -1
  3. data/README.md +184 -5
  4. data/lib/rachinations.rb +5 -4
  5. data/lib/rachinations/domain/diagrams/diagram.rb +35 -24
  6. data/lib/rachinations/domain/exceptions/bad_config.rb +1 -1
  7. data/lib/rachinations/domain/{exceptions → modules/common}/bad_options.rb +0 -0
  8. data/lib/rachinations/domain/modules/common/hash_init.rb +1 -0
  9. data/lib/rachinations/domain/modules/common/refiners/number_modifiers.rb +2 -1
  10. data/lib/rachinations/domain/modules/diagrams/verbose.rb +1 -1
  11. data/lib/rachinations/domain/nodes/gate.rb +23 -27
  12. data/lib/rachinations/domain/nodes/node.rb +4 -20
  13. data/lib/rachinations/domain/nodes/pool.rb +2 -3
  14. data/lib/rachinations/domain/nodes/resourceful_node.rb +4 -3
  15. data/lib/rachinations/domain/nodes/sink.rb +1 -1
  16. data/lib/rachinations/dsl/bad_dsl.rb +3 -0
  17. data/lib/rachinations/dsl/bootstrap.rb +10 -8
  18. data/lib/rachinations/dsl/diagram_shorthand_methods.rb +10 -5
  19. data/lib/rachinations/dsl/helpers/parser.rb +83 -49
  20. data/lib/rachinations/helpers/edge_helper.rb +105 -8
  21. data/lib/rachinations/utils/math_utils.rb +83 -0
  22. data/lib/rachinations/utils/string_utils.rb +8 -0
  23. data/lib/rachinations/version.rb +1 -1
  24. data/rachinations.gemspec +2 -1
  25. data/testing/spec/diagram_spec.rb +78 -17
  26. data/testing/spec/gate_spec.rb +223 -2
  27. data/testing/spec/non_deterministic_diagram_spec.rb +1 -1
  28. data/testing/spec/release1/dsl_spec.rb +100 -3
  29. data/testing/spec/release1/individual_cases_spec.rb +39 -0
  30. data/testing/spec/release1/monografia_spec.rb +69 -0
  31. metadata +24 -5
  32. data/lib/rachinations/utils/string_helper.rb +0 -7
@@ -1,6 +1,8 @@
1
1
  require_relative '../../domain/modules/common/invariant'
2
2
  require_relative '../../domain/modules/common/hash_init'
3
3
 
4
+ # @abstract Subclass and override {#take_resource!} and
5
+ # {#put_resource!} to implement nodes
4
6
  class Node
5
7
 
6
8
  include Invariant
@@ -35,7 +37,6 @@ class Node
35
37
 
36
38
  end
37
39
 
38
-
39
40
  def edges
40
41
  if @edges.is_a? Array
41
42
  @edges
@@ -102,34 +103,17 @@ class Node
102
103
  @triggers
103
104
  end
104
105
 
105
- # def clear_triggers
106
- # triggers.each do |t|
107
- # t[2]=true
108
- # end
109
- # end
110
-
111
106
  # Call trigger! on each node stored in self.triggers
112
107
  #
113
108
  def fire_triggers!
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
125
- end
109
+ triggers.each { |node| node.trigger! }
126
110
  end
127
111
 
128
112
  def enabled?
129
113
  status=true
130
114
  conditions.each do |condition|
131
115
  if condition.is_a? Proc
132
- status = (status && condition.call)
116
+ status = (status && condition.call())
133
117
  elsif condition === false
134
118
  return false
135
119
  elsif condition === true
@@ -3,7 +3,6 @@ require_relative '../../domain/nodes/node'
3
3
  require_relative '../../domain/resources/token'
4
4
  require_relative '../resource_bag'
5
5
  require_relative '../../domain/exceptions/no_elements_matching_condition_error'
6
- require_relative '../../domain/exceptions/bad_config'
7
6
  require_relative '../../domain/modules/common/refiners/proc_convenience_methods'
8
7
  require_relative '../../../../lib/rachinations/helpers/edge_helper'
9
8
 
@@ -11,7 +10,7 @@ using ProcConvenienceMethods
11
10
 
12
11
  class Pool < ResourcefulNode
13
12
 
14
- Helper = Helpers::EdgeHelper
13
+ EdgeHelper = Helpers::EdgeHelper
15
14
 
16
15
  def initialize(hsh={})
17
16
 
@@ -276,7 +275,7 @@ class Pool < ResourcefulNode
276
275
  enabled_outgoing_edges = outgoing_edges.select { |edge| edge.enabled? }
277
276
 
278
277
 
279
- if Helper.all_can_push?(edges, require_all: true)
278
+ if EdgeHelper.all_can_push?(edges, require_all: true)
280
279
 
281
280
  enabled_outgoing_edges.each do |edge|
282
281
 
@@ -5,6 +5,9 @@ require_relative '../../domain/modules/common/refiners/proc_convenience_methods'
5
5
 
6
6
  using ProcConvenienceMethods
7
7
 
8
+ # @abstract Subclass and override {#trigger!}, {#resource_count},
9
+ # {#pull_any!}, {#pull_all!}, {#push_any!}, {#push_all!}, {#take_resource!}
10
+ # and {#put_resource!} to implement nodes that can store resources
8
11
  class ResourcefulNode < Node
9
12
 
10
13
  include Invariant
@@ -25,13 +28,11 @@ class ResourcefulNode < Node
25
28
 
26
29
  end
27
30
 
28
- # pools are about resources
29
-
30
31
  def supports?(klass)
31
32
  if klass.eql?(Token)
32
33
  untyped?
33
34
  else
34
- #untyped nodes support everything.
35
+ # untyped nodes support everything.
35
36
  if untyped?
36
37
  true
37
38
  else
@@ -9,7 +9,7 @@ class Sink < Pool
9
9
  # do nothing
10
10
  end
11
11
 
12
- def put_resource!(obj)
12
+ def put_resource!(obj, edge=nil)
13
13
  inv{obj.unlocked?}
14
14
  # do nothing
15
15
  end
@@ -1,2 +1,5 @@
1
+ # To be used to signal that a DSL command has not been correctly used.
2
+ # For instance, when required options have been omitted or when an option
3
+ # of a different type than expected has been provided
1
4
  class BadDSL < RuntimeError
2
5
  end
@@ -1,5 +1,4 @@
1
1
  require_relative '../../../lib/rachinations/domain/diagrams/diagram'
2
- require_relative 'diagram_shorthand_methods'
3
2
  require_relative 'bad_dsl'
4
3
  require_relative 'helpers/parser'
5
4
 
@@ -7,15 +6,17 @@ require_relative 'helpers/parser'
7
6
  module DSL
8
7
  module Bootstrap
9
8
 
10
- def diagram(name='new_diagram', mode: :silent, &blk)
9
+ def diagram(name='new_diagram', mode: :default, &blk)
11
10
 
12
- # cant verbose be a simple boolean instead?
13
- if mode == :silent || mode == 'silent'
11
+ supported_modes = [:default, :silent, :verbose]
12
+
13
+ raise BadDSL, "Unknown diagram mode: #{mode.to_s}" unless supported_modes.include? mode
14
+
15
+ # right now silent and default are the same thing
16
+ if mode == :default || mode == :silent
14
17
  dia= Diagram.new(Parser.validate_name!(name))
15
18
  elsif mode == :verbose || mode == 'verbose'
16
19
  dia = VerboseDiagram.new(Parser.validate_name!(name))
17
- else
18
- raise BadDSL, "Unknown diagram mode: #{mode.to_s}"
19
20
  end
20
21
 
21
22
  # This is a modified version of Diagram#add_edge!. It defers some method
@@ -77,7 +78,7 @@ module DSL
77
78
 
78
79
  node.attach_condition &condition
79
80
 
80
- if !triggered_by.nil?
81
+ unless triggered_by.nil?
81
82
 
82
83
  attach_trigger_task = lambda do |triggeree, triggerer_name, diagram|
83
84
  # ask the diagram to evaluate what node it is
@@ -88,7 +89,7 @@ module DSL
88
89
 
89
90
  end
90
91
 
91
- if !triggers.nil?
92
+ unless triggers.nil?
92
93
 
93
94
  attach_trigger_task = lambda do |triggerer, triggeree_name, diagram|
94
95
  # ask the diagram to evaluate what node it is
@@ -105,6 +106,7 @@ module DSL
105
106
 
106
107
  end
107
108
 
109
+ # after defining all those methods we run
108
110
  dia.instance_eval(&blk)
109
111
  dia.run_scheduled_tasks
110
112
 
@@ -1,10 +1,6 @@
1
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
2
  require_relative '../domain/modules/common/refiners/proc_convenience_methods'
6
3
  require_relative './bad_dsl'
7
- require_relative '../utils/string_helper'
8
4
  require_relative 'helpers/parser'
9
5
 
10
6
  module DSL
@@ -15,6 +11,9 @@ module DSL
15
11
 
16
12
  # if you can turn this into a refiner and still keep all tests passing,
17
13
  # send a PR for me :smile:
14
+
15
+ # Reopen class Diagram and add methods that will only be visible
16
+ # inside a diagram do .. end block
18
17
  class ::Diagram
19
18
 
20
19
  alias_method :run, :run!
@@ -67,7 +66,6 @@ module DSL
67
66
 
68
67
  end
69
68
 
70
- # methods to create edges
71
69
  def edge(*args)
72
70
 
73
71
  hash = Parser.parse_edge_arguments(args)
@@ -76,6 +74,13 @@ module DSL
76
74
 
77
75
  end
78
76
 
77
+ def stop(*args)
78
+ hash = Parser.parse_stop_condition_arguments(args)
79
+
80
+ add_stop_condition!(hash)
81
+
82
+ end
83
+
79
84
  # so that I can easily access elements which have been given a name
80
85
  # (mostly nodes and maybe edges too)
81
86
  def method_missing(method_sym, *args, &block)
@@ -8,6 +8,7 @@ module DSL
8
8
  using ProcConvenienceMethods
9
9
 
10
10
  ConstantHash = ::Extras::ConstantHash
11
+ StringUtils = ::Utils::StringUtils
11
12
 
12
13
  # these patterns define what each argument should look like
13
14
 
@@ -17,86 +18,93 @@ module DSL
17
18
 
18
19
  MODE = proc { |arg| [:pull_any, :pull_all, :push_any, :push_all].include? arg }
19
20
 
21
+ GATE_MODE = proc { |arg| [:probabilistic, :deterministic].include? arg }
22
+
20
23
  ACTIVATION= proc { |arg| [:automatic, :passive, :start].include? arg }
21
24
 
22
25
  PROC = proc { |arg| arg.is_a? Proc }
23
26
 
24
27
  LABEL = proc { |arg| arg.is_a?(Numeric) || arg.is_a?(Proc) }
25
28
 
29
+ SHORT_STRING = proc { |arg| arg.is_a?(String) && arg.length.between?(1, 25) }
30
+
26
31
  # Parse an arbitrary list of arguments and returns a well-formed
27
32
  # Hash which can then be used as argument to method add_node!
28
33
  # @param [Array] arguments an array of arguments.
29
34
  # @return [Hash] a well-formed hash
30
35
  def self.parse_arguments(arguments)
31
- arguments.inject(ConstantHash.new) do |accumulator, arg|
36
+ arguments.inject(ConstantHash.new) do |acc, elem|
32
37
 
33
- # named parameters are expressed as hashes
34
- # and all arguments can (also) be passed as named parameters
35
- if arg.is_a? Hash
38
+ # elem is here either a hash or a single argument that's
39
+ # been passed to this method
40
+
41
+ if elem.is_a? Hash
36
42
 
37
- if arg.has_key? :activation
38
- if ACTIVATION.match? arg[:activation]
39
- accumulator[:activation] = arg[:activation]
43
+ if elem.has_key? :activation
44
+ if ACTIVATION.match? elem[:activation]
45
+ acc[:activation] = elem[:activation]
40
46
  else
41
47
  raise BadDSL.new
42
48
  end
43
49
  end
44
50
 
45
- if arg.has_key? :condition
46
- if PROC.match? arg[:condition]
47
- accumulator[:condition] = arg[:condition]
51
+ if elem.has_key? :condition
52
+ if PROC.match? elem[:condition]
53
+ acc[:condition] = elem[:condition]
48
54
  else
49
55
  raise BadDSL.new
50
56
  end
51
57
  end
52
58
 
53
- if arg.has_key? :initial_value
54
- if INITIAL_VALUE.match? arg[:initial_value]
55
- accumulator[:initial_value] = arg[:initial_value]
59
+ if elem.has_key? :initial_value
60
+ if INITIAL_VALUE.match? elem[:initial_value]
61
+ acc[:initial_value] = elem[:initial_value]
56
62
  else
57
63
  raise BadDSL.new
58
64
  end
59
65
  end
60
66
 
61
- if arg.has_key? :mode
62
- if MODE.match? arg[:mode]
63
- accumulator[:mode] = arg[:mode]
67
+ if elem.has_key? :mode
68
+ if MODE.match? elem[:mode]
69
+ acc[:mode] = elem[:mode]
64
70
  else
65
71
  raise BadDSL.new
66
72
  end
67
73
  end
68
74
 
69
- if arg.has_key? :triggered_by
70
- if IDENTIFIER.match? arg[:triggered_by]
71
- accumulator[:triggered_by] = arg[:triggered_by]
75
+ if elem.has_key? :triggered_by
76
+ if IDENTIFIER.match? elem[:triggered_by]
77
+ acc[:triggered_by] = elem[:triggered_by]
72
78
  else
73
79
  raise BadDSL.new
74
80
  end
75
81
  end
76
82
 
77
- if arg.has_key? :triggers
78
- if IDENTIFIER.match? arg[:triggers]
79
- accumulator[:triggers] = arg[:triggers]
83
+ if elem.has_key? :triggers
84
+ if IDENTIFIER.match? elem[:triggers]
85
+ acc[:triggers] = elem[:triggers]
80
86
  else
81
87
  raise BadDSL.new
82
88
  end
83
89
  end
84
90
 
85
91
  else
86
- if IDENTIFIER.match?(arg) # a node's name, if present, is always the first argument
87
- accumulator[:name] = arg
88
- elsif INITIAL_VALUE.match?(arg)
89
- accumulator[:initial_value] = arg
90
- elsif MODE.match?(arg)
91
- accumulator[:mode] = arg
92
- elsif ACTIVATION.match?(arg)
93
- accumulator[:activation] = arg
92
+ if IDENTIFIER.match?(elem) # a node's name, if present, is always the first elemument
93
+ acc[:name] = elem
94
+ elsif INITIAL_VALUE.match?(elem)
95
+ acc[:initial_value] = elem
96
+ elsif MODE.match?(elem)
97
+ acc[:mode] = elem
98
+ elsif ACTIVATION.match?(elem)
99
+ acc[:activation] = elem
100
+ elsif PROC.match?(elem)
101
+ acc[:condition] = elem
94
102
  else
95
- raise BadDSL, "Argument #{arg} doesn't fit any known signature"
103
+ raise BadDSL, "elemument #{elem} doesn't fit any known signature"
96
104
  end
97
105
  end
98
106
  # passing the accumulator onto the next iteration
99
- accumulator
107
+ acc
100
108
  end
101
109
  end
102
110
 
@@ -110,6 +118,8 @@ module DSL
110
118
  accumulator[:condition] = arg[:condition] if PROC.match?(arg[:condition])
111
119
  elsif arg.has_key? :triggered_by
112
120
  accumulator[:triggered_by] = arg[:triggered_by] if IDENTIFIER.match?(arg[:triggered_by])
121
+ elsif arg.has_key?[:mode]
122
+ accumulator[:mode] = arg[:mode] if GATE_MODE.match?(arg[:mode])
113
123
  else
114
124
  raise BadDSL, "Named argument doesn't fit any known signature"
115
125
  end
@@ -118,8 +128,10 @@ module DSL
118
128
  accumulator[:name] = arg
119
129
  elsif ACTIVATION.match?(arg)
120
130
  accumulator[:activation] = arg
131
+ elsif GATE_MODE.match?(arg)
132
+ accumulator[:mode] = arg
121
133
  else
122
- raise BadDSL, "Argument #{arg} doesn't fit any known signature"
134
+ raise BadDSL, "Argument '#{arg}' doesn't fit any known signature"
123
135
  end
124
136
  end
125
137
  accumulator
@@ -129,31 +141,53 @@ module DSL
129
141
 
130
142
  def self.parse_edge_arguments(arguments)
131
143
 
132
- arguments.inject(ConstantHash.new) do |accumulator, arg|
133
- if arg.is_a? Hash
134
- if arg.has_key? :from
135
- accumulator[:from] = arg[:from] if IDENTIFIER.match? arg[:from]
144
+ arguments.inject(ConstantHash.new) do |acc, elem|
145
+ if elem.is_a? Hash
146
+ if elem.has_key? :from
147
+ acc[:from] = elem[:from] if IDENTIFIER.match? elem[:from]
136
148
  end
137
- if arg.has_key? :to
138
- accumulator[:to] = arg[:to] if IDENTIFIER.match? arg[:to]
149
+ if elem.has_key? :to
150
+ acc[:to] = elem[:to] if IDENTIFIER.match? elem[:to]
139
151
  end
140
- if arg.has_key? :label
141
- accumulator[:label] = arg[:label] if LABEL.match? arg[:label]
152
+ if elem.has_key? :label
153
+ acc[:label] = elem[:label] if LABEL.match? elem[:label]
142
154
  end
143
155
  else
144
- if IDENTIFIER.match?(arg)
145
- accumulator[:name] = arg
146
- elsif LABEL.match?(arg)
147
- accumulator[:label]=arg
156
+ if IDENTIFIER.match?(elem)
157
+ acc[:name] = elem
158
+ elsif LABEL.match?(elem)
159
+ acc[:label]=elem
148
160
  else
149
- raise BadDSL, "Argument #{arg} doesn't fit any known signature."
161
+ raise BadDSL, "argument #{elem} doesn't fit any known signature."
150
162
  end
151
163
  end
152
- accumulator
164
+ acc
153
165
  end
154
166
 
155
167
  end
156
168
 
169
+ def self.parse_stop_condition_arguments(arguments)
170
+ arguments.inject(ConstantHash.new) do |acc, elem|
171
+
172
+ if elem.is_a? Hash
173
+
174
+ if elem.has_key? :message
175
+ acc[:message] = elem[:message] if IDENTIFIER.match? elem[:message]
176
+ end
177
+
178
+ if elem.has_key? :condition
179
+ acc[:condition] = elem[:condition] if PROC.match? elem[:condition]
180
+ end
181
+
182
+ else
183
+ acc[:condition] = elem if PROC.match? elem
184
+ acc[:message] = elem if SHORT_STRING.match? elem
185
+ end
186
+
187
+ acc
188
+ end
189
+ end
190
+
157
191
  # Used to validate that a string is a valid name for diagram components
158
192
  #
159
193
  # @param [String] text the name we want to validate
@@ -161,7 +195,7 @@ module DSL
161
195
  # @return [String] the text itself, but only if it's valid. Otherwise
162
196
  # an exception will be raised.
163
197
  def self.validate_name!(text)
164
- if StringHelper.valid_ruby_variable_name?(text)
198
+ if StringUtils.valid_ruby_variable_name?(text)
165
199
  text
166
200
  else
167
201
  raise BadDSL, "Invalid name: '#{text}'"
@@ -171,7 +205,7 @@ module DSL
171
205
  # @param [String] text the name we want to validate
172
206
  # @return [Boolean] whether or not given text is a valid name for diagram elements
173
207
  def self.valid_name?(text)
174
- StringHelper.valid_ruby_variable_name?(text)
208
+ StringUtils.valid_ruby_variable_name?(text)
175
209
  end
176
210
 
177
211
  end