objective_command 0.1.5.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 (40) hide show
  1. data/AUTHORS +5 -0
  2. data/ChangeLog +17 -0
  3. data/NEWS +3 -0
  4. data/README +2 -0
  5. data/Rakefile +8 -0
  6. data/SPEC.gemspec +15 -0
  7. data/SPEC.yml +43 -0
  8. data/bin/ocmd +32 -0
  9. data/lib/hookable.rb +284 -0
  10. data/lib/hooker.rb +47 -0
  11. data/lib/objective_command/all.rb +32 -0
  12. data/lib/objective_command/commands/command.rb +535 -0
  13. data/lib/objective_command/commands/factory.rb +69 -0
  14. data/lib/objective_command/commands/pipe.rb +121 -0
  15. data/lib/objective_command/commands/seq.rb +35 -0
  16. data/lib/objective_command/datas/composite.rb +55 -0
  17. data/lib/objective_command/datas/data.rb +175 -0
  18. data/lib/objective_command/datas/factory.rb +74 -0
  19. data/lib/objective_command/datas/pipe.rb +55 -0
  20. data/lib/objective_command/datas/temp.rb +24 -0
  21. data/lib/objective_command/datas.rb +11 -0
  22. data/lib/objective_command/helpers.rb +113 -0
  23. data/lib/objective_command/runners/exec.rb +46 -0
  24. data/lib/objective_command/runners/fork.rb +91 -0
  25. data/lib/objective_command/runners/mockable.rb +62 -0
  26. data/lib/objective_command/runners/no_run.rb +44 -0
  27. data/lib/objective_command/runners/popen.rb +49 -0
  28. data/lib/objective_command/runners/runner.rb +227 -0
  29. data/lib/objective_command/runners/system.rb +54 -0
  30. data/lib/objective_command/runners.rb +11 -0
  31. data/lib/objective_command/shell.rb +173 -0
  32. data/lib/objective_command/version_id.rb +10 -0
  33. data/lib/objective_command.rb +54 -0
  34. data/test/check-objective_command.yml +10 -0
  35. data/test/check-pkg-objective_command.yml +13 -0
  36. data/test/sanity/multiple-requires.yml +62 -0
  37. data/test/sanity/single-requires.yml +40 -0
  38. data/test/sanity-suite.yml +10 -0
  39. data/test/unit-suite.yml +14 -0
  40. metadata +97 -0
