em-systemcommand 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/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use --create --install 1.9.3@em-systemcommand
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in em-systemcommand.gemspec
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,15 @@
1
+ guard 'rspec', :version => 2 do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
4
+ watch('spec/spec_helper.rb') { "spec" }
5
+
6
+ # Rails example
7
+ watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
8
+ watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
9
+ watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
10
+ watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
11
+ watch('config/routes.rb') { "spec/routing" }
12
+ watch('app/controllers/application_controller.rb') { "spec/controllers" }
13
+ # Capybara request specs
14
+ watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/requests/#{m[1]}_spec.rb" }
15
+ end
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Arthur Andersen
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,86 @@
1
+ # EM::SystemCommand
2
+
3
+ `EM::SystemCommand` is an `popen3` abstraction for eventmachine to easily create subprocesses with eventmachine.
4
+ The goal is to provide an easy way to invoke system commands and to read and handle their outputs. When creating an
5
+ `EM::SystemCommand` object its basically like a popen. It has `#stdin`, `#stdout` and `#stderr`.
6
+ Which are related to `EM::Connection`.
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ gem 'em-systemcommand'
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install em-systemcommand
21
+
22
+ ## Usage
23
+
24
+ To simply run a process you can instatiate an `EM::SystemCommand`
25
+ object and set up the callbacks in the yielded block.
26
+
27
+ EM.run do
28
+ EM::SystemCommand 'my_command' do |on|
29
+ on.success do |ps|
30
+ puts "Success!"
31
+ end
32
+
33
+ on.failure do |ps|
34
+ puts "Failure with status #{ps.status.exitstatus}"
35
+ end
36
+ end
37
+ end
38
+
39
+ When you want to retreive output, you can use the methods
40
+ `#update`, `#line` and `#data` on a pipe object like so:
41
+
42
+ EM.run do
43
+ EM::SystemCommand 'my_command' do |on|
44
+ on.success do |ps|
45
+ puts "Success!"
46
+ end
47
+
48
+ on.stdout.data do |data|
49
+ puts "Data: #{data}"
50
+ end
51
+
52
+ on.stdout.line do |line|
53
+ puts "Line: #{line}"
54
+ end
55
+
56
+ # `#output` gets the whole output buffer.
57
+ # This means, it has theoretically the screen you´d get when
58
+ # invoking the command in the shell. Although only \r is used.
59
+ on.stdout.update do |output|
60
+ puts output
61
+ end
62
+ end
63
+ end
64
+
65
+ `Pipe` objects even have a nice convenient method `#match` which lets
66
+ you match output against a regular expression:
67
+
68
+ EM.run do
69
+ EM::SystemCommand 'echo "25%\n"; sleep 1; echo "50%\n"; sleep 1; echo "75%\n"; sleep 1; echo "100%\n"; exit 0;' do |on|
70
+ on.success do |ps|
71
+ puts "Success!"
72
+ end
73
+ .
74
+ on.stdout.match /([0-9]+)%/, in: :line do |match, progress|
75
+ puts "Percentage: #{progress}"
76
+ end
77
+ end
78
+ end
79
+
80
+ ## Contributing
81
+
82
+ 1. Fork it
83
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
84
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
85
+ 4. Push to the branch (`git push origin my-new-feature`)
86
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/em-systemcommand/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Arthur Andersen"]
6
+ gem.email = ["leoc.git@gmail.com"]
7
+ gem.description = %q{}
8
+ gem.summary = %q{}
9
+ gem.homepage = ""
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "em-systemcommand"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Em::Systemcommand::VERSION
17
+
18
+ gem.add_development_dependency 'rspec'
19
+ gem.add_development_dependency 'guard'
20
+ gem.add_development_dependency 'guard-rspec'
21
+
22
+ gem.add_dependency 'eventmachine'
23
+ end
@@ -0,0 +1,122 @@
1
+ module EventMachine
2
+ class SystemCommand
3
+ class Pipe < EM::Connection
4
+
5
+ def initialize(master, name)
6
+ @master = master
7
+ @name = name
8
+ @master.pipes[name] = self
9
+ begin
10
+ @outputbuffer = StringIO.new()
11
+ rescue Exception => e
12
+ puts "Uninitialized constant StringIO. This may happen when you forgot to use bundler. Use `bundle exec`."
13
+ end
14
+ end
15
+
16
+ def output
17
+ @outputbuffer.string
18
+ end
19
+
20
+ # Convenience method to create a callback that matches a regular expression
21
+ def match regexp, opt = {}, &block
22
+ opt = { in: :line }.merge opt
23
+ (opt[:in] == :output ? receive_update_callbacks : receive_line_callbacks) << lambda do |data|
24
+ if m = data.match(regexp)
25
+ block.call m.to_a
26
+ end
27
+ end
28
+ end
29
+
30
+ # Invoked when line was received
31
+ def receive_line line
32
+ receive_line_callbacks.each do |callback|
33
+ callback.call line
34
+ end
35
+ end
36
+
37
+ # Adds a callback for `receive_line` events.
38
+ def line &block
39
+ receive_line_callbacks << block
40
+ end
41
+
42
+ # Invoked when a line was written, but `\r` was received without
43
+ # a line-break in the end.
44
+ def receive_update line
45
+ receive_update_callbacks.each do |callback|
46
+ callback.call line
47
+ end
48
+ end
49
+
50
+ # Adds a callback for `receive_update` events.
51
+ def update &block
52
+ receive_update_callbacks << block
53
+ end
54
+
55
+ # Invoked when data was received.
56
+ def receive_data data
57
+ receive_data_callbacks.each do |callback|
58
+ callback.call data
59
+ end
60
+
61
+ @lt2_linebuffer ||= []
62
+
63
+ ix = data.index("\r")
64
+ if ix and data[ix+1] != "\n"
65
+ @lt2_linebuffer << data[0...ix]
66
+ ln = @lt2_linebuffer.join
67
+ @lt2_linebuffer.clear
68
+ @outputbuffer.print ln
69
+ @outputbuffer.pos -= ln.length
70
+ receive_line ln
71
+ receive_update @outputbuffer.string
72
+ receive_data data[(ix+1)..-1] # receive rest data
73
+ elsif ix = data.index("\n")
74
+ @lt2_linebuffer << data[0...ix]
75
+ ln = @lt2_linebuffer.join
76
+ @lt2_linebuffer.clear
77
+ ln.chomp!
78
+ @outputbuffer.puts ln
79
+ receive_line ln
80
+ receive_update @outputbuffer.string
81
+ receive_data data[(ix+1)..-1] # receive rest data
82
+ else
83
+ @lt2_linebuffer << data
84
+ end
85
+ end
86
+
87
+ # Adds a callback for `receive_data` events.
88
+ def data &block
89
+ receive_data_callbacks << block
90
+ end
91
+
92
+ # Close the attached IO object.
93
+ def close
94
+ begin
95
+ @io.close unless @io.closed?
96
+ rescue Exception => e
97
+ # ignore errors, when the io object might be closed already
98
+ end
99
+ end
100
+
101
+ # Invoked when the connection is terminated. Calls
102
+ # `unbind(@name)` on master.
103
+ def unbind
104
+ self.close
105
+ @master.unbind(@name)
106
+ end
107
+
108
+ private
109
+ def receive_line_callbacks
110
+ @receive_line_callbacks ||= []
111
+ end
112
+
113
+ def receive_update_callbacks
114
+ @receive_update_callbacks ||= []
115
+ end
116
+
117
+ def receive_data_callbacks
118
+ @receive_data_callbacks ||= []
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,33 @@
1
+ module EventMachine
2
+ class SystemCommand
3
+ module PipeHandler
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ def attach_pipe_handler name, io_object
9
+ EM.attach(io_object, pipe_handler_class(name), self, name)
10
+ end
11
+
12
+ def pipe_handler_class name
13
+ self.class.pipe_handlers[name]
14
+ end
15
+
16
+ module ClassMethods
17
+
18
+ def pipe_handler name, klass
19
+ pipe_handlers[name] = klass
20
+ end
21
+
22
+ def pipe_handler_class name
23
+ pipe_handlers[name]
24
+ end
25
+
26
+ def pipe_handlers
27
+ @pipe_handlers ||= {}
28
+ end
29
+
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,5 @@
1
+ module Em
2
+ module Systemcommand
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,65 @@
1
+ require 'open3'
2
+
3
+ require "em-systemcommand/version"
4
+
5
+ require "em-systemcommand/pipe"
6
+ require "em-systemcommand/pipe_handler"
7
+
8
+ module EventMachine
9
+ class SystemCommand
10
+ include EM::SystemCommand::PipeHandler
11
+ include EM::Deferrable
12
+
13
+ pipe_handler :stdin, EM::SystemCommand::Pipe
14
+ pipe_handler :stdout, EM::SystemCommand::Pipe
15
+ pipe_handler :stderr, EM::SystemCommand::Pipe
16
+
17
+ attr_accessor :pipes, :stdin, :stdout, :stderr
18
+
19
+ def initialize *arguments
20
+
21
+ @pipes = {}
22
+
23
+ stdin, stdout, stderr, @wait_thr = Open3.popen3(*arguments)
24
+
25
+ @stdin = attach_pipe_handler :stdin, stdin
26
+ @stdout = attach_pipe_handler :stdout, stdout
27
+ @stderr = attach_pipe_handler :stderr, stderr
28
+
29
+ yield self if block_given?
30
+
31
+ end
32
+
33
+ def pid
34
+ @wait_thr.pid
35
+ end
36
+
37
+ def status
38
+ @wait_thr.value
39
+ end
40
+
41
+ alias_method :success, :callback
42
+ alias_method :failure, :errback
43
+
44
+ def unbind name
45
+ pipes.delete name
46
+ if pipes.empty?
47
+ if status.exitstatus == 0
48
+ succeed
49
+ else
50
+ fail status
51
+ end
52
+ end
53
+ end
54
+
55
+ def kill signal = 'TERM', wait = false
56
+ Process.kill signal, self.pid
57
+ val = status if wait
58
+ @stdin.close
59
+ @stdout.close
60
+ @stderr.close
61
+ val
62
+ end
63
+
64
+ end
65
+ end
@@ -0,0 +1,59 @@
1
+ require 'spec_helper'
2
+
3
+ describe EM::SystemCommand do
4
+
5
+ it 'should take a success callback' do
6
+ called = false
7
+ EM.run do
8
+ EM::SystemCommand.new 'echo "123"; exit 0;' do |on|
9
+ on.success do |ps|
10
+ called = true
11
+ end
12
+ end
13
+
14
+ EM.assertions do
15
+ called.should == true
16
+ end
17
+ end
18
+ end
19
+
20
+ it 'should take a failure callback' do
21
+ called = false
22
+ EM.run do
23
+ EM::SystemCommand.new 'echo "123"; exit 1;' do |on|
24
+ on.failure do |ps|
25
+ called = true
26
+ end
27
+ end
28
+
29
+ EM.assertions do
30
+ called.should == true
31
+ end
32
+ end
33
+ end
34
+
35
+ it 'should have stdin pipe' do
36
+ EM.run do
37
+ ps = EM::SystemCommand.new 'echo "123"; exit 1;'
38
+ ps.stdin.should be_a EM::SystemCommand::Pipe
39
+ EM.stop_event_loop
40
+ end
41
+ end
42
+
43
+ it 'should have stdout pipe' do
44
+ EM.run do
45
+ ps = EM::SystemCommand.new 'echo "123"; exit 1;'
46
+ ps.stdout.should be_a EM::SystemCommand::Pipe
47
+ EM.stop_event_loop
48
+ end
49
+ end
50
+
51
+ it 'should have stderr pipe' do
52
+ EM.run do
53
+ ps = EM::SystemCommand.new 'echo "123"; exit 1;'
54
+ ps.stderr.should be_a EM::SystemCommand::Pipe
55
+ EM.stop_event_loop
56
+ end
57
+ end
58
+
59
+ end
@@ -0,0 +1,24 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # Require this file using `require "spec_helper.rb"` to ensure that it is only
4
+ # loaded once.
5
+ #
6
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+
8
+ require 'eventmachine'
9
+ require 'em-systemcommand'
10
+
11
+ EventMachine.instance_eval do
12
+ def assertions time = 1
13
+ EM.add_timer(time) do
14
+ EM.stop_event_loop
15
+ yield
16
+ end
17
+ end
18
+ end unless EM.respond_to?(:assertions)
19
+
20
+ RSpec.configure do |config|
21
+ config.treat_symbols_as_metadata_keys_with_true_values = true
22
+ config.run_all_when_everything_filtered = true
23
+ config.filter_run :focus
24
+ end
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: em-systemcommand
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Arthur Andersen
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-05-03 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &18939980 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *18939980
25
+ - !ruby/object:Gem::Dependency
26
+ name: guard
27
+ requirement: &18939560 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *18939560
36
+ - !ruby/object:Gem::Dependency
37
+ name: guard-rspec
38
+ requirement: &18939000 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *18939000
47
+ - !ruby/object:Gem::Dependency
48
+ name: eventmachine
49
+ requirement: &18938420 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: *18938420
58
+ description: ''
59
+ email:
60
+ - leoc.git@gmail.com
61
+ executables: []
62
+ extensions: []
63
+ extra_rdoc_files: []
64
+ files:
65
+ - .gitignore
66
+ - .rspec
67
+ - .rvmrc
68
+ - Gemfile
69
+ - Guardfile
70
+ - LICENSE
71
+ - README.md
72
+ - Rakefile
73
+ - em-systemcommand.gemspec
74
+ - lib/em-systemcommand.rb
75
+ - lib/em-systemcommand/pipe.rb
76
+ - lib/em-systemcommand/pipe_handler.rb
77
+ - lib/em-systemcommand/version.rb
78
+ - spec/em-systmcommand_spec.rb
79
+ - spec/spec_helper.rb
80
+ homepage: ''
81
+ licenses: []
82
+ post_install_message:
83
+ rdoc_options: []
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ! '>='
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ segments:
93
+ - 0
94
+ hash: -1466650369422800238
95
+ required_rubygems_version: !ruby/object:Gem::Requirement
96
+ none: false
97
+ requirements:
98
+ - - ! '>='
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ segments:
102
+ - 0
103
+ hash: -1466650369422800238
104
+ requirements: []
105
+ rubyforge_project:
106
+ rubygems_version: 1.8.17
107
+ signing_key:
108
+ specification_version: 3
109
+ summary: ''
110
+ test_files:
111
+ - spec/em-systmcommand_spec.rb
112
+ - spec/spec_helper.rb