tap 0.12.4 → 0.17.0
Sign up to get free protection for your applications and to get access to all the features.
- 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)
|