ellington 0.0.1 → 0.0.2

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.
@@ -0,0 +1,34 @@
1
+ require "delegate"
2
+
3
+ module Ellington
4
+ class LineInfo < SimpleDelegator
5
+ attr_reader :line, :station_info
6
+
7
+ def initialize(line, station_info)
8
+ @line = line
9
+ @station_info = station_info
10
+ super station_info
11
+ end
12
+
13
+ def station_full_name
14
+ @station_full_name ||= "#{station.class.name}::#{line.class.name}::#{line.route.name}"
15
+ end
16
+
17
+ def log_message(options={})
18
+ message = []
19
+ if options[:line_completed]
20
+ message << "[LINE COMPLETED]"
21
+ message << "[#{line.state(passenger)}]"
22
+ end
23
+ if options[:station_completed]
24
+ message << "[STATION COMPLETED]"
25
+ message << "[#{station.state(passenger)}]"
26
+ end
27
+ message << "[#{station_full_name}]"
28
+ line.route.log_passenger_attrs.each do |attr|
29
+ message << "[#{attr}:#{passenger.send(attr)}]"
30
+ end
31
+ message.join " "
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,24 @@
1
+ require "delegate"
2
+
3
+ module Ellington
4
+ class LineList < SimpleDelegator
5
+ attr_reader :route
6
+
7
+ def initialize(route)
8
+ @route = route
9
+ @inner_list = UniqueTypeArray.new
10
+ super @inner_list
11
+ end
12
+
13
+ def push(line)
14
+ value = inner_list << line
15
+ line.route = route
16
+ value
17
+ end
18
+ alias_method :<<, :push
19
+
20
+ protected
21
+
22
+ attr_reader :inner_list
23
+ end
24
+ end
@@ -0,0 +1,11 @@
1
+ module Ellington
2
+
3
+ def self.logger
4
+ @logger
5
+ end
6
+
7
+ def self.logger=(value)
8
+ @logger = value
9
+ end
10
+
11
+ end
@@ -0,0 +1,67 @@
1
+ require "observer"
2
+
3
+ module Ellington
4
+ class Passenger < SimpleDelegator
5
+ include Observable
6
+ attr_accessor :context, :ticket
7
+ attr_reader :route
8
+
9
+ def initialize(context, route, ticket=nil)
10
+ ticket ||= Ellington::Ticket.new
11
+ @context = context
12
+ @route = route
13
+ @ticket = ticket
14
+ super context
15
+ end
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
+ def current_state
33
+ return context.current_state if context.respond_to?(:current_state)
34
+ @current_state
35
+ end
36
+
37
+ def current_state=(value)
38
+ return context.current_state=(value) if context.respond_to?(:current_state=)
39
+ @current_state = value
40
+ end
41
+
42
+ def transition_to(new_state)
43
+ if !locked?
44
+ message = "Cannot transition an unlocked #{self.class.name}'s state"
45
+ raise Ellington::InvalidStateTransition.new(message)
46
+ end
47
+
48
+ if !route.states.can_transition?(current_state => new_state)
49
+ message = "Cannot transition #{self.class.name} from:#{current_state} to:#{new_state}"
50
+ raise Ellington::InvalidStateTransition.new(message)
51
+ end
52
+
53
+ old_state = current_state
54
+
55
+ if context.respond_to?(:transition_to)
56
+ return_value = context.transition_to(new_state)
57
+ else
58
+ self.current_state = new_state
59
+ end
60
+
61
+ changed
62
+ notify_observers Ellington::TransitionInfo.new(self, old_state, new_state)
63
+ return_value || new_state
64
+ end
65
+
66
+ end
67
+ end
@@ -0,0 +1,114 @@
1
+ require "delegate"
2
+
3
+ module Ellington
4
+ class Route < SimpleDelegator
5
+
6
+ def initialize
7
+ super self.class
8
+ init unless initialized?
9
+ end
10
+
11
+ class << self
12
+ include HasTargets
13
+ attr_reader :initialized
14
+
15
+ def initialized?
16
+ @initialized
17
+ end
18
+
19
+ def init
20
+ initialize_lines
21
+ states
22
+ @initialized = true
23
+ end
24
+
25
+ def initialize_lines
26
+ lines.each do |line|
27
+ line.add_observer self, :line_completed
28
+ line.stations.each do |station|
29
+ station.add_observer line, :station_completed
30
+ end
31
+ end
32
+ end
33
+
34
+ def board(passenger, options={})
35
+ lines.first.board passenger, options
36
+ end
37
+
38
+ def lines
39
+ @lines ||= Ellington::LineList.new(self)
40
+ end
41
+
42
+ def states
43
+ @states ||= begin
44
+ catalog = StateJacket::Catalog.new
45
+ catalog.add initial_state => lines.first.stations.first.states.keys
46
+
47
+ lines.each do |line|
48
+ catalog.merge! line.states
49
+ end
50
+
51
+ connections.each do |connection|
52
+ connection.states.each do |state|
53
+ catalog[state] ||= []
54
+ catalog[state].concat connection.line.stations.first.states.keys
55
+ end
56
+ end
57
+
58
+ catalog.lock
59
+ catalog
60
+ end
61
+ end
62
+
63
+ def initial_state
64
+ @initial_state ||= "PRE #{name}"
65
+ end
66
+
67
+ def pass_target(*line_goals)
68
+ @goal ||= Ellington::Target.new(*line_goals.flatten)
69
+ end
70
+ alias_method :passed, :pass_target
71
+ alias_method :goal, :pass_target
72
+
73
+ def connections
74
+ @connections ||= Ellington::ConnectionList.new
75
+ end
76
+
77
+ def connect_to(line, options)
78
+ connections << Ellington::Connection.new(line, options[:if])
79
+ end
80
+
81
+ def log_passenger_attrs(*attrs)
82
+ @log_passenger_attrs ||= attrs
83
+ end
84
+
85
+ def line_completed(line_info)
86
+ route_info = Ellington::RouteInfo.new(self, line_info)
87
+
88
+ required_connections = connections.select do |connection|
89
+ connection.states.satisfied?(route_info.passenger)
90
+ end
91
+
92
+ if required_connections.empty?
93
+ if passed.satisfied?(route_info.passenger) || failed.satisfied?(route_info.passenger)
94
+ log route_info
95
+ end
96
+ Ellington.logger.info "\n" if Ellington.logger
97
+ end
98
+
99
+ required_connections.each do |connection|
100
+ connection.line.board route_info.passenger, route_info.options
101
+ end
102
+ end
103
+
104
+ private
105
+
106
+ def log(route_info)
107
+ return unless Ellington.logger
108
+ Ellington.logger.info route_info.log_message
109
+ end
110
+
111
+ end
112
+
113
+ end
114
+ end
@@ -0,0 +1,25 @@
1
+ require "delegate"
2
+
3
+ module Ellington
4
+ class RouteInfo < SimpleDelegator
5
+ attr_reader :route, :line_info
6
+
7
+ def initialize(route, line_info)
8
+ @route = route
9
+ @line_info = line_info
10
+ super line_info
11
+ end
12
+
13
+ def log_message(options={})
14
+ message = []
15
+ message << "[ROUTE COMPLETED]"
16
+ message << "[#{route.state(passenger)}]"
17
+ message << "[#{station_full_name}]"
18
+ line.route.log_passenger_attrs.each do |attr|
19
+ message << "[#{attr}:#{passenger.send(attr)}]"
20
+ end
21
+ message.join " "
22
+ end
23
+ end
24
+ end
25
+
@@ -0,0 +1,88 @@
1
+ require "observer"
2
+ require "forwardable"
3
+
4
+ module Ellington
5
+
6
+ class Station
7
+ extend Forwardable
8
+ include Observable
9
+ attr_accessor :line
10
+ def_delegators :line, :route
11
+
12
+ def name
13
+ @name ||= "#{self.class.name}::#{line.name}"
14
+ end
15
+
16
+ def state_name(state)
17
+ "#{state.to_s.upcase} #{name}"
18
+ end
19
+
20
+ def passed
21
+ @pass_state ||= state_name(:pass)
22
+ end
23
+
24
+ def failed
25
+ @fail_state ||= state_name(:fail)
26
+ end
27
+
28
+ def errored
29
+ @error_state ||= state_name(:error)
30
+ end
31
+
32
+ def states
33
+ @states ||= begin
34
+ catalog = StateJacket::Catalog.new
35
+ catalog.add passed
36
+ catalog.add failed
37
+ catalog.add errored => [ passed, failed, errored ]
38
+ catalog.lock
39
+ catalog
40
+ end
41
+ end
42
+
43
+ def can_engage?(passenger, options={})
44
+ passenger.locked? &&
45
+ route.states.can_transition?(passenger.current_state => states.keys)
46
+ end
47
+
48
+ def engage(passenger, options={})
49
+ raise Ellington::NotImplementedError
50
+ end
51
+
52
+ def call(passenger, options={})
53
+ if can_engage?(passenger)
54
+ attendant = Ellington::Attendant.new(self)
55
+ passenger.add_observer attendant
56
+ engage passenger, options
57
+ passenger.delete_observer attendant
58
+ raise Ellington::AttendandDisapproves unless attendant.approve?
59
+ changed
60
+ notify_observers Ellington::StationInfo.new(self, passenger, attendant.passenger_transitions.first, options)
61
+ end
62
+
63
+ passenger
64
+ end
65
+
66
+ def pass(passenger)
67
+ passenger.transition_to passed
68
+ end
69
+
70
+ def fail(passenger)
71
+ passenger.transition_to failed
72
+ end
73
+
74
+ def error(passenger)
75
+ passenger.transition_to errored
76
+ end
77
+
78
+ def state(passenger)
79
+ case passenger.current_state
80
+ when passed then "PASS"
81
+ when failed then "FAIL"
82
+ when errored then "ERROR"
83
+ end
84
+ end
85
+
86
+ end
87
+
88
+ end
@@ -0,0 +1,11 @@
1
+ module Ellington
2
+ class StationInfo
3
+ attr_reader :station, :passenger, :transition, :options
4
+ def initialize(station, passenger, transition, options)
5
+ @station = station
6
+ @passenger = passenger
7
+ @transition = transition
8
+ @options = options
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,24 @@
1
+ require "delegate"
2
+
3
+ module Ellington
4
+ class StationList < SimpleDelegator
5
+ attr_reader :line
6
+
7
+ def initialize(line)
8
+ @line = line
9
+ @inner_list = UniqueTypeArray.new
10
+ super @inner_list
11
+ end
12
+
13
+ def push(station)
14
+ value = inner_list << station
15
+ station.line = line
16
+ value
17
+ end
18
+ alias_method :<<, :push
19
+
20
+ protected
21
+
22
+ attr_reader :inner_list
23
+ end
24
+ end
@@ -0,0 +1,25 @@
1
+ require "delegate"
2
+
3
+ module Ellington
4
+ class Target < SimpleDelegator
5
+
6
+ def initialize(*states)
7
+ @inner_list = states.flatten
8
+ super inner_list
9
+ end
10
+
11
+ def include?(state)
12
+ inner_list.include? state
13
+ end
14
+
15
+ def satisfied?(passenger)
16
+ return false if passenger.nil?
17
+ include? passenger.current_state
18
+ end
19
+
20
+ protected
21
+
22
+ attr_reader :inner_list
23
+
24
+ end
25
+ end
@@ -0,0 +1,22 @@
1
+ require "digest"
2
+ require "delegate"
3
+
4
+ module Ellington
5
+
6
+ class Ticket < SimpleDelegator
7
+ attr_reader :passenger, :goal
8
+
9
+ def initialize(goal=nil, hash={})
10
+ goal ||= Ellington::Target.new
11
+ @goal = goal
12
+ super hash
13
+ end
14
+
15
+ def id
16
+ Digest::SHA256.hexdigest values.map(&:to_s).join
17
+ end
18
+
19
+ end
20
+
21
+ end
22
+