rye 0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES.txt +26 -0
- data/LICENSE.txt +19 -0
- data/README.rdoc +176 -0
- data/Rakefile +83 -0
- data/bin/try +148 -0
- data/lib/esc.rb +301 -0
- data/lib/rye.rb +155 -0
- data/lib/rye/box.rb +312 -0
- data/lib/rye/cmd.rb +47 -0
- data/lib/rye/rap.rb +83 -0
- data/lib/rye/set.rb +145 -0
- data/lib/sys.rb +274 -0
- data/rye.gemspec +56 -0
- data/test/10_rye_test.rb +63 -0
- metadata +90 -0
data/CHANGES.txt
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
RYE, CHANGES
|
2
|
+
|
3
|
+
#### 0.3 (2009-04-05) ###############################
|
4
|
+
|
5
|
+
* ADDED: Rye::Set supports executing commands parallel
|
6
|
+
* ADDED: Rye::Rap now contains STDERR output from command
|
7
|
+
* FIXED: Rye::Box wasn't properly adding keypairs to SSH Agent
|
8
|
+
* CHANGED: Moved all SSH key stuff to Rye (used to be done per Box)
|
9
|
+
* ADDED: Supports all options provided by Net::SSH#start. This
|
10
|
+
includes support for password logins and proxies.
|
11
|
+
* ADDED: Safe mode can now be disabled (to allow file globs
|
12
|
+
and environment variable access).
|
13
|
+
* ADDED: Basic sanity test
|
14
|
+
* FIXED: Rye::Box.method_missing Symbol/String ambiguity
|
15
|
+
* ADDED: Mucho more rdocs and examples.
|
16
|
+
|
17
|
+
#### 0.2 (2009-04-04) ###############################
|
18
|
+
|
19
|
+
* FIXED: ssh-agent shutdown wasn't deleting the SSH tmp directory
|
20
|
+
* ADDED: Now with more rdocs!
|
21
|
+
|
22
|
+
|
23
|
+
#### 0.1 (2009-04-03) ###############################
|
24
|
+
|
25
|
+
Initial public release
|
26
|
+
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2009 Delano Mandelbaum, Solutious Inc
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,176 @@
|
|
1
|
+
= Rye - v0.3
|
2
|
+
|
3
|
+
Run system commands via SSH locally and remotely in a Ruby way.
|
4
|
+
|
5
|
+
Rye is similar to Rush[http://rush.heroku.com] but everything happens over SSH (no HTTP daemon) and the default settings are less powerful (for safety). For example, file globs are disabled so unless otherwise specified, you can't do this: <tt>rbox.rm('-rf', '/etc/**/*')</tt>.
|
6
|
+
|
7
|
+
See +bin/try+ for working examples.
|
8
|
+
|
9
|
+
== Installation
|
10
|
+
|
11
|
+
One of:
|
12
|
+
|
13
|
+
$ sudo gem install rye
|
14
|
+
$ sudo gem install delano-rye --source http://gems.github.com/
|
15
|
+
$ git clone git://github.com/delano/rye.git
|
16
|
+
|
17
|
+
|
18
|
+
== EXAMPLE 1 -- Basic Usage
|
19
|
+
|
20
|
+
rbox = Rye::Box.new('localhost')
|
21
|
+
|
22
|
+
# Commands are run as methods on the Rye::Box object
|
23
|
+
puts rbox.uptime # => 11:02 up 16:01, 3 users
|
24
|
+
|
25
|
+
# The response value for all commands is a Rye::Rap object. The rap is a
|
26
|
+
# subclass of Array so you can treat it as an Array, but it can also act
|
27
|
+
# like a String if there's only one element.
|
28
|
+
puts rbox.ls('rye.test') # => ""
|
29
|
+
puts rbox.ls('rye.test').stderr # => ls: rye.test: No such file or directory
|
30
|
+
|
31
|
+
puts rbox.touch('rye.test') # => ""
|
32
|
+
puts rbox.rm('rye.test') # => ""
|
33
|
+
|
34
|
+
# You can change directories
|
35
|
+
puts rbox.pwd # => /home/rye
|
36
|
+
puts rbox['/usr/bin'].pwd # => /usr/bin
|
37
|
+
puts rbox.pwd # => /usr/bin
|
38
|
+
puts rbox.cd('/home/rye').pwd # => /home/rye
|
39
|
+
|
40
|
+
# You can specify environment variables
|
41
|
+
rbox.add_env(:RYE, "Forty Creek")
|
42
|
+
rbox.env # => ['HOME=/home/rye', 'RYE=Forty Creek', ...]
|
43
|
+
|
44
|
+
# The commands method returns an Array of available commands:
|
45
|
+
puts rbox.commands.join(', ') # => pwd, touch, echo, wc, ...
|
46
|
+
|
47
|
+
# When you're done you can disconnect explicitly.
|
48
|
+
# (Although Rye does this automatically at exit.)
|
49
|
+
rbox.disconnect
|
50
|
+
|
51
|
+
|
52
|
+
== EXAMPLE 2 -- Disabling Safe-Mode
|
53
|
+
|
54
|
+
rbox_safe = Rye::Box.new('localhost')
|
55
|
+
rbox_wild = Rye::Box.new('localhost', :safe => false)
|
56
|
+
|
57
|
+
# Safe-mode is enabled by default. In safe-mode, all command
|
58
|
+
# arguments are thoroughly escaped. This prevents access to
|
59
|
+
# environment variables and file globs (among other things).
|
60
|
+
p rbox_safe.echo('$HOME') # => "$HOME"
|
61
|
+
p rbox_safe['/etc'].ls('host*') # =>
|
62
|
+
p rbox_safe.ls('-l | wc -l') # =>
|
63
|
+
p rbox_safe.echo('$HOME > /tmp/rye-home') # => "$HOME > /tmp/home"
|
64
|
+
p rbox_safe.cat('/tmp/rye-home') # =>
|
65
|
+
p rbox_safe.cat('/tmp/rye-home').stderr # => "No such file or directory"
|
66
|
+
|
67
|
+
# Here's the same commands with safe-mode disabled:
|
68
|
+
p rbox_wild.echo('$HOME') # => "/home/rye"
|
69
|
+
p rbox_wild['/etc'].ls('host*') # => ["hostconfig", "hosts"]
|
70
|
+
p rbox_wild.ls('-l | wc -l') # => 110
|
71
|
+
p rbox_wild.echo('$HOME > /tmp/rye-home') # =>
|
72
|
+
p rbox_wild.cat('/tmp/rye-home') # => "/home/rye"
|
73
|
+
p rbox_wild.rm('/tmp/rye-home') # =>
|
74
|
+
|
75
|
+
|
76
|
+
|
77
|
+
== EXAMPLE 3 -- Custom Commands
|
78
|
+
|
79
|
+
rbox = Rye::Box.new('localhost')
|
80
|
+
rbox.add_keys('/private/key/path') # Specify additional private keys
|
81
|
+
|
82
|
+
# There's currently no rye900 command
|
83
|
+
p rbox.commands.member?('rye9000') # => false
|
84
|
+
|
85
|
+
# But we can add our own commands to the Rye::Cmd class. They
|
86
|
+
# automatically become available to all Rye::Box objects.
|
87
|
+
module Rye::Cmd
|
88
|
+
def rye9000(*args)
|
89
|
+
add_command("ls", args)
|
90
|
+
end
|
91
|
+
def somescript(*args)
|
92
|
+
add_command("/path/to/my/script", args)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# We can now run rye9000 (with arguments)
|
97
|
+
p rbox.rye9000('-a') # => [".", "..", ".bashrc", ...]
|
98
|
+
p rbox.commands.member?('rye9000') # => true
|
99
|
+
|
100
|
+
|
101
|
+
== EXAMPLE 4 -- Accessing Multiple Machines
|
102
|
+
|
103
|
+
rset = Rye::Set.new
|
104
|
+
rbox = Rye::Box.new
|
105
|
+
|
106
|
+
rset.add_keys('/private/key/path') # For passwordless logins
|
107
|
+
rset.add_boxes(rbox, 'localhost') # Add boxes as hostnames or objects
|
108
|
+
|
109
|
+
# Calling methods on Rye::Set objects is very similar to calling them on
|
110
|
+
# Rye::Box objects. In fact, it's identical:
|
111
|
+
p rset.uptime # => [[14:19:02 up 32 days, 19:35 ...], [14:19:02 up 30 days, 01:35]]
|
112
|
+
p rset['/etc'].ls # => [['file1', 'file2', ...], ['life1', 'life2', ...]]
|
113
|
+
|
114
|
+
# Like Rye::Box, the response value is a Rye::Rap object containing the
|
115
|
+
# responses from each box. Each response is itself a Rye::Rap object.
|
116
|
+
unames = rset.uname
|
117
|
+
p unames # => [["Darwin"], ["Darwin"]]
|
118
|
+
puts unames.class # => Rye::Rap
|
119
|
+
|
120
|
+
# The Rye::Rap object also keeps a reference to the object that called the
|
121
|
+
# command. In this case, it will keep a reference to Rye::Set:
|
122
|
+
puts unames.set.class # => Rye::Set
|
123
|
+
puts unames.set == rset # => true
|
124
|
+
puts unames.size # => 2
|
125
|
+
puts unames.first # => Darwin
|
126
|
+
puts unames.first.class # => Rye::Rap
|
127
|
+
puts unames.first.box.class # => Rye::Box
|
128
|
+
puts unames.first.box == rbox # => true
|
129
|
+
|
130
|
+
# Envrionment variables can be set the same way as with Rye::Box
|
131
|
+
rset.add_env(:RYE, "Forty Creek")
|
132
|
+
p rset.env.first.select { |env| env =~ /RYE/ } # => ["RYE=Forty Creek"]
|
133
|
+
|
134
|
+
|
135
|
+
== About Safe-Mode
|
136
|
+
|
137
|
+
In safe-mode:
|
138
|
+
|
139
|
+
* You can't use file globs. This means you can't do this: <tt>rbox.ls('*.rb')</tt>.
|
140
|
+
* Commands arguments cannot contain environment variables. However, environment variables are available to the commands you run. This means you can't do this: <tt>rbox.echo('$HOME')</tt>.
|
141
|
+
* Pipes and operators don't work: <tt>|, &&, >, <, ||</tt>, etc...
|
142
|
+
* Backticks don't work either: <tt>procs=`ps aux`</tt>
|
143
|
+
|
144
|
+
Why? In safe-mode, all command arguments are escaped which turns all arguments into their literal values.
|
145
|
+
|
146
|
+
Using a Ruby interface to execute shell commands is pretty awesome, particularly to run them on several machines simultaneously. That's a lot of power and it's potentially very dangerous. That's why some stuff isn't available in Rye that you would otherwise expect to be able to do (particularly the file globs). There's probably a way to do it safely but it's not obvious yet (to me). If you have any ideas, I'd love to hear them!
|
147
|
+
|
148
|
+
|
149
|
+
== Credits
|
150
|
+
|
151
|
+
* Delano Mandelbaum (delano@solutious.com)
|
152
|
+
|
153
|
+
|
154
|
+
== Thanks
|
155
|
+
|
156
|
+
* Solutious Incorporated (http://solutious.com) for all the orange juice.
|
157
|
+
* The country of Canada for making Rye Whiskey.
|
158
|
+
|
159
|
+
|
160
|
+
== Kudos
|
161
|
+
|
162
|
+
* http://github.com/adamwiggins/rush
|
163
|
+
* http://github.com/jamis/capistrano/blob/master/lib/capistrano/shell.rb
|
164
|
+
* http://www.nofluffjuststuff.com/blog/david_bock/2008/10/ruby_s_closure_cleanup_idiom_and_net_ssh.html
|
165
|
+
* http://groups.google.com/group/ruby-talk-google/browse_thread/thread/674a6f6de15ceb49?pli=1
|
166
|
+
* http://paste.lisp.org/display/6912
|
167
|
+
|
168
|
+
|
169
|
+
== Credits
|
170
|
+
|
171
|
+
* Delano Mandelbaum (delano@solutious.com)
|
172
|
+
|
173
|
+
|
174
|
+
== License
|
175
|
+
|
176
|
+
See: LICENSE.txt
|
data/Rakefile
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake/clean'
|
3
|
+
require 'rake/gempackagetask'
|
4
|
+
require 'hanna/rdoctask'
|
5
|
+
require 'fileutils'
|
6
|
+
include FileUtils
|
7
|
+
|
8
|
+
task :default => :package
|
9
|
+
|
10
|
+
# CONFIG =============================================================
|
11
|
+
|
12
|
+
# Change the following according to your needs
|
13
|
+
README = "README.rdoc"
|
14
|
+
CHANGES = "CHANGES.txt"
|
15
|
+
LICENSE = "LICENSE.txt"
|
16
|
+
|
17
|
+
# Files and directories to be deleted when you run "rake clean"
|
18
|
+
CLEAN.include [ 'doc', 'pkg', '*.gem', '.config' ]
|
19
|
+
|
20
|
+
# Virginia assumes your project and gemspec have the same name
|
21
|
+
name = (Dir.glob('*.gemspec') || ['rye']).first.split('.').first
|
22
|
+
load "#{name}.gemspec"
|
23
|
+
version = @spec.version
|
24
|
+
|
25
|
+
# That's it! The following defaults should allow you to get started
|
26
|
+
# on other things.
|
27
|
+
|
28
|
+
|
29
|
+
# TESTS/SPECS =========================================================
|
30
|
+
|
31
|
+
|
32
|
+
|
33
|
+
# INSTALL =============================================================
|
34
|
+
|
35
|
+
Rake::GemPackageTask.new(@spec) do |p|
|
36
|
+
p.need_tar = true if RUBY_PLATFORM !~ /mswin/
|
37
|
+
end
|
38
|
+
|
39
|
+
task :release => [ :rdoc, :package ]
|
40
|
+
task :install => [ :rdoc, :package ] do
|
41
|
+
sh %{sudo gem install pkg/#{name}-#{version}.gem}
|
42
|
+
end
|
43
|
+
task :uninstall => [ :clean ] do
|
44
|
+
sh %{sudo gem uninstall #{name}}
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
# RUBYFORGE RELEASE / PUBLISH TASKS ==================================
|
49
|
+
|
50
|
+
if @spec.rubyforge_project
|
51
|
+
desc 'Publish website to rubyforge'
|
52
|
+
task 'publish:rdoc' => 'doc/index.html' do
|
53
|
+
sh "scp -rp doc/* rubyforge.org:/var/www/gforge-projects/#{name}/"
|
54
|
+
end
|
55
|
+
|
56
|
+
desc 'Public release to rubyforge'
|
57
|
+
task 'publish:gem' => [:package] do |t|
|
58
|
+
sh <<-end
|
59
|
+
rubyforge add_release -o Any -a #{CHANGES} -f -n #{README} #{name} #{name} #{@spec.version} pkg/#{name}-#{@spec.version}.gem &&
|
60
|
+
rubyforge add_file -o Any -a #{CHANGES} -f -n #{README} #{name} #{name} #{@spec.version} pkg/#{name}-#{@spec.version}.tgz
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
|
67
|
+
# RUBY DOCS TASK ==================================
|
68
|
+
|
69
|
+
Rake::RDocTask.new do |t|
|
70
|
+
t.rdoc_dir = 'doc'
|
71
|
+
t.title = @spec.summary
|
72
|
+
t.options << '--line-numbers' << '-A cattr_accessor=object'
|
73
|
+
t.options << '--charset' << 'utf-8'
|
74
|
+
t.rdoc_files.include(LICENSE)
|
75
|
+
t.rdoc_files.include(README)
|
76
|
+
t.rdoc_files.include(CHANGES)
|
77
|
+
#t.rdoc_files.include('bin/*')
|
78
|
+
t.rdoc_files.include('lib/**/*.rb')
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
|
83
|
+
|
data/bin/try
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
# Rye -- A working example
|
4
|
+
#
|
5
|
+
# If your reading this via the rdocs you won't be able to see the code
|
6
|
+
# See: http://github.com/delano/rye/blob/master/bin/try
|
7
|
+
#
|
8
|
+
# Usage: bin/try
|
9
|
+
#
|
10
|
+
|
11
|
+
$:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'stringio'
|
15
|
+
require 'yaml'
|
16
|
+
require 'rye'
|
17
|
+
|
18
|
+
|
19
|
+
puts %Q(
|
20
|
+
# ------------------------------------------------------------------
|
21
|
+
# EXAMPLE 1 -- Basic Usage
|
22
|
+
#)
|
23
|
+
|
24
|
+
rbox = Rye::Box.new('localhost')
|
25
|
+
|
26
|
+
# Commands are run as methods on the Rye::Box object
|
27
|
+
puts rbox.uptime # => 11:02 up 16:01, 3 users
|
28
|
+
|
29
|
+
# The response value for all commands is a Rye::Rap object. The rap is a
|
30
|
+
# subclass of Array so you can treat it as an Array, but it can also act
|
31
|
+
# like a String if there's only one element.
|
32
|
+
puts rbox.ls('rye.test') # => ""
|
33
|
+
puts rbox.ls('rye.test').stderr # => ls: rye.test: No such file or directory
|
34
|
+
|
35
|
+
puts rbox.touch('rye.test') # => ""
|
36
|
+
puts rbox.rm('rye.test') # => ""
|
37
|
+
|
38
|
+
# You can change directories
|
39
|
+
puts rbox.pwd # => /home/rye
|
40
|
+
puts rbox['/usr/bin'].pwd # => /usr/bin
|
41
|
+
puts rbox.pwd # => /usr/bin
|
42
|
+
puts rbox.cd('/home/rye').pwd # => /home/rye
|
43
|
+
|
44
|
+
# You can specify environment variables
|
45
|
+
rbox.add_env(:RYE, "Forty Creek")
|
46
|
+
rbox.env # => ['HOME=/home/rye', 'RYE=Forty Creek', ...]
|
47
|
+
|
48
|
+
# The commands method returns an Array of available commands:
|
49
|
+
puts rbox.commands.join(', ') # => pwd, touch, echo, wc, ...
|
50
|
+
|
51
|
+
# When you're done you can disconnect explicitly.
|
52
|
+
# (Although Rye does this automatically at exit.)
|
53
|
+
rbox.disconnect
|
54
|
+
|
55
|
+
|
56
|
+
puts %Q(
|
57
|
+
# ------------------------------------------------------------------
|
58
|
+
# EXAMPLE 2 -- Disabling Safe-Mode
|
59
|
+
#)
|
60
|
+
|
61
|
+
rbox_safe = Rye::Box.new('localhost')
|
62
|
+
rbox_wild = Rye::Box.new('localhost', :safe => false)
|
63
|
+
|
64
|
+
# Safe-mode is enabled by default. In safe-mode, all command
|
65
|
+
# arguments are thoroughly escaped. This prevents access to
|
66
|
+
# environment variables and file globs (among other things).
|
67
|
+
p rbox_safe.echo('$HOME') # => "$HOME"
|
68
|
+
p rbox_safe['/etc'].ls('host*') # =>
|
69
|
+
p rbox_safe.ls('-l | wc -l') # =>
|
70
|
+
p rbox_safe.echo('$HOME > /tmp/rye-home') # => "$HOME > /tmp/home"
|
71
|
+
p rbox_safe.cat('/tmp/rye-home') # =>
|
72
|
+
p rbox_safe.cat('/tmp/rye-home').stderr # => "No such file or directory"
|
73
|
+
|
74
|
+
# Here's the same commands with safe-mode disabled:
|
75
|
+
p rbox_wild.echo('$HOME') # => "/home/rye"
|
76
|
+
p rbox_wild['/etc'].ls('host*') # => ["hostconfig", "hosts"]
|
77
|
+
p rbox_wild.ls('-l | wc -l') # => 110
|
78
|
+
p rbox_wild.echo('$HOME > /tmp/rye-home') # =>
|
79
|
+
p rbox_wild.cat('/tmp/rye-home') # => "/home/rye"
|
80
|
+
p rbox_wild.rm('/tmp/rye-home') # =>
|
81
|
+
|
82
|
+
|
83
|
+
puts %Q(
|
84
|
+
# ------------------------------------------------------------------
|
85
|
+
# EXAMPLE 3 -- Custom Commands
|
86
|
+
#)
|
87
|
+
|
88
|
+
rbox = Rye::Box.new('localhost')
|
89
|
+
rbox.add_keys('/private/key/path') # Specify additional private keys
|
90
|
+
|
91
|
+
# There's currently no rye900 command
|
92
|
+
p rbox.commands.member?('rye9000') # => false
|
93
|
+
|
94
|
+
# But we can add our own commands to the Rye::Cmd class. They
|
95
|
+
# automatically become available to all Rye::Box objects.
|
96
|
+
module Rye::Cmd
|
97
|
+
def rye9000(*args)
|
98
|
+
add_command("ls", args)
|
99
|
+
end
|
100
|
+
def somescript(*args)
|
101
|
+
add_command("/path/to/my/script", args)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# We can now run rye9000 (with arguments)
|
106
|
+
p rbox.rye9000('-a') # => [".", "..", ".bashrc", ...]
|
107
|
+
p rbox.commands.member?('rye9000') # => true
|
108
|
+
|
109
|
+
|
110
|
+
puts %Q(
|
111
|
+
# ------------------------------------------------------------------
|
112
|
+
# EXAMPLE 4 -- Accessing Multiple Machines
|
113
|
+
#)
|
114
|
+
|
115
|
+
rset = Rye::Set.new
|
116
|
+
rbox = Rye::Box.new
|
117
|
+
|
118
|
+
rset.add_keys('/private/key/path') # For passwordless logins
|
119
|
+
rset.add_boxes(rbox, 'localhost') # Add boxes as hostnames or objects
|
120
|
+
|
121
|
+
# Calling methods on Rye::Set objects is very similar to calling them on
|
122
|
+
# Rye::Box objects. In fact, it's identical:
|
123
|
+
p rset.uptime # => [[14:19:02 up 32 days, 19:35 ...], [14:19:02 up 30 days, 01:35]]
|
124
|
+
p rset['/etc'].ls # => [['file1', 'file2', ...], ['life1', 'life2', ...]]
|
125
|
+
|
126
|
+
# Like Rye::Box, the response value is a Rye::Rap object containing the
|
127
|
+
# responses from each box. Each response is itself a Rye::Rap object.
|
128
|
+
unames = rset.uname
|
129
|
+
p unames # => [["Darwin"], ["Darwin"]]
|
130
|
+
puts unames.class # => Rye::Rap
|
131
|
+
|
132
|
+
# The Rye::Rap object also keeps a reference to the object that called the
|
133
|
+
# command. In this case, it will keep a reference to Rye::Set:
|
134
|
+
puts unames.set.class # => Rye::Set
|
135
|
+
puts unames.set == rset # => true
|
136
|
+
puts unames.size # => 2
|
137
|
+
puts unames.first # => Darwin
|
138
|
+
puts unames.first.class # => Rye::Rap
|
139
|
+
puts unames.first.box.class # => Rye::Box
|
140
|
+
puts unames.first.box == rbox # => true
|
141
|
+
|
142
|
+
# Envrionment variables can be set the same way as with Rye::Box
|
143
|
+
rset.add_env(:RYE, "Forty Creek")
|
144
|
+
p rset.env.first.select { |env| env =~ /RYE/ } # => ["RYE=Forty Creek"]
|
145
|
+
|
146
|
+
|
147
|
+
|
148
|
+
|