shr 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,18 @@
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
18
+ TODO
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ # -*- coding: utf-8; mode: ruby; -*-
2
+ source "http://rubygems.org"
3
+
4
+ def darwin_only(require_as)
5
+ RUBY_PLATFORM.include?('darwin') && require_as
6
+ end
7
+
8
+ group :development do
9
+ gem 'growl', :require => darwin_only('growl')
10
+ gem 'guard'
11
+ gem 'guard-rspec'
12
+ gem 'rb-fsevent', '~> 0.9.1', :require => darwin_only('rb-fsevent')
13
+ gem 'rspec'
14
+ end
15
+
16
+ gem 'os'
@@ -0,0 +1,7 @@
1
+ # -*- coding: utf-8; mode: ruby; -*-
2
+
3
+ guard 'rspec' do
4
+ watch(%r{^spec/.+_spec\.rb$})
5
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
6
+ watch('spec/spec_helper.rb') { "spec" }
7
+ end
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 kukenko
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.
@@ -0,0 +1,22 @@
1
+ Shr
2
+ ====
3
+
4
+ Description
5
+ -----------
6
+ Shr tries to make controlling subprocess easier in Ruby. It can be write
7
+ a shell command as like a Ruby's method and captures subprocess's output.
8
+
9
+ This is a port of Python's [sh](https://github.com/amoffat/sh).
10
+
11
+ Installation
12
+ ------------
13
+ TODO: Write installation here
14
+
15
+ Usage
16
+ -----
17
+ TODO: Write usage instructions here
18
+
19
+ License
20
+ -------
21
+ Released under the MIT License. See the [LICENCE][licence] file for further details.
22
+ [license]: https://github.com/kukenko/shr/LICENSE.md
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,11 @@
1
+ #coding: utf-8
2
+ require 'shr/version'
3
+ require 'shr/shell'
4
+
5
+ module Shr
6
+ class << self
7
+ def shell
8
+ Shr::Shell.new
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,46 @@
1
+ #coding: utf-8
2
+
3
+ module Shr
4
+ class Option
5
+ # xxx
6
+ def initialize
7
+ @stack = []
8
+ end
9
+
10
+ # xxx
11
+ # This method has not support Windows yet.
12
+ def parse(options)
13
+ options.each do |opt|
14
+ case opt
15
+ when String
16
+ unless opt.start_with? '-'
17
+ if @stack.last && @stack.last.start_with?('--')
18
+ @stack.push "#{@stack.pop}=#{opt}"
19
+ else
20
+ @stack.push opt
21
+ end
22
+ else
23
+ @stack.push opt
24
+ end
25
+ when Symbol
26
+ @stack.push << "-#{opt.length > 1 ? '-' : ''}#{opt}"
27
+ when Hash
28
+ opt.each do |k, v|
29
+ if v.eql?(true)
30
+ @stack.push << "-#{k.length > 1 ? '-' : ''}#{k}"
31
+ else
32
+ if k.length > 1
33
+ @stack << "--#{k}=#{v}"
34
+ else
35
+ @stack << "-#{k.length > 1 ? '-' : ''}#{k}" << v
36
+ end
37
+ end
38
+ end
39
+ when Fixnum
40
+ @stack.push << "-#{opt}"
41
+ end
42
+ end
43
+ @stack
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,107 @@
1
+ #coding: utf-8
2
+ require 'open3'
3
+ require 'shr/option'
4
+ require 'shr/which'
5
+
6
+ module Shr
7
+ class Shell
8
+
9
+ def initialize
10
+ @promise = []
11
+ @capture = false
12
+ @release = false
13
+ end
14
+
15
+ def method_missing(name, *args)
16
+ command = name.to_s.chomp('!')
17
+ unless Which.exist?(command)
18
+ super
19
+ else
20
+ delay(command, args)
21
+ release { force } if name.to_s.end_with?('!') || @release
22
+ self
23
+ end
24
+ end
25
+
26
+ def to_s
27
+ capture { force }
28
+ @command_out.read if filled?
29
+ end
30
+
31
+ # xxx
32
+ def inspect
33
+ puts "#<Shr::Shell - #{@promise.join(' | ')}>"
34
+ capture { force }
35
+ @command_out.read if filled?
36
+ end
37
+
38
+ def capture(&block)
39
+ @capture = true
40
+ instance_exec(&block)
41
+ @capture = false
42
+ end
43
+
44
+ def release(&block)
45
+ @release = true
46
+ instance_exec(&block)
47
+ @release = false
48
+ end
49
+
50
+ def each
51
+ capture { force }
52
+ if filled?
53
+ block_given? ? @command_out.each { |ln| yield ln } : @command_out.each
54
+ end
55
+ end
56
+
57
+ def exitstatus
58
+ capture { force }
59
+ if @wait_thread
60
+ proc = @wait_thread.value
61
+ proc.exitstatus
62
+ end
63
+ end
64
+
65
+ def redirect_from(src)
66
+ capture { force(:redirect => { :in => src }) }
67
+ self
68
+ end
69
+
70
+ def redirect_to(dest)
71
+ release { force(:redirect => { :out => dest }) }
72
+ end
73
+
74
+ alias_method :<, :redirect_from
75
+ alias_method :>, :redirect_to
76
+
77
+ def filled?
78
+ @command_out && !@command_out.closed?
79
+ end
80
+
81
+ def command_line(name, args)
82
+ options = Option.new.parse(args).join(' ')
83
+ "#{name} #{options}".strip
84
+ end
85
+
86
+ def delay(name, args)
87
+ @promise << command_line(name, args)
88
+ end
89
+
90
+ def force(args={})
91
+ args = { :redirect => {} }.merge(args)
92
+ return if @promise.empty?
93
+
94
+ if @release
95
+ Open3.pipeline(*@promise, args[:redirect])
96
+ elsif @capture
97
+ out, thr = Open3.pipeline_r(*@promise, args[:redirect])
98
+ @command_out = out
99
+ @wait_thread = thr[-1]
100
+ end
101
+
102
+ @promise.clear
103
+ end
104
+
105
+ private :filled?, :command_line, :delay, :force
106
+ end
107
+ end
@@ -0,0 +1,3 @@
1
+ module Shr
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,45 @@
1
+ #coding: utf-8
2
+ require 'os'
3
+
4
+ # The following code was referring to whichr.
5
+ # whichr - https://github.com/rdp/whichr
6
+
7
+ module Shr
8
+ class Which
9
+ @@path = ENV['PATH']
10
+ if OS.windows?
11
+ cwd = File::PATH_SEPARATOR + '.'
12
+ @@path += cwd
13
+ end
14
+
15
+ def self.exist?(program)
16
+ programs = add_extensions(program)
17
+
18
+ entries = paths.product(programs).map { |list| list.join '/' }
19
+ entries.find do |exe|
20
+ File.executable?(exe) && !File.directory?(exe)
21
+ end
22
+ end
23
+ class << self
24
+ alias :exists? :exist?
25
+ end
26
+
27
+ def self.add_extensions(program)
28
+ if OS.windows?
29
+ # add .bat, .exe, etc.
30
+ extensions = ENV['PATHEXT'].split(';')
31
+ [program].product(extensions).map(&:join)
32
+ else
33
+ [program]
34
+ end
35
+ end
36
+
37
+ def self.paths
38
+ @@path.split(File::PATH_SEPARATOR).map! do |path|
39
+ OS.windows? ? path.gsub('\\', '/') : path
40
+ end
41
+ end
42
+
43
+ private_class_method :add_extensions, :paths
44
+ end
45
+ end
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/shr/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["kukenko"]
6
+ gem.email = ["m.kukenko@gmail.com"]
7
+ gem.description = %q{Make controlling subprocess easier in Ruby.}
8
+ gem.summary = gem.description
9
+ gem.homepage = "https://github.com/kukenko/shr"
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 = "shr"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Shr::VERSION
17
+
18
+ gem.add_dependency "os"
19
+ end
@@ -0,0 +1,82 @@
1
+ #coding: utf-8
2
+ require 'spec_helper'
3
+
4
+ module Shr
5
+ describe Option do
6
+ let(:opt) { Option.new }
7
+
8
+ it 'has the following methods' do
9
+ o = opt.methods
10
+ o.should include(:parse)
11
+ end
12
+
13
+ describe '#parse' do
14
+ shared_examples 'with short-form options' do
15
+ it 'parses correctly' do
16
+ opt.parse(options).should eq(['-o', 'page.html'])
17
+ end
18
+ end
19
+
20
+ shared_examples 'with long-form options' do
21
+ it 'parses correctly' do
22
+ opt.parse(options).should eq(['--shell=/bin/sh', '--silent'])
23
+ end
24
+ end
25
+
26
+ shared_examples 'with mixed options' do
27
+ it 'parses correctly' do
28
+ opt.parse(options).should eq(['-o', 'page.html', '--shell=/bin/sh', '--silent'])
29
+ end
30
+ end
31
+
32
+ describe String do
33
+ it_behaves_like 'with short-form options' do
34
+ let(:options) { ['-o', 'page.html'] }
35
+ end
36
+
37
+ it_behaves_like 'with long-form options' do
38
+ let(:options) { ['--shell', '/bin/sh', '--silent'] }
39
+ end
40
+
41
+ it_behaves_like 'with mixed options' do
42
+ let(:options) { ['-o', 'page.html', '--shell', '/bin/sh', '--silent'] }
43
+ end
44
+ end
45
+
46
+ describe Symbol do
47
+ it_behaves_like 'with short-form options' do
48
+ let(:options) { [:o, 'page.html'] }
49
+ end
50
+
51
+ it_behaves_like 'with long-form options' do
52
+ let(:options) { [:shell, '/bin/sh', :silent] }
53
+ end
54
+
55
+ it_behaves_like 'with mixed options' do
56
+ let(:options) { [:o, 'page.html', :shell, '/bin/sh', :silent] }
57
+ end
58
+ end
59
+
60
+ describe Hash do
61
+ it_behaves_like 'with short-form options' do
62
+ let(:options) { [{ :o => 'page.html' }] }
63
+ end
64
+
65
+ it_behaves_like 'with long-form options' do
66
+ let(:options) { [{ :shell => '/bin/sh', :silent => true }] }
67
+ end
68
+
69
+ it_behaves_like 'with mixed options' do
70
+ let(:options) { [{ :o => 'page.html', :shell => '/bin/sh', :silent => true }] }
71
+ end
72
+ end
73
+
74
+ describe Fixnum do
75
+ it 'parses short-form options' do
76
+ options = [1, 2, 3]
77
+ opt.parse(options).should eq(['-1', '-2', '-3'])
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,104 @@
1
+ #coding: utf-8
2
+ require 'spec_helper'
3
+ require 'fileutils'
4
+ require 'tempfile'
5
+ require 'tmpdir'
6
+
7
+ module Shr
8
+ describe Shell do
9
+ let(:sh) { Shell.new }
10
+
11
+ before(:all) do
12
+ @tmpdir = Dir.mktmpdir
13
+ ['perl.pl', 'python.py', 'ruby.rb'].each do |file|
14
+ FileUtils.touch File.join(@tmpdir, file)
15
+ end
16
+ @tmpfile = File.join(@tmpdir, 'ruby.rb.back')
17
+ end
18
+
19
+ it 'has the following methods' do
20
+ m = sh.methods
21
+ m.should include(:capture)
22
+ m.should include(:release)
23
+ m.should include(:exitstatus)
24
+ m.should include(:each)
25
+ m.should include(:redirect_from)
26
+ m.should include(:<)
27
+ m.should include(:redirect_to)
28
+ m.should include(:>)
29
+ end
30
+
31
+ it 'executes OS commands' do
32
+ cmd_result = `pwd`
33
+ sh_result = sh.pwd.to_s
34
+ sh_result.should eq(cmd_result)
35
+ end
36
+
37
+ context "command end with '!'" do
38
+ it 'executes OS commands' do
39
+ sh.touch! @tmpfile
40
+ File.should exist(@tmpfile)
41
+ FileUtils.rm @tmpfile
42
+ end
43
+ end
44
+
45
+ describe '#capture' do
46
+ it 'captures output of commands'
47
+ end
48
+
49
+ describe '#release' do
50
+ it 'releases output of commands'
51
+ end
52
+
53
+ describe '#each' do
54
+ it 'yields each line (result of command) to the block' do
55
+ files = []
56
+ sh.ls(@tmpdir).sort(:r).each do |file|
57
+ files << file.strip
58
+ end
59
+ files.should eq(['ruby.rb', 'python.py', 'perl.pl'])
60
+ end
61
+
62
+ context 'without block' do
63
+ it 'returns Enumerator' do
64
+ sh.ls.each.should be_kind_of(Enumerator)
65
+ end
66
+ end
67
+ end
68
+
69
+ it "emulates the shell pipe operator by method chaining" do
70
+ cmd_result = `pwd | tr '[:lower:]' '[:upper:]'`
71
+ sh_result = sh.pwd.tr("'[:lower:]'", "'[:upper:]'").to_s
72
+ sh_result.should eq(cmd_result)
73
+ end
74
+
75
+ describe '#exitstatus' do
76
+ it "shows an exit status of process" do
77
+ sh.ruby(:e, "'exit 0'").exitstatus.should eq(0)
78
+ sh.ruby(:e, "'exit 1'").exitstatus.should eq(1)
79
+ end
80
+ end
81
+
82
+ describe '#redirect_from' do
83
+ it 'redirect from file to command' do
84
+ tempfile = Tempfile.new('temp')
85
+ sh.pwd.redirect_to(tempfile.path)
86
+ sh.cat.redirect_from(tempfile.path).to_s.should eq(`pwd`)
87
+ tempfile.close!
88
+ end
89
+ end
90
+
91
+ describe '#redirect_to' do
92
+ it 'redirecs result of command to file' do
93
+ tempfile = Tempfile.new('temp')
94
+ sh.pwd.redirect_to(tempfile.path)
95
+ File.read(tempfile.path).should eq(`pwd`)
96
+ tempfile.close!
97
+ end
98
+ end
99
+
100
+ after(:all) do
101
+ FileUtils.remove_entry_secure @tmpdir
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,28 @@
1
+ #coding: utf-8
2
+ require 'spec_helper'
3
+
4
+ module Shr
5
+ describe Which do
6
+ it 'has the following methods' do
7
+ m = Which.methods
8
+ m.should include(:exist?)
9
+ m.should include(:exists?)
10
+ end
11
+
12
+ describe '#exist?' do
13
+ it 'returns true if any of executables are found' do
14
+ Which.exist?(:pwd).should be_true
15
+ end
16
+
17
+ it 'returns false if any of executables are not found' do
18
+ Which.exist?(:not_found).should be_false
19
+ end
20
+
21
+ context 'with argument String' do
22
+ it 'returns true if any of executables are found' do
23
+ Which.exist?('pwd').should be_true
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,15 @@
1
+ #coding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe Shr do
5
+ it 'has the following methods' do
6
+ m = Shr.methods
7
+ m.should include(:shell)
8
+ end
9
+
10
+ describe '#shell' do
11
+ it 'returns an instance of Shr::Shell' do
12
+ Shr.shell.should be_kind_of(Shr::Shell)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,4 @@
1
+ #coding: utf-8
2
+ require 'shr'
3
+ require 'shr/option'
4
+
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: shr
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - kukenko
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-01-06 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: os
16
+ requirement: &70190202007440 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70190202007440
25
+ description: Make controlling subprocess easier in Ruby.
26
+ email:
27
+ - m.kukenko@gmail.com
28
+ executables: []
29
+ extensions: []
30
+ extra_rdoc_files: []
31
+ files:
32
+ - .gitignore
33
+ - Gemfile
34
+ - Guardfile
35
+ - LICENSE.md
36
+ - README.md
37
+ - Rakefile
38
+ - lib/shr.rb
39
+ - lib/shr/option.rb
40
+ - lib/shr/shell.rb
41
+ - lib/shr/version.rb
42
+ - lib/shr/which.rb
43
+ - shr.gemspec
44
+ - spec/lib/shr/option_spec.rb
45
+ - spec/lib/shr/shell_spec.rb
46
+ - spec/lib/shr/which_spec.rb
47
+ - spec/lib/shr_spec.rb
48
+ - spec/spec_helper.rb
49
+ homepage: https://github.com/kukenko/shr
50
+ licenses: []
51
+ post_install_message:
52
+ rdoc_options: []
53
+ require_paths:
54
+ - lib
55
+ required_ruby_version: !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ! '>='
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ! '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ requirements: []
68
+ rubyforge_project:
69
+ rubygems_version: 1.8.11
70
+ signing_key:
71
+ specification_version: 3
72
+ summary: Make controlling subprocess easier in Ruby.
73
+ test_files:
74
+ - spec/lib/shr/option_spec.rb
75
+ - spec/lib/shr/shell_spec.rb
76
+ - spec/lib/shr/which_spec.rb
77
+ - spec/lib/shr_spec.rb
78
+ - spec/spec_helper.rb
79
+ has_rdoc: