ellington 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
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