tap 0.11.1 → 0.12.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History +35 -1
- data/MIT-LICENSE +1 -1
- data/README +16 -15
- data/bin/tap +1 -1
- data/cmd/console.rb +4 -3
- data/cmd/manifest.rb +2 -2
- data/cmd/run.rb +12 -15
- data/doc/Class Reference +120 -117
- data/doc/Command Reference +27 -27
- data/doc/Syntax Reference +55 -111
- data/doc/Tutorial +69 -26
- data/lib/tap.rb +3 -8
- data/lib/tap/app.rb +122 -146
- data/lib/tap/constants.rb +2 -2
- data/lib/tap/env.rb +178 -252
- data/lib/tap/exe.rb +67 -30
- data/lib/tap/file_task.rb +224 -411
- data/lib/tap/generator/arguments.rb +13 -0
- data/lib/tap/generator/base.rb +112 -30
- data/lib/tap/generator/destroy.rb +36 -13
- data/lib/tap/generator/generate.rb +69 -48
- data/lib/tap/generator/generators/command/templates/command.erb +3 -3
- data/lib/tap/generator/generators/config/config_generator.rb +82 -10
- data/lib/tap/generator/generators/generator/generator_generator.rb +16 -6
- data/lib/tap/generator/generators/generator/templates/task.erb +2 -2
- data/lib/tap/generator/generators/generator/templates/test.erb +26 -0
- data/lib/tap/generator/generators/root/root_generator.rb +24 -13
- data/lib/tap/generator/generators/root/templates/Rakefile +4 -4
- data/lib/tap/generator/generators/root/templates/{tapfile → Rapfile} +6 -6
- data/lib/tap/generator/generators/root/templates/gemspec +0 -1
- data/lib/tap/generator/generators/task/task_generator.rb +3 -3
- data/lib/tap/generator/generators/task/templates/test.erb +1 -1
- data/lib/tap/generator/manifest.rb +7 -1
- data/lib/tap/generator/preview.rb +76 -0
- data/lib/tap/root.rb +222 -156
- data/lib/tap/spec.rb +41 -0
- data/lib/tap/support/aggregator.rb +25 -28
- data/lib/tap/support/audit.rb +278 -357
- data/lib/tap/support/constant.rb +2 -1
- data/lib/tap/support/constant_manifest.rb +28 -25
- data/lib/tap/support/dependency.rb +1 -1
- data/lib/tap/support/executable.rb +52 -183
- data/lib/tap/support/executable_queue.rb +50 -20
- data/lib/tap/support/gems.rb +1 -1
- data/lib/tap/support/intern.rb +0 -6
- data/lib/tap/support/join.rb +49 -83
- data/lib/tap/support/joins.rb +0 -3
- data/lib/tap/support/joins/switch.rb +13 -11
- data/lib/tap/support/joins/sync_merge.rb +25 -50
- data/lib/tap/support/manifest.rb +1 -0
- data/lib/tap/support/node.rb +140 -20
- data/lib/tap/support/parser.rb +56 -42
- data/lib/tap/support/schema.rb +183 -157
- data/lib/tap/support/templater.rb +9 -1
- data/lib/tap/support/versions.rb +39 -0
- data/lib/tap/task.rb +150 -177
- data/lib/tap/tasks/dump.rb +4 -4
- data/lib/tap/tasks/load.rb +29 -29
- data/lib/tap/test.rb +66 -53
- data/lib/tap/test/env_vars.rb +3 -3
- data/lib/tap/test/extensions.rb +11 -17
- data/lib/tap/test/file_test.rb +74 -132
- data/lib/tap/test/file_test_class.rb +4 -1
- data/lib/tap/test/regexp_escape.rb +2 -2
- data/lib/tap/test/script_test.rb +2 -2
- data/lib/tap/test/subset_test.rb +6 -6
- data/lib/tap/test/tap_test.rb +28 -154
- metadata +30 -51
- data/bin/rap +0 -118
- data/cgi/run.rb +0 -97
- data/lib/tap/declarations.rb +0 -229
- data/lib/tap/generator/generators/config/templates/doc.erb +0 -12
- data/lib/tap/generator/generators/config/templates/nodoc.erb +0 -8
- data/lib/tap/generator/generators/file_task/file_task_generator.rb +0 -27
- data/lib/tap/generator/generators/file_task/templates/file.txt +0 -11
- data/lib/tap/generator/generators/file_task/templates/result.yml +0 -6
- data/lib/tap/generator/generators/file_task/templates/task.erb +0 -33
- data/lib/tap/generator/generators/file_task/templates/test.erb +0 -29
- data/lib/tap/generator/generators/root/templates/test/tap_test_suite.rb +0 -5
- data/lib/tap/patches/optparse/summarize.rb +0 -62
- data/lib/tap/support/assignments.rb +0 -173
- data/lib/tap/support/class_configuration.rb +0 -182
- data/lib/tap/support/combinator.rb +0 -125
- data/lib/tap/support/configurable.rb +0 -113
- data/lib/tap/support/configurable_class.rb +0 -271
- data/lib/tap/support/configuration.rb +0 -170
- data/lib/tap/support/gems/rake.rb +0 -111
- data/lib/tap/support/instance_configuration.rb +0 -173
- data/lib/tap/support/joins/fork.rb +0 -19
- data/lib/tap/support/joins/merge.rb +0 -22
- data/lib/tap/support/joins/sequence.rb +0 -21
- data/lib/tap/support/lazy_attributes.rb +0 -45
- data/lib/tap/support/lazydoc.rb +0 -386
- data/lib/tap/support/lazydoc/comment.rb +0 -503
- data/lib/tap/support/lazydoc/config.rb +0 -17
- data/lib/tap/support/lazydoc/definition.rb +0 -36
- data/lib/tap/support/lazydoc/document.rb +0 -152
- data/lib/tap/support/lazydoc/method.rb +0 -24
- data/lib/tap/support/tdoc.rb +0 -409
- data/lib/tap/support/tdoc/tdoc_html_generator.rb +0 -38
- data/lib/tap/support/tdoc/tdoc_html_template.rb +0 -42
- data/lib/tap/support/validation.rb +0 -479
- data/lib/tap/tasks/rake.rb +0 -57
data/lib/tap.rb
CHANGED
@@ -1,10 +1,5 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
$:.unshift File.expand_path(File.dirname(__FILE__))
|
1
|
+
lib = File.expand_path(File.dirname(__FILE__))
|
2
|
+
$:.unshift(lib) unless $:.include?(lib)
|
4
3
|
|
5
4
|
require 'tap/constants'
|
6
|
-
|
7
|
-
# require in order...
|
8
|
-
require 'tap/exe'
|
9
|
-
require 'tap/task'
|
10
|
-
require 'tap/file_task'
|
5
|
+
require 'tap/exe'
|
data/lib/tap/app.rb
CHANGED
@@ -1,50 +1,95 @@
|
|
1
1
|
require 'logger'
|
2
|
+
|
3
|
+
require 'tap/root'
|
2
4
|
require 'tap/support/aggregator'
|
3
5
|
require 'tap/support/dependencies'
|
4
6
|
require 'tap/support/executable_queue'
|
7
|
+
require 'tap/task'
|
5
8
|
|
6
9
|
module Tap
|
7
10
|
|
8
11
|
# App coordinates the setup and running of tasks, and provides an interface
|
9
|
-
# to the application directory structure. All tasks have an
|
10
|
-
# App.instance) through which
|
11
|
-
#
|
12
|
+
# to the application directory structure. All tasks have an app (by default
|
13
|
+
# App.instance) through which they access application-wide resources like
|
14
|
+
# the logger, executable queue, and dependencies.
|
12
15
|
#
|
13
16
|
# === Running Tasks
|
14
17
|
#
|
15
|
-
#
|
18
|
+
# Tasks may be enqued and run by an App:
|
19
|
+
#
|
20
|
+
# app = App.instance
|
16
21
|
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
22
|
+
# t = Task.intern {|task, *inputs| inputs }
|
23
|
+
# t.enq('a', 'b', 'c')
|
24
|
+
# t.enq(1)
|
25
|
+
# t.enq(2)
|
26
|
+
# t.enq(3)
|
20
27
|
#
|
21
28
|
# app.run
|
22
|
-
# app.results(
|
29
|
+
# app.results(t) # => [['a', 'b', 'c'], [1], [2], [3]]
|
23
30
|
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
# execute or enque other tasks, allowing the construction of imperative
|
28
|
-
# workflows:
|
31
|
+
# By default apps simply run tasks and collect the results. To construct
|
32
|
+
# a workflow, set an on_complete block to receive the audited result and
|
33
|
+
# enque or execute the next series of tasks. Here is a simple sequence:
|
29
34
|
#
|
30
|
-
#
|
31
|
-
#
|
35
|
+
# t0 = Task.intern {|task| "0" }
|
36
|
+
# t1 = Task.intern {|task, input| "#{input}:1" }
|
37
|
+
# t2 = Task.intern {|task, input| "#{input}:2"}
|
32
38
|
#
|
33
|
-
# t1 = Task.intern {|task, input| "#{input}.1" }
|
34
39
|
# t0.on_complete {|_result| t1.enq(_result) }
|
35
|
-
#
|
36
|
-
#
|
40
|
+
# t1.on_complete {|_result| t2.enq(_result) }
|
41
|
+
#
|
42
|
+
# t0.enq
|
43
|
+
# app.run
|
44
|
+
# app.results(t0, t1) # => []
|
45
|
+
# app.results(t2) # => ["0:1:2"]
|
46
|
+
#
|
47
|
+
# Apps may be assigned an on_complete block as well; the app on_complete
|
48
|
+
# block is called when a task has no on_complete block set. If neither the
|
49
|
+
# task nor the app has an on_complete block, the app stores the audit in
|
50
|
+
# app.aggregator and makes it available through app.results. Note how after
|
51
|
+
# the sequence, the t0 and t1 results are not in the app (they were handled
|
52
|
+
# by the on_complete block).
|
53
|
+
#
|
54
|
+
# Tracking how inputs evolve through a workflow can be onerous. To help,
|
55
|
+
# Tap audits changes to the inputs. Audit values are by convention prefixed
|
56
|
+
# by an underscore; all on_complete block receive the audited values and not
|
57
|
+
# the actual result of a task. Aggregated audits are available through
|
58
|
+
# app._results.
|
59
|
+
#
|
60
|
+
# t2.enq("a")
|
61
|
+
# t1.enq("b")
|
37
62
|
# app.run
|
38
|
-
# app.results(
|
39
|
-
#
|
63
|
+
# app.results(t2) # => ["0:1:2", "a:2", "b:1:2"]
|
64
|
+
#
|
65
|
+
# t0.name = "zero"
|
66
|
+
# t1.name = "one"
|
67
|
+
# t2.name = "two"
|
68
|
+
#
|
69
|
+
# trails = app._results(t2).collect do |_result|
|
70
|
+
# _result.dump
|
71
|
+
# end
|
40
72
|
#
|
41
|
-
#
|
42
|
-
#
|
73
|
+
# "\n" + trails.join("\n")
|
74
|
+
# # => %q{
|
75
|
+
# # o-[zero] "0"
|
76
|
+
# # o-[one] "0:1"
|
77
|
+
# # o-[two] "0:1:2"
|
78
|
+
# #
|
79
|
+
# # o-[] "a"
|
80
|
+
# # o-[two] "a:2"
|
81
|
+
# #
|
82
|
+
# # o-[] "b"
|
83
|
+
# # o-[one] "b:1"
|
84
|
+
# # o-[two] "b:1:2"
|
85
|
+
# # }
|
86
|
+
#
|
87
|
+
# See Audit for more details.
|
43
88
|
#
|
44
89
|
# === Dependencies
|
45
90
|
#
|
46
|
-
# Tasks allow the construction of dependency-based workflows
|
47
|
-
#
|
91
|
+
# Tasks allow the construction of dependency-based workflows. A dependent
|
92
|
+
# task only executes after its dependencies have been resolved.
|
48
93
|
#
|
49
94
|
# runlist = []
|
50
95
|
# t0 = Task.intern {|task| runlist << task }
|
@@ -62,114 +107,35 @@ module Tap
|
|
62
107
|
# app.run
|
63
108
|
# runlist # => [t1, t0, t0]
|
64
109
|
#
|
65
|
-
# === Batching
|
66
|
-
#
|
67
|
-
# Tasks can be batched, allowing the same input to be enqued to multiple
|
68
|
-
# tasks at once.
|
69
|
-
#
|
70
|
-
# t0 = Task.intern {|task, input| "#{input}.0" }
|
71
|
-
# t1 = Task.intern {|task, input| "#{input}.1" }
|
72
|
-
#
|
73
|
-
# t0.batch_with(t1)
|
74
|
-
# t0.enq 'a'
|
75
|
-
# t1.enq 'b'
|
76
|
-
#
|
77
|
-
# app.run
|
78
|
-
# app.results(t0) # => ['a.0', 'b.0']
|
79
|
-
# app.results(t1) # => ['a.1', 'b.1']
|
80
|
-
#
|
81
110
|
# === Executables
|
82
111
|
#
|
83
112
|
# App can enque and run any Executable object. Arbitrary methods may be
|
84
|
-
# made into Executables using Object#_method.
|
85
|
-
# generates and enques methods in one step.
|
113
|
+
# made into Executables using Object#_method.
|
86
114
|
#
|
87
115
|
# array = []
|
88
116
|
#
|
89
|
-
# # longhand
|
90
117
|
# m = array._method(:push)
|
91
118
|
# m.enq(1)
|
92
|
-
#
|
93
|
-
#
|
94
|
-
# app.mq(array, :push, 2)
|
119
|
+
# m.enq(2)
|
120
|
+
# m.enq(3)
|
95
121
|
#
|
96
122
|
# array.empty? # => true
|
97
123
|
# app.run
|
98
|
-
# array # => [1, 2]
|
99
|
-
#
|
100
|
-
# === Auditing
|
101
|
-
#
|
102
|
-
# All results are audited to track how a given input evolves during a workflow.
|
103
|
-
# To illustrate auditing, consider and addition workflow that ends in eights.
|
104
|
-
#
|
105
|
-
# add_one = Tap::Task.intern({}, 'add_one') {|task, input| input += 1 }
|
106
|
-
# add_five = Tap::Task.intern({}, 'add_five') {|task, input| input += 5 }
|
107
|
-
#
|
108
|
-
# add_one.on_complete do |_result|
|
109
|
-
# # _result is the audit; use the _current method
|
110
|
-
# # to get the current value in the audit trail
|
111
|
-
# current_value = _result._current
|
112
|
-
#
|
113
|
-
# if current_value < 3
|
114
|
-
# add_one.enq(_result)
|
115
|
-
# else
|
116
|
-
# add_five.enq(_result)
|
117
|
-
# end
|
118
|
-
# end
|
119
|
-
#
|
120
|
-
# add_one.enq(0)
|
121
|
-
# add_one.enq(1)
|
122
|
-
# add_one.enq(2)
|
123
|
-
#
|
124
|
-
# app.run
|
125
|
-
# app.results(add_five) # => [8,8,8]
|
126
|
-
#
|
127
|
-
# Although the results are indistinguishable, each achieved the final value
|
128
|
-
# through a different series of tasks. With auditing you can see how each
|
129
|
-
# input came to the final value of 8:
|
130
|
-
#
|
131
|
-
# # app.results returns the actual result values
|
132
|
-
# # app._results returns the audits for these values
|
133
|
-
# app._results(add_five).each do |_result|
|
134
|
-
# puts "How #{_result._original} became #{_result._current}:"
|
135
|
-
# puts _result._to_s
|
136
|
-
# puts
|
137
|
-
# end
|
138
|
-
#
|
139
|
-
# Prints:
|
140
|
-
#
|
141
|
-
# How 2 became 8:
|
142
|
-
# o-[] 2
|
143
|
-
# o-[add_one] 3
|
144
|
-
# o-[add_five] 8
|
124
|
+
# array # => [1, 2, 3]
|
145
125
|
#
|
146
|
-
# How 1 became 8:
|
147
|
-
# o-[] 1
|
148
|
-
# o-[add_one] 2
|
149
|
-
# o-[add_one] 3
|
150
|
-
# o-[add_five] 8
|
151
|
-
#
|
152
|
-
# How 0 became 8:
|
153
|
-
# o-[] 0
|
154
|
-
# o-[add_one] 1
|
155
|
-
# o-[add_one] 2
|
156
|
-
# o-[add_one] 3
|
157
|
-
# o-[add_five] 8
|
158
|
-
#
|
159
|
-
# See Tap::Support::Audit for more details.
|
160
126
|
class App < Root
|
161
127
|
class << self
|
162
128
|
# Sets the current app instance
|
163
129
|
attr_writer :instance
|
164
130
|
|
165
131
|
# Returns the current instance of App. If no instance has been set,
|
166
|
-
# then a new App with the default configuration
|
132
|
+
# then instance initializes a new App with the default configuration.
|
167
133
|
def instance
|
168
134
|
@instance ||= App.new
|
169
135
|
end
|
170
136
|
end
|
171
137
|
|
172
|
-
# The
|
138
|
+
# The application logger
|
173
139
|
attr_reader :logger
|
174
140
|
|
175
141
|
# The application queue
|
@@ -178,13 +144,19 @@ module Tap
|
|
178
144
|
# The state of the application (see App::State)
|
179
145
|
attr_reader :state
|
180
146
|
|
181
|
-
# A Tap::Support::Aggregator
|
147
|
+
# A Tap::Support::Aggregator that collects the results of
|
182
148
|
# methods that have no on_complete block
|
183
149
|
attr_reader :aggregator
|
184
150
|
|
185
151
|
# A Tap::Support::Dependencies to track dependencies.
|
186
152
|
attr_reader :dependencies
|
187
153
|
|
154
|
+
# The block called when an executable completes and has no
|
155
|
+
# on_complete_block set. An on_complete_block effectively
|
156
|
+
# takes the place of aggregation (ie when set, no results
|
157
|
+
# will be collected by self).
|
158
|
+
attr_reader :on_complete_block
|
159
|
+
|
188
160
|
config :debug, false, &c.flag # Flag debugging
|
189
161
|
config :force, false, &c.flag # Force execution at checkpoints
|
190
162
|
config :quiet, false, &c.flag # Suppress logging
|
@@ -199,7 +171,7 @@ module Tap
|
|
199
171
|
|
200
172
|
module_function
|
201
173
|
|
202
|
-
# Returns
|
174
|
+
# Returns a string corresponding to the input state value.
|
203
175
|
# Returns nil for unknown states.
|
204
176
|
#
|
205
177
|
# State.state_str(0) # => 'READY'
|
@@ -209,16 +181,19 @@ module Tap
|
|
209
181
|
end
|
210
182
|
end
|
211
183
|
|
184
|
+
include MonitorMixin
|
185
|
+
|
212
186
|
# Creates a new App with the given configuration.
|
213
|
-
def initialize(config={}, logger=DEFAULT_LOGGER)
|
187
|
+
def initialize(config={}, logger=DEFAULT_LOGGER, &block)
|
214
188
|
super()
|
215
189
|
|
216
190
|
@state = State::READY
|
217
191
|
@queue = Support::ExecutableQueue.new
|
218
192
|
@aggregator = Support::Aggregator.new
|
219
193
|
@dependencies = Support::Dependencies.new
|
194
|
+
@on_complete_block = block
|
220
195
|
|
221
|
-
|
196
|
+
reconfigure(config)
|
222
197
|
self.logger = logger
|
223
198
|
end
|
224
199
|
|
@@ -250,20 +225,7 @@ module Tap
|
|
250
225
|
logger.add(level, msg, action.to_s) if !quiet || verbose
|
251
226
|
end
|
252
227
|
|
253
|
-
#
|
254
|
-
# File.join(app['config'], task_name + ".yml"). Returns nil if
|
255
|
-
# task_name is nil.
|
256
|
-
def config_filepath(name)
|
257
|
-
name == nil ? nil : filepath('config', "#{name}.yml")
|
258
|
-
end
|
259
|
-
|
260
|
-
# Sets state = State::READY unless the app is running. Returns self.
|
261
|
-
def ready
|
262
|
-
@state = State::READY unless state == State::RUN
|
263
|
-
self
|
264
|
-
end
|
265
|
-
|
266
|
-
# Sequentially calls execute with the [executable, inputs] pairs in
|
228
|
+
# Sequentially calls execute with the (executable, inputs) pairs in
|
267
229
|
# queue; run continues until the queue is empty and then returns self.
|
268
230
|
#
|
269
231
|
# ==== Run State
|
@@ -280,8 +242,10 @@ module Tap
|
|
280
242
|
# Calls to run when the state is not State::READY do nothing and
|
281
243
|
# return immediately.
|
282
244
|
def run
|
283
|
-
|
284
|
-
|
245
|
+
synchronize do
|
246
|
+
return self unless state == State::READY
|
247
|
+
@state = State::RUN
|
248
|
+
end
|
285
249
|
|
286
250
|
# TODO: log starting run
|
287
251
|
begin
|
@@ -296,7 +260,7 @@ module Tap
|
|
296
260
|
raise if debug?
|
297
261
|
log($!.class, $!.message)
|
298
262
|
ensure
|
299
|
-
@state = State::READY
|
263
|
+
synchronize { @state = State::READY }
|
300
264
|
end
|
301
265
|
|
302
266
|
# TODO: log run complete
|
@@ -309,7 +273,7 @@ module Tap
|
|
309
273
|
#
|
310
274
|
# Does nothing unless state is State::RUN.
|
311
275
|
def stop
|
312
|
-
@state = State::STOP if state == State::RUN
|
276
|
+
synchronize { @state = State::STOP if state == State::RUN }
|
313
277
|
self
|
314
278
|
end
|
315
279
|
|
@@ -321,7 +285,7 @@ module Tap
|
|
321
285
|
#
|
322
286
|
# Does nothing if state == State::READY.
|
323
287
|
def terminate
|
324
|
-
@state = State::TERMINATE unless state == State::READY
|
288
|
+
synchronize { @state = State::TERMINATE unless state == State::READY }
|
325
289
|
self
|
326
290
|
end
|
327
291
|
|
@@ -333,18 +297,18 @@ module Tap
|
|
333
297
|
"state: #{state} (#{State.state_str(state)}) queue: #{queue.size} results: #{aggregator.size}"
|
334
298
|
end
|
335
299
|
|
336
|
-
# Enques the task (or Executable) with the inputs.
|
337
|
-
#
|
300
|
+
# Enques the task (or Executable) with the inputs. Raises an error if the
|
301
|
+
# input is not an Executable, or is not assigned to self. Returns task.
|
338
302
|
def enq(task, *inputs)
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
else
|
346
|
-
raise ArgumentError, "not a Task or Executable: #{task}"
|
303
|
+
unless task.kind_of?(Support::Executable)
|
304
|
+
raise ArgumentError, "not an Executable: #{task.inspect}"
|
305
|
+
end
|
306
|
+
|
307
|
+
unless task.app == self
|
308
|
+
raise ArgumentError, "not assigned to enqueing app: #{task.inspect}"
|
347
309
|
end
|
310
|
+
|
311
|
+
queue.enq(task, inputs)
|
348
312
|
task
|
349
313
|
end
|
350
314
|
|
@@ -367,20 +331,32 @@ module Tap
|
|
367
331
|
#
|
368
332
|
# t0 = Task.intern {|task, input| "#{input}.0" }
|
369
333
|
# t1 = Task.intern {|task, input| "#{input}.1" }
|
370
|
-
# t2 = Task.intern {|task, input| "#{input}.2" }
|
371
|
-
# t1.batch_with(t2)
|
372
334
|
#
|
373
335
|
# t0.enq(0)
|
374
336
|
# t1.enq(1)
|
375
337
|
#
|
376
338
|
# app.run
|
377
|
-
# app.results(t0, t1.batch) # => ["0.0", "1.1", "1.2"]
|
378
339
|
# app.results(t1, t0) # => ["1.1", "0.0"]
|
379
340
|
#
|
380
341
|
def results(*tasks)
|
381
|
-
_results(tasks).collect {|_result| _result.
|
342
|
+
_results(tasks).collect {|_result| _result.value }
|
343
|
+
end
|
344
|
+
|
345
|
+
# Sets a block to receive the results tasks with no on_complete_block
|
346
|
+
# set. Raises an error if an on_complete_block is already set.
|
347
|
+
# Override the existing on_complete_block by specifying override = true.
|
348
|
+
#
|
349
|
+
# Note: the block recieves an audited result and not the result
|
350
|
+
# itself (see Audit for more information).
|
351
|
+
def on_complete(override=false, &block) # :yields: _result
|
352
|
+
unless on_complete_block == nil || override
|
353
|
+
raise "on_complete_block already set: #{self}"
|
354
|
+
end
|
355
|
+
@on_complete_block = block
|
356
|
+
self
|
382
357
|
end
|
383
358
|
|
359
|
+
# Returns a string like: "#<Tap::App:#{object_id} root: #{root} >"
|
384
360
|
def inspect
|
385
361
|
"#<#{self.class.to_s}:#{object_id} root: #{root} >"
|
386
362
|
end
|
data/lib/tap/constants.rb
CHANGED
data/lib/tap/env.rb
CHANGED
@@ -1,14 +1,23 @@
|
|
1
1
|
require 'tap/support/constant_manifest'
|
2
|
-
require 'tap/support/gems'
|
3
2
|
|
4
3
|
module Tap
|
5
|
-
|
4
|
+
module Support
|
5
|
+
autoload(:Templater, 'tap/support/templater')
|
6
|
+
autoload(:Gems, 'tap/support/gems')
|
7
|
+
end
|
8
|
+
|
9
|
+
# Envs are locations on the filesystem that have resources associated with
|
10
|
+
# them (commands, tasks, generators, etc). Envs may point to files, but it's
|
11
|
+
# more commonly environments are set to a directory and resources are various
|
12
|
+
# files within the directory.
|
13
|
+
#
|
14
|
+
#
|
6
15
|
#--
|
7
16
|
# Note that gems and env_paths reset envs -- custom modifications to envs will be lost
|
8
17
|
# whenever these configs are reset.
|
9
18
|
class Env
|
10
19
|
include Enumerable
|
11
|
-
include
|
20
|
+
include Configurable
|
12
21
|
include Support::Minimap
|
13
22
|
|
14
23
|
class << self
|
@@ -38,68 +47,45 @@ module Tap
|
|
38
47
|
# # File.expand_path("./path/to/config.yml") => e1,
|
39
48
|
# # File.expand_path("./path/to/dir/#{Tap::Env::DEFAULT_CONFIG_FILE}") => e2 }
|
40
49
|
#
|
41
|
-
# The Env is initialized using configurations read from the env config
|
42
|
-
#
|
43
|
-
#
|
44
|
-
def instantiate(path_or_root
|
45
|
-
path = path_or_root.kind_of?(Root) ? path_or_root.root : path_or_root
|
46
|
-
path
|
50
|
+
# The Env is initialized using configurations read from the env config
|
51
|
+
# file. An instance will be initialized regardless of whether the config
|
52
|
+
# file or directory exists.
|
53
|
+
def instantiate(path_or_root)
|
54
|
+
path = config_path(path_or_root.kind_of?(Root) ? path_or_root.root : path_or_root)
|
55
|
+
return instances[path] if instances.has_key?(path)
|
47
56
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
(instances[path] = new({}, root, logger)).reconfigure(config, &block)
|
55
|
-
rescue(Exception)
|
56
|
-
raise Env::ConfigError.new($!, path)
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
def instance_for(path)
|
61
|
-
path = pathify(path)
|
62
|
-
instances.has_key?(path) ? instances[path] : instantiate(path)
|
63
|
-
end
|
64
|
-
|
65
|
-
def pathify(path)
|
66
|
-
if File.directory?(path) || (!File.exists?(path) && File.extname(path) == "")
|
67
|
-
path = File.join(path, DEFAULT_CONFIG_FILE)
|
68
|
-
end
|
69
|
-
File.expand_path(path)
|
57
|
+
config = load_config(path)
|
58
|
+
root = path_or_root.kind_of?(Root) ? path_or_root : File.dirname(path)
|
59
|
+
|
60
|
+
# note the assignment of env to instances MUST occur
|
61
|
+
# before reconfigure to prevent infinite looping
|
62
|
+
(instances[path] = new(root)).reconfigure(config)
|
70
63
|
end
|
71
64
|
|
72
|
-
def manifest(name, &block) # :yields: env (
|
65
|
+
def manifest(name, &block) # :yields: env (and should return a manifest)
|
73
66
|
name = name.to_sym
|
74
67
|
define_method(name) do
|
75
68
|
self.manifests[name] ||= block.call(self).bind(self, name)
|
76
69
|
end
|
77
70
|
end
|
78
71
|
|
79
|
-
|
80
|
-
|
81
|
-
#
|
82
|
-
|
83
|
-
|
84
|
-
File.exists?(File.join(spec.full_gem_path, DEFAULT_CONFIG_FILE))
|
72
|
+
private
|
73
|
+
|
74
|
+
def config_path(path) # :nodoc:
|
75
|
+
if File.directory?(path) || (!File.exists?(path) && File.extname(path) == "")
|
76
|
+
path = File.join(path, DEFAULT_CONFIG_FILE)
|
85
77
|
end
|
78
|
+
|
79
|
+
File.expand_path(path)
|
86
80
|
end
|
87
81
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
# Single and nil arguments are allowed; they are arrayified
|
96
|
-
# and handled as above. Path configs raise an error if
|
97
|
-
# modified when the instance is active.
|
98
|
-
def path_config(key, value=[])
|
99
|
-
instance_variable = "@#{key}".to_sym
|
100
|
-
config_attr(key, value) do |input|
|
101
|
-
check_configurable
|
102
|
-
instance_variable_set(instance_variable, [*input].compact.collect {|path| root[path]}.uniq)
|
82
|
+
# helper to load path as YAML. load_file returns a hash if the path
|
83
|
+
# loads to nil or false (as happens for empty files)
|
84
|
+
def load_config(path) # :nodoc:
|
85
|
+
begin
|
86
|
+
Root.trivial?(path) ? {} : (YAML.load_file(path) || {})
|
87
|
+
rescue(Exception)
|
88
|
+
raise Env::ConfigError.new($!, path)
|
103
89
|
end
|
104
90
|
end
|
105
91
|
end
|
@@ -110,17 +96,19 @@ module Tap
|
|
110
96
|
# The default config file path
|
111
97
|
DEFAULT_CONFIG_FILE = "tap.yml"
|
112
98
|
|
113
|
-
#
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
attr_accessor :logger
|
99
|
+
# An array of nested Envs, by default comprised of the env_path
|
100
|
+
# + gem environments (in that order). Nested environments are
|
101
|
+
# activated/deactivated with self.
|
102
|
+
attr_reader :envs
|
118
103
|
|
119
|
-
#
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
104
|
+
# The Root directory structure for self.
|
105
|
+
nest(:root, Tap::Root) do |config|
|
106
|
+
case config
|
107
|
+
when Root then config
|
108
|
+
when String then Root.new(config)
|
109
|
+
else Root.new.reconfigure(config)
|
110
|
+
end
|
111
|
+
end
|
124
112
|
|
125
113
|
# Specify gems to load as nested Envs. Gems may be specified
|
126
114
|
# by name and/or version, like 'gemname >= 1.2'; by default the
|
@@ -128,14 +116,24 @@ module Tap
|
|
128
116
|
#
|
129
117
|
# Gems are immediately loaded (via gem) through this method.
|
130
118
|
config_attr :gems, [] do |input|
|
131
|
-
check_configurable
|
132
119
|
specs_by_name = {}
|
120
|
+
|
121
|
+
input = YAML.load(input) if input.kind_of?(String)
|
122
|
+
input = case input
|
123
|
+
when :latest, :all
|
124
|
+
Support::Gems.select_gems(input == :latest) do |spec|
|
125
|
+
env_config = File.join(spec.full_gem_path, Tap::Env::DEFAULT_CONFIG_FILE)
|
126
|
+
File.exists?(env_config)
|
127
|
+
end
|
128
|
+
else input
|
129
|
+
end
|
130
|
+
|
133
131
|
@gems = [*input].compact.collect do |gem_name|
|
134
132
|
spec = Support::Gems.gemspec(gem_name)
|
135
133
|
|
136
134
|
case spec
|
137
135
|
when nil then log(:warn, "unknown gem: #{gem_name}", Logger::WARN)
|
138
|
-
else Env.
|
136
|
+
else Env.instantiate(spec.full_gem_path)
|
139
137
|
end
|
140
138
|
|
141
139
|
(specs_by_name[spec.name] ||= []) << spec
|
@@ -157,21 +155,28 @@ module Tap
|
|
157
155
|
|
158
156
|
# Specify configuration files to load as nested Envs.
|
159
157
|
config_attr :env_paths, [] do |input|
|
160
|
-
|
158
|
+
input = YAML.load(input) if input.kind_of?(String)
|
161
159
|
@env_paths = [*input].compact.collect do |path|
|
162
|
-
Env.
|
160
|
+
Env.instantiate(root[path]).env_path
|
163
161
|
end.uniq
|
164
162
|
reset_envs
|
165
163
|
end
|
166
164
|
|
167
165
|
# Designate load paths.
|
168
|
-
|
166
|
+
config_attr :load_paths, ["lib"] do |paths|
|
167
|
+
raise "load_paths cannot be modified once active" if active?
|
168
|
+
@load_paths = resolve_paths(paths)
|
169
|
+
end
|
169
170
|
|
170
171
|
# Designate paths for discovering and executing commands.
|
171
|
-
|
172
|
+
config_attr :command_paths, ["cmd"] do |paths|
|
173
|
+
@command_paths = resolve_paths(paths)
|
174
|
+
end
|
172
175
|
|
173
176
|
# Designate paths for discovering generators.
|
174
|
-
|
177
|
+
config_attr :generator_paths, ["lib"] do |paths|
|
178
|
+
@generator_paths = resolve_paths(paths)
|
179
|
+
end
|
175
180
|
|
176
181
|
manifest(:commands) do |env|
|
177
182
|
paths = []
|
@@ -203,10 +208,8 @@ module Tap
|
|
203
208
|
# generators.cache = env.cache[:generators]
|
204
209
|
generators
|
205
210
|
end
|
206
|
-
|
207
|
-
def initialize(
|
208
|
-
@root = root
|
209
|
-
@logger = logger
|
211
|
+
|
212
|
+
def initialize(path_root_or_config=Dir.pwd)
|
210
213
|
@envs = []
|
211
214
|
@active = false
|
212
215
|
@manifests = {}
|
@@ -215,27 +218,31 @@ module Tap
|
|
215
218
|
@gems = []
|
216
219
|
@env_paths = []
|
217
220
|
|
218
|
-
initialize_config
|
221
|
+
initialize_config case path_root_or_config
|
222
|
+
when String, Root then {:root => path_root_or_config}
|
223
|
+
else path_root_or_config
|
224
|
+
end
|
219
225
|
end
|
220
226
|
|
221
|
-
#
|
222
|
-
def
|
223
|
-
@
|
224
|
-
@envs.freeze
|
225
|
-
@flat_envs = nil
|
227
|
+
# Clears manifests so they may be regenerated.
|
228
|
+
def reset
|
229
|
+
@manifests.clear
|
226
230
|
end
|
227
231
|
|
228
|
-
#
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
232
|
+
# Returns the key for self in Env.instances.
|
233
|
+
def env_path
|
234
|
+
Env.instances.each_pair {|path, env| return path if env == self }
|
235
|
+
nil
|
236
|
+
end
|
237
|
+
|
238
|
+
# Sets envs removing duplicates and instances of self. Setting envs
|
239
|
+
# overrides any environments specified by env_path and gem.
|
240
|
+
def envs=(envs)
|
241
|
+
raise "envs cannot be modified once active" if active?
|
242
|
+
@envs = envs.uniq.delete_if {|e| e == self }
|
236
243
|
end
|
237
244
|
|
238
|
-
# Unshifts env onto envs, removing duplicates.
|
245
|
+
# Unshifts env onto envs, removing duplicates.
|
239
246
|
# Self cannot be unshifted onto self.
|
240
247
|
def unshift(env)
|
241
248
|
unless env == self || envs[0] == env
|
@@ -256,27 +263,24 @@ module Tap
|
|
256
263
|
|
257
264
|
# Passes each nested env to the block in order, starting with self.
|
258
265
|
def each
|
259
|
-
|
266
|
+
visit_envs.each {|e| yield(e) }
|
260
267
|
end
|
261
268
|
|
262
269
|
# Passes each nested env to the block in reverse order, ending with self.
|
263
270
|
def reverse_each
|
264
|
-
|
271
|
+
visit_envs.reverse_each {|e| yield(e) }
|
265
272
|
end
|
266
273
|
|
267
|
-
#
|
268
|
-
#
|
269
|
-
# the env. The initial arguments are set when recursive_each is
|
270
|
-
# first called; subsequent arguements are the return values of the
|
271
|
-
# block.
|
274
|
+
# Recursively injects the memo to each env of self. Each env in envs
|
275
|
+
# receives the same memo from the parent.
|
272
276
|
#
|
273
|
-
#
|
277
|
+
# a,b,c,d,e = ('a'..'e').collect {|name| Tap::Env.new(:name => name) }
|
274
278
|
#
|
275
|
-
#
|
276
|
-
#
|
279
|
+
# a.push(b).push(c)
|
280
|
+
# b.push(d).push(e)
|
277
281
|
#
|
278
282
|
# lines = []
|
279
|
-
#
|
283
|
+
# a.recursive_inject(0) do |nesting_depth, env|
|
280
284
|
# lines << "\n#{'..' * nesting_depth}#{env.config[:name]} (#{nesting_depth})"
|
281
285
|
# nesting_depth + 1
|
282
286
|
# end
|
@@ -289,151 +293,74 @@ module Tap
|
|
289
293
|
# # ....e (2)
|
290
294
|
# # ..c (1)}
|
291
295
|
#
|
292
|
-
def
|
293
|
-
|
294
|
-
end
|
295
|
-
|
296
|
-
# Returns the total number of unique envs nested in self (including self).
|
297
|
-
def count
|
298
|
-
envs(true).length
|
296
|
+
def recursive_inject(memo, &block) # :yields: memo, env
|
297
|
+
inject_envs(memo, &block)
|
299
298
|
end
|
300
299
|
|
301
|
-
#
|
302
|
-
# and self. Reconfiguration consists of the following steps:
|
300
|
+
# Activates self by doing the following, in order:
|
303
301
|
#
|
304
|
-
# *
|
305
|
-
# *
|
306
|
-
# *
|
307
|
-
# * yield other configs to the block (if given)
|
302
|
+
# * sets Env.instance to self (unless already set)
|
303
|
+
# * activate nested environments
|
304
|
+
# * unshift load_paths to $LOAD_PATH
|
308
305
|
#
|
309
|
-
#
|
310
|
-
#
|
311
|
-
#
|
312
|
-
# that existing path configurations like load_paths will
|
313
|
-
# not automatically be reset using reconfigured root.)
|
314
|
-
def reconfigure(overrides={})
|
315
|
-
check_configurable
|
316
|
-
|
317
|
-
# partiton config into its parts
|
318
|
-
env_configs = {}
|
319
|
-
root_configs = {}
|
320
|
-
other_configs = {}
|
321
|
-
|
322
|
-
env_configurations = self.class.configurations
|
323
|
-
root_configurations = root.class.configurations
|
324
|
-
overrides.each_pair do |key, value|
|
325
|
-
key = key.to_sym
|
326
|
-
|
327
|
-
partition = case
|
328
|
-
when env_configurations.key?(key) then env_configs
|
329
|
-
when root_configurations.key?(key) then root_configs
|
330
|
-
else other_configs
|
331
|
-
end
|
332
|
-
|
333
|
-
partition[key] = value
|
334
|
-
end
|
335
|
-
|
336
|
-
# reconfigure root so it can resolve path_configs
|
337
|
-
root.reconfigure(root_configs)
|
338
|
-
|
339
|
-
# reconfigure self
|
340
|
-
super(env_configs)
|
341
|
-
|
342
|
-
# handle other configs
|
343
|
-
case
|
344
|
-
when block_given?
|
345
|
-
yield(other_configs)
|
346
|
-
when !other_configs.empty?
|
347
|
-
log(:warn, "ignoring non-env configs: #{other_configs.keys.join(',')}", Logger::DEBUG)
|
348
|
-
end
|
349
|
-
|
350
|
-
self
|
351
|
-
end
|
352
|
-
|
353
|
-
# Returns the path for self in Env.instances.
|
354
|
-
def env_path
|
355
|
-
Env.instances.each_pair {|path, env| return path if env == self }
|
356
|
-
nil
|
357
|
-
end
|
358
|
-
|
359
|
-
# Logs the action and message at the input level (default INFO).
|
360
|
-
# Logging is suppressed if no logger is set.
|
361
|
-
def log(action, msg="", level=Logger::INFO)
|
362
|
-
logger.add(level, msg, action.to_s) if logger
|
363
|
-
end
|
364
|
-
|
365
|
-
# Activates self by unshifting load_paths for self to the load_path_targets.
|
366
|
-
# Once active, self can be referenced from Env.instance and the current
|
367
|
-
# configurations are frozen. Env.instance is deactivated, if set, before
|
368
|
-
# self is activated. Returns true if activate succeeded, or false if self
|
369
|
-
# is already active.
|
306
|
+
# Once active, the current envs and load_paths are frozen and cannot be
|
307
|
+
# modified until deactivated. Returns true if activate succeeded, or
|
308
|
+
# false if self is already active.
|
370
309
|
def activate
|
371
310
|
return false if active?
|
372
311
|
|
373
312
|
@active = true
|
374
313
|
@@instance = self if @@instance == nil
|
375
314
|
|
376
|
-
# freeze
|
377
|
-
|
378
|
-
|
379
|
-
when Array then value.freeze
|
380
|
-
end
|
381
|
-
end
|
315
|
+
# freeze envs and load paths
|
316
|
+
@envs.freeze
|
317
|
+
@load_paths.freeze
|
382
318
|
|
383
319
|
# activate nested envs
|
384
320
|
envs.reverse_each do |env|
|
385
321
|
env.activate
|
386
322
|
end
|
387
|
-
|
323
|
+
|
388
324
|
# add load paths
|
389
325
|
load_paths.reverse_each do |path|
|
390
326
|
$LOAD_PATH.unshift(path)
|
391
327
|
end
|
392
|
-
|
393
|
-
$LOAD_PATH.uniq!
|
394
|
-
|
395
|
-
# perform requires
|
396
|
-
requires.each do |path|
|
397
|
-
require path
|
398
|
-
end
|
399
328
|
|
400
|
-
|
401
|
-
loads.each do |path|
|
402
|
-
load path
|
403
|
-
end
|
329
|
+
$LOAD_PATH.uniq!
|
404
330
|
|
405
331
|
true
|
406
332
|
end
|
407
333
|
|
408
|
-
# Deactivates self by
|
409
|
-
# from the load_path_targets. Env.instance will no longer reference self
|
410
|
-
# and the configurations are unfrozen (using duplication).
|
334
|
+
# Deactivates self by doing the following in order:
|
411
335
|
#
|
336
|
+
# * deactivates nested environments
|
337
|
+
# * removes load_paths from $LOAD_PATH
|
338
|
+
# * sets Env.instance to nil (if set to self)
|
339
|
+
# * clears cached manifest data
|
340
|
+
#
|
341
|
+
# Once deactivated, envs and load_paths are unfrozen and may be modified.
|
412
342
|
# Returns true if deactivate succeeded, or false if self is not active.
|
413
343
|
def deactivate
|
414
344
|
return false unless active?
|
345
|
+
@active = false
|
346
|
+
|
347
|
+
# dectivate nested envs
|
348
|
+
envs.reverse_each do |env|
|
349
|
+
env.deactivate
|
350
|
+
end
|
415
351
|
|
416
352
|
# remove load paths
|
417
353
|
load_paths.each do |path|
|
418
354
|
$LOAD_PATH.delete(path)
|
419
355
|
end
|
420
|
-
|
421
|
-
# unfreeze array configs by duplicating
|
422
|
-
self.config.class_config.each_pair do |key, value|
|
423
|
-
value = send(key)
|
424
|
-
case value
|
425
|
-
when Array then instance_variable_set("@#{key}", value.dup)
|
426
|
-
end
|
427
|
-
end
|
428
356
|
|
429
|
-
|
430
|
-
@
|
431
|
-
|
357
|
+
# unfreeze envs and load paths
|
358
|
+
@envs = @envs.dup
|
359
|
+
@load_paths = @load_paths.dup
|
432
360
|
|
433
|
-
#
|
434
|
-
|
435
|
-
|
436
|
-
end
|
361
|
+
# clear cached data
|
362
|
+
@@instance = nil if @@instance == self
|
363
|
+
@manifests.clear
|
437
364
|
|
438
365
|
true
|
439
366
|
end
|
@@ -444,36 +371,30 @@ module Tap
|
|
444
371
|
end
|
445
372
|
|
446
373
|
# Searches each env for the first existing file or directory at
|
447
|
-
# env.root.filepath(dir, path). Paths are expanded, and
|
374
|
+
# env.root.filepath(dir, path). Paths are expanded, and search
|
448
375
|
# checks to make sure the file is, in fact, relative to env.root[dir].
|
449
376
|
# An optional block may be used to check the file; the file will only
|
450
377
|
# be returned if the block returns true.
|
451
378
|
#
|
452
379
|
# Returns nil if no file can be found.
|
453
|
-
def
|
380
|
+
def search(dir, path, strict=true)
|
454
381
|
each do |env|
|
455
382
|
directory = env.root.filepath(dir)
|
456
383
|
file = env.root.filepath(dir, path)
|
384
|
+
next unless File.exists?(file)
|
457
385
|
|
458
|
-
# check the file is relative to
|
459
|
-
|
460
|
-
|
461
|
-
File.exists?(file) &&
|
462
|
-
(!block_given? || yield(file))
|
463
|
-
return file
|
386
|
+
# check the file is relative to directory
|
387
|
+
if strict && file.rindex(directory, 0) != 0
|
388
|
+
raise "not relative to search dir: #{file} (#{directory})"
|
464
389
|
end
|
390
|
+
|
391
|
+
# filter
|
392
|
+
return file if !block_given? || yield(file)
|
465
393
|
end
|
466
394
|
|
467
395
|
nil
|
468
396
|
end
|
469
397
|
|
470
|
-
# def reset(name, &block)
|
471
|
-
# each do |env|
|
472
|
-
# env.manifests[name].each(&block)
|
473
|
-
# env.manifests[name] = nil
|
474
|
-
# end
|
475
|
-
# end
|
476
|
-
|
477
398
|
#
|
478
399
|
TEMPLATES = {}
|
479
400
|
TEMPLATES[:commands] = %Q{<% if count > 1 %>
|
@@ -542,7 +463,7 @@ module Tap
|
|
542
463
|
|
543
464
|
attrs = {}
|
544
465
|
templaters = []
|
545
|
-
|
466
|
+
recursive_inject(args) do |argv, env|
|
546
467
|
templater = Support::Templater.new(template, :env => env)
|
547
468
|
next_args = block_given? ? yield(templater, attrs, *argv) : argv
|
548
469
|
templaters << templater if next_args
|
@@ -564,46 +485,51 @@ module Tap
|
|
564
485
|
env.root.root
|
565
486
|
end
|
566
487
|
|
567
|
-
# Raises an error if self is already active (and hence, configurations
|
568
|
-
# should not be modified)
|
569
|
-
def check_configurable
|
570
|
-
raise "path configurations are disabled when active" if active?
|
571
|
-
end
|
572
|
-
|
573
488
|
# Resets envs using the current env_paths and gems.
|
574
489
|
def reset_envs
|
575
490
|
self.envs = env_paths.collect do |path|
|
576
|
-
Env.
|
491
|
+
Env.instantiate(path)
|
577
492
|
end + gems.collect do |spec|
|
578
|
-
Env.
|
493
|
+
Env.instantiate(spec.full_gem_path)
|
579
494
|
end
|
580
495
|
end
|
581
496
|
|
582
|
-
#
|
583
|
-
#
|
584
|
-
#
|
585
|
-
|
586
|
-
|
587
|
-
|
497
|
+
# Arrayifies, compacts, and resolves input paths using root, and
|
498
|
+
# removes duplicates. In short
|
499
|
+
#
|
500
|
+
# resolve_paths ['lib', nil, 'lib', 'alt] # => [root['lib'], root['alt']]
|
501
|
+
#
|
502
|
+
def resolve_paths(paths) # :nodoc:
|
503
|
+
paths = YAML.load(paths) if paths.kind_of?(String)
|
504
|
+
[*paths].compact.collect {|path| root[path]}.uniq
|
505
|
+
end
|
506
|
+
|
507
|
+
# Recursively iterates through envs, starting with self, and
|
508
|
+
# collects the visited envs in order.
|
509
|
+
def visit_envs(visited=[], &block) # :nodoc:
|
510
|
+
unless visited.include?(self)
|
511
|
+
visited << self
|
512
|
+
yield(self) if block_given?
|
513
|
+
|
588
514
|
envs.each do |env|
|
589
|
-
env.
|
515
|
+
env.visit_envs(visited, &block)
|
590
516
|
end
|
591
517
|
end
|
592
518
|
|
593
|
-
|
519
|
+
visited
|
594
520
|
end
|
595
521
|
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
env.envs.each do |nested_env|
|
605
|
-
each_nested_env(nested_env, visited, next_args, &block)
|
522
|
+
# helper to recursively inject a memo to the children of env
|
523
|
+
def inject_envs(memo, visited=[], &block) # :nodoc:
|
524
|
+
unless visited.include?(self)
|
525
|
+
visited << self
|
526
|
+
next_memo = yield(memo, self)
|
527
|
+
envs.each do |env|
|
528
|
+
env.inject_envs(next_memo, visited, &block)
|
529
|
+
end
|
606
530
|
end
|
531
|
+
|
532
|
+
visited
|
607
533
|
end
|
608
534
|
|
609
535
|
# Raised when there is a Env-level configuration error.
|