tap 0.19.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. data/History +100 -45
  2. data/MIT-LICENSE +1 -1
  3. data/README +95 -51
  4. data/bin/tap +11 -57
  5. data/bin/tapexe +84 -0
  6. data/doc/API +91 -139
  7. data/doc/Configuration +93 -0
  8. data/doc/Examples/Command Line +10 -42
  9. data/doc/Examples/Tapfile +124 -0
  10. data/doc/Ruby to Ruby +87 -0
  11. data/doc/Workflow Syntax +185 -0
  12. data/lib/tap.rb +74 -5
  13. data/lib/tap/app.rb +217 -310
  14. data/lib/tap/app/api.rb +44 -23
  15. data/lib/tap/app/queue.rb +11 -12
  16. data/lib/tap/app/stack.rb +4 -4
  17. data/lib/tap/declarations.rb +200 -0
  18. data/lib/tap/declarations/context.rb +31 -0
  19. data/lib/tap/declarations/description.rb +33 -0
  20. data/lib/tap/env.rb +133 -779
  21. data/lib/tap/env/cache.rb +87 -0
  22. data/lib/tap/env/constant.rb +94 -39
  23. data/lib/tap/env/path.rb +71 -0
  24. data/lib/tap/join.rb +42 -78
  25. data/lib/tap/joins/gate.rb +85 -0
  26. data/lib/tap/joins/switch.rb +4 -2
  27. data/lib/tap/joins/sync.rb +3 -3
  28. data/lib/tap/middleware.rb +5 -5
  29. data/lib/tap/middlewares/debugger.rb +18 -58
  30. data/lib/tap/parser.rb +115 -183
  31. data/lib/tap/root.rb +162 -239
  32. data/lib/tap/signal.rb +72 -0
  33. data/lib/tap/signals.rb +20 -2
  34. data/lib/tap/signals/class_methods.rb +38 -43
  35. data/lib/tap/signals/configure.rb +19 -0
  36. data/lib/tap/signals/help.rb +5 -7
  37. data/lib/tap/signals/load.rb +49 -0
  38. data/lib/tap/signals/module_methods.rb +1 -0
  39. data/lib/tap/task.rb +46 -275
  40. data/lib/tap/tasks/dump.rb +21 -16
  41. data/lib/tap/tasks/list.rb +184 -0
  42. data/lib/tap/tasks/load.rb +4 -4
  43. data/lib/tap/tasks/prompt.rb +128 -0
  44. data/lib/tap/tasks/signal.rb +42 -0
  45. data/lib/tap/tasks/singleton.rb +35 -0
  46. data/lib/tap/tasks/stream.rb +64 -0
  47. data/lib/tap/utils.rb +83 -0
  48. data/lib/tap/version.rb +2 -2
  49. data/lib/tap/workflow.rb +124 -0
  50. data/tap.yml +0 -0
  51. metadata +59 -24
  52. data/cmd/console.rb +0 -43
  53. data/cmd/manifest.rb +0 -118
  54. data/cmd/run.rb +0 -145
  55. data/doc/Examples/Workflow +0 -40
  56. data/lib/tap/app/node.rb +0 -29
  57. data/lib/tap/env/context.rb +0 -61
  58. data/lib/tap/env/gems.rb +0 -63
  59. data/lib/tap/env/manifest.rb +0 -179
  60. data/lib/tap/env/minimap.rb +0 -308
  61. data/lib/tap/intern.rb +0 -50
  62. data/lib/tap/joins.rb +0 -9
  63. data/lib/tap/prompt.rb +0 -36
  64. data/lib/tap/root/utils.rb +0 -220
  65. data/lib/tap/root/versions.rb +0 -138
  66. data/lib/tap/signals/signal.rb +0 -68
