Getting Started

Getting Started

Install Terminalwire in a Rails app and build your first terminal

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