tap 0.12.4 → 0.17.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 +34 -0
- data/README +62 -41
- data/bin/tap +36 -40
- data/cmd/console.rb +14 -6
- data/cmd/manifest.rb +62 -58
- data/cmd/run.rb +49 -31
- data/doc/API +84 -0
- data/doc/Class Reference +83 -115
- data/doc/Examples/Command Line +36 -0
- data/doc/Examples/Workflow +40 -0
- data/lib/tap/app.rb +293 -214
- data/lib/tap/app/node.rb +43 -0
- data/lib/tap/app/queue.rb +77 -0
- data/lib/tap/app/stack.rb +16 -0
- data/lib/tap/app/state.rb +22 -0
- data/lib/tap/constants.rb +2 -2
- data/lib/tap/env.rb +400 -314
- data/lib/tap/env/constant.rb +227 -0
- data/lib/tap/env/gems.rb +63 -0
- data/lib/tap/env/manifest.rb +89 -0
- data/lib/tap/env/minimap.rb +292 -0
- data/lib/tap/{support → env}/string_ext.rb +2 -2
- data/lib/tap/exe.rb +113 -125
- data/lib/tap/join.rb +175 -0
- data/lib/tap/joins.rb +9 -0
- data/lib/tap/joins/switch.rb +44 -0
- data/lib/tap/joins/sync.rb +99 -0
- data/lib/tap/root.rb +100 -491
- data/lib/tap/root/utils.rb +220 -0
- data/lib/tap/{support → root}/versions.rb +31 -29
- data/lib/tap/schema.rb +248 -0
- data/lib/tap/schema/parser.rb +413 -0
- data/lib/tap/schema/utils.rb +82 -0
- data/lib/tap/support/intern.rb +19 -6
- data/lib/tap/support/templater.rb +8 -3
- data/lib/tap/task.rb +175 -171
- data/lib/tap/tasks/dump.rb +58 -0
- data/lib/tap/tasks/load.rb +62 -0
- metadata +30 -73
- data/cmd/destroy.rb +0 -27
- data/cmd/generate.rb +0 -27
- data/doc/Command Reference +0 -105
- data/doc/Syntax Reference +0 -234
- data/doc/Tutorial +0 -348
- data/lib/tap/dump.rb +0 -142
- data/lib/tap/file_task.rb +0 -384
- data/lib/tap/generator/arguments.rb +0 -13
- data/lib/tap/generator/base.rb +0 -176
- data/lib/tap/generator/destroy.rb +0 -60
- data/lib/tap/generator/generate.rb +0 -93
- data/lib/tap/generator/generators/command/command_generator.rb +0 -21
- data/lib/tap/generator/generators/command/templates/command.erb +0 -32
- data/lib/tap/generator/generators/config/config_generator.rb +0 -98
- data/lib/tap/generator/generators/generator/generator_generator.rb +0 -37
- data/lib/tap/generator/generators/generator/templates/task.erb +0 -27
- data/lib/tap/generator/generators/generator/templates/test.erb +0 -26
- data/lib/tap/generator/generators/root/root_generator.rb +0 -84
- data/lib/tap/generator/generators/root/templates/MIT-LICENSE +0 -22
- data/lib/tap/generator/generators/root/templates/README +0 -14
- data/lib/tap/generator/generators/root/templates/Rakefile +0 -84
- data/lib/tap/generator/generators/root/templates/Rapfile +0 -11
- data/lib/tap/generator/generators/root/templates/gemspec +0 -27
- data/lib/tap/generator/generators/root/templates/test/tap_test_helper.rb +0 -3
- data/lib/tap/generator/generators/task/task_generator.rb +0 -25
- data/lib/tap/generator/generators/task/templates/task.erb +0 -14
- data/lib/tap/generator/generators/task/templates/test.erb +0 -19
- data/lib/tap/generator/manifest.rb +0 -20
- data/lib/tap/generator/preview.rb +0 -69
- data/lib/tap/load.rb +0 -64
- data/lib/tap/spec.rb +0 -41
- data/lib/tap/support/aggregator.rb +0 -65
- data/lib/tap/support/audit.rb +0 -333
- data/lib/tap/support/constant.rb +0 -143
- data/lib/tap/support/constant_manifest.rb +0 -126
- data/lib/tap/support/dependencies.rb +0 -54
- data/lib/tap/support/dependency.rb +0 -44
- data/lib/tap/support/executable.rb +0 -198
- data/lib/tap/support/executable_queue.rb +0 -125
- data/lib/tap/support/gems.rb +0 -43
- data/lib/tap/support/join.rb +0 -144
- data/lib/tap/support/joins.rb +0 -12
- data/lib/tap/support/joins/switch.rb +0 -27
- data/lib/tap/support/joins/sync_merge.rb +0 -38
- data/lib/tap/support/manifest.rb +0 -171
- data/lib/tap/support/minimap.rb +0 -90
- data/lib/tap/support/node.rb +0 -176
- data/lib/tap/support/parser.rb +0 -450
- data/lib/tap/support/schema.rb +0 -385
- data/lib/tap/support/shell_utils.rb +0 -67
- data/lib/tap/test.rb +0 -77
- data/lib/tap/test/assertions.rb +0 -38
- data/lib/tap/test/env_vars.rb +0 -29
- data/lib/tap/test/extensions.rb +0 -73
- data/lib/tap/test/file_test.rb +0 -362
- data/lib/tap/test/file_test_class.rb +0 -15
- data/lib/tap/test/regexp_escape.rb +0 -87
- data/lib/tap/test/script_test.rb +0 -46
- data/lib/tap/test/script_tester.rb +0 -115
- data/lib/tap/test/subset_test.rb +0 -260
- data/lib/tap/test/subset_test_class.rb +0 -99
- data/lib/tap/test/tap_test.rb +0 -109
- data/lib/tap/test/utils.rb +0 -231
data/lib/tap/app/node.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
module Tap
|
2
|
+
class App
|
3
|
+
|
4
|
+
# Node adds the node API[link:files/doc/API.html] to objects responding
|
5
|
+
# to call. Additional helper methods are added to simplify the
|
6
|
+
# construction of workflows; they are not required by the API.
|
7
|
+
module Node
|
8
|
+
|
9
|
+
# The joins called when call completes
|
10
|
+
attr_accessor :joins
|
11
|
+
|
12
|
+
# An array of node dependencies
|
13
|
+
attr_reader :dependencies
|
14
|
+
|
15
|
+
# Interns a new node by extending the block with Node.
|
16
|
+
def self.intern(&block)
|
17
|
+
block.extend self
|
18
|
+
end
|
19
|
+
|
20
|
+
# Sets up required variables for extended objects.
|
21
|
+
def self.extended(obj) # :nodoc:
|
22
|
+
obj.instance_variable_set(:@joins, [])
|
23
|
+
obj.instance_variable_set(:@dependencies, [])
|
24
|
+
end
|
25
|
+
|
26
|
+
# Sets the block as a join for self.
|
27
|
+
def on_complete(&block) # :yields: result
|
28
|
+
self.joins << block if block
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
# Adds the dependency to self. Dependencies are resolved by an app
|
33
|
+
# during App#dispatch and must be valid nodes.
|
34
|
+
def depends_on(dependency)
|
35
|
+
raise "cannot depend on self" if dependency == self
|
36
|
+
unless dependencies.include?(dependency)
|
37
|
+
dependencies << dependency
|
38
|
+
end
|
39
|
+
self
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'monitor'
|
2
|
+
|
3
|
+
module Tap
|
4
|
+
class App
|
5
|
+
|
6
|
+
# Queue allows thread-safe enqueing and dequeing of nodes and inputs for
|
7
|
+
# execution.
|
8
|
+
class Queue < Monitor
|
9
|
+
|
10
|
+
attr_reader :queue
|
11
|
+
|
12
|
+
# Creates a new Queue
|
13
|
+
def initialize
|
14
|
+
super
|
15
|
+
@queue = []
|
16
|
+
end
|
17
|
+
|
18
|
+
# Clears self and returns an array of the enqueued methods and inputs,
|
19
|
+
# organized by round.
|
20
|
+
def clear
|
21
|
+
synchronize do
|
22
|
+
current = queue
|
23
|
+
@queue = []
|
24
|
+
current
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns the number of enqueued methods
|
29
|
+
def size
|
30
|
+
synchronize do
|
31
|
+
queue.size
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# True if no methods are enqueued
|
36
|
+
def empty?
|
37
|
+
synchronize { size == 0 }
|
38
|
+
end
|
39
|
+
|
40
|
+
# Enqueues the method and inputs.
|
41
|
+
def enq(method, inputs)
|
42
|
+
synchronize do
|
43
|
+
queue.push [method, inputs]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Enqueues the method and inputs, but to the top of the queue.
|
48
|
+
def unshift(method, inputs)
|
49
|
+
synchronize do
|
50
|
+
queue.unshift [method, inputs]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Concats an array of [method, input] entries to self.
|
55
|
+
def concat(entries)
|
56
|
+
synchronize do
|
57
|
+
entries.each do |method, inputs|
|
58
|
+
enq(method, inputs)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Dequeues the next method and inputs as an array like
|
64
|
+
# [method, inputs]. Returns nil if the queue is empty.
|
65
|
+
def deq
|
66
|
+
synchronize { queue.shift }
|
67
|
+
end
|
68
|
+
|
69
|
+
# Converts self to an array.
|
70
|
+
def to_a
|
71
|
+
synchronize do
|
72
|
+
queue.dup
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Tap
|
2
|
+
class App
|
3
|
+
# The constants defining the possible App states.
|
4
|
+
module State
|
5
|
+
READY = 0
|
6
|
+
RUN = 1
|
7
|
+
STOP = 2
|
8
|
+
TERMINATE = 3
|
9
|
+
|
10
|
+
module_function
|
11
|
+
|
12
|
+
# Returns a string corresponding to the input state value.
|
13
|
+
# Returns nil for unknown states.
|
14
|
+
#
|
15
|
+
# State.state_str(0) # => 'READY'
|
16
|
+
# State.state_str(12) # => nil
|
17
|
+
def state_str(state)
|
18
|
+
constants.inject(nil) {|str, s| const_get(s) == state ? s.to_s : str}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/tap/constants.rb
CHANGED
data/lib/tap/env.rb
CHANGED
@@ -1,257 +1,246 @@
|
|
1
|
-
require 'tap/
|
1
|
+
require 'tap/root'
|
2
|
+
require 'tap/env/manifest'
|
3
|
+
require 'tap/support/templater'
|
2
4
|
autoload(:YAML, 'yaml')
|
3
5
|
|
4
6
|
module Tap
|
5
|
-
|
6
|
-
autoload(:Templater, 'tap/support/templater')
|
7
|
-
autoload(:Gems, 'tap/support/gems')
|
8
|
-
end
|
9
|
-
|
10
|
-
# Envs are locations on the filesystem that have resources associated with
|
11
|
-
# them (commands, tasks, generators, etc). Envs may point to files, but it's
|
12
|
-
# more commonly environments are set to a directory and resources are various
|
13
|
-
# files within the directory.
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#--
|
17
|
-
# Note that gems and env_paths reset envs -- custom modifications to envs will be lost
|
18
|
-
# whenever these configs are reset.
|
7
|
+
# Env abstracts an execution environment that spans many directories.
|
19
8
|
class Env
|
20
|
-
|
21
|
-
|
22
|
-
include Support::Minimap
|
23
|
-
|
9
|
+
autoload(:Gems, 'tap/env/gems')
|
10
|
+
|
24
11
|
class << self
|
12
|
+
attr_writer :instance
|
25
13
|
|
26
|
-
|
27
|
-
|
28
|
-
@@instance
|
29
|
-
end
|
30
|
-
|
31
|
-
# A hash of (path, Env instance) pairs, generated by Env#instantiate. Used
|
32
|
-
# to prevent infinite loops of Env dependencies by assigning a single Env
|
33
|
-
# to a given path.
|
34
|
-
def instances
|
35
|
-
@@instances
|
14
|
+
def instance(auto_initialize=true)
|
15
|
+
@instance ||= (auto_initialize ? new : nil)
|
36
16
|
end
|
37
17
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
# path as the DEFAULT_CONFIG_FILE in that directory. All paths are expanded.
|
42
|
-
#
|
43
|
-
# e1 = Env.instantiate("./path/to/config.yml")
|
44
|
-
# e2 = Env.instantiate("./path/to/dir")
|
45
|
-
#
|
46
|
-
# Env.instances
|
47
|
-
# # => {
|
48
|
-
# # File.expand_path("./path/to/config.yml") => e1,
|
49
|
-
# # File.expand_path("./path/to/dir/#{Tap::Env::DEFAULT_CONFIG_FILE}") => e2 }
|
50
|
-
#
|
51
|
-
# The Env is initialized using configurations read from the env config
|
52
|
-
# file. An instance will be initialized regardless of whether the config
|
53
|
-
# file or directory exists.
|
54
|
-
def instantiate(path_or_root)
|
55
|
-
path = config_path(path_or_root.kind_of?(Root) ? path_or_root.root : path_or_root)
|
56
|
-
return instances[path] if instances.has_key?(path)
|
18
|
+
def from_gemspec(spec, context={})
|
19
|
+
path = spec.full_gem_path
|
20
|
+
basename = context[:basename]
|
57
21
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
22
|
+
dependencies = []
|
23
|
+
spec.dependencies.each do |dependency|
|
24
|
+
unless dependency.type == :runtime
|
25
|
+
next
|
26
|
+
end
|
27
|
+
|
28
|
+
unless gemspec = Gems.gemspec(dependency)
|
29
|
+
# this error may result when a dependency has
|
30
|
+
# been uninstalled for a particular gem
|
31
|
+
warn "missing gem dependency: #{dependency.to_s} (#{spec.full_name})"
|
32
|
+
next
|
33
|
+
end
|
34
|
+
|
35
|
+
if basename && !File.exists?(File.join(gemspec.full_gem_path, basename))
|
36
|
+
next
|
37
|
+
end
|
38
|
+
|
39
|
+
dependencies << gemspec
|
70
40
|
end
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
41
|
+
|
42
|
+
config = {
|
43
|
+
'root' => path,
|
44
|
+
'gems' => dependencies,
|
45
|
+
'load_paths' => spec.require_paths,
|
46
|
+
'set_load_paths' => false
|
47
|
+
}
|
48
|
+
|
49
|
+
if context[:basename]
|
50
|
+
config.merge!(Env.load_config(File.join(path, context[:basename])))
|
78
51
|
end
|
79
52
|
|
80
|
-
|
53
|
+
new(config, context)
|
81
54
|
end
|
82
55
|
|
83
|
-
#
|
84
|
-
# loads to nil or false (as happens for empty files)
|
85
|
-
def load_config(path)
|
56
|
+
# Loads configurations from path as YAML. Returns an empty hash if the path
|
57
|
+
# loads to nil or false (as happens for empty files), or doesn't exist.
|
58
|
+
def load_config(path)
|
59
|
+
return {} unless path
|
60
|
+
|
86
61
|
begin
|
87
|
-
Root.trivial?(path) ? {} : (YAML.load_file(path) || {})
|
62
|
+
Root::Utils.trivial?(path) ? {} : (YAML.load_file(path) || {})
|
88
63
|
rescue(Exception)
|
89
|
-
raise
|
64
|
+
raise ConfigError.new($!, path)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def scan(load_path, pattern='**/*.rb')
|
69
|
+
Dir.chdir(load_path) do
|
70
|
+
Dir.glob(pattern).each do |require_path|
|
71
|
+
next unless File.file?(require_path)
|
72
|
+
|
73
|
+
default_const_name = require_path.chomp('.rb').camelize
|
74
|
+
|
75
|
+
# note: the default const name has to be set here to allow for implicit
|
76
|
+
# constant attributes (because a dir is needed to figure the relative path).
|
77
|
+
# A conflict could arise if the same path is globed from two different
|
78
|
+
# dirs... no surefire solution.
|
79
|
+
document = Lazydoc[require_path]
|
80
|
+
case document.default_const_name
|
81
|
+
when nil then document.default_const_name = default_const_name
|
82
|
+
when default_const_name
|
83
|
+
else raise "found a conflicting default const name"
|
84
|
+
end
|
85
|
+
|
86
|
+
# scan for constants
|
87
|
+
Lazydoc::Document.scan(File.read(require_path)) do |const_name, type, comment|
|
88
|
+
const_name = default_const_name if const_name.empty?
|
89
|
+
constant = Constant.new(const_name, require_path, comment)
|
90
|
+
yield(type, constant)
|
91
|
+
|
92
|
+
###############################################################
|
93
|
+
# [depreciated] manifest as a task key will be removed at 1.0
|
94
|
+
if type == 'manifest'
|
95
|
+
warn "depreciation: ::task should be used instead of ::manifest as a resource key (#{require_path})"
|
96
|
+
yield('task', constant)
|
97
|
+
end
|
98
|
+
###############################################################
|
99
|
+
end
|
100
|
+
end
|
90
101
|
end
|
91
102
|
end
|
92
103
|
end
|
104
|
+
self.instance = nil
|
93
105
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
# The default config file path
|
98
|
-
DEFAULT_CONFIG_FILE = "tap.yml"
|
106
|
+
include Enumerable
|
107
|
+
include Configurable
|
108
|
+
include Minimap
|
99
109
|
|
110
|
+
# Matches a compound registry search key. After the match, if the key is
|
111
|
+
# compound then:
|
112
|
+
#
|
113
|
+
# $1:: env_key
|
114
|
+
# $2:: key
|
115
|
+
#
|
116
|
+
# If the key is not compound, $2 is nil and $1 is the key.
|
117
|
+
COMPOUND_KEY = /^((?:[A-z]:(?:\/|\\))?.*?)(?::(.*))?$/
|
118
|
+
|
100
119
|
# An array of nested Envs, by default comprised of the env_path
|
101
|
-
# + gem environments (in that order).
|
102
|
-
# activated/deactivated with self.
|
120
|
+
# + gem environments (in that order).
|
103
121
|
attr_reader :envs
|
104
122
|
|
105
|
-
|
106
|
-
|
123
|
+
attr_reader :context
|
124
|
+
|
125
|
+
attr_reader :manifests
|
107
126
|
|
108
|
-
#
|
127
|
+
# The Root directory structure for self.
|
128
|
+
nest(:root, Root, :set_default => false)
|
129
|
+
|
130
|
+
# Specify gems to add as nested Envs. Gems may be specified
|
109
131
|
# by name and/or version, like 'gemname >= 1.2'; by default the
|
110
|
-
# latest version of the gem is selected.
|
111
|
-
#
|
112
|
-
# Gems are immediately loaded (via gem) through this method.
|
132
|
+
# latest version of the gem is selected. Gems are not activated
|
133
|
+
# by Env.
|
113
134
|
config_attr :gems, [] do |input|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
135
|
+
input = yaml_load(input) if input.kind_of?(String)
|
136
|
+
|
137
|
+
@gems = case input
|
138
|
+
when false, nil, :NONE, :none
|
139
|
+
[]
|
140
|
+
when :LATEST, :ALL
|
141
|
+
# latest and all, no filter
|
142
|
+
Gems.select_gems(input == :LATEST)
|
118
143
|
when :latest, :all
|
119
|
-
|
120
|
-
|
121
|
-
File.exists?(
|
122
|
-
end
|
123
|
-
else input
|
124
|
-
end
|
125
|
-
|
126
|
-
@gems = [*input].compact.collect do |gem_name|
|
127
|
-
spec = Support::Gems.gemspec(gem_name)
|
128
|
-
|
129
|
-
case spec
|
130
|
-
when nil then log(:warn, "unknown gem: #{gem_name}", Logger::WARN)
|
131
|
-
else Env.instantiate(spec.full_gem_path)
|
144
|
+
# latest and all, filtering by basename
|
145
|
+
Gems.select_gems(input == :latest) do |spec|
|
146
|
+
basename == nil || File.exists?(File.join(spec.full_gem_path, basename))
|
132
147
|
end
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
# this song and dance is to ensure that the latest spec for a
|
139
|
-
# given gem appears first in the manifest
|
140
|
-
specs_by_name.each_pair do |name, specs|
|
141
|
-
specs_by_name[name] = specs.uniq.sort_by {|spec| spec.version }.reverse
|
148
|
+
else
|
149
|
+
# resolve gem names manually
|
150
|
+
[*input].collect do |name|
|
151
|
+
Gems.gemspec(name)
|
152
|
+
end.compact
|
142
153
|
end
|
143
|
-
|
144
|
-
@gems.collect! do |name|
|
145
|
-
specs_by_name[name]
|
146
|
-
end.flatten!
|
147
|
-
|
154
|
+
|
148
155
|
reset_envs
|
149
156
|
end
|
150
157
|
|
151
|
-
# Specify
|
158
|
+
# Specify directories to load as nested Envs.
|
152
159
|
config_attr :env_paths, [] do |input|
|
153
|
-
|
154
|
-
@env_paths = [*input].compact.collect do |path|
|
155
|
-
Env.instantiate(root[path]).env_path
|
156
|
-
end.uniq
|
160
|
+
@env_paths = resolve_paths(input)
|
157
161
|
reset_envs
|
158
162
|
end
|
159
163
|
|
160
|
-
#
|
161
|
-
|
164
|
+
# Designates paths added to $LOAD_PATH on activation (see set_load_paths).
|
165
|
+
# These paths are also the default directories searched for resources.
|
166
|
+
config_attr :load_paths, [:lib] do |input|
|
162
167
|
raise "load_paths cannot be modified once active" if active?
|
163
|
-
@load_paths = resolve_paths(
|
164
|
-
end
|
165
|
-
|
166
|
-
# Designate paths for discovering and executing commands.
|
167
|
-
config_attr :command_paths, ["cmd"] do |paths|
|
168
|
-
@command_paths = resolve_paths(paths)
|
169
|
-
end
|
170
|
-
|
171
|
-
# Designate paths for discovering generators.
|
172
|
-
config_attr :generator_paths, ["lib"] do |paths|
|
173
|
-
@generator_paths = resolve_paths(paths)
|
174
|
-
end
|
175
|
-
|
176
|
-
manifest(:commands) do |env|
|
177
|
-
paths = []
|
178
|
-
env.command_paths.each do |path_root|
|
179
|
-
paths.concat env.root.glob(path_root)
|
180
|
-
end
|
181
|
-
|
182
|
-
paths = paths.sort_by {|path| File.basename(path) }
|
183
|
-
Support::Manifest.new(paths)
|
168
|
+
@load_paths = resolve_paths(input)
|
184
169
|
end
|
185
170
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
end
|
191
|
-
# tasks.cache = env.cache[:tasks]
|
192
|
-
tasks
|
193
|
-
end
|
194
|
-
|
195
|
-
manifest(:generators) do |env|
|
196
|
-
generators = Support::ConstantManifest.intern('generator') do |manifest, const|
|
197
|
-
const.name.underscore.chomp('_generator')
|
198
|
-
end
|
199
|
-
|
200
|
-
env.generator_paths.each do |path_root|
|
201
|
-
generators.register(path_root, '**/*_generator.rb')
|
202
|
-
end
|
203
|
-
# generators.cache = env.cache[:generators]
|
204
|
-
generators
|
171
|
+
# If set to true load_paths are added to $LOAD_PATH on activation.
|
172
|
+
config_attr :set_load_paths, true do |input|
|
173
|
+
raise "set_load_paths cannot be modified once active" if active?
|
174
|
+
@set_load_paths = Configurable::Validation.boolean[input]
|
205
175
|
end
|
206
176
|
|
207
|
-
|
208
|
-
|
177
|
+
# Initializes a new Env linked to the specified directory. A config file
|
178
|
+
# basename may be specified to load configurations from 'dir/basename' as
|
179
|
+
# YAML. If a basename is specified, the same basename will be used to
|
180
|
+
# load configurations for nested envs.
|
181
|
+
#
|
182
|
+
# Configurations may be manually provided in the place of dir. In that
|
183
|
+
# case, the same rules apply for loading configurations for nested envs,
|
184
|
+
# but no configurations will be loaded for the current instance.
|
185
|
+
#
|
186
|
+
# The cache is used internally to prevent infinite loops of nested envs,
|
187
|
+
# and to optimize the generation of manifests.
|
188
|
+
def initialize(config_or_dir=Dir.pwd, context={})
|
209
189
|
@active = false
|
210
190
|
@manifests = {}
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
191
|
+
@context = context
|
192
|
+
|
193
|
+
# setup root
|
194
|
+
config = nil
|
195
|
+
@root = case config_or_dir
|
196
|
+
when Root then config_or_dir
|
197
|
+
when String then Root.new(config_or_dir)
|
198
|
+
else
|
199
|
+
config = config_or_dir
|
200
|
+
|
201
|
+
if config.has_key?(:root) && config.has_key?('root')
|
202
|
+
raise "multiple values mapped to :root"
|
203
|
+
end
|
204
|
+
|
205
|
+
root = config.delete(:root) || config.delete('root') || Dir.pwd
|
206
|
+
root.kind_of?(Root) ? root : Root.new(root)
|
207
|
+
end
|
215
208
|
|
216
|
-
|
217
|
-
|
218
|
-
when String then Root.new(path_root_or_config)
|
219
|
-
else Root.new
|
209
|
+
if basename && !config
|
210
|
+
config = Env.load_config(File.join(@root.root, basename))
|
220
211
|
end
|
221
212
|
|
222
|
-
|
223
|
-
|
213
|
+
if instance(@root.root)
|
214
|
+
raise "context already has an env for: #{@root.root}"
|
224
215
|
end
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
216
|
+
instances << self
|
217
|
+
|
218
|
+
# set these for reset_env
|
219
|
+
@gems = nil
|
220
|
+
@env_paths = nil
|
221
|
+
initialize_config(config || {})
|
231
222
|
end
|
232
223
|
|
233
|
-
#
|
234
|
-
def
|
235
|
-
|
236
|
-
nil
|
224
|
+
# The minikey for self (root.root).
|
225
|
+
def minikey
|
226
|
+
root.root
|
237
227
|
end
|
238
228
|
|
239
229
|
# Sets envs removing duplicates and instances of self. Setting envs
|
240
230
|
# overrides any environments specified by env_path and gem.
|
241
231
|
def envs=(envs)
|
242
232
|
raise "envs cannot be modified once active" if active?
|
243
|
-
@envs = envs.uniq.delete_if {|
|
233
|
+
@envs = envs.uniq.delete_if {|env| env == self }
|
244
234
|
end
|
245
|
-
|
246
|
-
# Unshifts env onto envs
|
247
|
-
# Self cannot be unshifted onto self.
|
235
|
+
|
236
|
+
# Unshifts env onto envs. Self cannot be unshifted onto self.
|
248
237
|
def unshift(env)
|
249
238
|
unless env == self || envs[0] == env
|
250
239
|
self.envs = envs.dup.unshift(env)
|
251
240
|
end
|
252
241
|
self
|
253
242
|
end
|
254
|
-
|
243
|
+
|
255
244
|
# Pushes env onto envs, removing duplicates.
|
256
245
|
# Self cannot be pushed onto self.
|
257
246
|
def push(env)
|
@@ -261,21 +250,23 @@ module Tap
|
|
261
250
|
end
|
262
251
|
self
|
263
252
|
end
|
264
|
-
|
253
|
+
|
265
254
|
# Passes each nested env to the block in order, starting with self.
|
266
255
|
def each
|
267
256
|
visit_envs.each {|e| yield(e) }
|
268
257
|
end
|
269
|
-
|
258
|
+
|
270
259
|
# Passes each nested env to the block in reverse order, ending with self.
|
271
260
|
def reverse_each
|
272
261
|
visit_envs.reverse_each {|e| yield(e) }
|
273
262
|
end
|
274
263
|
|
275
264
|
# Recursively injects the memo to each env of self. Each env in envs
|
276
|
-
# receives the same memo from the parent.
|
265
|
+
# receives the same memo from the parent. This is different from the
|
266
|
+
# inject provided via Enumerable, where each subsequent env receives
|
267
|
+
# the memo from the previous, not the parent, env.
|
277
268
|
#
|
278
|
-
# a,b,c,d,e = ('a'..'e').collect {|name|
|
269
|
+
# a,b,c,d,e = ('a'..'e').collect {|name| Env.new(:name => name) }
|
279
270
|
#
|
280
271
|
# a.push(b).push(c)
|
281
272
|
# b.push(d).push(e)
|
@@ -302,7 +293,7 @@ module Tap
|
|
302
293
|
#
|
303
294
|
# * sets Env.instance to self (unless already set)
|
304
295
|
# * activate nested environments
|
305
|
-
# * unshift load_paths to $LOAD_PATH
|
296
|
+
# * unshift load_paths to $LOAD_PATH (if set_load_paths is true)
|
306
297
|
#
|
307
298
|
# Once active, the current envs and load_paths are frozen and cannot be
|
308
299
|
# modified until deactivated. Returns true if activate succeeded, or
|
@@ -311,7 +302,9 @@ module Tap
|
|
311
302
|
return false if active?
|
312
303
|
|
313
304
|
@active = true
|
314
|
-
|
305
|
+
unless self.class.instance(false)
|
306
|
+
self.class.instance = self
|
307
|
+
end
|
315
308
|
|
316
309
|
# freeze envs and load paths
|
317
310
|
@envs.freeze
|
@@ -323,11 +316,13 @@ module Tap
|
|
323
316
|
end
|
324
317
|
|
325
318
|
# add load paths
|
326
|
-
|
327
|
-
|
328
|
-
|
319
|
+
if set_load_paths
|
320
|
+
load_paths.reverse_each do |path|
|
321
|
+
$LOAD_PATH.unshift(path)
|
322
|
+
end
|
329
323
|
|
330
|
-
|
324
|
+
$LOAD_PATH.uniq!
|
325
|
+
end
|
331
326
|
|
332
327
|
true
|
333
328
|
end
|
@@ -335,7 +330,7 @@ module Tap
|
|
335
330
|
# Deactivates self by doing the following in order:
|
336
331
|
#
|
337
332
|
# * deactivates nested environments
|
338
|
-
# * removes load_paths from $LOAD_PATH
|
333
|
+
# * removes load_paths from $LOAD_PATH (if set_load_paths is true)
|
339
334
|
# * sets Env.instance to nil (if set to self)
|
340
335
|
# * clears cached manifest data
|
341
336
|
#
|
@@ -353,15 +348,17 @@ module Tap
|
|
353
348
|
# remove load paths
|
354
349
|
load_paths.each do |path|
|
355
350
|
$LOAD_PATH.delete(path)
|
356
|
-
end
|
351
|
+
end if set_load_paths
|
357
352
|
|
358
353
|
# unfreeze envs and load paths
|
359
354
|
@envs = @envs.dup
|
360
355
|
@load_paths = @load_paths.dup
|
361
356
|
|
362
357
|
# clear cached data
|
363
|
-
|
364
|
-
|
358
|
+
klass = self.class
|
359
|
+
if klass.instance(false) == self
|
360
|
+
klass.instance = nil
|
361
|
+
end
|
365
362
|
|
366
363
|
true
|
367
364
|
end
|
@@ -371,157 +368,238 @@ module Tap
|
|
371
368
|
@active
|
372
369
|
end
|
373
370
|
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
371
|
+
def hlob(dir, pattern="**/*")
|
372
|
+
results = {}
|
373
|
+
each do |env|
|
374
|
+
root = env.root
|
375
|
+
root.glob(dir, pattern).each do |path|
|
376
|
+
relative_path = root.relative_path(dir, path)
|
377
|
+
results[relative_path] ||= path
|
378
|
+
end
|
379
|
+
end
|
380
|
+
results
|
381
|
+
end
|
382
|
+
|
383
|
+
def glob(dir, pattern="**/*")
|
384
|
+
hlob(dir, pattern).values.sort!
|
385
|
+
end
|
386
|
+
|
387
|
+
def path(dir, *paths)
|
382
388
|
each do |env|
|
383
|
-
|
384
|
-
|
385
|
-
|
389
|
+
path = env.root.path(dir, *paths)
|
390
|
+
return path if !block_given? || yield(path)
|
391
|
+
end
|
392
|
+
nil
|
393
|
+
end
|
394
|
+
|
395
|
+
# Retrieves a path associated with the inheritance hierarchy of an object.
|
396
|
+
# An array of modules (which naturally can include classes) are provided
|
397
|
+
# and module_path traverses each, forming paths like:
|
398
|
+
#
|
399
|
+
# path(dir, module_path, *paths)
|
400
|
+
#
|
401
|
+
# By default, 'module_path' is 'module.to_s.underscore', but modules can
|
402
|
+
# specify an alternative by providing a module_path method.
|
403
|
+
#
|
404
|
+
# The paths are yielded to the block and when the block returns true,
|
405
|
+
# the path will be returned. If no block is given, the first module path
|
406
|
+
# is returned. Returns nil if the block never returns true.
|
407
|
+
#
|
408
|
+
def module_path(dir, modules, *paths, &block)
|
409
|
+
paths.compact!
|
410
|
+
while current = modules.shift
|
411
|
+
module_path = if current.respond_to?(:module_path)
|
412
|
+
current.module_path
|
413
|
+
else
|
414
|
+
current.to_s.underscore
|
415
|
+
end
|
386
416
|
|
387
|
-
|
388
|
-
|
389
|
-
|
417
|
+
if path = self.path(dir, module_path, *paths, &block)
|
418
|
+
return path
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
nil
|
423
|
+
end
|
424
|
+
|
425
|
+
# Returns the module_path traversing the inheritance hierarchy for the
|
426
|
+
# class of obj (or obj if obj is a Class). Included modules are not
|
427
|
+
# visited, only the superclasses.
|
428
|
+
def class_path(dir, obj, *paths, &block)
|
429
|
+
klass = obj.kind_of?(Class) ? obj : obj.class
|
430
|
+
superclasses = klass.ancestors - klass.included_modules
|
431
|
+
module_path(dir, superclasses, *paths, &block)
|
432
|
+
end
|
433
|
+
|
434
|
+
def registry(build=false)
|
435
|
+
builders.each_pair do |type, builder|
|
436
|
+
registry[type] ||= builder.call(self)
|
437
|
+
end if build
|
438
|
+
|
439
|
+
registries[minikey] ||= begin
|
440
|
+
registry = {}
|
441
|
+
load_paths.each do |load_path|
|
442
|
+
next unless File.directory?(load_path)
|
443
|
+
|
444
|
+
Env.scan(load_path) do |type, constant|
|
445
|
+
entries = registry[type.to_sym] ||= []
|
446
|
+
entries << constant
|
447
|
+
end
|
390
448
|
end
|
391
449
|
|
392
|
-
|
393
|
-
|
450
|
+
registry
|
451
|
+
end
|
452
|
+
end
|
453
|
+
|
454
|
+
def register(type, override=false, &block)
|
455
|
+
type = type.to_sym
|
456
|
+
|
457
|
+
# error for existing, or overwrite
|
458
|
+
case
|
459
|
+
when override
|
460
|
+
builders.delete(type)
|
461
|
+
registries.each {|root, registry| registry.delete(type) }
|
462
|
+
when builders.has_key?(type)
|
463
|
+
raise "a builder is already registered for: #{type.inspect}"
|
464
|
+
when registries.any? {|root, registry| registry.has_key?(type) }
|
465
|
+
raise "entries are already registered for: #{type.inspect}"
|
394
466
|
end
|
395
467
|
|
396
|
-
|
468
|
+
builders[type] = block
|
397
469
|
end
|
398
470
|
|
399
|
-
#
|
400
|
-
|
401
|
-
TEMPLATES[:commands] = %Q{<% if count > 1 %>
|
402
|
-
<%= env_name %>:
|
403
|
-
<% end %>
|
404
|
-
<% entries.each do |name, const| %>
|
405
|
-
<%= name.ljust(width) %>
|
406
|
-
<% end %>}
|
407
|
-
TEMPLATES[:tasks] = %Q{<% if count > 1 %>
|
408
|
-
<%= env_name %>:
|
409
|
-
<% end %>
|
410
|
-
<% entries.each do |name, const| %>
|
411
|
-
<% desc = const.document[const.name]['manifest'] %>
|
412
|
-
<%= name.ljust(width) %><%= desc.empty? ? '' : ' # ' %><%= desc %>
|
413
|
-
<% end %>}
|
414
|
-
TEMPLATES[:generators] = %Q{<% if count > 1 %>
|
415
|
-
<%= env_name %>:
|
416
|
-
<% end %>
|
417
|
-
<% entries.each do |name, const| %>
|
418
|
-
<% desc = const.document[const.name]['generator'] %>
|
419
|
-
<%= name.ljust(width) %><%= desc.empty? ? '' : ' # ' %><%= desc %>
|
420
|
-
<% end %>}
|
421
|
-
|
422
|
-
def summarize(name, template=TEMPLATES[name])
|
423
|
-
count = 0
|
424
|
-
width = 10
|
471
|
+
def manifest(type) # :yields: env
|
472
|
+
type = type.to_sym
|
425
473
|
|
426
|
-
|
427
|
-
|
428
|
-
|
474
|
+
registry[type] ||= begin
|
475
|
+
builder = builders[type]
|
476
|
+
builder ? builder.call(self) : []
|
429
477
|
end
|
430
478
|
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
479
|
+
manifests[type] ||= Manifest.new(self, type)
|
480
|
+
end
|
481
|
+
|
482
|
+
def [](type)
|
483
|
+
manifest(type)
|
484
|
+
end
|
485
|
+
|
486
|
+
def reset
|
487
|
+
manifests.clear
|
488
|
+
registries.clear
|
489
|
+
end
|
490
|
+
|
491
|
+
#--
|
492
|
+
# Environment-seek
|
493
|
+
def eeek(type, key)
|
494
|
+
key =~ COMPOUND_KEY
|
495
|
+
envs = if $2
|
496
|
+
# compound key, match for env
|
497
|
+
key = $2
|
498
|
+
[minimatch($1)].compact
|
499
|
+
else
|
500
|
+
# not a compound key, search all envs by iterating self
|
501
|
+
self
|
502
|
+
end
|
503
|
+
|
504
|
+
# traverse envs looking for the first
|
505
|
+
# manifest entry matching key
|
506
|
+
envs.each do |env|
|
507
|
+
if result = env.manifest(type).minimatch(key)
|
508
|
+
return [env, result]
|
442
509
|
end
|
443
|
-
|
444
|
-
share[:count] = count
|
445
|
-
share[:width] = width
|
446
|
-
true
|
447
510
|
end
|
511
|
+
|
512
|
+
nil
|
448
513
|
end
|
449
514
|
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
end.compact.collect do |templater|
|
458
|
-
templater.build(attrs)
|
459
|
-
end.join
|
515
|
+
# Searches across each for the first registered object minimatching key. A
|
516
|
+
# single env can be specified by using a compound key like 'env_key:key'.
|
517
|
+
#
|
518
|
+
# Returns nil if no matching object is found.
|
519
|
+
def seek(type, key, &block) # :yields: env, key
|
520
|
+
env, result = eeek(type, key, &block)
|
521
|
+
result
|
460
522
|
end
|
461
523
|
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
recursive_inject(args) do |argv, env|
|
468
|
-
templater = Support::Templater.new(template, :env => env)
|
469
|
-
next_args = block_given? ? yield(templater, attrs, *argv) : argv
|
470
|
-
templaters << templater if next_args
|
471
|
-
|
472
|
-
next_args
|
524
|
+
# All templaters are yielded to the block before any are built. This
|
525
|
+
# allows globals to be determined for all environments.
|
526
|
+
def inspect(template=nil, globals={}, filename=nil) # :yields: templater, globals
|
527
|
+
if template == nil
|
528
|
+
return "#<#{self.class}:#{object_id} root='#{root.root}'>"
|
473
529
|
end
|
474
530
|
|
475
|
-
|
476
|
-
|
531
|
+
env_keys = minihash(true)
|
532
|
+
collect do |env|
|
533
|
+
templater = Support::Templater.new(template, :env => env, :env_key => env_keys[env])
|
534
|
+
yield(templater, globals) if block_given?
|
535
|
+
templater
|
536
|
+
end.collect! do |templater|
|
537
|
+
templater.build(globals, filename)
|
477
538
|
end.join
|
478
539
|
end
|
479
540
|
|
480
541
|
protected
|
481
542
|
|
482
|
-
|
483
|
-
|
543
|
+
def registries # :nodoc:
|
544
|
+
context[:registries] ||= {}
|
545
|
+
end
|
484
546
|
|
485
|
-
def
|
486
|
-
|
547
|
+
def basename # :nodoc:
|
548
|
+
context[:basename]
|
487
549
|
end
|
488
550
|
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
end
|
551
|
+
def builders # :nodoc:
|
552
|
+
context[:builders] ||= {}
|
553
|
+
end
|
554
|
+
|
555
|
+
def instances # :nodoc:
|
556
|
+
context[:instances] ||= []
|
496
557
|
end
|
497
558
|
|
498
|
-
|
499
|
-
|
559
|
+
def instance(path) # :nodoc:
|
560
|
+
instances.find {|env| env.root.root == path }
|
561
|
+
end
|
562
|
+
|
563
|
+
# resets envs using the current env_paths and gems. does nothing
|
564
|
+
# until both env_paths and gems are set.
|
565
|
+
def reset_envs # :nodoc:
|
566
|
+
if env_paths && gems
|
567
|
+
self.envs = env_paths.collect do |path|
|
568
|
+
instance(path) || Env.new(path, context)
|
569
|
+
end + gems.collect do |spec|
|
570
|
+
instance(spec.full_gem_path) || Env.from_gemspec(spec, context)
|
571
|
+
end
|
572
|
+
end
|
573
|
+
end
|
574
|
+
|
575
|
+
# arrayifies, compacts, and resolves input paths using root, and
|
576
|
+
# removes duplicates. in short:
|
500
577
|
#
|
501
578
|
# resolve_paths ['lib', nil, 'lib', 'alt] # => [root['lib'], root['alt']]
|
502
579
|
#
|
503
580
|
def resolve_paths(paths) # :nodoc:
|
504
|
-
paths =
|
505
|
-
[*paths].compact.collect {|path| root[path]}.uniq
|
581
|
+
paths = yaml_load(paths) if paths.kind_of?(String)
|
582
|
+
[*paths].compact.collect {|path| root[path] }.uniq
|
506
583
|
end
|
507
|
-
|
508
|
-
#
|
509
|
-
#
|
584
|
+
|
585
|
+
# helper to recursively iterate through envs, starting with self.
|
586
|
+
# visited envs are collected in order and are used to ensure a
|
587
|
+
# given env will only be visited once.
|
510
588
|
def visit_envs(visited=[], &block) # :nodoc:
|
511
589
|
unless visited.include?(self)
|
512
590
|
visited << self
|
513
591
|
yield(self) if block_given?
|
514
|
-
|
592
|
+
|
515
593
|
envs.each do |env|
|
516
594
|
env.visit_envs(visited, &block)
|
517
595
|
end
|
518
596
|
end
|
519
|
-
|
597
|
+
|
520
598
|
visited
|
521
599
|
end
|
522
|
-
|
600
|
+
|
523
601
|
# helper to recursively inject a memo to the children of env
|
524
|
-
def inject_envs(memo, visited=[], &block)
|
602
|
+
def inject_envs(memo, visited=[], &block) # :nodoc:
|
525
603
|
unless visited.include?(self)
|
526
604
|
visited << self
|
527
605
|
next_memo = yield(memo, self)
|
@@ -529,12 +607,20 @@ module Tap
|
|
529
607
|
env.inject_envs(next_memo, visited, &block)
|
530
608
|
end
|
531
609
|
end
|
532
|
-
|
610
|
+
|
533
611
|
visited
|
534
612
|
end
|
535
613
|
|
536
|
-
|
537
|
-
|
614
|
+
private
|
615
|
+
|
616
|
+
# A 'quick' yaml load where empty strings will not cause YAML to autoload.
|
617
|
+
# This is a silly song and dance, but provides for optimal launch times.
|
618
|
+
def yaml_load(str) # :nodoc:
|
619
|
+
str.empty? ? false : YAML.load(str)
|
620
|
+
end
|
621
|
+
|
622
|
+
# Raised when there is a configuration error from Env.load_config.
|
623
|
+
class ConfigError < StandardError # :nodoc:
|
538
624
|
attr_reader :original_error, :env_path
|
539
625
|
|
540
626
|
def initialize(original_error, env_path)
|