shr 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.
@@ -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: