shellter 0.9.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.
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +89 -0
- data/Rakefile +2 -0
- data/lib/shellter/class_methods.rb +39 -0
- data/lib/shellter/command.rb +111 -0
- data/lib/shellter/core_ext.rb +36 -0
- data/lib/shellter/runners/background.rb +42 -0
- data/lib/shellter/runners/base.rb +13 -0
- data/lib/shellter/runners/windows.rb +9 -0
- data/lib/shellter/version.rb +3 -0
- data/lib/shellter.rb +17 -0
- data/shellter.gemspec +25 -0
- metadata +156 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Andrew Eberbach
|
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,89 @@
|
|
1
|
+
# Shellter
|
2
|
+
|
3
|
+
Do YOU like to fork(2)? Do YOU like cocaine(https://github.com/thoughtbot/cocaine)? Of course, we all do. The problem is that I needed a ever-rounder wheel than what I've found. The Escape gem was a nice place to find shell escaping... but it didn't do interpolation or actually execute anything. The Cocaine gem was a nice place to find interpolation, but its escaping was a bit too simple for my tastes and it didn't manage STDERR, STDOUT in a way that let me inspect them easily afterward. I've also always wanted to be able to start a process in the background without having to worry about what I need to close and open and how many times I need to fork (but if you want to know everything about it, I'd suggest you read http://workingwithunixprocesses.com/).
|
4
|
+
|
5
|
+
So in the grand spirit of the GitHubs here's a gem that does all of the above. The idea is that your result is going to be more than just an exit status. Here's what you'll get:
|
6
|
+
|
7
|
+
* The fully interpolated command that was last run
|
8
|
+
* The exit status
|
9
|
+
* The entire STDERR and STDOUT
|
10
|
+
* The PID
|
11
|
+
|
12
|
+
Yes, of course I'm using POpen4 under the covers. The interpolation is taken from Cocaine and modified to use Escape to do the shell escaping.
|
13
|
+
|
14
|
+
The point is that it's supposed to be an easy-to-remember, easy-to-use library that takes care of everything else.
|
15
|
+
|
16
|
+
## Backgrounding
|
17
|
+
|
18
|
+
Sure there's a ton of gems that let you manage background processes and the like. Daemons (http://daemons.rubyforge.org/) is an obvious example. Sometimes, though, you just want to "fire and forget." Shellter provides a SIMPLE way of demonizing a command whilst returning you its PID so that you can do with it what you like.
|
19
|
+
|
20
|
+
## Installation
|
21
|
+
|
22
|
+
Add this line to your application's Gemfile:
|
23
|
+
|
24
|
+
gem 'shellter'
|
25
|
+
|
26
|
+
And then execute:
|
27
|
+
|
28
|
+
$ bundle
|
29
|
+
|
30
|
+
Or install it yourself as:
|
31
|
+
|
32
|
+
$ gem install shellter
|
33
|
+
|
34
|
+
## Usage
|
35
|
+
|
36
|
+
The most basic usage is like this:
|
37
|
+
|
38
|
+
result = Shellter.run("ls")
|
39
|
+
|
40
|
+
That's it. This returns a Shellter::Command which has the following variables:
|
41
|
+
|
42
|
+
result.pid
|
43
|
+
result.stdout
|
44
|
+
result.stderr
|
45
|
+
result.last_command # the fully-interpolated command that was run
|
46
|
+
|
47
|
+
Want to pass arguments that you KNOW are safe?
|
48
|
+
|
49
|
+
Shellter.run("ls", "-l", "*.rb") # ls -l *.rb
|
50
|
+
|
51
|
+
Want to pass arguments that might have spaces in them?
|
52
|
+
|
53
|
+
myvar = "'\"\'some ../../weird#""'`#{}` filena_?me"
|
54
|
+
Shellter.run("ls", "-l", ":weird", :weird => myvar) # ls -l \''"'\''some ../../weird#'\''`` filena_?me'
|
55
|
+
|
56
|
+
hateful = "'$(rm -rf *.foo)"
|
57
|
+
Shellter.run("ls", "-l", ":weird", :weird => hateful) # ls -l \''$(rm -rf *.foo)'
|
58
|
+
|
59
|
+
Want to raise an exception if the command doesn't return 0?
|
60
|
+
|
61
|
+
Shellter.run!("ls", "*.missing") # RuntimeError: Execution failed, with exit code 256
|
62
|
+
|
63
|
+
Want to find out if the command ran ok?
|
64
|
+
|
65
|
+
Shellter.run("ls", "*.missing").success? # false
|
66
|
+
|
67
|
+
Want to provide a list of exit codes that you're ok with?
|
68
|
+
|
69
|
+
Shellter.run!("ls", "*.missing", :expected_outcodes => [0, 256]).success? # true
|
70
|
+
|
71
|
+
Want to start that command in the background?
|
72
|
+
|
73
|
+
Shellter.run("ls", :background => true)
|
74
|
+
|
75
|
+
*NOTE:* When backgrounded, the command `fork`s twice in order to fully detach from the session. This, of course, means that the stderr, and stdout will be empty. The pid, however, will be of the last `fork` before `exec`-ing.
|
76
|
+
|
77
|
+
*NOTE:* Before backgrounding we check to see if `fork` is actually available and throw an Exception if it isn't.
|
78
|
+
|
79
|
+
Also included is an implementation of the UNIX `which` command:
|
80
|
+
|
81
|
+
Shellter.which("ls") # /bin/ls
|
82
|
+
|
83
|
+
## Contributing
|
84
|
+
|
85
|
+
1. Fork it
|
86
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
87
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
88
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
89
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
module Shellter
|
2
|
+
module ClassMethods
|
3
|
+
def run(command, *arguments)
|
4
|
+
Command.new(command, *arguments).tap do |command|
|
5
|
+
options = arguments.extract_options!
|
6
|
+
command.run(options)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def run!(command, *arguments)
|
11
|
+
run(command, *arguments).tap do |result|
|
12
|
+
raise "Execution failed, with exit code #{result.exit_code}" unless result.success?
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def which(command, paths = nil)
|
17
|
+
paths ||= ENV["PATH"].split(File::PATH_SEPARATOR)
|
18
|
+
extensions = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
|
19
|
+
|
20
|
+
paths.each do |path|
|
21
|
+
extensions.each do |extension|
|
22
|
+
path_with_extension(path, command, extension).tap do |path_to_command|
|
23
|
+
if File.exists?(path_to_command) && File.executable?(path_to_command)
|
24
|
+
return path_to_command
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def path_with_extension(path, command, extension)
|
36
|
+
File.join(File.expand_path(path), "#{command}#{extension}")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
module Shellter
|
2
|
+
class Command
|
3
|
+
|
4
|
+
attr_reader :pid
|
5
|
+
attr_reader :stdout
|
6
|
+
attr_reader :stderr
|
7
|
+
attr_reader :last_command
|
8
|
+
|
9
|
+
def initialize(command, *arguments)
|
10
|
+
options = arguments.extract_options!
|
11
|
+
|
12
|
+
@expected_outcodes = options.delete(:expected_outcodes)
|
13
|
+
@expected_outcodes ||= [0]
|
14
|
+
|
15
|
+
@command = command
|
16
|
+
@arguments = arguments
|
17
|
+
end
|
18
|
+
|
19
|
+
def run(options = {})
|
20
|
+
with_env(options) do
|
21
|
+
with_runner(options) do |runner|
|
22
|
+
with_interpolated_command(options) do |command|
|
23
|
+
@status = runner.run(command) do |stdout, stderr, stdin, pid|
|
24
|
+
stdin.close
|
25
|
+
@stdout = stdout.read
|
26
|
+
@stderr = stderr.read
|
27
|
+
@pid = pid
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def exit_code
|
35
|
+
@status.to_i
|
36
|
+
end
|
37
|
+
|
38
|
+
def success?
|
39
|
+
@expected_outcodes.include?(exit_code)
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def interpolate(pattern, vars)
|
45
|
+
# interpolates :variables and :{variables}
|
46
|
+
pattern.gsub(%r#:(?:\w+|\{\w+\})#) do |match|
|
47
|
+
key = match[1..-1]
|
48
|
+
key = key[1..-2] if key[0,1] == '{'
|
49
|
+
if invalid_variables.include?(key)
|
50
|
+
raise InterpolationError,
|
51
|
+
"Interpolation of #{key} isn't allowed."
|
52
|
+
end
|
53
|
+
interpolation(vars, key) || match
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def invalid_variables
|
58
|
+
%w(expected_outcodes swallow_stderr logger)
|
59
|
+
end
|
60
|
+
|
61
|
+
def interpolation(vars, key)
|
62
|
+
if vars.key?(key.to_sym)
|
63
|
+
Escape.shell_single_word(vars[key.to_sym])
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def with_env(options = {}, &block)
|
68
|
+
begin
|
69
|
+
saved_env = ENV.to_hash
|
70
|
+
# Bundler.with_clean_env(&block)
|
71
|
+
# command = %Q{. #{vars_file_path} && #{script}}
|
72
|
+
# ENV.update(self.class.environment)
|
73
|
+
yield
|
74
|
+
ensure
|
75
|
+
ENV.update(saved_env)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def with_interpolated_command(options, &block)
|
80
|
+
[].tap do |command_line|
|
81
|
+
command_line << Escape.shell_single_word(@command)
|
82
|
+
command_line += interpolated_arguments(options)
|
83
|
+
|
84
|
+
command_line.join(" ").tap do |command|
|
85
|
+
@last_command = command
|
86
|
+
yield command
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def interpolated_arguments(vars)
|
92
|
+
@arguments.map do |argument|
|
93
|
+
interpolate(argument, vars)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def with_runner(options = {}, &block)
|
98
|
+
if options[:background]
|
99
|
+
yield Runners::Background.new(options)
|
100
|
+
elsif windows?
|
101
|
+
yield Runners::Windows.new(options)
|
102
|
+
else
|
103
|
+
yield Runners::Base.new(options)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def windows?
|
108
|
+
!!(RbConfig::CONFIG['host_os'] =~ /mswin|mingw/)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
begin
|
2
|
+
require 'active_support/core_ext/array/extract_options'
|
3
|
+
rescue LoadError
|
4
|
+
|
5
|
+
class Hash
|
6
|
+
# By default, only instances of Hash itself are extractable.
|
7
|
+
# Subclasses of Hash may implement this method and return
|
8
|
+
# true to declare themselves as extractable. If a Hash
|
9
|
+
# is extractable, Array#extract_options! pops it from
|
10
|
+
# the Array when it is the last element of the Array.
|
11
|
+
def extractable_options?
|
12
|
+
instance_of?(Hash)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class Array
|
17
|
+
# Extracts options from a set of arguments. Removes and returns the last
|
18
|
+
# element in the array if it's a hash, otherwise returns a blank hash.
|
19
|
+
#
|
20
|
+
# def options(*args)
|
21
|
+
# args.extract_options!
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# options(1, 2) # => {}
|
25
|
+
# options(1, 2, :a => :b) # => {:a=>:b}
|
26
|
+
def extract_options!
|
27
|
+
if last.is_a?(Hash) && last.extractable_options?
|
28
|
+
pop
|
29
|
+
else
|
30
|
+
{}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Shellter
|
2
|
+
module Runners
|
3
|
+
class Background < Base
|
4
|
+
def run(command, &block)
|
5
|
+
ensure_forking!
|
6
|
+
|
7
|
+
# io for child process
|
8
|
+
reader, writer = IO.pipe
|
9
|
+
|
10
|
+
# first fork, create a session leader
|
11
|
+
fork do
|
12
|
+
reader.close
|
13
|
+
Process.setsid
|
14
|
+
|
15
|
+
# second fork, create a detached child
|
16
|
+
# and write its pid to the writer pipe
|
17
|
+
result = fork do
|
18
|
+
Dir.chdir "/"
|
19
|
+
STDIN.reopen "/dev/null"
|
20
|
+
STDOUT.reopen "/dev/null", "a"
|
21
|
+
STDERR.reopen "/dev/null", "a"
|
22
|
+
|
23
|
+
exec command
|
24
|
+
end
|
25
|
+
|
26
|
+
# write the pid of the forked child to the pipe
|
27
|
+
writer.puts result
|
28
|
+
end
|
29
|
+
|
30
|
+
writer.close
|
31
|
+
|
32
|
+
yield StringIO.new, StringIO.new, StringIO.new, reader.gets.strip.to_i
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def ensure_forking!
|
38
|
+
raise RuntimeError, "Cannot fork on this system!" unless Process.respond_to?(:fork)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/shellter.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'escape'
|
2
|
+
require 'popen4'
|
3
|
+
|
4
|
+
require 'shellter/core_ext'
|
5
|
+
|
6
|
+
module Shellter
|
7
|
+
autoload :ClassMethods, "shellter/class_methods"
|
8
|
+
autoload :Command, "shellter/command"
|
9
|
+
autoload :VERSION, "shellter/version"
|
10
|
+
|
11
|
+
module Runners
|
12
|
+
autoload :Background, "shellter/runners/background"
|
13
|
+
autoload :Base, "shellter/runners/base"
|
14
|
+
end
|
15
|
+
|
16
|
+
extend ClassMethods
|
17
|
+
end
|
data/shellter.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/shellter/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Andrew Eberbach"]
|
6
|
+
gem.email = ["andrew@ebertech.ca"]
|
7
|
+
gem.description = %q{A library to help with command line launching}
|
8
|
+
gem.summary = %q{A rounder wheel version of its predecessors.}
|
9
|
+
gem.homepage = "https://github.com/ebertech/shellter"
|
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 = "shellter"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = Shellter::VERSION
|
17
|
+
|
18
|
+
gem.add_dependency "popen4"
|
19
|
+
gem.add_dependency "escape"
|
20
|
+
|
21
|
+
gem.add_development_dependency "pry"
|
22
|
+
gem.add_development_dependency "pry-doc"
|
23
|
+
gem.add_development_dependency "pry-nav"
|
24
|
+
gem.add_development_dependency "simplecov"
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: shellter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.9.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Andrew Eberbach
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-06-23 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: popen4
|
16
|
+
requirement: !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: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: escape
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: pry
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: pry-doc
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: pry-nav
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: simplecov
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
description: A library to help with command line launching
|
111
|
+
email:
|
112
|
+
- andrew@ebertech.ca
|
113
|
+
executables: []
|
114
|
+
extensions: []
|
115
|
+
extra_rdoc_files: []
|
116
|
+
files:
|
117
|
+
- .gitignore
|
118
|
+
- Gemfile
|
119
|
+
- LICENSE
|
120
|
+
- README.md
|
121
|
+
- Rakefile
|
122
|
+
- lib/shellter.rb
|
123
|
+
- lib/shellter/class_methods.rb
|
124
|
+
- lib/shellter/command.rb
|
125
|
+
- lib/shellter/core_ext.rb
|
126
|
+
- lib/shellter/runners/background.rb
|
127
|
+
- lib/shellter/runners/base.rb
|
128
|
+
- lib/shellter/runners/windows.rb
|
129
|
+
- lib/shellter/version.rb
|
130
|
+
- shellter.gemspec
|
131
|
+
homepage: https://github.com/ebertech/shellter
|
132
|
+
licenses: []
|
133
|
+
post_install_message:
|
134
|
+
rdoc_options: []
|
135
|
+
require_paths:
|
136
|
+
- lib
|
137
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
138
|
+
none: false
|
139
|
+
requirements:
|
140
|
+
- - ! '>='
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: '0'
|
143
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
144
|
+
none: false
|
145
|
+
requirements:
|
146
|
+
- - ! '>='
|
147
|
+
- !ruby/object:Gem::Version
|
148
|
+
version: '0'
|
149
|
+
requirements: []
|
150
|
+
rubyforge_project:
|
151
|
+
rubygems_version: 1.8.24
|
152
|
+
signing_key:
|
153
|
+
specification_version: 3
|
154
|
+
summary: A rounder wheel version of its predecessors.
|
155
|
+
test_files: []
|
156
|
+
has_rdoc:
|