tap 0.18.0 → 0.19.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 +21 -0
- data/MIT-LICENSE +17 -15
- data/README +13 -30
- data/bin/tap +19 -24
- data/cmd/console.rb +1 -12
- data/cmd/manifest.rb +14 -19
- data/cmd/run.rb +96 -86
- data/doc/API +194 -54
- data/doc/Examples/Command Line +27 -1
- data/lib/tap.rb +2 -1
- data/lib/tap/app.rb +613 -166
- data/lib/tap/app/api.rb +115 -0
- data/lib/tap/app/queue.rb +36 -37
- data/lib/tap/app/state.rb +2 -1
- data/lib/tap/env.rb +454 -270
- data/lib/tap/env/constant.rb +83 -33
- data/lib/tap/env/context.rb +61 -0
- data/lib/tap/env/manifest.rb +140 -50
- data/lib/tap/env/minimap.rb +55 -39
- data/lib/tap/join.rb +71 -53
- data/lib/tap/joins/sync.rb +3 -1
- data/lib/tap/middleware.rb +4 -25
- data/lib/tap/middlewares/debugger.rb +75 -0
- data/lib/tap/parser.rb +268 -0
- data/lib/tap/prompt.rb +36 -0
- data/lib/tap/root.rb +3 -3
- data/lib/tap/signals.rb +26 -0
- data/lib/tap/signals/class_methods.rb +222 -0
- data/lib/tap/signals/help.rb +40 -0
- data/lib/tap/signals/module_methods.rb +20 -0
- data/lib/tap/signals/signal.rb +68 -0
- data/lib/tap/task.rb +28 -79
- data/lib/tap/tasks/dump.rb +6 -0
- data/lib/tap/tasks/load.rb +9 -37
- data/lib/tap/templater.rb +12 -1
- data/lib/tap/version.rb +1 -1
- metadata +22 -16
- data/doc/Class Reference +0 -330
- data/lib/tap/exe.rb +0 -130
- data/lib/tap/schema.rb +0 -374
- data/lib/tap/schema/parser.rb +0 -425
- data/lib/tap/schema/utils.rb +0 -56
data/lib/tap/env/constant.rb
CHANGED
@@ -17,10 +17,10 @@ module Tap
|
|
17
17
|
# === Unloading
|
18
18
|
#
|
19
19
|
# Constant also supports constant unloading. Unloading can be useful in
|
20
|
-
# various development modes, but
|
21
|
-
# When a Constant unloads, the constant value is
|
22
|
-
# constant and the require
|
23
|
-
# statement to re-require, and in theory, reload the constant.
|
20
|
+
# various development modes, but may cause code to behave unpredictably.
|
21
|
+
# When a Constant unloads, the constant value is removed from the nesting
|
22
|
+
# constant and the require paths are removed from $". This allows a
|
23
|
+
# require statement to re-require, and in theory, reload the constant.
|
24
24
|
#
|
25
25
|
# # [simple.rb]
|
26
26
|
# # class Simple
|
@@ -39,6 +39,17 @@ module Tap
|
|
39
39
|
# Unloading and reloading works best for scripts that have no side effects;
|
40
40
|
# ie scripts that do not require other files and only define the specified
|
41
41
|
# class or module.
|
42
|
+
#
|
43
|
+
#--
|
44
|
+
# ==== Rationale for that last statement
|
45
|
+
#
|
46
|
+
# Scripts that require other files will not re-require the other files
|
47
|
+
# because unload doesn't remove the other files from $". Likewise scripts
|
48
|
+
# that define other constants effectively overwrite the existing constant;
|
49
|
+
# that may or may not be a big deal, but it can cause warnings. Moreover,
|
50
|
+
# if a script actually DOES something (like create a file), that something
|
51
|
+
# will be repeated when it gets re-required.
|
52
|
+
#
|
42
53
|
class Constant
|
43
54
|
class << self
|
44
55
|
|
@@ -72,12 +83,40 @@ module Tap
|
|
72
83
|
end
|
73
84
|
const
|
74
85
|
end
|
86
|
+
|
87
|
+
# Scans the directory and pattern for constants and adds them to the
|
88
|
+
# constants hash by name.
|
89
|
+
def scan(dir, pattern="**/*.rb", constants={})
|
90
|
+
if pattern.include?("..")
|
91
|
+
raise "patterns cannot include relative paths: #{pattern.inspect}"
|
92
|
+
end
|
93
|
+
|
94
|
+
# note changing dir here makes require paths relative to load_path,
|
95
|
+
# hence they can be directly converted into a default_const_name
|
96
|
+
# rather than first performing Root.relative_path
|
97
|
+
Dir.chdir(dir) do
|
98
|
+
Dir.glob(pattern).each do |path|
|
99
|
+
default_const_name = path.chomp(File.extname(path)).camelize
|
100
|
+
|
101
|
+
# scan for constants
|
102
|
+
Lazydoc::Document.scan(File.read(path)) do |const_name, type, summary|
|
103
|
+
const_name = default_const_name if const_name.empty?
|
104
|
+
|
105
|
+
constant = (constants[const_name] ||= Constant.new(const_name))
|
106
|
+
constant.register_as(type, summary)
|
107
|
+
constant.require_paths << path
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
constants
|
113
|
+
end
|
75
114
|
|
76
115
|
private
|
77
116
|
|
78
117
|
# helper method. Determines if the named constant is defined in const.
|
79
|
-
# The implementation
|
80
|
-
#
|
118
|
+
# The implementation has to be different for ruby 1.9 due to changes
|
119
|
+
# in the API.
|
81
120
|
case RUBY_VERSION
|
82
121
|
when /^1.9/
|
83
122
|
def const_is_defined?(const, const_name) # :nodoc:
|
@@ -96,19 +135,20 @@ module Tap
|
|
96
135
|
# The full constant name
|
97
136
|
attr_reader :const_name
|
98
137
|
|
99
|
-
#
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
138
|
+
# An array of paths that will be required when the constantize is called
|
139
|
+
# and the constant does not exist. Require paths are required in order.
|
140
|
+
attr_reader :require_paths
|
141
|
+
|
142
|
+
# A hash of (type, summary) pairs used to classify self.
|
143
|
+
attr_reader :types
|
104
144
|
|
105
145
|
# Initializes a new Constant with the specified constant name,
|
106
146
|
# require_path, and comment. The const_name should be a valid
|
107
147
|
# constant name.
|
108
|
-
def initialize(const_name,
|
148
|
+
def initialize(const_name, *require_paths)
|
109
149
|
@const_name = const_name
|
110
|
-
@
|
111
|
-
@
|
150
|
+
@require_paths = require_paths
|
151
|
+
@types = {}
|
112
152
|
end
|
113
153
|
|
114
154
|
# Returns the underscored const_name.
|
@@ -158,35 +198,45 @@ module Tap
|
|
158
198
|
def nesting_depth
|
159
199
|
@nesting_depth ||= nesting.split(/::/).length
|
160
200
|
end
|
161
|
-
|
162
|
-
# Returns the Lazydoc document for require_path.
|
163
|
-
def document
|
164
|
-
require_path ? Lazydoc[require_path] : nil
|
165
|
-
end
|
166
|
-
|
201
|
+
|
167
202
|
# True if another is a Constant with the same const_name,
|
168
203
|
# require_path, and comment as self.
|
169
204
|
def ==(another)
|
170
205
|
another.kind_of?(Constant) &&
|
171
206
|
another.const_name == self.const_name &&
|
172
|
-
another.
|
173
|
-
another.comment == self.comment
|
207
|
+
another.require_paths == self.require_paths
|
174
208
|
end
|
175
209
|
|
210
|
+
# Registers the type and summary with self. Raises an error if self is
|
211
|
+
# already registerd as the type and override is false.
|
212
|
+
def register_as(type, summary=nil, override=false)
|
213
|
+
if types.include?(type) && !override
|
214
|
+
raise "already registered as a #{type.inspect}"
|
215
|
+
end
|
216
|
+
|
217
|
+
types[type] = summary
|
218
|
+
self
|
219
|
+
end
|
220
|
+
|
176
221
|
# Looks up and returns the constant indicated by const_name. If the
|
177
|
-
# constant cannot be found, constantize requires
|
178
|
-
# tries again.
|
222
|
+
# constant cannot be found, constantize requires the require_paths
|
223
|
+
# in order and tries again.
|
179
224
|
#
|
180
225
|
# Raises a NameError if the constant cannot be found.
|
181
|
-
def constantize
|
226
|
+
def constantize(autorequire=true)
|
182
227
|
Constant.constantize(const_name) do
|
183
|
-
|
228
|
+
break unless autorequire
|
229
|
+
|
230
|
+
require_paths.each do |require_path|
|
231
|
+
require require_path
|
232
|
+
end
|
233
|
+
|
184
234
|
Constant.constantize(const_name)
|
185
235
|
end
|
186
236
|
end
|
187
237
|
|
188
238
|
# Undefines the constant indicated by const_name. The nesting constants
|
189
|
-
# are not removed. If specified,
|
239
|
+
# are not removed. If specified, the require_paths will be removed from $".
|
190
240
|
#
|
191
241
|
# When removing require_path, unload will add '.rb' to the require_path if
|
192
242
|
# require_path has no extension (this echos the behavior of require).
|
@@ -199,10 +249,10 @@ module Tap
|
|
199
249
|
const = nesting.empty? ? Object : Constant.constantize(nesting) { Object }
|
200
250
|
|
201
251
|
if const.const_defined?(name)
|
202
|
-
|
252
|
+
require_paths.each do |require_path|
|
203
253
|
path = File.extname(require_path).empty? ? "#{require_path}.rb" : require_path
|
204
254
|
$".delete(path)
|
205
|
-
end
|
255
|
+
end if unrequire
|
206
256
|
|
207
257
|
return const.send(:remove_const, name)
|
208
258
|
end
|
@@ -215,12 +265,12 @@ module Tap
|
|
215
265
|
# "#<Tap::Env::Constant:object_id Const::Name (require_path)>"
|
216
266
|
#
|
217
267
|
def inspect
|
218
|
-
"#<#{self.class}:#{object_id} #{const_name}
|
268
|
+
"#<#{self.class}:#{object_id} #{const_name} #{require_paths.inspect}>"
|
219
269
|
end
|
220
270
|
|
221
|
-
# Returns
|
222
|
-
def
|
223
|
-
|
271
|
+
# Returns const_name
|
272
|
+
def to_s
|
273
|
+
const_name
|
224
274
|
end
|
225
275
|
end
|
226
276
|
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Tap
|
2
|
+
class Env
|
3
|
+
|
4
|
+
# Context instances track information shared by a set of Env instances, for
|
5
|
+
# instance cached manifest data. Caching cross-env data in a shared space
|
6
|
+
# simplifies managment of this data, especially when dumping and loading it
|
7
|
+
# from a static file.
|
8
|
+
#
|
9
|
+
# Contexts also ensure that only one env in initialized to a given
|
10
|
+
# directory (at least among envs that share the same context). This
|
11
|
+
# prevents errors that arise when one env eventually nests itself.
|
12
|
+
class Context
|
13
|
+
|
14
|
+
# A hash of cached manifest data
|
15
|
+
attr_reader :cache
|
16
|
+
|
17
|
+
# The config file basename
|
18
|
+
attr_reader :basename
|
19
|
+
|
20
|
+
# An array of Env instances registered with self
|
21
|
+
attr_reader :instances
|
22
|
+
|
23
|
+
# Initializes a new Context. Options can specify a cache or a basename.
|
24
|
+
def initialize(options={})
|
25
|
+
options = {
|
26
|
+
:cache => {},
|
27
|
+
:basename => nil
|
28
|
+
}.merge(options)
|
29
|
+
|
30
|
+
@cache = options[:cache]
|
31
|
+
@basename = options[:basename]
|
32
|
+
@instances = []
|
33
|
+
end
|
34
|
+
|
35
|
+
# Registers env with self by adding env to instances. Raises an error
|
36
|
+
# if instances contains an env with the same root directory.
|
37
|
+
def register(env)
|
38
|
+
path = env.root.root
|
39
|
+
|
40
|
+
if instance(path)
|
41
|
+
raise "context already has an env for: #{path}"
|
42
|
+
end
|
43
|
+
|
44
|
+
instances << env
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
# Gets the instance for the directory currently in instances, or nil
|
49
|
+
# if such an instance does not exist.
|
50
|
+
def instance(dir)
|
51
|
+
instances.find {|env| env.root.root == dir }
|
52
|
+
end
|
53
|
+
|
54
|
+
# Returns the config filepath for the directory (ie basename under dir).
|
55
|
+
# If basename is nil, then config_file always return nil.
|
56
|
+
def config_file(dir)
|
57
|
+
basename ? File.join(dir, basename) : nil
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/lib/tap/env/manifest.rb
CHANGED
@@ -1,89 +1,179 @@
|
|
1
1
|
require 'tap/env/minimap'
|
2
|
-
require 'tap/env/constant'
|
3
2
|
|
4
3
|
module Tap
|
5
4
|
class Env
|
6
5
|
|
6
|
+
# Manifests provide concise access to resources within a nested Env.
|
7
7
|
class Manifest
|
8
|
+
class << self
|
9
|
+
|
10
|
+
# Interns a new Manifest using the block as the builder.
|
11
|
+
def intern(env, cache={}, &block)
|
12
|
+
new(env, block, cache)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
8
16
|
include Enumerable
|
9
|
-
include Minimap
|
10
17
|
|
11
|
-
#
|
18
|
+
# Matches a compound registry search key. After the match, if the key is
|
19
|
+
# compound then:
|
20
|
+
#
|
21
|
+
# $1:: env_key
|
22
|
+
# $2:: key
|
23
|
+
#
|
24
|
+
# If the key is not compound, $2 is nil and $1 is the key.
|
25
|
+
COMPOUND_KEY = /^((?:[A-z]:(?:\/|\\))?.*?)(?::(.*))?$/
|
26
|
+
|
27
|
+
# The default summary template
|
28
|
+
DEFAULT_TEMPLATE = %q{<% if !minimap.empty? && count > 1 %>
|
29
|
+
<%= env_key %>:
|
30
|
+
<% end %>
|
31
|
+
<% minimap.each do |key, entry| %>
|
32
|
+
<%= key.ljust(width) %> # <%= entry %>
|
33
|
+
<% end %>
|
34
|
+
}
|
35
|
+
|
36
|
+
# The Env queried for manifest data
|
12
37
|
attr_reader :env
|
13
38
|
|
14
|
-
|
39
|
+
# An object that responds to call, typically a block, that recieves
|
40
|
+
# an env and returns an array of resources, each of which must be
|
41
|
+
# minimappable. Alternatively, the builder may return a Minimap.
|
42
|
+
attr_reader :builder
|
15
43
|
|
16
|
-
#
|
17
|
-
|
18
|
-
|
19
|
-
@type = type
|
20
|
-
end
|
44
|
+
# A cache of (dir, [entries]) pairs mapping the root of an env
|
45
|
+
# to the array of resources associated with the env.
|
46
|
+
attr_reader :cache
|
21
47
|
|
22
|
-
def
|
23
|
-
env
|
48
|
+
def initialize(env, builder, cache={})
|
49
|
+
@env = env
|
50
|
+
@builder = builder
|
51
|
+
@cache = cache
|
52
|
+
|
53
|
+
cache.each_value do |value|
|
54
|
+
ensure_minimap(value)
|
55
|
+
end
|
24
56
|
end
|
25
57
|
|
26
|
-
#
|
27
|
-
def
|
28
|
-
entries
|
58
|
+
# Builds the manifest for each env in env.
|
59
|
+
def build
|
60
|
+
self.env.each {|env| entries(env) }
|
61
|
+
self
|
29
62
|
end
|
30
63
|
|
31
|
-
|
32
|
-
|
33
|
-
|
64
|
+
# Returns the entries associated with env. If no entries are currently
|
65
|
+
# registered to env, the env is passed to the builder and the results
|
66
|
+
# stored in the cache.
|
67
|
+
def entries(env)
|
68
|
+
cache[env] ||= begin
|
69
|
+
ensure_minimap builder.call(env)
|
34
70
|
end
|
35
71
|
end
|
36
72
|
|
37
|
-
#
|
73
|
+
# Yields each entry for each env to the block.
|
38
74
|
def each
|
39
|
-
|
75
|
+
self.env.each do |env|
|
76
|
+
entries(env).each do |entry|
|
77
|
+
yield(entry)
|
78
|
+
end
|
79
|
+
end
|
40
80
|
end
|
41
|
-
|
42
|
-
# Searches
|
43
|
-
#
|
81
|
+
|
82
|
+
# Searches for the first entry mini-matching the key. A single env can
|
83
|
+
# be specified by using a compound key like 'env_key:key'.
|
84
|
+
#
|
85
|
+
# If a block is provided, each matching entry is yielded until the
|
86
|
+
# block returns true. Set env_also to true to return an array like
|
87
|
+
# [env, entry], where env is the env where the entry was found.
|
44
88
|
#
|
45
|
-
# Returns nil if no matching entry is found.
|
46
|
-
def seek(key,
|
47
|
-
|
89
|
+
# Returns nil if no matching entry is found.
|
90
|
+
def seek(key, env_also=false)
|
91
|
+
key =~ COMPOUND_KEY
|
92
|
+
envs = if $2
|
93
|
+
# compound key, match for env
|
94
|
+
key = $2
|
95
|
+
[env.minimatch($1)].compact
|
96
|
+
else
|
97
|
+
# not a compound key, search all envs by iterating env
|
98
|
+
env
|
99
|
+
end
|
100
|
+
|
101
|
+
# traverse envs looking for the first
|
102
|
+
# manifest entry matching key
|
103
|
+
envs.each do |env|
|
104
|
+
if entry = entries(env).minimatch(key)
|
105
|
+
next if block_given? && !yield(entry)
|
106
|
+
return env_also ? [env, entry] : entry
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
nil
|
48
111
|
end
|
49
112
|
|
50
|
-
|
51
|
-
|
52
|
-
|
113
|
+
# Unseek looks up the key identifying a specific entry. The entry is
|
114
|
+
# identified by the block, which receives each entry (in order) until
|
115
|
+
# the block returns true. Returns nil if no entry returns true.
|
116
|
+
#
|
117
|
+
# The env key will be prepended to the result if env_also is set to true.
|
118
|
+
def unseek(env_also=false)
|
119
|
+
self.env.each do |env|
|
120
|
+
objects = entries(env)
|
121
|
+
if value = objects.find {|entry| yield(entry) }
|
122
|
+
key = objects.minihash(true)[value]
|
123
|
+
return env_also ? "#{env.minihash(true)[env]}:#{key}" : key
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
nil
|
53
128
|
end
|
54
129
|
|
55
|
-
#
|
56
|
-
|
57
|
-
|
130
|
+
# Generates a summary of the entries in self. Summarize uses the inspect
|
131
|
+
# functionality of Env to format the entries for each env in order; the
|
132
|
+
# results are concatenated.
|
133
|
+
#
|
134
|
+
# The template should be ERB; it will have the following local variables:
|
135
|
+
#
|
136
|
+
# env the current env being summarized
|
137
|
+
# minimap an array of [key, entry] pairs representing
|
138
|
+
# the minipaths and entries for the env
|
139
|
+
# width the maximum width of any key across all envs
|
140
|
+
# count the number of envs with at least one entry
|
141
|
+
#
|
142
|
+
# A block may be given to filter and pre-process minimap entries. Each
|
143
|
+
# (key, entry) pair will be yielded to the block; the block return
|
144
|
+
# replaces the entry and any pairs that return nil are removed.
|
145
|
+
#
|
146
|
+
def summarize(template=DEFAULT_TEMPLATE)
|
147
|
+
env.inspect(template, :width => 11, :count => 0) do |templater, globals|
|
148
|
+
width = globals[:width]
|
58
149
|
|
59
|
-
|
60
|
-
env = templater.env
|
61
|
-
templater.manifest = env.manifest(type)
|
62
|
-
yield(templater, globalz) if block_given?
|
63
|
-
end
|
64
|
-
end
|
150
|
+
minimap = entries(templater.env).minimap
|
65
151
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
152
|
+
if block_given?
|
153
|
+
minimap.collect! do |key, entry|
|
154
|
+
entry = yield(entry)
|
155
|
+
entry ? [key, entry] : nil
|
156
|
+
end
|
157
|
+
minimap.compact!
|
158
|
+
end
|
73
159
|
|
74
|
-
|
75
|
-
inspect(template, :width => 11, :count => 0) do |templater, globals|
|
76
|
-
width = globals[:width]
|
77
|
-
templater.entries = templater.manifest.minimap.collect! do |key, entry|
|
160
|
+
minimap.each do |key, entry|
|
78
161
|
width = key.length if width < key.length
|
79
|
-
[key, entry]
|
80
162
|
end
|
81
163
|
|
82
164
|
globals[:width] = width
|
83
|
-
globals[:count] += 1 unless
|
165
|
+
globals[:count] += 1 unless minimap.empty?
|
166
|
+
|
167
|
+
templater.minimap = minimap
|
84
168
|
end
|
85
169
|
end
|
86
170
|
|
171
|
+
private
|
172
|
+
|
173
|
+
# helper to make obj into a minimap if necessary
|
174
|
+
def ensure_minimap(obj) # :nodoc:
|
175
|
+
obj.kind_of?(Minimap) ? obj : obj.extend(Minimap)
|
176
|
+
end
|
87
177
|
end
|
88
178
|
end
|
89
179
|
end
|