bahuvrihi-tap 0.10.1 → 0.10.2

Sign up to get free protection for your applications and to get access to all the features.
data/History CHANGED
@@ -1,3 +1,12 @@
1
+ == 0.10.1 / 2008-08-21
2
+
3
+ Update of Tap with a few improvements to manifests
4
+ and a new manifest command.
5
+
6
+ * Fixed some bugs and extended manifests
7
+ * Bug fixes in generators
8
+ * Added task definitions to Workflow
9
+
1
10
  == 0.10.0 / 2008-08-08
2
11
 
3
12
  Major revision. Reworked configurations and the execution
data/README CHANGED
@@ -14,6 +14,7 @@ development, and bug tracking.
14
14
  * Website[http://tap.rubyforge.org]
15
15
  * Lighthouse[http://bahuvrihi.lighthouseapp.com/projects/9908-tap-task-application/overview]
16
16
  * Github[http://github.com/bahuvrihi/tap/tree/master]
17
+ * {Google Group}[http://groups.google.com/group/ruby-on-tap]
17
18
 
18
19
  === Additional Notes:
19
20
 
data/bin/tap CHANGED
@@ -58,7 +58,7 @@ end
58
58
 
59
59
  begin
60
60
  env.activate
61
- env.run do
61
+ env.launch(ARGV) do
62
62
  # give some help
63
63
  require 'tap/support/command_line'
64
64
 
data/cmd/run.rb CHANGED
@@ -7,7 +7,6 @@
7
7
 
8
8
  env = Tap::Env.instance
9
9
  app = Tap::App.instance
10
- cmdline = Tap::Support::CommandLine
11
10
 
12
11
  #
13
12
  # handle options
@@ -15,6 +14,7 @@ cmdline = Tap::Support::CommandLine
15
14
 
16
15
  dump = false
17
16
  OptionParser.new do |opts|
17
+ cmdline = Tap::Support::CommandLine
18
18
 
19
19
  opts.separator ""
20
20
  opts.separator "configurations:"
@@ -48,30 +48,9 @@ end.parse!(ARGV)
48
48
  # handle options for each specified task
49
49
  #
50
50
 
51
- rounds = cmdline.split(ARGV).collect do |argvs|
52
- argvs.each do |argv|
53
- ARGV.clear
54
- ARGV.concat(argv)
55
-
56
- unless td = cmdline.shift(ARGV)
57
- # warn nil?
58
- next
59
- end
60
-
61
- # attempt lookup the task class
62
- const = env.search(:tasks, td) or raise "unknown task: #{td}"
63
- task_class = const.constantize or raise "unknown task: #{td}"
64
-
65
- # now let the class handle the argv
66
- task, argv = task_class.instantiate(ARGV, app)
67
- task.enq *argv.collect! {|str| cmdline.parse_yaml(str) }
68
- end
69
-
70
- app.queue.clear
71
- end
51
+ rounds = env.parse(ARGV)
72
52
  ARGV.clear
73
53
 
74
- rounds.delete_if {|round| round.empty? }
75
54
  if rounds.empty?
76
55
  puts "no task specified"
77
56
  exit
data/lib/tap/constants.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  module Tap
2
2
  MAJOR = 0
3
3
  MINOR = 10
4
- TINY = 1
4
+ TINY = 2
5
5
 
6
6
  VERSION="#{MAJOR}.#{MINOR}.#{TINY}"
7
7
  WEBSITE="http://tap.rubyforge.org"
data/lib/tap/exe.rb CHANGED
@@ -1,5 +1,8 @@
1
+ require 'tap/support/command_line/parser'
2
+
1
3
  module Tap
2
4
  class Exe < Env
5
+ Parser = Support::CommandLine::Parser
3
6
 
4
7
  class << self
5
8
  def instantiate
@@ -28,18 +31,22 @@ module Tap
28
31
  config :before, nil
29
32
  config :after, nil
30
33
  config :aliases, {}, &c.hash_or_nil
31
-
32
- def handle_error(err)
33
- case
34
- when $DEBUG
35
- puts err.message
36
- puts
37
- puts err.backtrace
38
- else puts err.message
39
- end
40
- end
41
34
 
42
- def run(argv=ARGV)
35
+ def app
36
+ root
37
+ end
38
+
39
+ def handle_error(err)
40
+ case
41
+ when $DEBUG
42
+ puts err.message
43
+ puts
44
+ puts err.backtrace
45
+ else puts err.message
46
+ end
47
+ end
48
+
49
+ def launch(argv=ARGV)
43
50
  command = argv.shift.to_s
44
51
 
45
52
  if aliases && aliases.has_key?(command)
@@ -59,5 +66,58 @@ module Tap
59
66
  end
60
67
  end
61
68
  end
69
+
70
+ def parse(argv=ARGV)
71
+ parser = Parser.new(argv)
72
+ targets = parser.targets
73
+
74
+ tasks = []
75
+ rounds = parser.rounds.collect do |round|
76
+ round.each do |argv|
77
+ unless td = Parser.shift_arg(argv)
78
+ # warn nil?
79
+ next
80
+ end
81
+
82
+ # attempt lookup the task class
83
+ const = search(:tasks, td) or raise ArgumentError, "unknown task: #{td}"
84
+ task_class = const.constantize or raise ArgumentError, "unknown task: #{td}"
85
+
86
+ # now let the class handle the argv
87
+ task, args = task_class.instantiate(argv, app)
88
+
89
+ if !targets.include?(tasks.length)
90
+ task.enq(*args)
91
+ elsif !args.empty?
92
+ raise ArgumentError, "workflow target receives argv: [#{argv.unshift(td).join(', ')}]"
93
+ end
94
+
95
+ tasks << task
96
+ end
97
+
98
+ app.queue.clear
99
+ end
100
+ rounds.delete_if {|round| round.empty? }
101
+
102
+ # build the workflow
103
+
104
+ parser.sequences.each do |sequence|
105
+ app.sequence(*sequence.collect {|s| tasks[s]})
106
+ end
107
+
108
+ parser.forks.each do |source, targets|
109
+ app.fork(tasks[source], *targets.collect {|t| tasks[t]})
110
+ end
111
+
112
+ parser.merges.each do |target, sources|
113
+ app.merge(tasks[target], *sources.collect {|s| tasks[s]})
114
+ end
115
+
116
+ parser.sync_merges.each do |target, sources|
117
+ app.sync_merge(tasks[target], *sources.collect {|s| tasks[s]})
118
+ end
119
+
120
+ rounds
121
+ end
62
122
  end
63
123
  end
data/lib/tap/root.rb CHANGED
@@ -78,6 +78,19 @@ module Tap
78
78
  # as in: relative_filepath('/path', '/path') then the first arg returns nil, and an
79
79
  # empty string is returned
80
80
  expanded_path[( expanded_dir.chomp("/").length + 1)..-1] || ""
81
+ end
82
+
83
+ # Generates a target filepath translated from the source_dir to
84
+ # the target_dir. Raises an error if the filepath is not relative
85
+ # to the source_dir.
86
+ #
87
+ # Root.translate("/path/to/file.txt", "/path", "/another/path") # => '/another/path/to/file.txt'
88
+ #
89
+ def translate(path, source_dir, target_dir)
90
+ unless relative_path = relative_filepath(source_dir, path)
91
+ raise ArgumentError, "\n#{path}\nis not relative to:\n#{source_dir}"
92
+ end
93
+ File.join(target_dir, relative_path)
81
94
  end
82
95
 
83
96
  # Lists all unique paths matching the input glob patterns.
@@ -109,10 +122,10 @@ module Tap
109
122
  end.flatten.uniq
110
123
  end
111
124
 
112
- # Executes the block in the specified directory. Makes the directory, if
113
- # necessary when mkdir is specified. Otherwise, indir raises an error
114
- # for non-existant directories, as well as non-directory inputs.
115
- def indir(dir, mkdir=false)
125
+ # Like Dir.chdir but makes the directory, if necessary, when
126
+ # mkdir is specified. chdir raises an error for non-existant
127
+ # directories, as well as non-directory inputs.
128
+ def chdir(dir, mkdir=false, &block)
116
129
  unless File.directory?(dir)
117
130
  if !File.exists?(dir) && mkdir
118
131
  FileUtils.mkdir_p(dir)
@@ -120,14 +133,8 @@ module Tap
120
133
  raise "not a directory: #{dir}"
121
134
  end
122
135
  end
123
-
124
- pwd = Dir.pwd
125
- begin
126
- Dir.chdir(dir)
127
- yield
128
- ensure
129
- Dir.chdir(pwd)
130
- end
136
+
137
+ Dir.chdir(dir, &block)
131
138
  end
132
139
 
133
140
  # The path root type indicating windows, *nix, or some unknown
@@ -527,17 +534,14 @@ module Tap
527
534
  Root.relative_filepath(self[dir], filepath)
528
535
  end
529
536
 
530
- # Generates a target filepath translated from the aliased input dir to
531
- # the aliased output dir. Raises an error if the filepath is not relative
532
- # to the aliased input dir.
537
+ # Generates a target filepath translated from the aliased source_dir to
538
+ # the aliased target_dir. Raises an error if the filepath is not relative
539
+ # to the aliased source_dir.
533
540
  #
534
541
  # fp = r.filepath(:in, 'path/to/file.txt') # => '/root_dir/in/path/to/file.txt'
535
542
  # r.translate(fp, :in, :out) # => '/root_dir/out/path/to/file.txt'
536
- def translate(filepath, input_dir, output_dir)
537
- unless relative_path = relative_filepath(input_dir, filepath)
538
- raise "\n#{filepath}\nis not relative to:\n#{input_dir}"
539
- end
540
- filepath(output_dir, relative_path)
543
+ def translate(filepath, source_dir, target_dir)
544
+ Root.translate(filepath, self[source_dir], self[target_dir])
541
545
  end
542
546
 
543
547
  # Lists all files in the aliased dir matching the input patterns. Patterns
@@ -555,9 +559,9 @@ module Tap
555
559
  Root.vglob(filepath(dir, filename), *vpatterns)
556
560
  end
557
561
 
558
- # Executes the provided block in the specified directory using Root.indir.
559
- def indir(dir, mkdir=false)
560
- Root.indir(self[dir], mkdir) { yield }
562
+ # chdirs to the specified directory using Root.chdir.
563
+ def chdir(dir, mkdir=false, &block)
564
+ Root.chdir(self[dir], mkdir, &block)
561
565
  end
562
566
 
563
567
  private
data/lib/tap/spec.rb ADDED
@@ -0,0 +1,67 @@
1
+ module Tap
2
+ module Spec
3
+
4
+ def self.included(base)
5
+ super
6
+ base.send(:include, Tap::Spec::Adapter)
7
+ end
8
+
9
+ # Causes a TestCase to act as a file test, by instantiating a class Tap::Root
10
+ # (trs), and including FileMethods. The root and directories used to
11
+ # instantiate trs can be specified as options. By default file_test_root
12
+ # and the directories {:input => 'input', :output => 'output', :expected => 'expected'}
13
+ # will be used.
14
+ #
15
+ # Note: file_test_root determines a root directory <em>based on the calling file</em>.
16
+ # Be sure to specify the root directory explicitly if you call acts_as_file_test
17
+ # from a file that is NOT meant to be test file.
18
+ def acts_as_file_spec(options={})
19
+ include Tap::Spec::FileMethods
20
+
21
+ options = {
22
+ :root => file_test_root,
23
+ :directories => {:input => 'input', :output => 'output', :expected => 'expected'}
24
+ }.merge(options)
25
+ self.trs = Tap::Root.new(options[:root], options[:directories])
26
+ end
27
+
28
+ # Causes a unit test to act as a tap test -- resulting in the following:
29
+ # - setup using acts_as_file_test
30
+ # - inclusion of Tap::Test::SubsetMethods
31
+ # - inclusion of Tap::Test::InstanceMethods
32
+ #
33
+ # Note: Unless otherwise specified, <tt>acts_as_tap_test</tt> infers a root directory
34
+ # based on the calling file. Be sure to specify the root directory explicitly
35
+ # if you call acts_as_file_test from a file that is NOT meant to be test file.
36
+ # def acts_as_tap_spec(options={})
37
+ # include Tap::Test::SubsetMethods
38
+ # include Tap::Test::FileMethods
39
+ # include Tap::Test::TapMethods
40
+ #
41
+ # acts_as_file_test({:root => file_test_root}.merge(options))
42
+ # end
43
+ #
44
+ # def acts_as_script_spec(options={})
45
+ # include Tap::Test::FileMethods
46
+ # include Tap::Test::ScriptMethods
47
+ #
48
+ # acts_as_file_test({:root => file_test_root}.merge(options))
49
+ # end
50
+ end
51
+ end
52
+
53
+ module Tap
54
+
55
+ # Modules facilitating testing. TapMethods are specific to
56
+ # Tap, but SubsetMethods and FileMethods are more general in
57
+ # their utility.
58
+ module Spec
59
+ autoload(:SubsetMethods, 'tap/spec/subset_methods')
60
+ autoload(:FileMethods, 'tap/spec/file_methods')
61
+ autoload(:TapMethods, 'tap/spec/tap_methods')
62
+ end
63
+ end
64
+
65
+
66
+
67
+
@@ -0,0 +1,48 @@
1
+ module Tap
2
+ module Spec
3
+ module Adapter
4
+ def method_name
5
+ self.description.strip.gsub(/\s+/, "_")
6
+ end
7
+
8
+ # A method serving as the dumping ground for 'should ==' statements
9
+ # that otherwise cause a 'useless use of <method> in void context'
10
+ # warning. Useful if you're running tests with -w.
11
+ #
12
+ # check 1.should == 1
13
+ #
14
+ # Check will validate that the line calling check contains a
15
+ # should/should_not statement; the check fails if it does not.
16
+ def check(return_value)
17
+ return false unless SCRIPT_LINES__
18
+
19
+ caller[0] =~ /^(([A-z]:)?[^:]+):(\d+)/
20
+
21
+ check_file = SCRIPT_LINES__[$1]
22
+ violated("could not validate check: #{$1} (#{$3})") unless check_file
23
+
24
+ # note the line number in caller
25
+ # starts at 1, not 0
26
+ line = check_file[$3.to_i - 1]
27
+ case line
28
+ when /\.should(_not)?[^\w]/
29
+ true # pass
30
+ when /^\s+check/
31
+ violated("check used without should/should_not statement: #{line} (#{caller[0]})")
32
+ else
33
+ violated("multiline check statements are not allowed: #{caller[0]}")
34
+ end
35
+ end
36
+
37
+ # Maps flunk to violated.
38
+ def flunk(msg)
39
+ violated(msg)
40
+ end
41
+
42
+ # Maps assert_equal to actual.should == expected.
43
+ def assert_equal(expected, actual, msg=nil)
44
+ check actual.should == expected
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,16 @@
1
+ require 'tap/spec/adapter'
2
+ require 'tap/test/file_methods'
3
+ require 'tap/spec/file_methods_class'
4
+
5
+ module Tap
6
+ module Spec
7
+ module FileMethods
8
+ def self.included(base)
9
+ super
10
+ base.send(:include, Tap::Spec::Adapter)
11
+ base.send(:include, Tap::Test::FileMethods)
12
+ base.extend Tap::Spec::FileMethodsClass
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,13 @@
1
+ module Tap
2
+ module Spec
3
+ module FileMethodsClass
4
+ def trs
5
+ @trs ||= (superclass.respond_to?(:trs) ? superclass.trs : nil)
6
+ end
7
+
8
+ def file_test_root
9
+ super.chomp("_spec")
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ require 'tap/spec/adapter'
2
+ require 'tap/test/subset_methods'
3
+
4
+ module Tap
5
+ module Spec
6
+ module SubsetMethods
7
+ def self.included(base)
8
+ super
9
+ base.send(:include, Tap::Spec::Adapter)
10
+ base.send(:include, Tap::Test::SubsetMethods)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -7,49 +7,6 @@ module Tap
7
7
  module CommandLine
8
8
  module_function
9
9
 
10
- # Parses the input string as YAML, if the string matches the YAML document
11
- # specifier (ie it begins with "---\s*\n"). Otherwise returns the string.
12
- #
13
- # str = {'key' => 'value'}.to_yaml # => "--- \nkey: value\n"
14
- # Tap::Script.parse_yaml(str) # => {'key' => 'value'}
15
- # Tap::Script.parse_yaml("str") # => "str"
16
- def parse_yaml(str)
17
- str =~ /\A---\s*\n/ ? YAML.load(str) : str
18
- end
19
-
20
- SPLIT_ARGV_REGEXP = /\A-{2}(\+*)\z/
21
-
22
- def split(argv)
23
- current = []
24
- current_split = []
25
- splits = [current_split]
26
-
27
- argv.each do |arg|
28
- if arg =~ SPLIT_ARGV_REGEXP
29
- current_split << current unless current.empty?
30
- current = []
31
- current_split = (splits[$1.length] ||= [])
32
- else
33
- current << arg
34
- end
35
- end
36
-
37
- current_split << current unless current.empty?
38
- splits.delete_if {|split| split.nil? || split.empty? }
39
- splits
40
- end
41
-
42
- def shift(argv)
43
- index = nil
44
- argv.each_with_index do |arg, i|
45
- if arg !~ /\A-/
46
- index = i
47
- break
48
- end
49
- end
50
- index == nil ? nil : argv.delete_at(index)
51
- end
52
-
53
10
  def usage(path, cols=80)
54
11
  parse_usage(File.read(path), cols)
55
12
  end