funkysystem 0.0.1

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/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in funkysystem.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,24 @@
1
+ Copyright (c) 2013 Geoff Youngs
2
+
3
+
4
+
5
+ MIT License
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining
8
+ a copy of this software and associated documentation files (the
9
+ "Software"), to deal in the Software without restriction, including
10
+ without limitation the rights to use, copy, modify, merge, publish,
11
+ distribute, sublicense, and/or sell copies of the Software, and to
12
+ permit persons to whom the Software is furnished to do so, subject to
13
+ the following conditions:
14
+
15
+ The above copyright notice and this permission notice shall be
16
+ included in all copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,37 @@
1
+ # FunkySystem
2
+
3
+ An enhanced version of system() that allows the capture of stdout,
4
+ stderr & also feeding data to stdin.
5
+
6
+ The main method is FunkySystem.run(cmd, stdin).
7
+
8
+ e.g.
9
+
10
+ result = FunkySystem.run(["bc"], "3+2\n4+23\nquit")
11
+ result.stdout # => "5\n27\n"
12
+
13
+ ## Installation
14
+
15
+ Add this line to your application's Gemfile:
16
+
17
+ gem 'funkysystem'
18
+
19
+ And then execute:
20
+
21
+ $ bundle
22
+
23
+ Or install it yourself as:
24
+
25
+ $ gem install funkysystem
26
+
27
+ ## Usage
28
+
29
+ TODO: Write usage instructions here
30
+
31
+ ## Contributing
32
+
33
+ 1. Fork it
34
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
35
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
36
+ 4. Push to the branch (`git push origin my-new-feature`)
37
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'funkysystem/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "funkysystem"
8
+ spec.version = FunkySystem::VERSION
9
+ spec.authors = ["Geoff Youngs"]
10
+ spec.email = ["git@intersect-uk.co.uk"]
11
+ spec.description = %q{Like system() but with input feeding & output capture}
12
+ spec.summary = %q{Like system() but funkier}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ end
@@ -0,0 +1,174 @@
1
+ require "funkysystem/version"
2
+ require 'io/nonblock'
3
+
4
+ module FunkySystem
5
+ # Run block, but with output redirected to elsewhere
6
+ #
7
+ # @param outputs [Hash] :stdout & :stderr - either an IO object, a filename or nil to redirect stream to /dev/null
8
+ def self.redirect_output(options = {}, &block)
9
+ stderr_dup = STDERR.dup
10
+ stdout_dup = STDOUT.dup
11
+ begin
12
+ output_to_writeable_io(options[:stdout]) do |out|
13
+ STDOUT.reopen(out)
14
+ output_to_writeable_io(options[:stderr]) do |err|
15
+ STDERR.reopen(err)
16
+ block.call
17
+ end
18
+ end
19
+ ensure
20
+ STDERR.reopen(stderr_dup)
21
+ STDOUT.reopen(stdout_dup)
22
+ end
23
+ end
24
+ def self.output_to_writeable_io(out, &block)
25
+ case out
26
+ when String
27
+ File.open(out, 'w', &block)
28
+ when IO
29
+ yield(out)
30
+ #when nil
31
+ else
32
+ File.open('/dev/null', 'w', &block)
33
+ end
34
+ end
35
+
36
+
37
+ class ProgramOutput
38
+ attr_reader :pid, :status, :stdout, :stderr, :unused_stdin
39
+ def initialize(pid, status, stdout, stderr, unused_stdin)
40
+ @pid, @status, @stdout, @stderr, @unused_stdin =
41
+ pid, status, stdout, stderr, unused_stdin
42
+ end
43
+ def error?
44
+ ! @status.success?
45
+ end
46
+ def success?
47
+ @status.success?
48
+ end
49
+ end
50
+ def self.find_exec(name)
51
+ ENV["PATH"].split(/:/).each do |bin|
52
+ path = File.join(File.expand_path(bin), name)
53
+ begin
54
+ stat = File.stat(path)
55
+ if stat.executable? && stat.file?
56
+ return path
57
+ end
58
+ rescue Errno::ENOENT
59
+ end
60
+ end
61
+ nil
62
+ end
63
+ def self.run(cmd, stdin=nil)
64
+ fps = {
65
+ :stdout => IO.pipe,
66
+ :stderr => IO.pipe
67
+ }
68
+
69
+ fps[:stdin] = IO.pipe if stdin
70
+
71
+ fps.each do |key,fpa|
72
+ class << fpa
73
+ alias :reader :first
74
+ alias :writer :last
75
+ end
76
+ end
77
+
78
+ pid = fork do
79
+ keep = [STDERR, STDOUT, STDIN] + fps.values.flatten
80
+ ObjectSpace.each_object(IO) { |io|
81
+ begin
82
+ io.close() unless keep.include?(io)
83
+ rescue Exception
84
+ end
85
+ }
86
+ if fps[:stdin]
87
+ fps[:stdin].writer.close
88
+ STDIN.reopen(fps[:stdin].reader)
89
+ fps[:stdin].reader.close
90
+ else
91
+ # No input - child shouldn't
92
+ # be allowed to expect any...
93
+ STDIN.close()
94
+ end
95
+
96
+ fps[:stdout].reader.close
97
+ STDOUT.reopen(fps[:stdout].writer)
98
+ fps[:stdout].writer.close
99
+
100
+ fps[:stderr].reader.close
101
+ STDERR.reopen(fps[:stderr].writer)
102
+ fps[:stderr].writer.close
103
+
104
+ exec(* ( cmd.is_a?(Array) ? cmd : [cmd] ) )
105
+ end
106
+
107
+ fps[:stderr].writer.close
108
+ fps[:stdout].writer.close
109
+
110
+ if fps[:stdin]
111
+ fps[:stdin].reader.close
112
+ out_fds = [fps[:stdin].writer]
113
+ else
114
+ out_fds = []
115
+ end
116
+
117
+ in_fds = [fps[:stderr].reader, fps[:stdout].reader]
118
+ all_fds = in_fds + out_fds
119
+
120
+ all_fds.each { |fd| fd.nonblock = true }
121
+
122
+ stdout = ''
123
+ stderr = ''
124
+ exited = nil
125
+ exit_time = nil
126
+
127
+ until exited && (in_fds.all? { |fd| fd.closed? or fd.eof? } || exit_time < (Time.now.to_i - 3) )
128
+ exited ||= Process.waitpid2(pid, Process::WNOHANG)
129
+ exit_time ||= Time.now.to_i if exited
130
+ begin
131
+ rd, wr, er = select(in_fds, out_fds, all_fds, 0.05)
132
+ rescue IOError
133
+ [in_fds, out_fds, all_fds].each { |grp|
134
+ grp.reject!{ |fd| fd.closed? }
135
+ }
136
+ rd = wr = er = nil
137
+ end
138
+ wr.each do |fd|
139
+ begin
140
+ case fd
141
+ when fps[:stdin].writer
142
+ size = fd.syswrite(stdin)
143
+ stdin = stdin[size..-1]
144
+ if stdin.empty?
145
+ fd.close
146
+ out_fds = []
147
+ stdin = nil
148
+ end
149
+ end
150
+ rescue Errno::EPIPE, IOError
151
+ end
152
+ end if wr.respond_to?(:each)
153
+
154
+ rd.each do |fd|
155
+ begin
156
+ case fd
157
+ when fps[:stderr].reader
158
+ stderr << fd.sysread(4096 * 4)
159
+ when fps[:stdout].reader
160
+ stdout << fd.sysread(4096 * 4)
161
+ end
162
+ rescue Errno::EPIPE, IOError
163
+ end
164
+ end if rd.respond_to?(:each)
165
+
166
+ end
167
+
168
+ ProgramOutput.new(pid, exited.last, stdout, stderr, stdin)
169
+ end
170
+
171
+ end
172
+
173
+
174
+
@@ -0,0 +1,3 @@
1
+ module FunkySystem
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,17 @@
1
+ require 'test/unit'
2
+ require 'funkysystem'
3
+
4
+ class TestFunkySystem < Test::Unit::TestCase
5
+ def test_bc
6
+ prg = FunkySystem.run(["bc"], "3+2\n4+23\nquit")
7
+ assert ! prg.error?
8
+ assert_equal "5\n27\n", prg.stdout
9
+ #p prg, prg.error?
10
+ end
11
+ def test_false
12
+ assert FunkySystem.run(['false']).error?
13
+ end
14
+ def test_true
15
+ assert ! FunkySystem.run(['true']).error?
16
+ end
17
+ end
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: funkysystem
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Geoff Youngs
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2013-08-05 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ version_requirements: &id001 !ruby/object:Gem::Requirement
22
+ none: false
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ hash: 9
27
+ segments:
28
+ - 1
29
+ - 3
30
+ version: "1.3"
31
+ name: bundler
32
+ prerelease: false
33
+ type: :development
34
+ requirement: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ version_requirements: &id002 !ruby/object:Gem::Requirement
37
+ none: false
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ hash: 3
42
+ segments:
43
+ - 0
44
+ version: "0"
45
+ name: rake
46
+ prerelease: false
47
+ type: :development
48
+ requirement: *id002
49
+ description: Like system() but with input feeding & output capture
50
+ email:
51
+ - git@intersect-uk.co.uk
52
+ executables: []
53
+
54
+ extensions: []
55
+
56
+ extra_rdoc_files: []
57
+
58
+ files:
59
+ - .gitignore
60
+ - Gemfile
61
+ - LICENSE.txt
62
+ - README.md
63
+ - Rakefile
64
+ - funkysystem.gemspec
65
+ - lib/funkysystem.rb
66
+ - lib/funkysystem/version.rb
67
+ - test/test_simple.rb
68
+ homepage: ""
69
+ licenses:
70
+ - MIT
71
+ post_install_message:
72
+ rdoc_options: []
73
+
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ none: false
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ hash: 3
82
+ segments:
83
+ - 0
84
+ version: "0"
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ hash: 3
91
+ segments:
92
+ - 0
93
+ version: "0"
94
+ requirements: []
95
+
96
+ rubyforge_project:
97
+ rubygems_version: 1.8.25
98
+ signing_key:
99
+ specification_version: 3
100
+ summary: Like system() but funkier
101
+ test_files:
102
+ - test/test_simple.rb