tap 0.19.0 → 1.3.0
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/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
|