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