@@ -1,50 +0,0 @@
1
- module Tap
2
- # Generates an Intern module to override the specified method_name. Intern
3
- # modules are useful to override a tiny bit of functionality without having
4
- # to generate a full subclass.
5
- #
6
- # An Intern module:
7
- #
8
- # - adds an accessor for <method_name>_block
9
- # - overrides <method_name> to call the block, prepending self to
10
- # the input arguments
11
- #
12
- # For example:
13
- #
14
- # array = [1,2,3].extend Intern(:last)
15
- #
16
- # array.last # => 3
17
- # array.last_block = lambda {|arr| arr.first }
18
- # array.last # => 3
19
- #
20
- def self.Intern(method_name)
21
- mod = INTERN_MODULES[method_name.to_sym]
22
- return mod unless mod == nil
23
-
24
- mod = INTERN_MODULES[method_name.to_sym] = Module.new
25
- mod.module_eval %Q{
26
- attr_accessor :#{method_name}_block
27
-
28
- def #{method_name}(*inputs)
29
- return super unless #{method_name}_block
30
- inputs.unshift(self)
31
-
32
- arity = #{method_name}_block.arity
33
- n = inputs.length
34
- unless n == arity || (arity < 0 && (-1-n) <= arity)
35
- raise ArgumentError.new("wrong number of arguments (\#{n} for \#{arity})")
36
- end
37
-
38
- #{method_name}_block.call(*inputs)
39
- end
40
- }
41
- mod
42
- end
43
-
44
- # An array of already-declared intern modules,
45
- # keyed by method_name.
46
- INTERN_MODULES = {}
47
-
48
- # An Intern module for :process.
49
- Intern = Tap::Intern(:process)
50
- end
@@ -1,9 +0,0 @@
1
- require 'tap/join'
2
- require 'tap/joins/switch'
3
- require 'tap/joins/sync'
4
-
5
- module Tap
6
- # A module of standard Join classes supported by Tap.
7
- module Joins
8
- end
9
- end
@@ -1,36 +0,0 @@
1
- require 'tap/app/api'
2
- require 'readline'
3
-
4
- module Tap
5
-
6
- # :startdoc::prompt
7
- #
8
- # A prompt to signal a running app. Any signals that return app (ie /run
9
- # /stop /terminate) will exit the prompt.
10
- class Prompt < App::Api
11
-
12
- def call
13
- puts "starting prompt (help for help):"
14
- loop do
15
- begin
16
- line = Readline.readline('--/', true).strip
17
- next if line.empty?
18
-
19
- args = Shellwords.shellwords(line)
20
- "/#{args.shift}" =~ Tap::Parser::SIGNAL
21
-
22
- result = app.call('obj' => $1, 'sig' => $2, 'args' => args)
23
- if result == app
24
- break
25
- else
26
- puts "=> #{result}"
27
- end
28
- rescue
29
- puts $!.message
30
- puts $!.backtrace if app.debug?
31
- end
32
- end
33
- end
34
- end
35
- end
36
-
@@ -1,220 +0,0 @@
1
- require 'tap/root/versions'
2
- autoload(:FileUtils, 'fileutils')
3
-
4
- module Tap
5
- class Root
6
-
7
- # A variety of utility methods for working with paths.
8
- module Utils
9
- include Versions
10
-
11
- # Regexp to match a windows-style root path.
12
- WIN_ROOT_PATTERN = /^[A-z]:\//
13
-
14
- module_function
15
-
16
- # Returns the path of 'path' relative to dir. Both dir and path will
17
- # be expanded to dir_string, if specified, before the relative path is
18
- # determined. Returns nil if the path is not relative to dir.
19
- #
20
- # relative_path('dir', "dir/path/to/file.txt") # => "path/to/file.txt"
21
- #
22
- def relative_path(dir, path, dir_string=Dir.pwd)
23
- if dir_string
24
- dir = File.expand_path(dir, dir_string)
25
- path = File.expand_path(path, dir_string)
26
- end
27
- return nil unless Utils.relative?(dir, path, false)
28
-
29
- # use dir.length + 1 to remove a leading '/'. If dir.length + 1 >= expanded.length
30
- # as in: relative_path('/path', '/path') then the first arg returns nil, and an
31
- # empty string is returned
32
- path[(dir.chomp("/").length + 1)..-1] || ""
33
- end
34
-
35
- # Generates a target path translated from the source_dir to the
36
- # target_dir. Raises an error if the path is not relative to the
37
- # source_dir.
38
- #
39
- # translate("/path/to/file.txt", "/path", "/another/path") # => '/another/path/to/file.txt'
40
- #
41
- def translate(path, source_dir, target_dir)
42
- unless relative_path = relative_path(source_dir, path)
43
- raise ArgumentError, "\n#{path}\nis not relative to:\n#{source_dir}"
44
- end
45
- File.join(target_dir, relative_path)
46
- end
47
-
48
- # Returns the path, exchanging the extension with extname. Extname may
49
- # optionally omit the leading period.
50
- #
51
- # exchange('path/to/file.txt', '.html') # => 'path/to/file.html'
52
- # exchange('path/to/file.txt', 'rb') # => 'path/to/file.rb'
53
- #
54
- def exchange(path, extname)
55
- "#{path.chomp(File.extname(path))}#{extname[0] == ?. ? '' : '.'}#{extname}"
56
- end
57
-
58
- # Lists all unique paths matching the input glob patterns.
59
- def glob(*patterns)
60
- Dir[*patterns].uniq
61
- end
62
-
63
- # Lists all unique versions of path matching the glob version patterns. If
64
- # no patterns are specified, then all versions of path will be returned.
65
- def version_glob(path, *vpatterns)
66
- paths = []
67
-
68
- vpatterns << "*" if vpatterns.empty?
69
- vpatterns.each do |vpattern|
70
- paths.concat Dir.glob(version(path, vpattern))
71
-
72
- # extra work to include the default version path for any version
73
- paths << path if vpattern == "*" && File.exists?(path)
74
- end
75
-
76
- paths.uniq
77
- end
78
-
79
- # Path suffix glob. Globs along the paths for the specified suffix
80
- # pattern.
81
- def suffix_glob(suffix_pattern, *paths)
82
- paths.collect! {|path| File.join(path, suffix_pattern) }
83
- Dir[*paths].uniq
84
- end
85
-
86
- # Like Dir.chdir but makes the directory, if necessary, when mkdir is
87
- # specified. chdir raises an error for non-existant directories, as well
88
- # as non-directory inputs.
89
- def chdir(dir, mkdir=false, &block)
90
- dir = File.expand_path(dir)
91
-
92
- unless File.directory?(dir)
93
- if !File.exists?(dir) && mkdir
94
- FileUtils.mkdir_p(dir)
95
- else
96
- raise ArgumentError, "not a directory: #{dir}"
97
- end
98
- end
99
-
100
- Dir.chdir(dir, &block)
101
- end
102
-
103
- # Prepares the input path by making the parent directory for path. If a
104
- # block is given, a file is created at path and passed to it; in this
105
- # way files with non-existant parent directories are readily made.
106
- #
107
- # Returns path.
108
- def prepare(path)
109
- dirname = File.dirname(path)
110
- FileUtils.mkdir_p(dirname) unless File.exists?(dirname)
111
- File.open(path, "w") {|io| yield(io) } if block_given?
112
- path
113
- end
114
-
115
- # The path root type indicating windows, *nix, or some unknown style of
116
- # paths (:win, :nix, :unknown).
117
- def path_root_type
118
- @path_root_type ||= case
119
- when RUBY_PLATFORM =~ /mswin/ && File.expand_path(".") =~ WIN_ROOT_PATTERN then :win
120
- when File.expand_path(".")[0] == ?/ then :nix
121
- else :unknown
122
- end
123
- end
124
-
125
- # Returns true if the input path appears to be an expanded path, based on
126
- # path_root_type.
127
- #
128
- # If root_type == :win returns true if the path matches WIN_ROOT_PATTERN.
129
- #
130
- # expanded?('C:/path') # => true
131
- # expanded?('c:/path') # => true
132
- # expanded?('D:/path') # => true
133
- # expanded?('path') # => false
134
- #
135
- # If root_type == :nix, then expanded? returns true if the path begins
136
- # with '/'.
137
- #
138
- # expanded?('/path') # => true
139
- # expanded?('path') # => false
140
- #
141
- # Otherwise expanded? always returns nil.
142
- def expanded?(path, root_type=path_root_type)
143
- case root_type
144
- when :win
145
- path =~ WIN_ROOT_PATTERN ? true : false
146
- when :nix
147
- path[0] == ?/
148
- else
149
- nil
150
- end
151
- end
152
-
153
- # Returns true if path is relative to dir. Both path and dir will be
154
- # expanded relative to dir_string, if specified.
155
- def relative?(dir, path, dir_string=Dir.pwd)
156
- if dir_string
157
- dir = File.expand_path(dir, dir_string)
158
- path = File.expand_path(path, dir_string)
159
- end
160
-
161
- path.rindex(dir, 0) == 0
162
- end
163
-
164
- # Trivial indicates when a path does not have content to load. Returns
165
- # true if the file at path is empty, non-existant, a directory, or nil.
166
- def trivial?(path)
167
- path == nil || !File.file?(path) || File.size(path) == 0
168
- end
169
-
170
- # Empty returns true when dir is an existing directory that has no files.
171
- def empty?(dir)
172
- File.directory?(dir) && (Dir.entries(dir) - ['.', '..']).empty?
173
- end
174
-
175
- # Returns the path segments for the given path, splitting along the path
176
- # divider. Env paths are always represented by a string, if only an
177
- # empty string.
178
- #
179
- # os divider example
180
- # windows '\' split('C:\path\to\file') # => ["C:", "path", "to", "file"]
181
- # *nix '/' split('/path/to/file') # => ["", "path", "to", "file"]
182
- #
183
- # The path is always expanded relative to the expand_dir; so '.' and
184
- # '..' are resolved. However, unless expand_path == true, only the
185
- # segments relative to the expand_dir are returned.
186
- #
187
- # On windows (note that expanding paths allows the use of slashes or
188
- # backslashes):
189
- #
190
- # Dir.pwd # => 'C:/'
191
- # split('path\to\..\.\to\file') # => ["C:", "path", "to", "file"]
192
- # split('path/to/.././to/file', false) # => ["path", "to", "file"]
193
- #
194
- # On *nix (or more generally systems with '/' roots):
195
- #
196
- # Dir.pwd # => '/'
197
- # split('path/to/.././to/file') # => ["", "path", "to", "file"]
198
- # split('path/to/.././to/file', false) # => ["path", "to", "file"]
199
- #
200
- def split(path, expand_path=true, expand_dir=Dir.pwd)
201
- path = if expand_path
202
- File.expand_path(path, expand_dir)
203
- else
204
- # normalize the path by expanding it, then
205
- # work back to the relative path as needed
206
- expanded_dir = File.expand_path(expand_dir)
207
- expanded_path = File.expand_path(path, expand_dir)
208
- expanded_path.index(expanded_dir) != 0 ? expanded_path : relative_path(expanded_dir, expanded_path)
209
- end
210
-
211
- segments = path.scan(/[^\/]+/)
212
-
213
- # add back the root path as needed on *nix
214
- segments.unshift "" if path[0] == ?/
215
- segments
216
- end
217
-
218
- end
219
- end
220
- end
@@ -1,138 +0,0 @@
1
- module Tap
2
- class Root
3
- # Version provides methods for adding, removing, and incrementing versions
4
- # at the end of paths. Versions are all formatted like:
5
- # 'path-version.extension'.
6
- #
7
- module Versions
8
-
9
- # Adds a version to the path. Versioned paths follow the format:
10
- # 'path-version.extension'. If no version is specified, then the
11
- # path is returned.
12
- #
13
- # version("path/to/file.txt", 1.0) # => "path/to/file-1.0.txt"
14
- #
15
- def version(path, version)
16
- version = version.to_s.strip
17
- if version.empty?
18
- path
19
- else
20
- extname = File.extname(path)
21
- path.chomp(extname) + '-' + version + extname
22
- end
23
- end
24
-
25
- # Increments the version of the path by the specified increment.
26
- #
27
- # increment("path/to/file-1.0.txt", "0.0.1") # => "path/to/file-1.0.1.txt"
28
- # increment("path/to/file.txt", 1.0) # => "path/to/file-1.0.txt"
29
- #
30
- def increment(path, increment)
31
- path, version = deversion(path)
32
-
33
- # split the version and increment into integer arrays of equal length
34
- increment, version = [increment, version].collect do |vstr|
35
- begin
36
- vstr.to_s.split(/\./).collect {|v| v.to_i}
37
- rescue
38
- raise "Bad version or increment: #{vstr}"
39
- end
40
- end
41
-
42
- if increment.length > version.length
43
- version.concat Array.new(increment.length - version.length, 0)
44
- end
45
-
46
- # add the increment to version
47
- 0.upto(version.length-1) do |i|
48
- version[i] += (increment[i] || 0)
49
- end
50
-
51
- self.version(path, version.join("."))
52
- end
53
-
54
- # Splits the version from the input path, then returns the path and
55
- # version. If no version is specified, then the returned version will be
56
- # nil.
57
- #
58
- # deversion("path/to/file-1.0.txt") # => ["path/to/file.txt", "1.0"]
59
- # deversion("path/to/file.txt") # => ["path/to/file.txt", nil]
60
- #
61
- def deversion(path)
62
- extname = File.extname(path)
63
- extname = '' if extname =~ /^\.\d+$/
64
- path =~ /^(.*)-(\d(\.?\d)*)#{extname}$/ ? [$1 + extname, $2] : [path, nil]
65
- end
66
-
67
- # A <=> comparison for versions. compare_versions can take strings,
68
- # integers, or even arrays representing the parts of a version.
69
- #
70
- # compare_versions("1.0.0", "0.9.9") # => 1
71
- # compare_versions(1.1, 1.1) # => 0
72
- # compare_versions([0,9], [0,9,1]) # => -1
73
- def compare_versions(a,b)
74
- a, b = [a,b].collect {|item| to_integer_array(item) }
75
-
76
- # equalize the lengths of the integer arrays
77
- d = b.length - a.length
78
- case
79
- when d < 0 then b.concat Array.new(-d, 0)
80
- when d > 0 then a.concat Array.new(d, 0)
81
- end
82
-
83
- a <=> b
84
- end
85
-
86
- # Version unique. Select the latest or earliest versions of each file
87
- # in the array. For paths that have no version, vniq considers any
88
- # version to beat no version. The order of paths is preserved by
89
- # default, but the extra sort doing so may be turned off.
90
- #
91
- # paths = [
92
- # "/path/to/two-0.0.1.txt",
93
- # "/path/to/one-0.0.1.txt",
94
- # "/path/to/one.txt",
95
- # "/path/to/two-1.0.1.txt",
96
- # "/path/to/three.txt"]
97
- #
98
- # vniq(paths)
99
- # # => [
100
- # # "/path/to/one-0.0.1.txt",
101
- # # "/path/to/two-1.0.1.txt",
102
- # # "/path/to/three.txt"]
103
- #
104
- def vniq(array, earliest=false, preserve_order=true)
105
- unique = {}
106
- array.sort.each do |path|
107
- base, version = deversion(path)
108
- (unique[base] ||= []) << version
109
- end
110
-
111
- results = []
112
- unique.each_pair do |base, versions|
113
- versions = versions.sort {|a, b| compare_versions(a,b) }
114
- winner = earliest ? versions.shift : versions.pop
115
- results << version(base, winner)
116
- end
117
-
118
- results = results.sort_by do |path|
119
- array.index(path)
120
- end if preserve_order
121
-
122
- results
123
- end
124
-
125
- private
126
-
127
- # Converts an input argument (typically a string or an array) to an
128
- # array of integers. Splits version string on "."
129
- def to_integer_array(arg)
130
- arr = case arg
131
- when Array then arg
132
- else arg.to_s.split('.')
133
- end
134
- arr.collect {|i| i.to_i}
135
- end
136
- end
137
- end
138
- end