ellington 0.1.2 → 0.1.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 473bfcef639bc18d7f003e00ccafba00e7ec5c55
4
- data.tar.gz: d89e7c0560cfddd667f92eb03960b8f9269c0d23
3
+ metadata.gz: b39e107345c1fad43d8eb115683b52627b71978f
4
+ data.tar.gz: f0406c1407a63cfc8bcbd48b6265ed24d2e4ad76
5
5
  SHA512:
6
- metadata.gz: ec471346cc54bd230a9458ff9b7de7a01ba2f01884700250d5ca572ca79fa0def291caa53cd51773b645212d85a341c840eb1928e114e951820cb77e1ba5d194
7
- data.tar.gz: e9784da113e2ff9d2c5adbf80f36415b4718dabaccba66dcfe5ad30a899161b373d15a56c66733e754ae710b3ce7b9cc16bf24c7f7bcf6c6f23acbc64121e084
6
+ metadata.gz: a272385be445c2afd786c7ec4eba7b2d75381b833d58771ce7298d8fa3a80694df58229347e92745fdeb59744a2b54aaf40a1606f30a9713c6c6c060aa0c8715
7
+ data.tar.gz: 4c2ef0b65ed7d78c481b2573dd8d9ebf555d845654eb6e176508cd2f103b0ed537c806fb88614fefe2d580dfcecda92605e04e7ccb0e54f40a83d606eca06155
data/README.md CHANGED
@@ -3,6 +3,7 @@
3
3
  [![Build Status](https://travis-ci.org/hopsoft/ellington.png)](https://travis-ci.org/hopsoft/ellington)
4
4
  [![Dependency Status](https://gemnasium.com/hopsoft/ellington.png)](https://gemnasium.com/hopsoft/ellington)
5
5
  [![Code Climate](https://codeclimate.com/github/hopsoft/ellington.png)](https://codeclimate.com/github/hopsoft/ellington)
6
+ [![Coverage Status](https://coveralls.io/repos/hopsoft/ellington/badge.png?branch=master)](https://coveralls.io/r/hopsoft/ellington?branch=master)
6
7
 
7
8
  Named after [Duke Ellington](http://www.dukeellington.com/) whose signature tune was ["Take the 'A' Train"](http://en.wikipedia.org/wiki/Take_the_%22A%22_Train).
8
9
  The song was written about [New York City's A train](http://en.wikipedia.org/wiki/A_%28New_York_City_Subway_service%29).
@@ -29,7 +30,7 @@ We've found that using consistent and cohesive physical metaphors helps people r
29
30
 
30
31
  ## Lexicon
31
32
 
32
- - **[Conductor](https://github.com/hopsoft/ellington/wiki/Conductor)** - A supervisor responsible for gathering `passengers` and putting them on a `route`.
33
+ - **[Conductor](https://github.com/hopsoft/ellington/wiki/Conductor)** - A supervisor responsible for putting `passengers` on a `route`.
33
34
  - **[Passenger](https://github.com/hopsoft/ellington/wiki/Passenger)** - The stateful context that will be riding the virtual subway.
34
35
  - **[Route](https://github.com/hopsoft/ellington/wiki/Route)** - A collection of `lines` and their `connections`.
35
36
  - **[Line](https://github.com/hopsoft/ellington/wiki/Line)** - A linear track that moves the `passenger` from point A to point B.
@@ -1,67 +1,20 @@
1
- require "thread"
2
-
3
1
  module Ellington
4
2
  class Conductor
5
3
  attr_reader :route
6
4
 
7
5
  def initialize(route)
8
6
  @route = route
9
- @conducting = false
10
- end
11
-
12
- def conducting?
13
- @conducting
14
- end
15
-
16
- def start
17
- return if conducting?
18
-
19
- mutex.synchronize do
20
- @stop = false
21
- @conducting = true
22
- end
23
-
24
- @thread = Thread.new do
25
- loop do
26
- if @stop
27
- mutex.synchronize { @conducting = false }
28
- Thread.current.exit
29
- end
30
- gather_passengers.each do |passenger|
31
- escort(passenger)
32
- end
33
- end
34
- end
35
- end
36
-
37
- def stop
38
- mutex.synchronize { @stop = true }
39
- end
40
-
41
- def wait
42
- thread.join
43
7
  end
44
8
 
9
+ # override this method in a subclass
10
+ # to perform actual passenger verification
45
11
  def verify(passenger)
46
12
  true
47
13
  end
48
14
 
49
- def gather_passengers
50
- raise Ellington::NotImplementedError
51
- end
52
-
53
- def escort(passenger)
54
- return unless verify(passenger) && passenger.locked?
15
+ def conduct(passenger)
16
+ return unless verify(passenger)
55
17
  route.lines.first.board passenger
56
18
  end
57
-
58
- protected
59
-
60
- attr_reader :thread
61
-
62
- def mutex
63
- @mutex ||= Mutex.new
64
- end
65
-
66
19
  end
67
20
  end
@@ -91,19 +91,23 @@ module Ellington
91
91
  line_info = Ellington::LineInfo.new(self, station_info)
92
92
  if line_info.station == stations.last ||
93
93
  line_info.passenger.current_state == line_info.station.failed
94
- log line_info, :station_completed => true
95
- log line_info, :line_completed => true
96
- changed
97
- notify_observers line_info
98
- else
99
- log line_info, :station_completed => true
100
- if line_info.passenger.current_state == line_info.station.errored
101
- Ellington.logger.info "\n" if Ellington.logger
102
- end
94
+ return complete_line(line_info)
95
+ end
96
+
97
+ log line_info, :station_completed => true
98
+ if line_info.passenger.current_state == line_info.station.errored
99
+ Ellington.logger.info "\n" if Ellington.logger
103
100
  end
104
101
  end
105
102
 
106
- private
103
+ protected
104
+
105
+ def complete_line(line_info)
106
+ log line_info, :station_completed => true
107
+ log line_info, :line_completed => true
108
+ changed
109
+ notify_observers line_info
110
+ end
107
111
 
108
112
  def log(line_info, options={})
109
113
  return unless Ellington.logger
@@ -4,31 +4,16 @@ module Ellington
4
4
  class Passenger < SimpleDelegator
5
5
  include Observable
6
6
  attr_accessor :context, :ticket
7
- attr_reader :route
7
+ attr_reader :route, :state_history
8
8
 
9
- def initialize(context, route, ticket=nil)
10
- ticket ||= Ellington::Ticket.new
9
+ def initialize(context, route, options={})
11
10
  @context = context
12
11
  @route = route
13
- @ticket = ticket
12
+ @ticket = options[:ticket] || Ellington::Ticket.new
13
+ @state_history = options[:state_history] || []
14
14
  super context
15
15
  end
16
16
 
17
- def lock
18
- return context.lock if context.respond_to?(:lock)
19
- @locked = true
20
- end
21
-
22
- def unlock
23
- return context.unlock if context.respond_to?(:unlock)
24
- @locked = false
25
- end
26
-
27
- def locked?
28
- return context.locked? if context.respond_to?(:locked?)
29
- @locked
30
- end
31
-
32
17
  def current_state
33
18
  return context.current_state if context.respond_to?(:current_state)
34
19
  @current_state
@@ -39,20 +24,11 @@ module Ellington
39
24
  @current_state = value
40
25
  end
41
26
 
42
- def state_history
43
- @state_history ||= []
44
- end
45
-
46
27
  def state_history_includes?(*states)
47
28
  (state_history & states).length == states.length
48
29
  end
49
30
 
50
31
  def transition_to(new_state)
51
- if !locked?
52
- message = "Cannot transition an unlocked #{self.class.name}'s state"
53
- raise Ellington::InvalidStateTransition.new(message)
54
- end
55
-
56
32
  if !route.states.can_transition?(current_state => new_state)
57
33
  message = "Cannot transition #{self.class.name} from:#{current_state} to:#{new_state}"
58
34
  raise Ellington::InvalidStateTransition.new(message)
@@ -61,7 +37,7 @@ module Ellington
61
37
  old_state = current_state
62
38
 
63
39
  if context.respond_to?(:transition_to)
64
- return_value = context.transition_to(new_state)
40
+ return_value = context.transition_to(new_state)
65
41
  else
66
42
  self.current_state = new_state
67
43
  end
@@ -43,14 +43,6 @@ module Ellington
43
43
  (@subclasses ||= []) << subclass
44
44
  end
45
45
 
46
- def generate_graphs(dir, options={})
47
- options[:format] ||= :svg
48
- FileUtils.mkdir_p(dir)
49
- @subclasses.each do |subclass|
50
- Ellington::Visualizer.new(subclass.new, dir, options[:format]).graph_all
51
- end
52
- end
53
-
54
46
  def initialized?
55
47
  @initialized
56
48
  end
@@ -116,26 +108,29 @@ module Ellington
116
108
 
117
109
  def line_completed(line_info)
118
110
  route_info = Ellington::RouteInfo.new(self, line_info)
119
-
120
- required_connections = connections.select do |connection|
121
- connection.required?(route_info.passenger)
111
+ connections = required_connections(route_info.passenger)
112
+ return complete_route(route_info) if connections.empty?
113
+ connections.each do |connection|
114
+ connection.line.board route_info.passenger, route_info.options
122
115
  end
116
+ end
123
117
 
124
- if required_connections.empty?
125
- if passed.satisfied?(route_info.passenger) || failed.satisfied?(route_info.passenger)
126
- log route_info
127
- changed
128
- notify_observers route_info
129
- end
130
- Ellington.logger.info "\n" if Ellington.logger
131
- end
118
+ protected
132
119
 
133
- required_connections.each do |connection|
134
- connection.line.board route_info.passenger, route_info.options
120
+ def complete_route(route_info)
121
+ if passed.satisfied?(route_info.passenger) || failed.satisfied?(route_info.passenger)
122
+ log route_info
123
+ changed
124
+ notify_observers route_info
135
125
  end
126
+ Ellington.logger.info "\n" if Ellington.logger
136
127
  end
137
128
 
138
- private
129
+ def required_connections(passenger)
130
+ connections.select do |connection|
131
+ connection.required?(passenger)
132
+ end
133
+ end
139
134
 
140
135
  def log(route_info)
141
136
  return unless Ellington.logger
@@ -47,8 +47,9 @@ module Ellington
47
47
  end
48
48
 
49
49
  def can_engage?(passenger, options={})
50
- passenger.locked? &&
51
- route.states.can_transition?(passenger.current_state => states.keys)
50
+ return false unless route.states.can_transition?(passenger.current_state => states.keys)
51
+ return false if passenger.state_history_includes?(passed)
52
+ true
52
53
  end
53
54
 
54
55
  def engage(passenger, options={})
@@ -92,3 +93,4 @@ module Ellington
92
93
  end
93
94
 
94
95
  end
96
+
@@ -1,3 +1,3 @@
1
1
  module Ellington
2
- VERSION = "0.1.2"
2
+ VERSION = "0.1.3"
3
3
  end
@@ -15,6 +15,11 @@ module Ellington
15
15
  super children
16
16
  end
17
17
 
18
+ def add(node)
19
+ self << node
20
+ node
21
+ end
22
+
18
23
  def name
19
24
  viz.id
20
25
  end
@@ -28,11 +33,10 @@ module Ellington
28
33
  end
29
34
  end
30
35
 
31
- attr_reader :route, :dir, :format
36
+ attr_reader :route, :format
32
37
 
33
- def initialize(route, dir, format=:svg)
38
+ def initialize(route, format=:svg)
34
39
  @route = route
35
- @dir = dir
36
40
  @format = format
37
41
  end
38
42
 
@@ -59,51 +63,31 @@ module Ellington
59
63
  CLUSTER_FILLCOLOR = "gray70"
60
64
  CLUSTER_PENCOLOR = "gray50"
61
65
 
62
- def graph_all(passenger=nil)
63
- graph_route_basic passenger
64
- graph_route passenger
65
- graph_lines_basic passenger
66
- graph_lines passenger
67
- end
68
-
69
66
  def graph_lines_basic(passenger=nil)
70
67
  g = Node.new(nil, GraphViz.new("GraphLinesBasic"))
71
68
  set_graph_defaults g.viz
72
69
  g.viz["label"] = "#{route.name} Lines - basic"
73
70
 
74
71
  route.lines.each_with_index do |line, index|
75
- line_cluster = Node.new(line, g.viz.add_graph("cluster#{index}"))
72
+ line_cluster = g.add(Node.new(line, g.viz.add_graph("cluster#{index}")))
76
73
  set_cluster_defaults line_cluster.viz
77
74
  line_cluster.viz["label"] = line.class.name
78
- g << line_cluster
79
75
 
80
76
  line.stations.each do |station|
81
- station_node = Node.new(station, line_cluster.viz.add_nodes(station.class.name))
82
- line_cluster << station_node
83
-
84
- if !(line.goal & station.states.keys).empty?
85
- station_node.viz["color"] = NODE_COLOR_LINE_GOAL
86
- station_node.viz["fillcolor"] = NODE_COLOR_LINE_GOAL
87
- end
88
-
89
- if !(route.goal & station.states.keys).empty?
90
- station_node.viz["color"] = NODE_COLOR_ROUTE_GOAL
91
- station_node.viz["fillcolor"] = NODE_COLOR_ROUTE_GOAL
92
- end
93
-
94
- if passenger && !(passenger.state_history & station.states.keys).empty?
95
- station_node.viz["color"] = NODE_COLOR_PASSENGER_HIT
96
- station_node.viz["penwidth"] = NODE_PENWIDTH_PASSENGER_HIT
97
- end
77
+ states = station.states.keys
78
+ station_node = line_cluster.add(Node.new(station, line_cluster.viz.add_nodes(station.class.name)))
79
+ style_node_for_line(station_node, line, *states)
80
+ style_node_for_route(station_node, route, *states)
81
+ style_node_for_passenger(station_node, passenger, *states)
98
82
  end
99
83
 
100
84
  line_cluster.each_with_index.each do |node, node_index|
101
85
  next_node = line_cluster[node_index + 1]
102
86
  if next_node
103
- station = node.base
104
- next_station = next_node.base
87
+ #station = node.base
88
+ #next_station = next_node.base
105
89
  edge = line_cluster.viz.add_edges(node.viz, next_node.viz)
106
- if passenger
90
+ if passenger
107
91
  if color_name(node.viz["color"]) == NODE_COLOR_PASSENGER_HIT &&
108
92
  color_name(next_node.viz["color"]) == NODE_COLOR_PASSENGER_HIT
109
93
  edge["color"] = EDGE_COLOR_PASSENGER_HIT
@@ -116,8 +100,7 @@ module Ellington
116
100
  end
117
101
  end
118
102
 
119
- file_name = "#{route.name.downcase.gsub("::", "_")}-lines-basic.#{format}"
120
- g.viz.output(format => File.join(dir, file_name))
103
+ g.viz.output(format => String)
121
104
  end
122
105
 
123
106
  def graph_lines(passenger=nil)
@@ -126,34 +109,17 @@ module Ellington
126
109
  g.viz["label"] = "#{route.name} Lines"
127
110
 
128
111
  route.lines.each_with_index do |line, index|
129
- line_cluster = Node.new(line, g.viz.add_graph("cluster#{index}"))
112
+ line_cluster = g.add(Node.new(line, g.viz.add_graph("cluster#{index}")))
130
113
  set_cluster_defaults line_cluster.viz
131
114
  line_cluster.viz["label"] = line.class.name
132
- g << line_cluster
133
-
134
- line.states.keys.each do |state|
135
- state_node = Node.new(state, line_cluster.viz.add_nodes(state))
136
- if line.goal.include?(state)
137
- state_node.viz["color"] = NODE_COLOR_LINE_GOAL
138
- state_node.viz["fillcolor"] = NODE_FILLCOLOR_LINE_GOAL
139
- end
140
- if route.goal.include?(state)
141
- state_node.viz["color"] = NODE_COLOR_ROUTE_GOAL
142
- state_node.viz["fillcolor"] = NODE_FILLCOLOR_ROUTE_GOAL
143
- end
144
- if passenger && passenger.state_history_includes?(state)
145
- state_node.viz["color"] = NODE_COLOR_PASSENGER_HIT
146
- state_node.viz["penwidth"] = NODE_PENWIDTH_PASSENGER_HIT
147
- end
148
- line_cluster << state_node
149
- end
115
+ add_state_nodes_for_line line_cluster, line, passenger
150
116
 
151
117
  line.states.each do |state, transitions|
152
118
  a = line_cluster.find(state)
153
119
  (transitions || []).each do |transition|
154
120
  b = line_cluster.find(transition)
155
121
  edge = line_cluster.viz.add_edges(a.viz, b.viz)
156
- if passenger
122
+ if passenger
157
123
  if passenger.state_history_includes?(state, transition)
158
124
  edge["color"] = EDGE_COLOR_PASSENGER_HIT
159
125
  edge["penwidth"] = EDGE_PENWIDTH_PASSENGER_HIT
@@ -165,8 +131,7 @@ module Ellington
165
131
  end
166
132
  end
167
133
 
168
- file_name = "#{route.name.downcase.gsub("::", "_")}-lines.#{format}"
169
- g.viz.output(format => File.join(dir, file_name))
134
+ g.viz.output(format => String)
170
135
  end
171
136
 
172
137
  def graph_route_basic(passenger=nil)
@@ -176,34 +141,17 @@ module Ellington
176
141
  g.viz["label"] = "#{route.name} Route - basic"
177
142
 
178
143
  route.lines.each_with_index do |line, index|
179
- line_cluster = Node.new(line, g.viz.add_graph("cluster#{index}"))
144
+ line_cluster = g.add(Node.new(line, g.viz.add_graph("cluster#{index}")))
180
145
  set_cluster_defaults line_cluster.viz
181
146
  line_cluster.viz["label"] = line.class.name
182
- g << line_cluster
183
147
 
184
148
  passenger_hit = false
185
149
  %w{PASS FAIL ERROR}.each do |state|
186
- state_node = Node.new(state, line_cluster.viz.add_nodes("#{line.class.name}#{state}", "label" => state))
187
- line_cluster << state_node
150
+ state_node = line_cluster.add(Node.new(state, line_cluster.viz.add_nodes("#{line.class.name}#{state}", "label" => state)))
188
151
  states = line.stations.map{ |s| "#{state} #{s.name}" }
189
-
190
- if !(line.goal & states).empty?
191
- state_node.viz["color"] = NODE_COLOR_LINE_GOAL
192
- state_node.viz["fillcolor"] = NODE_COLOR_LINE_GOAL
193
- end
194
-
195
- if !(route.goal & states).empty?
196
- state_node.viz["color"] = NODE_COLOR_ROUTE_GOAL
197
- state_node.viz["fillcolor"] = NODE_COLOR_ROUTE_GOAL
198
- end
199
-
200
- if passenger
201
- if !passenger_hit && !(passenger.state_history & line.send("#{state.downcase}ed")).empty?
202
- passenger_hit = true
203
- state_node.viz["color"] = NODE_COLOR_PASSENGER_HIT
204
- state_node.viz["penwidth"] = NODE_PENWIDTH_PASSENGER_HIT
205
- end
206
- end
152
+ style_node_for_line(state_node, line, *states)
153
+ style_node_for_route(state_node, route, *states)
154
+ passenger_hit ||= style_node_for_passenger(state_node, passenger, *line.send("#{state.downcase}ed"))
207
155
  end
208
156
  end
209
157
 
@@ -255,8 +203,7 @@ module Ellington
255
203
  end
256
204
  end
257
205
 
258
- file_name = "#{route.name.downcase.gsub("::", "_")}-route-basic.#{format}"
259
- g.viz.output(format => File.join(dir, file_name))
206
+ g.viz.output(format => String)
260
207
  end
261
208
 
262
209
  def graph_route(passenger=nil)
@@ -266,35 +213,18 @@ module Ellington
266
213
  g.viz["ranksep"] = 0.8
267
214
 
268
215
  route.lines.each_with_index do |line, index|
269
- line_cluster = Node.new(line, g.viz.add_graph("cluster#{index}"))
216
+ line_cluster = g.add(Node.new(line, g.viz.add_graph("cluster#{index}")))
270
217
  set_cluster_defaults line_cluster.viz
271
218
  line_cluster.viz["label"] = line.class.name
272
- g << line_cluster
273
-
274
- line.states.keys.each do |state|
275
- state_node = Node.new(state, line_cluster.viz.add_nodes(state))
276
- if line.goal.include?(state)
277
- state_node.viz["color"] = NODE_COLOR_LINE_GOAL
278
- state_node.viz["fillcolor"] = NODE_FILLCOLOR_LINE_GOAL
279
- end
280
- if route.goal.include?(state)
281
- state_node.viz["color"] = NODE_COLOR_ROUTE_GOAL
282
- state_node.viz["fillcolor"] = NODE_FILLCOLOR_ROUTE_GOAL
283
- end
284
- if passenger && passenger.state_history_includes?(state)
285
- state_node.viz["color"] = NODE_COLOR_PASSENGER_HIT
286
- state_node.viz["penwidth"] = NODE_PENWIDTH_PASSENGER_HIT
287
- end
288
- line_cluster << state_node
289
- end
219
+ add_state_nodes_for_line line_cluster, line, passenger
290
220
  end
291
221
 
292
222
  viz = g.viz.add_nodes(route.initial_state)
293
223
  rendered_edges = {}
294
224
 
295
225
  if passenger
296
- passenger_nodes = g.reduce([]) do |memo, line_cluster|
297
- line_cluster.children.each do |node|
226
+ passenger_nodes = g.reduce([]) do |memo, line_cluster|
227
+ line_cluster.children.each do |node|
298
228
  if node.viz["color"].to_s.gsub(/\W/, "") == NODE_COLOR_PASSENGER_HIT
299
229
  memo << node
300
230
  end
@@ -334,11 +264,43 @@ module Ellington
334
264
  end
335
265
  end
336
266
 
337
- file_name = "#{route.name.downcase.gsub("::", "_")}-route.#{format}"
338
- g.viz.output(format => File.join(dir, file_name))
267
+ g.viz.output(format => String)
339
268
  end
340
269
 
341
- private
270
+ protected
271
+
272
+ def add_state_nodes_for_line(cluster, line, passenger)
273
+ line.states.keys.each do |state|
274
+ node = cluster.add(Node.new(state, cluster.viz.add_nodes(state)))
275
+ style_node_for_line(node, line, state)
276
+ style_node_for_route(node, line.route, state)
277
+ style_node_for_passenger(node, passenger, state)
278
+ end
279
+ end
280
+
281
+ def style_node(node, color)
282
+ node.viz["color"] = color
283
+ node.viz["fillcolor"] = color
284
+ true
285
+ end
286
+
287
+ def style_node_for_line(node, line, *states)
288
+ return false if (line.goal & states).empty?
289
+ style_node node, NODE_COLOR_LINE_GOAL
290
+ end
291
+
292
+ def style_node_for_route(node, route, *states)
293
+ return false if (route.goal & states).empty?
294
+ style_node node, NODE_COLOR_ROUTE_GOAL
295
+ end
296
+
297
+ def style_node_for_passenger(node, passenger, *states)
298
+ return false if passenger.nil?
299
+ return false if (passenger.state_history & states).empty?
300
+ node.viz["color"] = NODE_COLOR_PASSENGER_HIT
301
+ node.viz["penwidth"] = NODE_PENWIDTH_PASSENGER_HIT
302
+ true
303
+ end
342
304
 
343
305
  def color_name(graphviz_color)
344
306
  graphviz_color.to_s.gsub("\"", "")
@@ -8,7 +8,6 @@ class AttendantTest < MicroTest::Test
8
8
  @station = line.stations.first
9
9
  @passenger = Ellington::Passenger.new(NumberWithHistory.new(0), route)
10
10
  @passenger.current_state = route.initial_state
11
- @passenger.lock
12
11
  @attendant = Ellington::Attendant.new(@station)
13
12
  @passenger.add_observer @attendant
14
13
  end
@@ -7,55 +7,23 @@ class ConductorTest < MicroTest::Test
7
7
  @conductor = Ellington::Conductor.new(@route)
8
8
  @passenger = Ellington::Passenger.new(NumberWithHistory.new(0), @route)
9
9
  @passenger.current_state = @route.initial_state
10
- @passenger.lock
11
- end
12
-
13
- test "not conducting" do
14
- assert !@conductor.conducting?
15
10
  end
16
11
 
17
12
  test "verify" do
18
13
  assert @conductor.verify(@passenger)
19
14
  end
20
15
 
21
- test "gather_passengers is abstract" do
22
- error = nil
23
- begin
24
- @conductor.gather_passengers
25
- rescue Ellington::NotImplementedError => e
26
- error = e
27
- end
28
- assert !error.nil?
29
- end
30
-
31
- test "escort" do
32
- @conductor.escort(@passenger)
16
+ test "conduct" do
17
+ @conductor.conduct(@passenger)
33
18
  assert @passenger.current_state != @route.initial_state
34
19
  end
35
20
 
36
- test "escort prevented when passenger doesn't verify" do
21
+ test "conduct prevented when passenger doesn't verify" do
37
22
  def @conductor.verify(passenger)
38
23
  false
39
24
  end
40
- @conductor.escort(@passenger)
25
+ @conductor.conduct(@passenger)
41
26
  assert @passenger.current_state == @route.initial_state
42
27
  end
43
28
 
44
- test "escort prevented when passenger is not locked" do
45
- @passenger.unlock
46
- assert @passenger.current_state == @route.initial_state
47
- end
48
-
49
- test "start/stop with conducting? checks" do
50
- def @conductor.gather_passengers
51
- sleep 0.1
52
- []
53
- end
54
- @conductor.start
55
- assert @conductor.conducting?
56
- @conductor.stop
57
- sleep 0.2
58
- puts !@conductor.conducting?
59
- end
60
-
61
29
  end
data/test/example.rb CHANGED
@@ -190,27 +190,3 @@ class BasicMath < Ellington::Route
190
190
  log_options :passenger => [:original_value, :current_value]
191
191
  end
192
192
 
193
- # conductor ---------------------------------------------------------------
194
- class NumberConductor < Ellington::Conductor
195
-
196
- def gather_passengers
197
- sleep 1
198
- (0..999).to_a.sample(10).map do |num|
199
- num = NumberWithHistory.new(num)
200
- passenger = Ellington::Passenger.new(num, route)
201
- passenger.current_state = route.initial_state
202
- passenger.lock
203
- passenger
204
- end
205
- end
206
-
207
- end
208
-
209
- if ENV["START"]
210
- route = BasicMath.new
211
- conductor = NumberConductor.new(route)
212
- conductor.start
213
- #conductor.wait
214
- sleep 5
215
- conductor.stop
216
- end
@@ -8,41 +8,26 @@ class PassengerTest < MicroTest::Test
8
8
  @passenger = Ellington::Passenger.new(@number, @route)
9
9
  end
10
10
 
11
- test "lock" do
12
- @passenger.lock
13
- assert @passenger.locked?
11
+ test "construct with ticket option" do
12
+ ticket = Ellington::Ticket.new
13
+ passenger = Ellington::Passenger.new(@number, @route, :ticket => ticket)
14
+ assert passenger.ticket == ticket
14
15
  end
15
16
 
16
- test "unlock" do
17
- @passenger.lock
18
- @passenger.unlock
19
- assert !@passenger.locked?
20
- end
21
-
22
- test "transition_to fails when unlocked" do
23
- error = nil
24
- begin
25
- @passenger.current_state = @route.initial_state
26
- @passenger.transition_to @route.lines.first.states.keys.first
27
- rescue Ellington::InvalidStateTransition => e
28
- error = e
29
- end
30
- assert !error.nil?
31
- assert error.message == "Cannot transition an unlocked Ellington::Passenger's state"
17
+ test "construct with state_history option" do
18
+ passenger = Ellington::Passenger.new(@number, @route, :state_history => [:foo, :bar])
19
+ assert passenger.state_history == [:foo, :bar]
32
20
  end
33
21
 
34
22
  test "transition_to valid state" do
35
- @passenger.lock
36
23
  @passenger.current_state = @route.initial_state
37
24
  @passenger.transition_to @route.lines.first.states.keys.first
38
- @passenger.unlock
39
25
  assert @passenger.current_state == @route.lines.first.states.keys.first
40
26
  end
41
27
 
42
28
  test "transition_to invalid state" do
43
29
  error = nil
44
30
  begin
45
- @passenger.lock
46
31
  @passenger.current_state = :error
47
32
  @passenger.transition_to :happy
48
33
  rescue Ellington::InvalidStateTransition => e
@@ -60,10 +45,8 @@ class PassengerTest < MicroTest::Test
60
45
  end
61
46
 
62
47
  @passenger.add_observer(watcher)
63
- @passenger.lock
64
48
  @passenger.current_state = @route.initial_state
65
49
  @passenger.transition_to @route.lines.first.states.keys.first
66
- @passenger.unlock
67
50
 
68
51
  assert watcher.info.passenger == @passenger
69
52
  assert watcher.info.old_state == "PRE BasicMath"
@@ -71,11 +54,9 @@ class PassengerTest < MicroTest::Test
71
54
  end
72
55
 
73
56
  test "state_history" do
74
- @passenger.lock
75
57
  @passenger.current_state = @route.initial_state
76
58
  @passenger.transition_to @route.lines[0].states.keys.first
77
59
  @passenger.transition_to @route.lines[2].states.keys.first
78
- @passenger.unlock
79
60
  assert @passenger.state_history == ["PASS Addition Add10", "PASS Multiplication MultiplyBy10"]
80
61
  end
81
62
 
data/test/station_test.rb CHANGED
@@ -8,7 +8,6 @@ class StationTest < MicroTest::Test
8
8
  @station = @line.stations.first
9
9
  @passenger = Ellington::Passenger.new(NumberWithHistory.new(0), @route)
10
10
  @passenger.current_state = @route.initial_state
11
- @passenger.lock
12
11
  end
13
12
 
14
13
  test "name" do
@@ -89,6 +88,11 @@ class StationTest < MicroTest::Test
89
88
  assert !@station.can_engage?(@passenger)
90
89
  end
91
90
 
91
+ test "can_engage? returns false if the passed state exists in state_history" do
92
+ @passenger.current_state = @station.passed
93
+ assert !@station.can_engage?(@passenger)
94
+ end
95
+
92
96
  test "engage" do
93
97
  @passenger.current_state = @route.initial_state
94
98
  @station.engage(@passenger, nil)
data/test/target_test.rb CHANGED
@@ -24,28 +24,28 @@ class TargetTest < MicroTest::Test
24
24
  end
25
25
 
26
26
  test "not satisfied for passenger in the wrong state" do
27
- passenger = Ellington::Passenger.new(@number, Ellington::Ticket.new, StateJacket::Catalog.new)
27
+ passenger = Ellington::Passenger.new(@number, nil)
28
28
  passenger.current_state = :open
29
29
  goal = Ellington::Target.new(:closed)
30
30
  assert !goal.satisfied?(passenger)
31
31
  end
32
32
 
33
33
  test "satisfied for passenger in the right state" do
34
- passenger = Ellington::Passenger.new(@number, Ellington::Ticket.new, StateJacket::Catalog.new)
34
+ passenger = Ellington::Passenger.new(@number, nil)
35
35
  passenger.current_state = :closed
36
36
  goal = Ellington::Target.new(:closed)
37
37
  assert goal.satisfied?(passenger)
38
38
  end
39
39
 
40
40
  test "satisfied for passenger in an expected state" do
41
- passenger = Ellington::Passenger.new(@number, Ellington::Ticket.new, StateJacket::Catalog.new)
41
+ passenger = Ellington::Passenger.new(@number, nil)
42
42
  passenger.current_state = :two
43
43
  goal = Ellington::Target.new(:one, :two, :three)
44
44
  assert goal.satisfied?(passenger)
45
45
  end
46
46
 
47
47
  test "not satisfied for passenger not in an expected state" do
48
- passenger = Ellington::Passenger.new(@number, Ellington::Ticket.new, StateJacket::Catalog.new)
48
+ passenger = Ellington::Passenger.new(@number, nil)
49
49
  passenger.current_state = :four
50
50
  goal = Ellington::Target.new(:one, :two, :three)
51
51
  assert !goal.satisfied?(passenger)
data/test/test_helper.rb CHANGED
@@ -1,7 +1,16 @@
1
- require "micro_test"
2
- require "micro_mock"
3
1
  require "logger"
4
2
  require "delegate"
3
+ require "micro_test"
4
+ require "micro_mock"
5
+ require 'simplecov'
6
+ require "coveralls"
7
+ SimpleCov.formatter = Coveralls::SimpleCov::Formatter
8
+ SimpleCov.command_name 'micro_test'
9
+ SimpleCov.start do
10
+ add_filter "/test/"
11
+ end
12
+ Coveralls.wear!
13
+
5
14
  require_relative "../lib/ellington"
6
15
  require_relative "example"
7
16
 
data/test/ticket_test.rb CHANGED
@@ -10,7 +10,7 @@ class TicketTest < MicroTest::Test
10
10
  :debit_id => "b834fb69-2eb3-4e57-b313-16b863e91f74",
11
11
  :credit_id => "9a670fce-4826-4a90-aaf7-a58738a4ce5b"
12
12
  )
13
- @passenger = Ellington::Passenger.new(0, @route, @ticket)
13
+ @passenger = Ellington::Passenger.new(0, @route, :ticket => @ticket)
14
14
  end
15
15
 
16
16
  test "basic ticket" do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ellington
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nathan Hopkins
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-06-13 00:00:00.000000000 Z
11
+ date: 2013-08-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: hero
@@ -150,6 +150,34 @@ dependencies:
150
150
  - - '>='
151
151
  - !ruby/object:Gem::Version
152
152
  version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: simplecov
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - '>='
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - '>='
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: coveralls
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - '>='
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - '>='
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
153
181
  description: An opinionated framework for modeling complex business processes.
154
182
  email:
155
183
  - natehop@gmail.com
@@ -181,7 +209,6 @@ files:
181
209
  - lib/ellington/visualizer.rb
182
210
  - lib/ellington.rb
183
211
  - Gemfile
184
- - Gemfile.lock
185
212
  - LICENSE.txt
186
213
  - Rakefile
187
214
  - README.md
@@ -222,7 +249,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
222
249
  version: '0'
223
250
  requirements: []
224
251
  rubyforge_project:
225
- rubygems_version: 2.0.0
252
+ rubygems_version: 2.0.3
226
253
  signing_key:
227
254
  specification_version: 4
228
255
  summary: An opinionated framework for modeling complex business processes.
@@ -244,3 +271,4 @@ test_files:
244
271
  - test/ticket_test.rb
245
272
  - test/transition_info_test.rb
246
273
  - test/unique_type_array_test.rb
274
+ has_rdoc:
data/Gemfile.lock DELETED
@@ -1,51 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- ellington (0.1.2)
5
- hero (~> 0.1.9)
6
- ruby-graphviz (~> 1.0.8)
7
- state_jacket (~> 0.1.0)
8
-
9
- GEM
10
- remote: https://rubygems.org/
11
- specs:
12
- awesome_print (1.1.0)
13
- binding_of_caller (0.7.2)
14
- debug_inspector (>= 0.0.1)
15
- coderay (1.0.9)
16
- debug_inspector (0.0.2)
17
- hero (0.1.9)
18
- interception (0.3)
19
- method_source (0.8.1)
20
- micro_mock (1.1.0)
21
- micro_test (0.3.8)
22
- os
23
- os (0.9.6)
24
- pry (0.9.12.2)
25
- coderay (~> 1.0.5)
26
- method_source (~> 0.8)
27
- slop (~> 3.4)
28
- pry-rescue (1.1.1)
29
- interception (>= 0.3)
30
- pry
31
- pry-stack_explorer (0.4.9)
32
- binding_of_caller (>= 0.7)
33
- pry (~> 0.9.11)
34
- rake (10.0.4)
35
- ruby-graphviz (1.0.9)
36
- slop (3.4.5)
37
- state_jacket (0.1.0)
38
- yell (1.3.0)
39
-
40
- PLATFORMS
41
- ruby
42
-
43
- DEPENDENCIES
44
- awesome_print
45
- ellington!
46
- micro_mock
47
- micro_test
48
- pry-rescue
49
- pry-stack_explorer
50
- rake
51
- yell