Before starting, make sure you have Ruby installed on your system and the Rails gem.
Create a Rails app
If you don’t already have Rails app, create one.
rails new my-app
cd my-app
Install Terminalwire in Rails
Add the Terminalwire Rails gem to your app.
bundle add terminalwire
Then install Terminalwire in your Rails app.
rails g terminalwire:install my-app
Configure the terminal
The rails g terminalwire:install my-app
command creates files that you may configure for your application’s needs.
Thor command-line parser
The ./app/terminals
folder has two files.
The application_terminal.rb
file is a Thor command-line parser that sets everything up needed to stream terminal I/O between the client and server, sets the basename
for the command-line application, and provides a current_user
method to authenticate users that other subclasses and commands can use.
# ./app/terminals/application_terminal.rb # Learn how to use Thor at http://whatisthor.com. class ApplicationTerminal < Thor # Enables IO Streaming. include Terminalwire::Thor # The name of your binary. Thor uses this for its help output. def self.basename = "<%= binary_name %>" private def current_user=(user) # The Session object is a hash-like object that encrypts and signs a hash that's # stored on the client's file sytem. Conceptually, it's similar to Rails signed # and encrypted client-side cookies. session["user_id"] = user.id end def current_user @current_user ||= User.find(session["user_id"]) end end
The main_terminal.rb
file is where all your application’s commands are defined. You can add, remove, or modify commands as needed. This file inherits from ApplicationTerminal
and includes the Terminalwire::Thor
module to enable I/O streaming and current_user
authentication needed to manage sessions.
# ./app/terminals/main_terminal.rb class MainTerminal < ApplicationTerminal desc "hello NAME", "say hello to NAME" def hello(name) puts "Hello #{name}" end desc "login", "Login to your account" def login print "Email: " email = gets.chomp print "Password: " password = getpass # Replace this with your own authentication logic; this is an example # of how you might do this with Devise. user = User.find_for_authentication(email: email) if user && user.valid_password?(password) self.current_user = user puts "Successfully logged in as #{current_user.email}." else puts "Could not find a user with that email and password." end end desc "whoami", "Displays current user information." def whoami if self.current_user puts "Logged in as #{current_user.email}." else puts "Not logged in. Run `#{self.class.basename} login` to login." end end desc "logout", "Logout of your account" def logout session.reset puts "Successfully logged out." end end
Routes configuration
The ./config/routes.rb
file mounts the MainTerminal
in a Terminalwire::Thor::Server
WebSocket server to the /terminal
endpoint. This is the URL your client will connect to.
# ./config/routes.rb Rails.application.routes.draw do match '/terminal', to: Terminalwire::Thor::Server.new(MainTerminal), via: [:get, :connect] end
Terminalwire binary stub configuration
Terminalwire generates a binary stub in the Rails ./bin
folder that you may use to interact with your application in a development environment. You may need to change the host if the server is running on a different port.
!/usr/bin/env terminalwire-exec
url: "ws://localhost:3000"
To test the intergration, restart your Rails server, then run ./bin/my-app version
in your terminal and you should see the following:
./bin/my-app version
1.0.0