Skip to main content

Ruby SDK

The official Ruby SDK for Rexec provides an idiomatic Ruby interface for Terminal as a Service.

Installation

Add to your Gemfile:
gem 'rexec'
Or install directly:
gem install rexec

Requirements

  • Ruby 3.0+
  • faraday for HTTP requests
  • websocket-client-simple for terminal connections

Quick Start

require 'rexec'

client = Rexec::Client.new("https://your-instance.com", "your-token")

# Create a container
container = client.containers.create(
  image: "ubuntu:24.04",
  name: "my-sandbox"
)
puts "Created container: #{container.id}"

# Connect to terminal
terminal = client.terminal.connect(container.id)

terminal.on_data { |data| print data }
terminal.on_close { puts "\nConnection closed" }

terminal.write("echo 'Hello from Rexec!'\n")

sleep 2  # Wait for output

# Clean up
terminal.close
client.containers.delete(container.id)

Client Initialization

Basic Client

require 'rexec'

client = Rexec::Client.new(
  "https://your-instance.com",
  "your-api-token"
)
The main module is defined at /home/daytona/workspace/source/sdk/ruby/lib/rexec.rb:24.

With Options

client = Rexec::Client.new(
  "https://your-instance.com",
  "your-api-token",
  timeout: 30  # optional
)

Container Operations

The Containers service provides methods for managing sandboxed environments.

List Containers

containers = client.containers.list
containers.each do |c|
  puts "#{c.name}: #{c.status}"
end

Get Container

container = client.containers.get(container_id)
puts "Container #{container.name} is #{container.status}"

Create Container

container = client.containers.create(
  image: "ubuntu:24.04",
  name: "my-container",
  environment: { "MY_VAR" => "value", "DEBUG" => "true" },
  labels: { "project" => "demo" }
)

puts "Created: #{container.id}"

Start Container

client.containers.start(container_id)

Stop Container

client.containers.stop(container_id)

Delete Container

client.containers.delete(container_id)

File Operations

Manage files and directories within containers.

List Files

files = client.files.list(container_id, "/home")
files.each do |f|
  icon = f.directory? ? "📁" : "📄"
  puts "#{icon} #{f.name}"
end

Download File

content = client.files.download(container_id, "/etc/passwd")
puts content

Create Directory

client.files.mkdir(container_id, "/home/mydir")

Delete File

client.files.delete(container_id, "/home/file.txt")

Terminal Operations

Connect to containers via WebSocket for real-time terminal access.

Connect to Terminal

terminal = client.terminal.connect(container_id)

# With custom size
terminal = client.terminal.connect(container_id, cols: 120, rows: 40)

Handle Terminal Events

# Handle output
terminal.on_data do |data|
  print data
end

# Handle close
terminal.on_close do
  puts "Connection closed"
end

# Handle errors
terminal.on_error do |error|
  puts "Error: #{error}"
end

Write to Terminal

terminal.write("ls -la\n")
terminal.write("cd /home && pwd\n")

Resize Terminal

terminal.resize(150, 50)

Close Terminal

terminal.close

Advanced Examples

Run a Script

def run_script(client, container_id, script)
  output = []
  done = false
  
  terminal = client.terminal.connect(container_id)
  
  terminal.on_data { |data| output << data }
  terminal.on_close { done = true }
  
  terminal.write("#{script}\nexit\n")
  
  sleep 0.1 until done
  
  output.join
end

# Usage
result = run_script(
  client,
  container.id,
  "apt update && apt install -y curl"
)
puts result

Batch Container Operations

require 'concurrent'

def create_batch(client, count)
  futures = (0...count).map do |i|
    Concurrent::Future.execute do
      client.containers.create(
        image: "ubuntu:24.04",
        name: "worker-#{i}"
      )
    end
  end
  
  futures.map(&:value)
end

# Create 5 containers in parallel
containers = create_batch(client, 5)
puts "Created #{containers.length} containers"

File Upload

def upload_file(client, container_id, local_path, remote_path)
  content = File.read(local_path)
  client.files.upload(container_id, remote_path, content)
end

# Usage
upload_file(
  client,
  container.id,
  './local-script.sh',
  '/home/script.sh'
)

Directory Sync

require 'find'

def sync_directory(client, container_id, local_dir, remote_dir)
  client.files.mkdir(container_id, remote_dir)
  
  Find.find(local_dir) do |path|
    next unless File.file?(path)
    
    relative_path = path.sub(local_dir, '')
    remote_path = File.join(remote_dir, relative_path)
    
    content = File.read(path)
    client.files.upload(container_id, remote_path, content)
    
    puts "Uploaded: #{remote_path}"
  end
end

# Usage
sync_directory(
  client,
  container.id,
  './local-dir',
  '/app'
)

Real-time Log Streaming

def stream_logs(client, container_id, command)
  terminal = client.terminal.connect(container_id)
  
  terminal.on_data do |data|
    print data
    
    # Could also save to file
    # File.open('logs.txt', 'a') { |f| f.write(data) }
  end
  
  terminal.on_close do
    puts "\nStream ended"
  end
  
  terminal.write("#{command}\n")
  
  # Keep the connection open
  sleep
end

# Usage
stream_logs(client, container.id, "tail -f /var/log/app.log")

Interactive Session with Input

def interactive_session(client, container_id)
  terminal = client.terminal.connect(container_id)
  
  # Handle output
  terminal.on_data do |data|
    print data
  end
  
  # Handle input from stdin
  Thread.new do
    while input = STDIN.gets
      terminal.write(input)
    end
  end
  
  # Keep connection open
  sleep
end

# Usage
interactive_session(client, container.id)

Error Handling

begin
  container = client.containers.get("invalid-id")
rescue Rexec::APIError => e
  puts "API Error #{e.status_code}: #{e.message}"
rescue Rexec::ConnectionError => e
  puts "Connection Error: #{e.message}"
rescue StandardError => e
  puts "Unexpected error: #{e.message}"
end

Blocks and Iteration

The SDK uses idiomatic Ruby patterns:
# Iterate over containers
client.containers.list.each do |container|
  puts container.name
end

# Use blocks for callbacks
terminal.on_data { |data| process(data) }

# Chainable methods
container = client.containers
  .create(image: "ubuntu:24.04")
  .tap { |c| puts "Created: #{c.id}" }

Thread Safety

The Ruby SDK uses thread-safe operations for concurrent access:
require 'concurrent'

# Create thread pool
pool = Concurrent::FixedThreadPool.new(5)

# Execute tasks in parallel
10.times do |i|
  pool.post do
    container = client.containers.create(
      image: "ubuntu:24.04",
      name: "worker-#{i}"
    )
    puts "Created: #{container.id}"
  end
end

pool.shutdown
pool.wait_for_termination

Source Code

View the full source code on GitHub:

License

MIT License - see LICENSE for details.

Build docs developers (and LLMs) love