commute 0.2.0.rc.2 → 0.3.0.pre
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.
- data/.todo +28 -12
- data/README.md +0 -1
- data/commute.gemspec +4 -5
- data/lib/commute/common/basic_auth.rb +10 -9
- data/lib/commute/common/caching.rb +208 -0
- data/lib/commute/common/chemicals.rb +47 -24
- data/lib/commute/common/eventmachine.rb +68 -0
- data/lib/commute/common/synchrony.rb +42 -0
- data/lib/commute/common/typhoeus.rb +64 -0
- data/lib/commute/core/api.rb +42 -29
- data/lib/commute/core/builder.rb +4 -15
- data/lib/commute/core/context.rb +156 -15
- data/lib/commute/core/http.rb +124 -0
- data/lib/commute/core/layer.rb +187 -0
- data/lib/commute/core/sequence.rb +83 -132
- data/lib/commute/core/stack.rb +63 -72
- data/lib/commute/core/status.rb +45 -0
- data/lib/commute/core/util/event_emitter.rb +58 -0
- data/lib/commute/core/util/path.rb +37 -0
- data/lib/commute/core/util/stream.rb +141 -0
- data/lib/commute/extensions/crud.rb +88 -0
- data/lib/commute/extensions/param.rb +20 -0
- data/lib/commute/extensions/url.rb +53 -0
- data/lib/commute/version.rb +1 -1
- data/spec/commute/common/caching_spec.rb +158 -0
- data/spec/commute/common/eventmachine_spec.rb +74 -0
- data/spec/commute/common/typhoeus_spec.rb +67 -0
- data/spec/commute/core/api_spec.rb +3 -1
- data/spec/commute/core/builder_spec.rb +8 -8
- data/spec/commute/core/http_spec.rb +39 -0
- data/spec/commute/core/layer_spec.rb +81 -0
- data/spec/commute/core/sequence_spec.rb +36 -150
- data/spec/commute/core/stack_spec.rb +33 -83
- data/spec/commute/core/util/event_emitter_spec.rb +35 -0
- data/spec/commute/core/util/path_spec.rb +29 -0
- data/spec/commute/core/util/stream_spec.rb +90 -0
- data/spec/commute/extensions/url_spec.rb +76 -0
- data/spec/spec_helper.rb +3 -1
- metadata +61 -48
- data/examples/gist_api.rb +0 -71
- data/examples/highrise_task_api.rb +0 -59
- data/examples/pastie_api.rb +0 -18
- data/lib/commute/aspects/caching.rb +0 -37
- data/lib/commute/aspects/crud.rb +0 -41
- data/lib/commute/aspects/pagination.rb +0 -16
- data/lib/commute/aspects/url.rb +0 -57
- data/lib/commute/common/cache.rb +0 -43
- data/lib/commute/common/conditional.rb +0 -27
- data/lib/commute/common/em-synchrony_adapter.rb +0 -29
- data/lib/commute/common/em_http_request_adapter.rb +0 -57
- data/lib/commute/common/typhoeus_adapter.rb +0 -40
- data/lib/commute/common/xml.rb +0 -7
- data/lib/commute/core/commuter.rb +0 -116
- data/lib/commute/core/processors/code_status_processor.rb +0 -40
- data/lib/commute/core/processors/hook.rb +0 -14
- data/lib/commute/core/processors/request_builder.rb +0 -26
- data/lib/commute/core/processors/sequencer.rb +0 -46
- data/lib/commute/core/request.rb +0 -58
- data/lib/commute/core/response.rb +0 -18
- data/spec/commute/aspects/caching_spec.rb +0 -12
- data/spec/commute/aspects/url_spec.rb +0 -61
- data/spec/commute/core/commuter_spec.rb +0 -64
- data/spec/commute/core/processors/code_status_processor_spec.rb +0 -5
- data/spec/commute/core/processors/hook_spec.rb +0 -25
- data/spec/commute/core/processors/request_builder_spec.rb +0 -25
- data/spec/commute/core/processors/sequencer_spec.rb +0 -33
@@ -1,16 +1,29 @@
|
|
1
|
-
require 'commute/core/
|
1
|
+
require 'commute/core/layer'
|
2
2
|
|
3
3
|
module Commute
|
4
4
|
|
5
|
-
# Internal: A Sequence is an ordered list of
|
6
|
-
#
|
5
|
+
# Internal: A Sequence is an ordered list of Layers.
|
6
|
+
# It is a thin layer around an Array that enables it to
|
7
|
+
# add/remove/replace Layers more easily.
|
7
8
|
#
|
8
|
-
|
9
|
+
# Examples
|
10
|
+
#
|
11
|
+
# Sequence.new do
|
12
|
+
# append Commute::Common::Auth::Basic, as: :auth
|
13
|
+
# append Commute::Common::Json::Render, before: :auth
|
14
|
+
# replace :auth, Commute::Common::Auth::OAuth
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
class Sequence < Array
|
9
18
|
|
10
|
-
#
|
11
|
-
|
12
|
-
|
13
|
-
|
19
|
+
# Internal: Name of this Sequence.
|
20
|
+
attr_reader :name
|
21
|
+
|
22
|
+
# Public: Initializes a sequence without layers.
|
23
|
+
# Alters the Sequence if a block is given.
|
24
|
+
def initialize name, &alter
|
25
|
+
super()
|
26
|
+
@name = name
|
14
27
|
self.alter &alter if block_given?
|
15
28
|
end
|
16
29
|
|
@@ -30,151 +43,89 @@ module Commute
|
|
30
43
|
return self
|
31
44
|
end
|
32
45
|
|
33
|
-
#
|
34
|
-
|
35
|
-
@processors.clone
|
36
|
-
end
|
46
|
+
# Alias Array#insert.
|
47
|
+
alias :index_insert :insert
|
37
48
|
|
38
|
-
#
|
49
|
+
# Public: Appends a layer to a the list of layers.
|
39
50
|
#
|
40
|
-
#
|
41
|
-
#
|
42
|
-
#
|
51
|
+
# options - Optional parameters for layer naming and placement (default: {}):
|
52
|
+
# :as - The name of the layer in the sequence (optional).
|
53
|
+
# :after - A reference layer id after which the processor needs to be appended.
|
43
54
|
#
|
44
|
-
# Returns
|
45
|
-
def
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
#
|
51
|
-
|
52
|
-
#
|
53
|
-
|
54
|
-
# Identify the processor.
|
55
|
-
# TODO: not very performant
|
56
|
-
processor_id = @index.key processor
|
57
|
-
# Immediately jumpt to the next processor if this one is disabled.
|
58
|
-
if commuter.enabled? processor_id
|
59
|
-
# Get the parameters.
|
60
|
-
parameters = commuter.parameters(processor_id) if processor_id
|
61
|
-
# Calling the processor.
|
62
|
-
if parameters
|
63
|
-
processor.call commuter, parameters
|
64
|
-
else
|
65
|
-
processor.call commuter
|
66
|
-
end
|
67
|
-
end
|
68
|
-
# Call self for the next processor.
|
69
|
-
call_next = Proc.new { call_one index + 1, commuter }
|
70
|
-
if commuter.delayed?
|
71
|
-
commuter.wait &call_next
|
72
|
-
else
|
73
|
-
call_next.call
|
74
|
-
end
|
55
|
+
# Returns the appended processor.
|
56
|
+
def append callable, options = {}
|
57
|
+
# Create a layer.
|
58
|
+
layer = Layer.new(callable, options[:as])
|
59
|
+
# Index to insert at.
|
60
|
+
index = (options[:after] && layer_index(options[:after])+1) || -1
|
61
|
+
# Append the layer.
|
62
|
+
index_insert index, layer
|
63
|
+
# Return the appended layer.
|
64
|
+
layer
|
75
65
|
end
|
76
66
|
|
77
|
-
# Public:
|
67
|
+
# Public: Inserts a layer to a the list of layers.
|
78
68
|
#
|
79
|
-
# options - Optional parameters for
|
80
|
-
# :as - The name of the
|
81
|
-
# :after - A reference
|
69
|
+
# options - Optional parameters for layer naming and placement (default: {}):
|
70
|
+
# :as - The name of the layer in the sequence (optional).
|
71
|
+
# :after - A reference layer id before which the processor needs to be inserted.
|
82
72
|
#
|
83
73
|
# Returns the appended processor.
|
84
|
-
def
|
85
|
-
#
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
#
|
92
|
-
|
93
|
-
# Add the processor to the index for direct access purposes.
|
94
|
-
@index[id] = processor if id
|
95
|
-
# Return the appended processor.
|
96
|
-
processor
|
97
|
-
end
|
98
|
-
|
99
|
-
# TODO
|
100
|
-
def insert processor, options = {}
|
101
|
-
# When a reference is given, insert before it.
|
102
|
-
if options[:before]
|
103
|
-
@processors.insert id_index(options[:before]) - 1, processor
|
104
|
-
else
|
105
|
-
@processors.unshift processor
|
106
|
-
end
|
107
|
-
# Identify the processor using different methods.
|
108
|
-
id = identify processor, options[:as]
|
109
|
-
# Add the processor to the index for direct access purposes.
|
110
|
-
@index[id] = processor if id
|
111
|
-
# Return the appended processor.
|
112
|
-
processor
|
74
|
+
def insert callable, options = {}
|
75
|
+
# Create a layer.
|
76
|
+
layer = Layer.new(callable, options[:as])
|
77
|
+
# Index to insert at.
|
78
|
+
index = (options[:before] && layer_index(options[:before])) || 0
|
79
|
+
# Append the layer.
|
80
|
+
index_insert index, layer
|
81
|
+
# Return the appended layer.
|
82
|
+
layer
|
113
83
|
end
|
114
84
|
|
115
|
-
#
|
116
|
-
def replace processor_id
|
117
|
-
processor = at processor_id
|
118
|
-
raise "processor does not exist #{processor_id}" unless processor
|
119
|
-
replacement = yield processor
|
120
|
-
@processors[@processors.index processor] = replacement
|
121
|
-
@index[processor_id] = replacement
|
122
|
-
end
|
123
|
-
|
124
|
-
# Public: Removes a processor from the sequence.
|
85
|
+
# Public: Removes a layer from the sequence.
|
125
86
|
#
|
126
|
-
#
|
127
|
-
# Symbol identifier is given, the associated processor is removed.
|
87
|
+
# name - The name of the layer to be removed.
|
128
88
|
#
|
129
|
-
# Returns the removed
|
130
|
-
def remove
|
131
|
-
|
132
|
-
|
133
|
-
processor = at(processor)
|
134
|
-
@processors.delete processor
|
135
|
-
@index.delete processor_id
|
136
|
-
else
|
137
|
-
# Remove from list of processors.
|
138
|
-
@processors.delete processor
|
139
|
-
# Remove from index (if necessary).
|
140
|
-
@index.delete_if { |id, pr| pr == processor}
|
141
|
-
end
|
142
|
-
# Return the deleted processor.
|
143
|
-
processor
|
89
|
+
# Returns the removed layer.
|
90
|
+
def remove name
|
91
|
+
# Remove the layer.
|
92
|
+
delete_at layer_index(name)
|
144
93
|
end
|
145
94
|
|
146
|
-
# Public
|
147
|
-
# Returns the processor with the given id.
|
95
|
+
# Public: Replaces a layer in the sequence.
|
148
96
|
#
|
149
|
-
#
|
150
|
-
|
151
|
-
|
97
|
+
# name - The name of the layer to be replaced.
|
98
|
+
# layer - The callable to replace it with.
|
99
|
+
#
|
100
|
+
# Returns the new layer.
|
101
|
+
def replace name, callable
|
102
|
+
self[layer_index name] = Layer.new callable, name
|
152
103
|
end
|
153
104
|
|
154
|
-
# Internal:
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
105
|
+
# Internal: Lazily adds the sequence (as a Layer)
|
106
|
+
# with this name to the router path.
|
107
|
+
#
|
108
|
+
# Lazily here means that when this sequence is added as a Layer
|
109
|
+
# it is not necessarely complete yet. Layers could be appended or
|
110
|
+
# removed later on. Therefor, the sequence is fetched from
|
111
|
+
# the current context.
|
112
|
+
#
|
113
|
+
def call router, request, options = {}
|
114
|
+
context = request.http.context
|
115
|
+
router << context.stack.sequence(name).reject { |layer|
|
116
|
+
!layer.callable?(context)
|
117
|
+
}.each
|
118
|
+
router.succ.call router, request
|
160
119
|
end
|
161
120
|
|
162
121
|
private
|
163
122
|
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
end
|
171
|
-
|
172
|
-
def id_index id
|
173
|
-
@processors.index at(id)
|
123
|
+
# Internal: Returns the index of a layer.
|
124
|
+
#
|
125
|
+
# name - The name of the layer.
|
126
|
+
#
|
127
|
+
def layer_index name
|
128
|
+
index { |layer| layer.name == name }
|
174
129
|
end
|
175
|
-
|
176
|
-
protected
|
177
|
-
|
178
|
-
attr_writer :processors, :index
|
179
130
|
end
|
180
131
|
end
|
data/lib/commute/core/stack.rb
CHANGED
@@ -1,76 +1,61 @@
|
|
1
1
|
require 'commute/core/sequence'
|
2
|
+
require 'commute/core/util/path'
|
2
3
|
|
3
4
|
module Commute
|
4
5
|
|
5
6
|
# Internal: A stack is a collection of sequences with
|
6
7
|
# A main sequence uses as a starting point for processing.
|
7
8
|
#
|
8
|
-
# When the stack is called with a
|
9
|
+
# When the stack is called with a request, it gets
|
9
10
|
# processed by the main sequence. That main sequence
|
10
11
|
# can easily "embed" other sequences by adding them as
|
11
|
-
# a
|
12
|
+
# a layer (a sequence is also a layer (composite)).
|
12
13
|
#
|
13
14
|
# Example:
|
14
15
|
#
|
15
|
-
# stack = Stack.new do
|
16
|
-
#
|
17
|
-
# append
|
18
|
-
# append
|
16
|
+
# stack = Stack.new do
|
17
|
+
# sequence(:request_body) do
|
18
|
+
# append Commute::Common::Json::Render
|
19
|
+
# append Commute::Common::Auth::Basic
|
19
20
|
# end
|
20
21
|
#
|
21
|
-
# sequence.append
|
22
|
-
# sequence.append Proc.new { |p| p.process { |n| "The result is #{n}" }}
|
22
|
+
# sequence.append sequence(:request_body)
|
23
23
|
# end
|
24
24
|
#
|
25
|
-
# stack.call
|
26
|
-
#
|
25
|
+
# request = stack.call Request.new(context) do |response, status|
|
26
|
+
# # Process the response.
|
27
|
+
# end
|
28
|
+
# request.write data
|
29
|
+
# request.end
|
27
30
|
#
|
28
31
|
class Stack
|
29
32
|
|
30
|
-
#
|
31
|
-
#
|
32
|
-
# sequence - Sequence used as main sequence.
|
33
|
-
#
|
34
|
-
# Yields
|
35
|
-
# 1. The Stack itself. (If the arity of the block is 2)
|
36
|
-
# 2. a new sequence to use a main sequence when no
|
37
|
-
# sequence is explicitely given.
|
33
|
+
# Internal: A Router that routes http request to the next layer.
|
38
34
|
#
|
39
|
-
#
|
35
|
+
# The router wraps the Http Request in a Layer Request and
|
36
|
+
# attaches the correct handler to the response event.
|
40
37
|
#
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
38
|
+
class Router < Path
|
39
|
+
|
40
|
+
# Public: Call the next layer.
|
41
|
+
#
|
42
|
+
# http - Http Request to send to the next layer on the Path.
|
43
|
+
#
|
44
|
+
# Yields a response and a status.
|
45
|
+
# Returns the Layer Request.
|
46
|
+
def call http, &on_response
|
47
|
+
Layer::Request.new(http).tap do |request|
|
48
|
+
request.on :response, &on_response
|
49
|
+
succ.call self, request
|
53
50
|
end
|
54
51
|
end
|
55
52
|
end
|
56
53
|
|
57
|
-
# Public:
|
58
|
-
#
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
# Returns the Sequence that was added.
|
63
|
-
def add sequence, name
|
64
|
-
@sequences[name] = sequence
|
65
|
-
end
|
66
|
-
|
67
|
-
# Public: Get a sequence from the stack.
|
68
|
-
#
|
69
|
-
# name - The name of the wanted Sequence.
|
70
|
-
#
|
71
|
-
# Returns the asked sequence.
|
72
|
-
def get name
|
73
|
-
@sequences[name]
|
54
|
+
# Public: Constructs a stack with a main sequence.
|
55
|
+
# Akter the Stack if a block is given.
|
56
|
+
def initialize &alter
|
57
|
+
@sequences = {}
|
58
|
+
self.alter &alter if block_given?
|
74
59
|
end
|
75
60
|
|
76
61
|
# Public: Manipulate an either existing or new sequence.
|
@@ -84,25 +69,10 @@ module Commute
|
|
84
69
|
#
|
85
70
|
# Returns the Sequence.
|
86
71
|
def sequence name = nil, &alter
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
# Public: Creates a processor that lazily fetches a named sequence
|
95
|
-
# on a call.
|
96
|
-
#
|
97
|
-
# name - The name of the sequence this sequencer stands in for.
|
98
|
-
#
|
99
|
-
# Returns a Sequencer
|
100
|
-
def sequencer name = nil
|
101
|
-
if name
|
102
|
-
Sequencer.new.here(name)
|
103
|
-
else
|
104
|
-
Sequencer.new
|
105
|
-
end
|
72
|
+
name ||= :main
|
73
|
+
sequence = @sequences[name] || add(Sequence.new name)
|
74
|
+
sequence.alter &alter if block_given?
|
75
|
+
sequence
|
106
76
|
end
|
107
77
|
|
108
78
|
# Internal: Calls the stack aka call the main sequence.
|
@@ -110,8 +80,9 @@ module Commute
|
|
110
80
|
# processable - The thing to process.
|
111
81
|
#
|
112
82
|
# Returns the processable
|
113
|
-
def call
|
114
|
-
|
83
|
+
def call request, &on_response
|
84
|
+
router = Router.new([sequence].each)
|
85
|
+
router.call request, &on_response
|
115
86
|
end
|
116
87
|
|
117
88
|
# Public: Alters the stack.
|
@@ -124,19 +95,39 @@ module Commute
|
|
124
95
|
if alter.arity == 0
|
125
96
|
instance_eval &alter
|
126
97
|
else
|
127
|
-
yield self
|
98
|
+
yield self
|
128
99
|
end
|
129
100
|
# Return the Stack.
|
130
101
|
return self
|
131
102
|
end
|
132
103
|
|
133
|
-
# Internal:
|
104
|
+
# Internal: Duplicates the stack.
|
134
105
|
def dup
|
135
|
-
Stack.new
|
106
|
+
Stack.new.tap do |stack|
|
136
107
|
stack.sequences = Hash[@sequences.map { |k,v| [k, v.dup] }]
|
137
108
|
end
|
138
109
|
end
|
139
110
|
|
111
|
+
private
|
112
|
+
|
113
|
+
# Internal: Adds a sequence to the stack.
|
114
|
+
#
|
115
|
+
# sequence - The Sequence to be added.
|
116
|
+
#
|
117
|
+
# Returns the Sequence that was added.
|
118
|
+
def add sequence
|
119
|
+
@sequences[sequence.name] = sequence
|
120
|
+
end
|
121
|
+
|
122
|
+
# Internal: Get a sequence from the stack.
|
123
|
+
#
|
124
|
+
# name - The name of the wanted Sequence.
|
125
|
+
#
|
126
|
+
# Returns the asked sequence.
|
127
|
+
def get name
|
128
|
+
@sequences[name]
|
129
|
+
end
|
130
|
+
|
140
131
|
protected
|
141
132
|
|
142
133
|
attr_writer :sequences
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Commute
|
2
|
+
|
3
|
+
# Public: A Generic status object that can represent
|
4
|
+
# either a positive or negative status.
|
5
|
+
#
|
6
|
+
# When the status is negative, an additional error
|
7
|
+
# detail can be provided.
|
8
|
+
#
|
9
|
+
# Examples
|
10
|
+
#
|
11
|
+
# Status.new false, { reason: 'No access' }
|
12
|
+
# Status.ok # Same as Status.new true
|
13
|
+
#
|
14
|
+
class Status
|
15
|
+
|
16
|
+
# Public: Creates a positive status.
|
17
|
+
#
|
18
|
+
def self.ok
|
19
|
+
self.new
|
20
|
+
end
|
21
|
+
|
22
|
+
# Public: Creates a new status object.
|
23
|
+
#
|
24
|
+
# success - Whether the status is positive or not (default: true).
|
25
|
+
# error - Optional error detail (can be anything).
|
26
|
+
#
|
27
|
+
def initialize success = true, error = nil
|
28
|
+
@success = success
|
29
|
+
end
|
30
|
+
|
31
|
+
# Public: Check if a status is a success.
|
32
|
+
#
|
33
|
+
# Returns true if the status is positive.
|
34
|
+
def success?
|
35
|
+
@success
|
36
|
+
end
|
37
|
+
|
38
|
+
# Public: Check if a status is a fail.
|
39
|
+
#
|
40
|
+
# Returns true if the status is negative.
|
41
|
+
def fail?
|
42
|
+
!success?
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Commute
|
2
|
+
|
3
|
+
# Internal: A Simple EventEmitter modelled after node.js' EventEmitter.
|
4
|
+
# More info: http://nodejs.org/api/events.html
|
5
|
+
#
|
6
|
+
# Include this module in any model that wants to send events to its listeners.
|
7
|
+
#
|
8
|
+
# Listen to events using `on(:event)`. In the model, notify listeners
|
9
|
+
# by calling `emit(:event, ...)` with some arguments.
|
10
|
+
module EventEmitter
|
11
|
+
|
12
|
+
# Internal: Initializes an empty handler hash.
|
13
|
+
def initialize *args
|
14
|
+
super *args
|
15
|
+
@handlers = Hash.new { |h, k| h[k] = [] }
|
16
|
+
end
|
17
|
+
|
18
|
+
# Internal: Subscribe a handler to an event.
|
19
|
+
#
|
20
|
+
# event - The name of the event to listen for.
|
21
|
+
# handler - The proc to call when this event occurs.
|
22
|
+
#
|
23
|
+
# Returns Nothing.
|
24
|
+
def on event, &handler
|
25
|
+
@handlers[event] << handler
|
26
|
+
nil
|
27
|
+
end
|
28
|
+
|
29
|
+
# Internal: Subscribe a handler to an event and
|
30
|
+
# unsuscribe it when the event fired once.
|
31
|
+
#
|
32
|
+
def once event, &handler
|
33
|
+
on event do |*args, &block|
|
34
|
+
handler.call *args, &block
|
35
|
+
remove event, handler
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Internal: Remove an event handler.
|
40
|
+
def remove event, handler
|
41
|
+
@handlers[event].delete handler
|
42
|
+
end
|
43
|
+
|
44
|
+
protected
|
45
|
+
|
46
|
+
# Internal: Emit an event to all handlers.
|
47
|
+
#
|
48
|
+
# event - The name of the event.
|
49
|
+
# args - Arguments to send to the listeners.
|
50
|
+
#
|
51
|
+
# Returns the notified handlers.
|
52
|
+
def emit event, *args
|
53
|
+
@handlers[event].each do |handler|
|
54
|
+
handler.call *args
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Commute
|
2
|
+
|
3
|
+
# Internal: A Path is that wraps some enums and
|
4
|
+
# returns there values in the order that you added them.
|
5
|
+
#
|
6
|
+
class Path
|
7
|
+
|
8
|
+
# Internal: Initialize the path with a main route.
|
9
|
+
#
|
10
|
+
# main - An enumerable to start the path with.
|
11
|
+
#
|
12
|
+
def initialize main
|
13
|
+
@path = []
|
14
|
+
self << main
|
15
|
+
end
|
16
|
+
|
17
|
+
# Internal: Push a route on the path (detour).
|
18
|
+
#
|
19
|
+
# route - An enumerable to walk first before continuing.
|
20
|
+
def << route
|
21
|
+
@path << route
|
22
|
+
end
|
23
|
+
|
24
|
+
# Internal: Figures out the successor in the path.
|
25
|
+
#
|
26
|
+
# Returns the next stop on the path.
|
27
|
+
def succ
|
28
|
+
begin
|
29
|
+
return nil if @path.empty?
|
30
|
+
@path.last.next
|
31
|
+
rescue StopIteration
|
32
|
+
@path.pop
|
33
|
+
retry
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|