rachinations 0.0.7 → 0.0.8

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 (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