@@ -0,0 +1,35 @@
1
+ # Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
2
+ # Copyright:: Copyright (c) 2005, 2006 Nicolas Pouillard. All rights reserved.
3
+ # License:: GNU General Public License (GPL).
4
+ # Revision:: $Id: /w/fey/cmd/trunk/lib/objective_command/commands/seq.rb 22684 2006-03-12T16:36:11.301558Z pouillar $
5
+
6
+ module ObjectiveCommand
7
+
8
+ module Commands
9
+
10
+ class Seq < Command
11
+
12
+ def initialize ( *cmds )
13
+ @cmds = cmds
14
+ @input, @output, @error = nil, nil, nil
15
+ end
16
+
17
+ def run ( *a )
18
+ datas = []
19
+ @cmds.each do |cmd|
20
+ datas.last.waitpid unless datas.empty?
21
+ datas << cmd.run(*a)
22
+ end
23
+ Datas::Composite.new(datas)
24
+ end
25
+
26
+ def to_sh
27
+ strs = @cmds.map { |cmd| "(#{cmd.to_sh})" }
28
+ "(#{strs.join(' ; ')})#{sh_args}"
29
+ end
30
+
31
+ end # class Seq
32
+
33
+ end # module Commands
34
+
35
+ end # module ObjectiveCommand
@@ -0,0 +1,55 @@
1
+ # Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
2
+ # Copyright:: Copyright (c) 2005, 2006 Nicolas Pouillard. All rights reserved.
3
+ # License:: GNU General Public License (GPL).
4
+ # Revision:: $Id: /w/fey/cmd/trunk/lib/objective_command/datas/composite.rb 22684 2006-03-12T16:36:11.301558Z pouillar $
5
+
6
+ module ObjectiveCommand
7
+
8
+ module Datas
9
+
10
+ class Composite < self::Data
11
+
12
+ attr_accessor :contents
13
+
14
+ def initialize ( *contents )
15
+ super()
16
+ @contents = contents.flatten
17
+ end
18
+
19
+ def each ( &block )
20
+ @contents.each(&block)
21
+ end
22
+
23
+ def waitpid
24
+ each { |x| x.waitpid }
25
+ end
26
+
27
+ def kill ( *a, &b )
28
+ each { |x| x.kill(*a, &b) }
29
+ end
30
+
31
+ def << ( data )
32
+ @contents << data
33
+ end
34
+
35
+ def status
36
+ @contents.last.status
37
+ end
38
+
39
+ def output
40
+ @contents.last.output
41
+ end
42
+
43
+ def error
44
+ @contents.last.error
45
+ end
46
+
47
+ def input
48
+ @contents.first.input
49
+ end
50
+
51
+ end # class Composite
52
+
53
+ end # module Datas
54
+
55
+ end # module ObjectiveCommand
@@ -0,0 +1,175 @@
1
+ # Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
2
+ # Copyright:: Copyright (c) 2005, 2006 Nicolas Pouillard. All rights reserved.
3
+ # License:: GNU General Public License (GPL).
4
+ # Revision:: $Id: /w/fey/cmd/trunk/lib/objective_command/datas/data.rb 24389 2006-07-09T20:37:31.813443Z ertai $
5
+
6
+ module ObjectiveCommand
7
+
8
+ module Datas
9
+
10
+ #
11
+ # A CommandData instance contains all information returned by a Command#run:
12
+ #
13
+ # * It's exit status:
14
+ # - Exit number.
15
+ # - Signals
16
+ # - ...
17
+ #
18
+ # * It's outputs:
19
+ # - What it writes on stdout.
20
+ # - stderr.
21
+ class Data
22
+
23
+ attr_accessor :pid
24
+ attr_reader :input
25
+ attr_reader :output
26
+ attr_reader :error
27
+ attr_accessor :status
28
+ attr_reader :open_mode
29
+
30
+ @@datas ||= Set.new
31
+
32
+ def initialize
33
+ @open_mode = :w
34
+ @status, @pid = nil, nil
35
+ @@datas << self
36
+ end
37
+
38
+
39
+ def waitpid
40
+ return @status unless @status.nil?
41
+ return if @pid.nil?
42
+ Process.waitpid(@pid)
43
+ @status = $?
44
+ @@datas.delete(self)
45
+ self
46
+ end
47
+
48
+
49
+ def | ( rhs ) # FIXME CHECK ME
50
+ raise unless rhs.is_a? Commands::Command
51
+ case output
52
+ when Pathname then rhs < output
53
+ when File then rhs < output.path.to_path
54
+ else raise "Unexpected class #{rhs.class}"
55
+ end
56
+ end
57
+
58
+
59
+ def method_missing ( meth, *a, &b )
60
+ if output.respond_to?(meth)
61
+ output.send(meth, *a, &b)
62
+ else
63
+ super
64
+ end
65
+ end
66
+
67
+
68
+ def display ( out=STDOUT, err=STDERR )
69
+ waitpid
70
+ out.print @output.read
71
+ err.print @error.read
72
+ end
73
+
74
+ def input= ( anObject )
75
+ if anObject.is_a? Pathname
76
+ anObject = anObject.dup
77
+ anObject.open_mode = :r
78
+ end
79
+ @input = anObject
80
+ end
81
+
82
+ def output= ( anObject )
83
+ if anObject.is_a? Pathname
84
+ anObject = anObject.dup
85
+ anObject.open_mode = @open_mode
86
+ end
87
+ @output = anObject
88
+ end
89
+
90
+
91
+ def error= ( anObject )
92
+ if anObject.is_a? Pathname
93
+ anObject = anObject.dup
94
+ anObject.open_mode = @open_mode
95
+ end
96
+ @error = anObject
97
+ end
98
+
99
+
100
+ def open_mode= ( mode )
101
+ raise ArgumentError, "bad mode #{mode} need :w or :a" if mode == :r
102
+ @open_mode = mode
103
+ end
104
+
105
+ def exit
106
+ if status.signaled?
107
+ 128 + status.termsig # Common exit status returned by sh
108
+ else
109
+ status.exitstatus
110
+ end
111
+ end
112
+
113
+ have YamlExtension, :'commands::data'
114
+
115
+ def to_yaml_string
116
+ suppress(IOError) { out = output.read }
117
+ suppress(IOError) { err = error.read }
118
+ out ||= ''
119
+ err ||= ''
120
+
121
+ msg = []
122
+ msg << "exit: #{status.exitstatus}" unless status.nil?
123
+ unless out.empty?
124
+ out.gsub!(/^/, ' ')
125
+ msg << "output: |\n #{out}"
126
+ end
127
+ unless err.empty?
128
+ err.gsub!(/^/, ' ')
129
+ msg << "error: |\n #{err}"
130
+ end
131
+ "\n" + msg.join("\n")
132
+ end
133
+
134
+ def kill ( sig='KILL' )
135
+ Process.kill sig, @pid if @status.nil? and not @pid.nil?
136
+ waitpid
137
+ end
138
+
139
+ def clean
140
+ return unless @status.nil?
141
+ return if @pid.nil?
142
+ if Process.waitpid(@pid, Process::WNOHANG)
143
+ @status = $?
144
+ @@datas.delete(self)
145
+ else
146
+ kill
147
+ end
148
+ [input, output, error].each do |stream|
149
+ next if stream.nil? or stream.is_a? IO
150
+ if stream.is_a? File
151
+ stream.close unless stream.closed?
152
+ stream = stream.path.to_path
153
+ end
154
+ if stream.is_a? Pathname
155
+ stream.unlink if stream.exist? and stream.temp?
156
+ elsif stream.is_a? String
157
+ elsif stream.respond_to? :clean
158
+ stream.clean
159
+ else
160
+ raise ArgumentError, "must at least respond to :clean (#{stream})"
161
+ end
162
+ end
163
+ end
164
+
165
+ def self.clean_all
166
+ @@datas.dup.each { |data| data.clean }
167
+ end
168
+
169
+ at_exit { clean_all }
170
+
171
+ end # class Data
172
+
173
+ end # module Datas
174
+
175
+ end # module ObjectiveCommand
@@ -0,0 +1,74 @@
1
+ # Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
2
+ # Copyright:: Copyright (c) 2005, 2006 Nicolas Pouillard. All rights reserved.
3
+ # License:: GNU General Public License (GPL).
4
+ # Revision:: $Id: /w/fey/cmd/trunk/lib/objective_command/datas/factory.rb 22684 2006-03-12T16:36:11.301558Z pouillar $
5
+
6
+ module ObjectiveCommand
7
+
8
+ module Datas
9
+
10
+ class Factory < self::Data
11
+
12
+ attr_accessor :command_data_class
13
+
14
+ def initialize ( values={}, *a, &b )
15
+ super(*a, &b)
16
+ values.each do |k, v|
17
+ send("#{k}=", v)
18
+ end
19
+ @command_data_class ||= Temp
20
+ end
21
+
22
+ def new_command_data ( *a, &b )
23
+ data = @command_data_class.new(*a, &b)
24
+ instance_variables.each do |var|
25
+ next if var == '@command_data_class'
26
+ next if var =~ /^@__/
27
+ val = instance_variable_get(var)
28
+ data.send(var[1..-1] + '=', val) unless val.nil?
29
+ end
30
+ data
31
+ end
32
+
33
+ def create ( *a, &b )
34
+ new_command_data(*a, &b)
35
+ end
36
+
37
+ end # class Factory
38
+
39
+
40
+
41
+
42
+ test_section __FILE__ do
43
+
44
+ class FactoryTest < Test::Unit::TestCase
45
+
46
+ def setup
47
+ assert_nothing_raised { @fa = Factory.new }
48
+ end
49
+
50
+ def test_0_initialize
51
+ end
52
+
53
+ def test_new_command_data
54
+ assert_nothing_raised do
55
+ assert_kind_of(Data, @fa.create)
56
+ assert_kind_of(Data, @fa.new_command_data)
57
+ end
58
+ end
59
+
60
+ def test_simple
61
+ assert_nothing_raised do
62
+ @fa.pid = 42
63
+ @data = @fa.create
64
+ end
65
+ assert_equal(42, @data.pid)
66
+ end
67
+
68
+ end # class FactoryTest
69
+
70
+ end
71
+
72
+ end # module Datas
73
+
74
+ end # module ObjectiveCommand
@@ -0,0 +1,55 @@
1
+ # Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
2
+ # Copyright:: Copyright (c) 2005, 2006 Nicolas Pouillard. All rights reserved.
3
+ # License:: GNU General Public License (GPL).
4
+ # Revision:: $Id: /w/fey/cmd/trunk/lib/objective_command/datas/pipe.rb 24389 2006-07-09T20:37:31.813443Z ertai $
5
+
6
+ class IOPipe
7
+ attr_accessor :reader, :writer, :open_mode
8
+ def initialize
9
+ @reader, @writer = IO.pipe
10
+ @open_mode = :w
11
+ end
12
+ def open ( mode=:r )
13
+ case mode.to_sym
14
+ when :r
15
+ @writer.close
16
+ @reader
17
+ when :w
18
+ @reader.close
19
+ @writer
20
+ else raise ArgumentError, "Bad open mode: #{mode.inspect}"
21
+ end
22
+ end
23
+ def close
24
+ @reader.close unless @reader.closed?
25
+ @writer.close unless @writer.closed?
26
+ end
27
+ alias_method :clean, :close
28
+ def to_io_for_ocmd
29
+ open(open_mode)
30
+ end
31
+ def each_line &block
32
+ open.each_line(&block)
33
+ end
34
+ def read
35
+ open.read
36
+ end
37
+ end
38
+
39
+ module ObjectiveCommand
40
+
41
+ module Datas
42
+
43
+ class Pipe < self::Data
44
+
45
+ def initialize ( *a, &b )
46
+ super
47
+ @output = IOPipe.new
48
+ @error = IOPipe.new
49
+ end
50
+
51
+ end # class Pipe
52
+
53
+ end # module Datas
54
+
55
+ end # module ObjectiveCommand
@@ -0,0 +1,24 @@
1
+ # Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
2
+ # Copyright:: Copyright (c) 2005, 2006 Nicolas Pouillard. All rights reserved.
3
+ # License:: GNU General Public License (GPL).
4
+ # Revision:: $Id: /w/fey/cmd/trunk/lib/objective_command/datas/temp.rb 22684 2006-03-12T16:36:11.301558Z pouillar $
5
+
6
+ module ObjectiveCommand
7
+
8
+ module Datas
9
+
10
+ class Temp < self::Data
11
+
12
+ def initialize ( *a, &b )
13
+ super
14
+ @output = TempPath.new('cmd-output')
15
+ @output.open_mode = :w
16
+ @error = TempPath.new('cmd-error')
17
+ @error.open_mode = :w
18
+ end
19
+
20
+ end # class Temp
21
+
22
+ end # module Datas
23
+
24
+ end # module ObjectiveCommand
@@ -0,0 +1,11 @@
1
+ # Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
2
+ # Copyright:: Copyright (c) 2005, 2006 Nicolas Pouillard. All rights reserved.
3
+ # License:: GNU General Public License (GPL).
4
+ # Revision:: $Id: /w/fey/cmd/trunk/lib/objective_command/datas.rb 22684 2006-03-12T16:36:11.301558Z pouillar $
5
+
6
+ module ObjectiveCommand
7
+
8
+ module Datas
9
+ end # module Datas
10
+
11
+ end # module ObjectiveCommand
@@ -0,0 +1,113 @@
1
+ # Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
2
+ # Copyright:: Copyright (c) 2005, 2006 Nicolas Pouillard. All rights reserved.
3
+ # License:: GNU General Public License (GPL).
4
+ # Revision:: $Id: /w/fey/cmd/trunk/lib/objective_command/helpers.rb 53907 2007-01-13T18:40:33.521685Z ertai $
5
+
6
+ require 'shellwords'
7
+
8
+ module ObjectiveCommand
9
+
10
+ module Helpers
11
+
12
+ class ::Object
13
+
14
+ def to_io_for_ocmd
15
+ to_io
16
+ end
17
+
18
+ def to_ocmd_args
19
+ [self]
20
+ end
21
+
22
+ end
23
+
24
+
25
+ class ::String
26
+
27
+ #
28
+ # Make a command from a String.
29
+ #
30
+ # Split your command using the Shellwords module.
31
+ #
32
+ def to_ocmd
33
+ Shellwords.shellwords(self).to_ocmd
34
+ end
35
+
36
+ def to_io_for_ocmd
37
+ tmp = TempPath.new
38
+ tmp.open('w') { |f| f.write self }
39
+ tmp.to_io_for_ocmd
40
+ end
41
+
42
+ def to_ocmd_args
43
+ Shellwords.shellwords(self)
44
+ end
45
+
46
+ def to_sh
47
+ (self =~ /[^-\w:_\/\.@]/)? dump : self
48
+ end
49
+
50
+ end # class ::String
51
+
52
+
53
+ class ::Array
54
+
55
+ def to_ocmd
56
+ ObjectiveCommand::Commands::Command.new(*self)
57
+ end
58
+
59
+ def to_ocmd_args
60
+ dup
61
+ end
62
+
63
+ end # class ::Array
64
+
65
+
66
+ class ::Pathname
67
+
68
+ def to_ocmd
69
+ to_s.to_ocmd
70
+ end
71
+
72
+ def to_ocmd_args
73
+ to_s.to_ocmd_args
74
+ end
75
+
76
+ end # class ::Pathname
77
+
78
+
79
+ class ::NilClass
80
+
81
+ def to_ocmd_args
82
+ []
83
+ end
84
+
85
+ end # class ::NilClass
86
+
87
+
88
+ end # module Helpers
89
+
90
+ test_section __FILE__ do
91
+
92
+ class HelpersTest < Test::Unit::TestCase
93
+
94
+ def test_string_to_ocmd
95
+ assert_equal(['foo', 'bar'], "foo bar".to_ocmd.to_a)
96
+
97
+ assert_equal(['foo', 'b/ar', 'qu44x32'],
98
+ "foo b/ar \t \n qu44x32".to_ocmd.to_a)
99
+
100
+ s = "foo 'b/ar \t' \n qu44x32"
101
+ assert_equal(["foo", "b/ar \t", "qu44x32"], s.to_ocmd.to_a)
102
+ end
103
+
104
+ def test_array_to_ocmd
105
+ a = ['foo', "b/ar \t", "\n qu44x32"]
106
+ assert_equal(a, a.to_ocmd.to_a)
107
+ end
108
+
109
+ end # class HelpersTest
110
+
111
+ end
112
+
113
+ end # module ObjectiveCommand
@@ -0,0 +1,46 @@
1
+ # Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
2
+ # Copyright:: Copyright (c) 2005, 2006 Nicolas Pouillard. All rights reserved.
3
+ # License:: GNU General Public License (GPL).
4
+ # Revision:: $Id: /w/fey/cmd/trunk/lib/objective_command/runners/exec.rb 23187 2006-03-31T21:59:37.612710Z ertai $
5
+
6
+ module ObjectiveCommand
7
+
8
+ module Runners
9
+
10
+ class Exec < Runner
11
+ make Concrete
12
+
13
+ def exec ( aCommand, data )
14
+ Kernel.exec(*aCommand.to_a)
15
+ end
16
+
17
+ end # class Exec
18
+
19
+ test_section __FILE__ do
20
+
21
+ class ExecTest < Test::Unit::TestCase
22
+
23
+ def setup
24
+ assert_nothing_raised { @runner = Exec.new }
25
+ end
26
+
27
+ def test_0_initialize
28
+ end
29
+
30
+ def test_simple
31
+ TempPath.new do |tmp|
32
+ cmd = %Q[ruby -e "File.open('#{tmp}', 'w') { |f| f.puts :foo }"]
33
+ pid = fork do
34
+ @runner.run(cmd.to_ocmd)
35
+ end
36
+ Process.waitpid pid
37
+ end
38
+ end
39
+
40
+ end # class ExecTest
41
+
42
+ end
43
+
44
+ end # module Runners
45
+
46
+ end # module ObjectiveCommand
@@ -0,0 +1,91 @@
1
+ # Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
2
+ # Copyright:: Copyright (c) 2005, 2006 Nicolas Pouillard. All rights reserved.
3
+ # License:: GNU General Public License (GPL).
4
+ # Revision:: $Id: /w/fey/cmd/trunk/lib/objective_command/runners/fork.rb 24389 2006-07-09T20:37:31.813443Z ertai $
5
+
6
+ module ObjectiveCommand
7
+
8
+ module Runners
9
+
10
+ class Fork < Exec
11
+ make Concrete
12
+
13
+ #
14
+ # Methods
15
+ #
16
+
17
+ def fork data, &block
18
+ data.pid = Kernel.fork do
19
+ TempPath.fork_init true
20
+ block.call
21
+ end
22
+ end
23
+
24
+
25
+ def abort ( data )
26
+ if data.pid
27
+ begin
28
+ hook_trigger :kill, data
29
+ hook_trigger :waitpid, data
30
+ rescue Errno::ESRCH
31
+ hook_trigger :already_killed, data
32
+ end
33
+ end
34
+ end
35
+
36
+
37
+ def exception_raised_during_exec ( anException )
38
+ STDERR.reopen(File.new(1))
39
+ STDERR.puts anException
40
+ exit! 127
41
+ end
42
+
43
+
44
+ def kill ( data )
45
+ data.kill
46
+ end
47
+
48
+
49
+ def failure command, data
50
+ if data.status and data.status.signaled?
51
+ data.status.instance_eval { @exitstatus = 134 }
52
+ end
53
+ end
54
+
55
+
56
+ def before_exec ( command, data )
57
+ TempPath.clean
58
+ end
59
+
60
+ end # class Fork
61
+
62
+
63
+ test_section __FILE__ do
64
+
65
+ class ForkTest < Test::Unit::TestCase
66
+
67
+ def setup
68
+ assert_nothing_raised { @runner = Fork.new }
69
+ end
70
+
71
+ def test_0_initialize
72
+ end
73
+
74
+ def test_simple
75
+ TempPath.new do |tmp|
76
+ cmd = %Q[ruby -e "sleep 1
77
+ File.open('#{tmp}', 'w') { |f| f.puts :foo }"]
78
+ data = @runner.run(cmd.to_ocmd)
79
+ assert(! tmp.exist?)
80
+ data.waitpid
81
+ assert(tmp.exist?)
82
+ end
83
+ end
84
+
85
+ end # class ForkTest
86
+
87
+ end
88
+
89
+ end # module Runners
90
+
91
+ end # module ObjectiveCommand