tap 0.19.0 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History +100 -45
- data/MIT-LICENSE +1 -1
- data/README +95 -51
- data/bin/tap +11 -57
- data/bin/tapexe +84 -0
- data/doc/API +91 -139
- data/doc/Configuration +93 -0
- data/doc/Examples/Command Line +10 -42
- data/doc/Examples/Tapfile +124 -0
- data/doc/Ruby to Ruby +87 -0
- data/doc/Workflow Syntax +185 -0
- data/lib/tap.rb +74 -5
- data/lib/tap/app.rb +217 -310
- data/lib/tap/app/api.rb +44 -23
- data/lib/tap/app/queue.rb +11 -12
- data/lib/tap/app/stack.rb +4 -4
- data/lib/tap/declarations.rb +200 -0
- data/lib/tap/declarations/context.rb +31 -0
- data/lib/tap/declarations/description.rb +33 -0
- data/lib/tap/env.rb +133 -779
- data/lib/tap/env/cache.rb +87 -0
- data/lib/tap/env/constant.rb +94 -39
- data/lib/tap/env/path.rb +71 -0
- data/lib/tap/join.rb +42 -78
- data/lib/tap/joins/gate.rb +85 -0
- data/lib/tap/joins/switch.rb +4 -2
- data/lib/tap/joins/sync.rb +3 -3
- data/lib/tap/middleware.rb +5 -5
- data/lib/tap/middlewares/debugger.rb +18 -58
- data/lib/tap/parser.rb +115 -183
- data/lib/tap/root.rb +162 -239
- data/lib/tap/signal.rb +72 -0
- data/lib/tap/signals.rb +20 -2
- data/lib/tap/signals/class_methods.rb +38 -43
- data/lib/tap/signals/configure.rb +19 -0
- data/lib/tap/signals/help.rb +5 -7
- data/lib/tap/signals/load.rb +49 -0
- data/lib/tap/signals/module_methods.rb +1 -0
- data/lib/tap/task.rb +46 -275
- data/lib/tap/tasks/dump.rb +21 -16
- data/lib/tap/tasks/list.rb +184 -0
- data/lib/tap/tasks/load.rb +4 -4
- data/lib/tap/tasks/prompt.rb +128 -0
- data/lib/tap/tasks/signal.rb +42 -0
- data/lib/tap/tasks/singleton.rb +35 -0
- data/lib/tap/tasks/stream.rb +64 -0
- data/lib/tap/utils.rb +83 -0
- data/lib/tap/version.rb +2 -2
- data/lib/tap/workflow.rb +124 -0
- data/tap.yml +0 -0
- metadata +59 -24
- data/cmd/console.rb +0 -43
- data/cmd/manifest.rb +0 -118
- data/cmd/run.rb +0 -145
- data/doc/Examples/Workflow +0 -40
- data/lib/tap/app/node.rb +0 -29
- data/lib/tap/env/context.rb +0 -61
- data/lib/tap/env/gems.rb +0 -63
- data/lib/tap/env/manifest.rb +0 -179
- data/lib/tap/env/minimap.rb +0 -308
- data/lib/tap/intern.rb +0 -50
- data/lib/tap/joins.rb +0 -9
- data/lib/tap/prompt.rb +0 -36
- data/lib/tap/root/utils.rb +0 -220
- data/lib/tap/root/versions.rb +0 -138
- data/lib/tap/signals/signal.rb +0 -68
data/lib/tap/env.rb
CHANGED
@@ -1,850 +1,204 @@
|
|
1
|
-
require 'tap/
|
1
|
+
require 'tap/signals'
|
2
|
+
require 'tap/env/cache'
|
2
3
|
require 'tap/env/constant'
|
3
|
-
require 'tap/env/context'
|
4
|
-
require 'tap/env/manifest'
|
5
|
-
require 'tap/templater'
|
6
|
-
autoload(:YAML, 'yaml')
|
7
4
|
|
5
|
+
autoload(:YAML, 'yaml')
|
8
6
|
module Tap
|
9
|
-
|
10
|
-
# == Description
|
11
|
-
#
|
12
|
-
# Env provides access to an execution environment spanning many directories,
|
13
|
-
# such as the working directory plus a series of gem directories. Envs merge
|
14
|
-
# the files from each directory into an abstract directory that may be
|
15
|
-
# globbed and accessed as a single unit. For example:
|
16
|
-
#
|
17
|
-
# # /one
|
18
|
-
# # |-- a.rb
|
19
|
-
# # `-- b.rb
|
20
|
-
# #
|
21
|
-
# # /two
|
22
|
-
# # |-- b.rb
|
23
|
-
# # `-- c.rb
|
24
|
-
# env = Env.new('/one')
|
25
|
-
# env << Env.new('/two')
|
26
|
-
#
|
27
|
-
# env.collect {|e| e.root.root}
|
28
|
-
# # => ["/one", "/two"]
|
29
|
-
#
|
30
|
-
# env.glob(:root, "*.rb")
|
31
|
-
# # => [
|
32
|
-
# # "/one/a.rb",
|
33
|
-
# # "/one/b.rb",
|
34
|
-
# # "/two/c.rb"
|
35
|
-
# # ]
|
36
|
-
#
|
37
|
-
# As illustrated, files in the nested environment are accessible within the
|
38
|
-
# nesting environment. Envs provide methods for finding files associated
|
39
|
-
# with a specific class, and allow the generation of manifests that provide
|
40
|
-
# succinct access to various environment resources.
|
41
|
-
#
|
42
|
-
# Usage of Envs is fairly straightforward, but the internals and default
|
43
|
-
# setup require some study as they have to span numerous functional domains.
|
44
|
-
# The most common features are detailed below.
|
45
|
-
#
|
46
|
-
# ==== Class Paths
|
47
|
-
#
|
48
|
-
# Class paths are a kind of inheritance for files associated with a class.
|
49
|
-
# Say we had the following classes:
|
50
|
-
#
|
51
|
-
# class A; end
|
52
|
-
# class B < A; end
|
53
|
-
#
|
54
|
-
# The naturally associated directories are 'a' and 'b'. To look these up:
|
55
|
-
#
|
56
|
-
# env.class_path(:root, A) # => "/one/a"
|
57
|
-
# env.class_path(:root, B) # => "/one/b"
|
58
|
-
#
|
59
|
-
# And to look up an associated file:
|
60
|
-
#
|
61
|
-
# env.class_path(:root, A, "index.html") # => "/one/a/index.html"
|
62
|
-
# env.class_path(:root, B, "index.html") # => "/one/b/index.html"
|
63
|
-
#
|
64
|
-
# A block may be given to filter paths, for instance to test if a given file
|
65
|
-
# exists. The class_path method will check each env then roll up the
|
66
|
-
# inheritance hierarchy until the block returns true.
|
67
|
-
#
|
68
|
-
# FileUtils.touch("/two/a/index.html")
|
69
|
-
#
|
70
|
-
# visited_paths = []
|
71
|
-
# env.class_path(:root, B, "index.html) do |path|
|
72
|
-
# visited_paths << path
|
73
|
-
# File.exists?(path)
|
74
|
-
# end # => "/two/a/index.html"
|
75
|
-
#
|
76
|
-
# visited_paths
|
77
|
-
# # => [
|
78
|
-
# # "/one/b/index.html",
|
79
|
-
# # "/two/b/index.html",
|
80
|
-
# # "/one/a/index.html",
|
81
|
-
# # "/two/a/index.html"
|
82
|
-
# # ]
|
83
|
-
#
|
84
|
-
# This behavior is very useful for associating views with a class.
|
85
|
-
#
|
86
|
-
# ==== Manifest
|
87
|
-
#
|
88
|
-
# Envs can generate manifests of various resources so they may be identified
|
89
|
-
# using minipaths (see Minimap for details regarding minipaths). Command
|
90
|
-
# files used by the tap executable are one example of a resource, and the
|
91
|
-
# constants used in building a workflow are another.
|
92
|
-
#
|
93
|
-
# Manifest are generated by defining a builder, typically a block, that
|
94
|
-
# receives an env and returns an array of the associated resources.
|
95
|
-
# Using the same env as above:
|
96
|
-
#
|
97
|
-
# manifest = env.manifest {|e| e.root.glob(:root, "*.rb") }
|
98
|
-
#
|
99
|
-
# manifest.seek("a") # => "/one/a.rb"
|
100
|
-
# manifest.seek("b") # => "/one/b.rb"
|
101
|
-
# manifest.seek("c") # => "/two/c.rb"
|
102
|
-
#
|
103
|
-
# As illustrated, seek finds the first entry across all envs that matches the
|
104
|
-
# input minipath. A minipath for the env may be prepended to only search
|
105
|
-
# within a specific env.
|
106
|
-
#
|
107
|
-
# manifest.seek("one:b") # => "/one/b.rb"
|
108
|
-
# manifest.seek("two:b") # => "/two/b.rb"
|
109
|
-
#
|
110
|
-
# Env caches a manifest of constants identified by {constant attributes}[http://tap.rubyforge.org/lazydoc]
|
111
|
-
# in files specified by under the Env.const_paths. These constants are used
|
112
|
-
# when interpreting signals from the command line. Constants may be manually
|
113
|
-
# registered to the constants manifest and classified by type like this:
|
114
|
-
#
|
115
|
-
# class CustomTask
|
116
|
-
# def call; end
|
117
|
-
# end
|
118
|
-
# env.register(CustomTask).register_as(:task, "this is a custom task")
|
119
|
-
#
|
120
|
-
# const = env.constants.seek('custom_task')
|
121
|
-
# const.const_name # => "CustomTask"
|
122
|
-
# const.types # => {:task => "this is a custom task"}
|
123
|
-
# const.constantize # => CustomTask
|
124
|
-
#
|
125
|
-
# == Setup
|
126
|
-
#
|
127
|
-
# Envs may be manually setup in code by individually generating instances
|
128
|
-
# and nesting them. More commonly envs are defined in configuration files
|
129
|
-
# and instantiated by specifying where the files are located. The default
|
130
|
-
# config basename is 'tap.yml'; any env_paths specified in the config file
|
131
|
-
# will be added.
|
132
|
-
#
|
133
|
-
# This type of instantiation is recursive:
|
134
|
-
#
|
135
|
-
# # [/one/tap.yml]
|
136
|
-
# # env_paths: [/two]
|
137
|
-
# #
|
138
|
-
# # [/two/tap.yml]
|
139
|
-
# # env_paths: [/three]
|
140
|
-
# #
|
141
|
-
#
|
142
|
-
# env = Env.new("/one", :basename => 'tap.yml')
|
143
|
-
# env.collect {|e| e.root.root}
|
144
|
-
# # => ["/one", "/two", "/three"]
|
145
|
-
#
|
146
|
-
# Gem directories are fair game. Env allows specific gems to be specified
|
147
|
-
# by name (via the 'gems' config), and if a gem has a tap.yml file then it
|
148
|
-
# will be used to configure the gem env. Alternatively, an env may be set
|
149
|
-
# to automatically discover and nest gem environments. In this case gems
|
150
|
-
# are discovered when they have a tap.yml file.
|
151
|
-
#
|
152
|
-
# ==== ENV configs
|
153
|
-
#
|
154
|
-
# Configurations may be also specified as an ENV variables. This type of
|
155
|
-
# configuration is very useful on the command line. Config variables
|
156
|
-
# should be prefixed by TAP_ and named like the capitalized config key
|
157
|
-
# (ex: TAP_GEMS or TAP_ENV_PATHS). See the
|
158
|
-
# {Command Line Examples}[link:files/doc/Examples/Command%20Line.html]
|
159
|
-
# to see ENV configs in action.
|
160
|
-
#
|
161
|
-
# These configurations may be accessed from Env#config, and are
|
162
|
-
# automatically incorporated by Env#setup.
|
163
|
-
#
|
164
7
|
class Env
|
165
|
-
autoload(:Gems, 'tap/env/gems')
|
166
|
-
|
167
8
|
class << self
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
env_vars.each_pair do |key, value|
|
175
|
-
if key =~ /\ATAP_(.*)\z/
|
176
|
-
config[$1.downcase] = value
|
177
|
-
end
|
178
|
-
end
|
179
|
-
config
|
180
|
-
end
|
181
|
-
|
182
|
-
# Initializes and activates an env as described in the config file under
|
183
|
-
# dir. The config file should be a relative path and will be used for
|
184
|
-
# determining configuration files under each env_path.
|
185
|
-
#
|
186
|
-
# The env configuration is determined by merging the following in order:
|
187
|
-
# * defaults {root => dir, gems => all}
|
188
|
-
# * ENV configs
|
189
|
-
# * config_file configs
|
190
|
-
#
|
191
|
-
# The HOME directory for Tap will be added as an additonal environment
|
192
|
-
# if not already added somewhere in the env hierarchy. By default all
|
193
|
-
# gems will be included in the Env.
|
194
|
-
def setup(dir=Dir.pwd, config_file=CONFIG_FILE)
|
195
|
-
# setup configurations
|
196
|
-
config = {'root' => dir, 'gems' => :all}
|
197
|
-
|
198
|
-
user_config_file = config_file ? File.join(dir, config_file) : nil
|
199
|
-
user = load_config(user_config_file)
|
200
|
-
|
201
|
-
config.merge!(self.config)
|
202
|
-
config.merge!(user)
|
203
|
-
|
204
|
-
# keys must be symbolized as they are immediately
|
205
|
-
# used to initialize the Env configs
|
206
|
-
config = config.inject({}) do |options, (key, value)|
|
207
|
-
options[key.to_sym || key] = value
|
208
|
-
options
|
209
|
-
end
|
9
|
+
def generate(options={})
|
10
|
+
options = {
|
11
|
+
:register => true,
|
12
|
+
:load_paths => true,
|
13
|
+
:set => true
|
14
|
+
}.merge(options)
|
210
15
|
|
211
|
-
|
212
|
-
|
213
|
-
|
16
|
+
dir = File.expand_path(options[:dir] || Dir.pwd)
|
17
|
+
pathfile = options[:pathfile] || File.expand_path(Path::FILE, dir)
|
18
|
+
map = options[:map] || Path.load(pathfile)
|
19
|
+
lib = options[:lib] || 'lib'
|
20
|
+
pattern = options[:pattern] || '**/*.rb'
|
214
21
|
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
end
|
22
|
+
register = options[:register]
|
23
|
+
load_paths = options[:load_paths]
|
24
|
+
set = options[:set]
|
219
25
|
|
220
|
-
|
221
|
-
|
222
|
-
end
|
223
|
-
|
224
|
-
# Generates an Env for the specified gem or Gem::Specification. The
|
225
|
-
# gemspec for the gem is used to determine the env configuration in
|
226
|
-
# the following way:
|
227
|
-
#
|
228
|
-
# root: the gem path
|
229
|
-
# gems: all gem dependencies with a config_file
|
230
|
-
# const_paths: the gem require paths
|
231
|
-
# set_const_paths: false (because RubyGems sets them for you)
|
232
|
-
#
|
233
|
-
# Configurations specified in the gem config_file override these
|
234
|
-
# defaults.
|
235
|
-
def setup_gem(gem_name, context=Context.new)
|
236
|
-
spec = Gems.gemspec(gem_name)
|
237
|
-
path = spec.full_gem_path
|
26
|
+
lines = []
|
27
|
+
lines << "register #{Path.escape(dir)}" if register
|
238
28
|
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
spec.dependencies.each do |dependency|
|
243
|
-
unless dependency.type == :runtime
|
244
|
-
next
|
245
|
-
end
|
246
|
-
|
247
|
-
unless gemspec = Gems.gemspec(dependency)
|
248
|
-
# this error may result when a dependency has
|
249
|
-
# been uninstalled for a particular gem
|
250
|
-
warn "missing gem dependency: #{dependency.to_s} (#{spec.full_name})"
|
251
|
-
next
|
252
|
-
end
|
29
|
+
path = Path.new(dir, map)
|
30
|
+
path[lib].each do |lib_dir|
|
31
|
+
lines << "loadpath #{Path.escape(lib_dir)}" if load_paths
|
253
32
|
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
end
|
260
|
-
|
261
|
-
config = {
|
262
|
-
'root' => path,
|
263
|
-
'gems' => dependencies,
|
264
|
-
'const_paths' => spec.require_paths,
|
265
|
-
'set_const_paths' => false
|
266
|
-
}
|
267
|
-
|
268
|
-
# override the default configs with whatever configs
|
269
|
-
# are specified in the gem config file
|
270
|
-
if config_file = context.config_file(path)
|
271
|
-
config.merge!(load_config(config_file))
|
33
|
+
Constant.scan(lib_dir, pattern).each do |constant|
|
34
|
+
require_paths = Path.join(constant.require_paths)
|
35
|
+
types = constant.types.to_a.collect {|type| Path.escape(Path.join(type)) }
|
36
|
+
lines << "set #{constant.const_name} #{Path.escape require_paths} #{types.join(' ')}"
|
37
|
+
end if set
|
272
38
|
end
|
273
39
|
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
# Loads configurations from path as YAML. Returns an empty hash if the path
|
278
|
-
# loads to nil or false (as happens for empty files), or doesn't exist.
|
279
|
-
#
|
280
|
-
# Raises a ConfigError if the configurations do not load properly.
|
281
|
-
def load_config(path)
|
282
|
-
return {} unless path
|
283
|
-
|
284
|
-
begin
|
285
|
-
Root::Utils.trivial?(path) ? {} : (YAML.load_file(path) || {})
|
286
|
-
rescue(Exception)
|
287
|
-
raise ConfigError.new($!, path)
|
288
|
-
end
|
40
|
+
lines.uniq!
|
41
|
+
lines.sort!
|
42
|
+
lines
|
289
43
|
end
|
290
44
|
end
|
291
45
|
|
292
|
-
include
|
293
|
-
include Enumerable
|
294
|
-
include Minimap
|
295
|
-
|
296
|
-
# The config file path
|
297
|
-
CONFIG_FILE = "tap.yml"
|
46
|
+
include Signals
|
298
47
|
|
299
|
-
#
|
300
|
-
HOME = File.expand_path("#{File.dirname(__FILE__)}/../..")
|
301
|
-
|
302
|
-
# An array of nested Envs, by default comprised of the env_path
|
303
|
-
# + gem environments (in that order). Envs can be manually set
|
304
|
-
# to override these defaults.
|
305
|
-
attr_reader :envs
|
306
|
-
|
307
|
-
# A Context tracking information shared among a set of envs.
|
308
|
-
attr_reader :context
|
309
|
-
|
310
|
-
# The Root directory structure for self.
|
311
|
-
nest(:root, Root, :init => false)
|
312
|
-
|
313
|
-
# Specify gems to add as nested Envs. Gems may be specified by name
|
314
|
-
# and/or version, like 'gemname >= 1.2'; by default the latest version
|
315
|
-
# of the gem is selected.
|
316
|
-
#
|
317
|
-
# Several special values also exist:
|
48
|
+
# Matches an inline type. After the match:
|
318
49
|
#
|
319
|
-
#
|
320
|
-
#
|
321
|
-
# :ALL all gems
|
322
|
-
# :latest the latest version of all gems with a config file
|
323
|
-
# :all all gems with a config file
|
50
|
+
# $1:: The prefix string (ex 'Const' for '::Const::type')
|
51
|
+
# $2:: The inline type (ex 'type' for '::Const::type')
|
324
52
|
#
|
325
|
-
|
326
|
-
config_attr :gems, [] do |input|
|
327
|
-
input = yaml_load(input) if input.kind_of?(String)
|
328
|
-
|
329
|
-
@gems = case input
|
330
|
-
when false, nil, :NONE, :none
|
331
|
-
[]
|
332
|
-
when :LATEST, :ALL
|
333
|
-
# latest and all, no filter
|
334
|
-
Gems.select_gems(input == :LATEST)
|
335
|
-
when :latest, :all
|
336
|
-
# latest and all, filtering by the existence of a
|
337
|
-
# config file; all gems are selected if no config
|
338
|
-
# file can be determined.
|
339
|
-
Gems.select_gems(input == :latest) do |spec|
|
340
|
-
config_file = context.config_file(spec.full_gem_path)
|
341
|
-
config_file == nil || File.exists?(config_file)
|
342
|
-
end
|
343
|
-
else
|
344
|
-
# resolve gem names manually
|
345
|
-
[*input].collect do |name|
|
346
|
-
Gems.gemspec(name)
|
347
|
-
end.compact
|
348
|
-
end
|
53
|
+
INLINE_TYPE = /(.*)::([a-z_]*)\z/
|
349
54
|
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
# Specify directories to load as nested Envs. Configurations for the
|
354
|
-
# env are loaded from the config file under dir, if it exists.
|
355
|
-
config_attr :env_paths, [] do |input|
|
356
|
-
@env_paths = resolve_paths(input)
|
357
|
-
reset_envs
|
358
|
-
end
|
55
|
+
attr_reader :paths
|
56
|
+
attr_reader :constants
|
359
57
|
|
360
|
-
#
|
361
|
-
|
362
|
-
config_attr :const_paths, [:lib] do |input|
|
363
|
-
raise "const_paths cannot be modified once active" if active?
|
364
|
-
@const_paths = resolve_paths(input)
|
365
|
-
end
|
58
|
+
signal_hash :auto, # auto-scan resources from a dir
|
59
|
+
:signature => [:dir, :pathfile, :lib, :pattern]
|
366
60
|
|
367
|
-
|
368
|
-
config_attr :set_const_paths, true do |input|
|
369
|
-
raise "set_const_paths cannot be modified once active" if active?
|
370
|
-
@set_const_paths = Configurable::Validation.boolean[input]
|
371
|
-
end
|
61
|
+
signal :activate, :signature => [:name, :version]
|
372
62
|
|
373
|
-
#
|
374
|
-
#
|
375
|
-
#
|
376
|
-
#
|
377
|
-
# A configuration hash may be manually provided in the place of dir. In
|
378
|
-
# that case, no configurations will be loaded, even if the config file
|
379
|
-
# exists.
|
380
|
-
#
|
381
|
-
# Context can be specified as a Context, or a Hash used to initialize a
|
382
|
-
# Context.
|
383
|
-
def initialize(config_or_dir=Dir.pwd, context={})
|
384
|
-
|
385
|
-
# setup root
|
386
|
-
config = nil
|
387
|
-
@root = case config_or_dir
|
388
|
-
when Root
|
389
|
-
config_or_dir
|
390
|
-
when String
|
391
|
-
Root.new(config_or_dir)
|
392
|
-
else
|
393
|
-
config = config_or_dir
|
394
|
-
|
395
|
-
if config.has_key?(:root) && config.has_key?('root')
|
396
|
-
raise "multiple values mapped to :root"
|
397
|
-
end
|
398
|
-
|
399
|
-
root = config.delete(:root) || config.delete('root') || Dir.pwd
|
400
|
-
root.kind_of?(Root) ? root : Root.new(root)
|
401
|
-
end
|
402
|
-
|
403
|
-
# note registration requires root.root, and so the
|
404
|
-
# setup of context must follow the setup of root.
|
405
|
-
@context = case context
|
406
|
-
when Context
|
407
|
-
context
|
408
|
-
when Hash
|
409
|
-
Context.new(context)
|
410
|
-
else raise "cannot convert #{context.inspect} to Tap::Env::Context"
|
411
|
-
end
|
412
|
-
@context.register(self)
|
413
|
-
|
414
|
-
# these need to be set for reset_env
|
415
|
-
@active = false
|
416
|
-
@gems = nil
|
417
|
-
@env_paths = nil
|
418
|
-
|
419
|
-
# only load configurations if configs were not provided
|
420
|
-
config ||= Env.load_config(@context.config_file(@root.root))
|
421
|
-
initialize_config(config)
|
422
|
-
|
423
|
-
# set the invert flag
|
424
|
-
@invert = false
|
425
|
-
end
|
426
|
-
|
427
|
-
# Sets envs removing duplicates and instances of self. Setting envs
|
428
|
-
# overrides any environments specified by env_path and gem.
|
429
|
-
def envs=(envs)
|
430
|
-
raise "envs cannot be modified once active" if active?
|
431
|
-
@envs = envs.uniq.delete_if {|env| env == self }
|
432
|
-
end
|
433
|
-
|
434
|
-
# Unshifts env onto envs. Self cannot be unshifted onto self.
|
435
|
-
def unshift(env)
|
436
|
-
unless env == self || envs[0] == env
|
437
|
-
self.envs = envs.dup.unshift(env)
|
438
|
-
end
|
439
|
-
self
|
440
|
-
end
|
441
|
-
|
442
|
-
# Pushes env onto envs, removing duplicates.
|
443
|
-
# Self cannot be pushed onto self.
|
444
|
-
def push(env)
|
445
|
-
unless env == self || envs[-1] == env
|
446
|
-
envs = self.envs.reject {|e| e == env }
|
447
|
-
self.envs = envs.push(env)
|
448
|
-
end
|
449
|
-
self
|
450
|
-
end
|
451
|
-
alias_method :<<, :push
|
452
|
-
|
453
|
-
# Passes each nested env to the block in order, starting with self.
|
454
|
-
def each
|
455
|
-
visit_envs.each {|e| yield(e) }
|
456
|
-
end
|
457
|
-
|
458
|
-
# Passes each nested env to the block in reverse order, ending with self.
|
459
|
-
def reverse_each
|
460
|
-
visit_envs.reverse_each {|e| yield(e) }
|
461
|
-
end
|
462
|
-
|
463
|
-
# Recursively injects the memo to each env of self. Each env in envs
|
464
|
-
# receives the same memo from the parent. This is different from the
|
465
|
-
# inject provided via Enumerable, where each subsequent env receives
|
466
|
-
# the memo from the previous, not the parent, env.
|
467
|
-
#
|
468
|
-
# a,b,c,d,e = ('a'..'e').collect {|name| Env.new(:name => name) }
|
469
|
-
#
|
470
|
-
# a.push(b).push(c)
|
471
|
-
# b.push(d).push(e)
|
472
|
-
#
|
473
|
-
# lines = []
|
474
|
-
# a.recursive_inject(0) do |nesting_depth, env|
|
475
|
-
# lines << "\n#{'..' * nesting_depth}#{env.config[:name]} (#{nesting_depth})"
|
476
|
-
# nesting_depth + 1
|
477
|
-
# end
|
478
|
-
#
|
479
|
-
# lines.join
|
480
|
-
# # => %Q{
|
481
|
-
# # a (0)
|
482
|
-
# # ..b (1)
|
483
|
-
# # ....d (2)
|
484
|
-
# # ....e (2)
|
485
|
-
# # ..c (1)}
|
486
|
-
#
|
487
|
-
def recursive_inject(memo, &block) # :yields: memo, env
|
488
|
-
inject_envs(memo, &block)
|
489
|
-
end
|
490
|
-
|
491
|
-
# Activates self by doing the following, in order:
|
492
|
-
#
|
493
|
-
# * activate nested environments
|
494
|
-
# * unshift const_paths to $LOAD_PATH (if set_const_paths is true)
|
495
|
-
#
|
496
|
-
# Once active, the current envs and const_paths are frozen and cannot be
|
497
|
-
# modified until deactivated. Returns true if activate succeeded, or
|
498
|
-
# false if self is already active.
|
499
|
-
def activate
|
500
|
-
return false if active?
|
501
|
-
|
502
|
-
@active = true
|
503
|
-
|
504
|
-
# freeze envs and const paths
|
505
|
-
@envs.freeze
|
506
|
-
@const_paths.freeze
|
507
|
-
|
508
|
-
# activate nested envs
|
509
|
-
envs.reverse_each do |env|
|
510
|
-
env.activate
|
511
|
-
end
|
512
|
-
|
513
|
-
# add const paths
|
514
|
-
if set_const_paths
|
515
|
-
const_paths.reverse_each do |path|
|
516
|
-
$LOAD_PATH.unshift(path)
|
517
|
-
end
|
518
|
-
|
519
|
-
$LOAD_PATH.uniq!
|
520
|
-
end
|
521
|
-
|
522
|
-
true
|
523
|
-
end
|
63
|
+
signal :register # add a resource path
|
64
|
+
signal :loadpath # add a load path
|
65
|
+
signal :set # add a constant
|
524
66
|
|
525
|
-
#
|
526
|
-
#
|
527
|
-
#
|
528
|
-
# * removes const_paths from $LOAD_PATH (if set_const_paths is true)
|
529
|
-
#
|
530
|
-
# Once deactivated, envs and const_paths are unfrozen and may be modified.
|
531
|
-
# Returns true if deactivate succeeded, or false if self is not active.
|
532
|
-
#
|
533
|
-
# ==== Note
|
534
|
-
#
|
535
|
-
# Deactivation does not necessarily leave $LOAD_PATH in the same condition
|
536
|
-
# as before activation. A pre-existing $LOAD_PATH entry can go missing if
|
537
|
-
# it is also registered as an env load_path (deactivation doesn't know to
|
538
|
-
# leave such paths alone).
|
539
|
-
#
|
540
|
-
# Deactivation, like constant unloading should be done with caution.
|
541
|
-
def deactivate
|
542
|
-
return false unless active?
|
543
|
-
@active = false
|
544
|
-
|
545
|
-
# dectivate nested envs
|
546
|
-
envs.reverse_each do |env|
|
547
|
-
env.deactivate
|
548
|
-
end
|
549
|
-
|
550
|
-
# remove const paths
|
551
|
-
const_paths.each do |path|
|
552
|
-
$LOAD_PATH.delete(path)
|
553
|
-
end if set_const_paths
|
554
|
-
|
555
|
-
# unfreeze envs and const paths
|
556
|
-
@envs = @envs.dup
|
557
|
-
@const_paths = @const_paths.dup
|
558
|
-
|
559
|
-
true
|
560
|
-
end
|
67
|
+
signal :unregister # remove a resource path
|
68
|
+
signal :unloadpath # remove a load path
|
69
|
+
signal :unset # remove a constant
|
561
70
|
|
562
|
-
|
563
|
-
|
564
|
-
@active
|
565
|
-
end
|
71
|
+
define_signal :load, Load # load a tapenv file
|
72
|
+
define_signal :help, Help # signals help
|
566
73
|
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
74
|
+
def initialize(options={})
|
75
|
+
@paths = options[:paths] || []
|
76
|
+
@paths.collect! {|path| path.kind_of?(Path) ? path : Path.new(*path) }
|
77
|
+
@constants = options[:constants] || []
|
78
|
+
@constants.collect! {|constant| constant.kind_of?(Constant) ? constant : Constant.new(constant) }
|
572
79
|
end
|
573
80
|
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
results = {}
|
579
|
-
each do |env|
|
580
|
-
root = env.root
|
581
|
-
root.glob(dir, pattern).each do |path|
|
582
|
-
relative_path = root.relative_path(dir, path)
|
583
|
-
results[relative_path] ||= path
|
584
|
-
end
|
81
|
+
def path(type)
|
82
|
+
result = []
|
83
|
+
paths.each do |path|
|
84
|
+
result.concat path[type]
|
585
85
|
end
|
586
|
-
|
86
|
+
result
|
587
87
|
end
|
588
88
|
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
# returns a true value. Returns nil if the block never returns true.
|
593
|
-
def path(dir = :root, *paths)
|
594
|
-
each do |env|
|
595
|
-
path = env.root.path(dir, *paths)
|
596
|
-
return path if !block_given? || yield(path)
|
597
|
-
end
|
598
|
-
nil
|
89
|
+
def match(const_str, type=nil)
|
90
|
+
const_str = const_str.to_s
|
91
|
+
const_str =~ Constant::CONST_REGEXP ? constants_by_const_name($1) : constants_by_path(const_str, type)
|
599
92
|
end
|
600
93
|
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
# By default 'module_path' is 'module.to_s.underscore' but modules can
|
608
|
-
# specify an alternative by providing a module_path method.
|
609
|
-
#
|
610
|
-
# Paths are yielded to the block until the block returns true, at which
|
611
|
-
# point the current the path is returned. If no block is given, the
|
612
|
-
# first path is returned. Returns nil if the block never returns true.
|
613
|
-
def module_path(dir, modules, *paths, &block)
|
614
|
-
paths.compact!
|
615
|
-
while current = modules.shift
|
616
|
-
module_path = if current.respond_to?(:module_path)
|
617
|
-
current.module_path
|
618
|
-
else
|
619
|
-
current.to_s.underscore
|
620
|
-
end
|
621
|
-
|
622
|
-
if path = self.path(dir, module_path, *paths, &block)
|
623
|
-
return path
|
624
|
-
end
|
94
|
+
def resolve(const_str, type=nil)
|
95
|
+
matches = match(const_str, type)
|
96
|
+
case matches.length
|
97
|
+
when 0 then raise "unresolvable constant: #{const_str.inspect}"
|
98
|
+
when 1 then matches.at(0)
|
99
|
+
else raise "multiple matching constants: #{const_str.inspect} (#{matches.join(', ')})"
|
625
100
|
end
|
626
|
-
|
627
|
-
nil
|
628
|
-
end
|
629
|
-
|
630
|
-
# Returns the module_path traversing the inheritance hierarchy for the
|
631
|
-
# class of obj (or obj if obj is a Class). Included modules are not
|
632
|
-
# visited, only the superclasses.
|
633
|
-
def class_path(dir, obj, *paths, &block)
|
634
|
-
klass = obj.kind_of?(Class) ? obj : obj.class
|
635
|
-
superclasses = klass.ancestors - klass.included_modules
|
636
|
-
module_path(dir, superclasses, *paths, &block)
|
637
101
|
end
|
638
102
|
|
639
|
-
|
640
|
-
|
641
|
-
# can be minimappped. Minimapping requires that the resource is either
|
642
|
-
# a path string, or provides a 'path' method that returns a path string.
|
643
|
-
# Alternatively, a Minimap may be returned.
|
644
|
-
#
|
645
|
-
# If a type is specified, then the manifest cache will be linked to the
|
646
|
-
# context cache.
|
647
|
-
def manifest(type=nil, &block) # :yields: env
|
648
|
-
cache = type ? (context.cache[type] ||= {}) : {}
|
649
|
-
Manifest.new(self, block, cache)
|
103
|
+
def constant(const_str, type=nil)
|
104
|
+
const_str.kind_of?(Module) ? const_str : resolve(const_str, type).constantize
|
650
105
|
end
|
651
106
|
|
652
|
-
#
|
653
|
-
#
|
654
|
-
#
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
env.const_paths.each do |load_path|
|
661
|
-
next unless File.directory?(load_path)
|
662
|
-
Constant.scan(load_path, "**/*.rb", constants)
|
663
|
-
end
|
664
|
-
|
665
|
-
constants.keys.sort!.collect! do |key|
|
666
|
-
constants[key]
|
667
|
-
end
|
668
|
-
end
|
107
|
+
# Registers the directory and path mappings as a Path, into paths. The
|
108
|
+
# path is unshifted to paths to provide similar functionality as loadpath.
|
109
|
+
# Returns the new path.
|
110
|
+
def register(dir, map={})
|
111
|
+
new_path = Path.new(dir, map)
|
112
|
+
paths.unshift new_path
|
113
|
+
new_path
|
669
114
|
end
|
670
115
|
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
if invert
|
676
|
-
const_name = key.to_s
|
677
|
-
constants.unseek(true) do |const|
|
678
|
-
const_name == const.const_name
|
679
|
-
end
|
680
|
-
else
|
681
|
-
if constant = constants.seek(key)
|
682
|
-
constant.constantize
|
683
|
-
else
|
684
|
-
nil
|
685
|
-
end
|
116
|
+
def auto(options, log=nil)
|
117
|
+
Env.generate(options).each do |line|
|
118
|
+
sig, *args = Utils.shellsplit(line)
|
119
|
+
signal(sig).call(args)
|
686
120
|
end
|
121
|
+
self
|
687
122
|
end
|
688
123
|
|
689
|
-
|
690
|
-
|
691
|
-
def invert?
|
692
|
-
@invert
|
124
|
+
def activate(name, version)
|
125
|
+
Gem.activate(name, version)
|
693
126
|
end
|
694
127
|
|
695
|
-
|
696
|
-
|
697
|
-
|
128
|
+
def unregister(*dirs)
|
129
|
+
dirs.collect! {|dir| File.expand_path(dir) }
|
130
|
+
paths.delete_if {|path| dirs.include?(path.base) }
|
698
131
|
self
|
699
132
|
end
|
700
133
|
|
701
|
-
#
|
702
|
-
|
703
|
-
|
134
|
+
# Expands and prepends the specified paths to $LOAD_PATH, removing any
|
135
|
+
# duplicates. Returns $LOAD_PATH.
|
136
|
+
def loadpath(*paths)
|
137
|
+
paths.reverse_each do |path|
|
138
|
+
$LOAD_PATH.unshift File.expand_path(path)
|
139
|
+
end
|
140
|
+
|
141
|
+
$LOAD_PATH.uniq!
|
142
|
+
$LOAD_PATH
|
704
143
|
end
|
705
144
|
|
706
|
-
#
|
707
|
-
#
|
708
|
-
def
|
709
|
-
|
710
|
-
|
711
|
-
entries.each {|const| new_entries[const.const_name] = const }
|
712
|
-
|
713
|
-
Constant.scan(root[dir], pattern, new_entries)
|
714
|
-
|
715
|
-
entries.replace(new_entries.keys)
|
716
|
-
entries.sort!.collect! {|key| new_entries[key] }
|
717
|
-
entries
|
145
|
+
# Expands and removes the specified paths from $LOAD_PATH. Returns
|
146
|
+
# $LOAD_PATH.
|
147
|
+
def unloadpath(*paths)
|
148
|
+
paths.each {|path| $LOAD_PATH.delete File.expand_path(path) }
|
149
|
+
$LOAD_PATH
|
718
150
|
end
|
719
151
|
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
152
|
+
def set(const_name, require_path=nil, *types)
|
153
|
+
if const_name =~ INLINE_TYPE
|
154
|
+
const_name = $1
|
155
|
+
types << $2
|
156
|
+
end
|
157
|
+
|
158
|
+
constant = constants.find {|c| c.const_name == const_name }
|
727
159
|
|
728
|
-
|
729
|
-
unless constant = entries.find {|const| const.const_name == const_name}
|
160
|
+
unless constant
|
730
161
|
constant = Constant.new(const_name)
|
731
|
-
|
732
|
-
entries.replace entries.sort_by {|const| const.const_name }
|
162
|
+
constants << constant
|
733
163
|
end
|
734
164
|
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
# When no template is specified, inspect generates a fairly standard
|
739
|
-
# inspection string. When a template is provided, inspect builds a
|
740
|
-
# Templater for each env with the following local variables:
|
741
|
-
#
|
742
|
-
# variable value
|
743
|
-
# env the current env
|
744
|
-
# env_keys a minihash for all envs
|
745
|
-
#
|
746
|
-
# If a block is given, the globals and templater are yielded before
|
747
|
-
# any templater is built; this allows each env to add env-specific
|
748
|
-
# variables. After this preparation, each templater is built with
|
749
|
-
# the globals and the results concatenated.
|
750
|
-
#
|
751
|
-
# The template is built with filename, if specified (for debugging).
|
752
|
-
def inspect(template=nil, globals={}, filename=nil) # :yields: templater, globals
|
753
|
-
if template == nil
|
754
|
-
return "#<#{self.class}:#{object_id} root='#{root.root}'>"
|
165
|
+
require_paths = require_path ? Path.split(require_path, nil) : []
|
166
|
+
if require_paths.empty? && const_name.kind_of?(String)
|
167
|
+
require_paths << const_name.underscore
|
755
168
|
end
|
756
169
|
|
757
|
-
|
758
|
-
|
759
|
-
templater = Templater.new(template, :env => env, :env_key => env_keys[env])
|
760
|
-
yield(templater, globals) if block_given?
|
761
|
-
templater
|
762
|
-
end.collect! do |templater|
|
763
|
-
templater.build(globals, filename)
|
764
|
-
end.join
|
765
|
-
end
|
766
|
-
|
767
|
-
protected
|
768
|
-
|
769
|
-
# helper for Minimap; note that specifying env.root.root via path
|
770
|
-
# is not possible because path is required for other purposes.
|
771
|
-
def entry_to_path(env) # :nodoc:
|
772
|
-
env.root.root
|
773
|
-
end
|
774
|
-
|
775
|
-
# resets envs using the current env_paths and gems. does nothing
|
776
|
-
# until both env_paths and gems are set.
|
777
|
-
def reset_envs # :nodoc:
|
778
|
-
if env_paths && gems
|
779
|
-
self.envs = env_paths.collect do |path|
|
780
|
-
context.instance(path) || Env.new(path, context)
|
781
|
-
end + gems.collect do |spec|
|
782
|
-
context.instance(spec.full_gem_path) || Env.setup_gem(spec, context)
|
783
|
-
end
|
784
|
-
end
|
785
|
-
end
|
786
|
-
|
787
|
-
# arrayifies, compacts, and resolves input paths using root.
|
788
|
-
# also removes duplicates. in short:
|
789
|
-
#
|
790
|
-
# resolve_paths ['lib', nil, 'lib', 'alt] # => [root['lib'], root['alt']]
|
791
|
-
#
|
792
|
-
def resolve_paths(paths) # :nodoc:
|
793
|
-
paths = yaml_load(paths) if paths.kind_of?(String)
|
794
|
-
[*paths].compact.collect {|path| root[path] }.uniq
|
795
|
-
end
|
796
|
-
|
797
|
-
# helper to recursively iterate through envs, starting with self.
|
798
|
-
# visited envs are collected in order and are used to ensure a
|
799
|
-
# given env will only be visited once.
|
800
|
-
def visit_envs(visited=[], &block) # :nodoc:
|
801
|
-
unless visited.include?(self)
|
802
|
-
visited << self
|
803
|
-
yield(self) if block_given?
|
170
|
+
constant.require_paths.concat(require_paths).uniq!
|
171
|
+
types.each {|type| constant.register_as(*Path.split(type, nil)) }
|
804
172
|
|
805
|
-
|
806
|
-
env.visit_envs(visited, &block)
|
807
|
-
end
|
808
|
-
end
|
809
|
-
|
810
|
-
visited
|
173
|
+
constant
|
811
174
|
end
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
next_memo = yield(memo, self)
|
818
|
-
envs.each do |env|
|
819
|
-
env.inject_envs(next_memo, visited, &block)
|
175
|
+
|
176
|
+
def unset(*const_names)
|
177
|
+
const_names.each do |const_name|
|
178
|
+
constants.delete_if do |constant|
|
179
|
+
constant.const_name == const_name
|
820
180
|
end
|
821
181
|
end
|
822
|
-
|
823
|
-
visited
|
182
|
+
self
|
824
183
|
end
|
825
184
|
|
826
185
|
private
|
827
186
|
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
187
|
+
def constants_by_const_name(const_str) # :nodoc:
|
188
|
+
constants.select do |constant|
|
189
|
+
constant.const_name == const_str
|
190
|
+
end
|
832
191
|
end
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
192
|
+
|
193
|
+
def constants_by_path(const_str, type) # :nodoc:
|
194
|
+
const_str, inline_type = const_str.split('::', 2)
|
195
|
+
type = inline_type if inline_type
|
837
196
|
|
838
|
-
|
839
|
-
|
840
|
-
@env_path = env_path
|
841
|
-
super()
|
842
|
-
end
|
197
|
+
head, tail = const_str.split(':', 2)
|
198
|
+
head, tail = nil, head unless tail
|
843
199
|
|
844
|
-
|
845
|
-
|
846
|
-
($DEBUG ? "#{original_error.backtrace}\n" : "") +
|
847
|
-
"Check '#{env_path}' configurations"
|
200
|
+
constants.select do |constant|
|
201
|
+
constant.type_match?(type) && constant.path_match?(head, tail)
|
848
202
|
end
|
849
203
|
end
|
850
204
|
end
|