https://a.storyblok.com/f/270183/52330/d68bc1a580/blog_ruby_video-api-part2_1200x600.png

Crie uma festa com Ruby on Rails e a Video API da Vonage – Parte 2

Publicado em May 5, 2021

Tempo de leitura: 13 minutos

Esta é a segunda parte de uma série de duas partes sobre a criação de um aplicativo para sessões coletivas de exibição de vídeos usando a Video API da Vonage e o Ruby on Rails.

No primeiro artigo, abordamos as etapas de construção do back-end do aplicativo. Se você ainda não leu esse post, seria um bom ponto de partida. Agora vamos nos concentrar no front-end do nosso aplicativo. Enquanto o back-end foi escrito principalmente em Ruby, o front-end será composto principalmente por JavaScript do lado do cliente.

Quando terminarmos, teremos um aplicativo para assistir a vídeos em grupo, que poderemos usar para conversar com nossos amigos e assistir a vídeos juntos!

Landing Page

Vamos começar!

Resumo Se você quiser pular essa parte e ir direto para a implantação, pode encontrar todo o código do aplicativo no GitHub.

O que vamos construir?

Antes de começarmos a programar, é uma boa ideia reservarmos um momento para discutir o que vamos criar.

Se você se lembra do primeiro post, criamos uma instância do ID de sessão da Video API e estamos gerando tokens para cada participante. Essas informações estão sendo passadas para o front-end por meio de variáveis JavaScript recém-criadas nos arquivos de visualização ERB. Além disso, também estamos passando dados de nossas variáveis de ambiente para o front-end. Usaremos todas essas informações no código que escreveremos para criar a experiência do aplicativo.

O Ruby on Rails evoluiu bastante na integração do JavaScript do lado do cliente diretamente na pilha, com a introdução do Webpack no Rails a partir da versão 5.1. O JavaScript é incorporado por meio de pacotes colocados dentro /app/javascript/packs e adicionados como import ou require() dentro do application.js arquivo dentro do diretório.

Vamos separar os diversos aspectos do nosso código em arquivos diferentes, de modo que, no final, sua pasta terá os seguintes arquivos:

# app/javascript/packs - application.js - app_helpers.js - chat.js - opentok_screenshare.js - opentok_video.js - party.js - screenshare.js

Cada arquivo, além de application.js, conterá código para abordar questões distintas:

  • app_helpers.js: Código multifuncional necessário em todo o front-end

  • chat.js: Criação de uma Chat classe que será usada para instanciar instâncias do chat de texto

  • opentok_screenshare.js: O código do lado do cliente para a visualização do compartilhamento de tela

  • opentok_video.js: O código do lado do cliente para a visualização do bate-papo por Video

  • party.js: Criação de uma Party classe que será usada para instanciar instâncias do chat de vídeo

  • screenshare.js: Criação de uma Screenshare classe que será usada para instanciar instâncias da funcionalidade de compartilhamento de tela

Antes de criar o código, vamos adicionar esses arquivos ao application.js arquivo, o que instruirá o Webpack a compilá-los em tempo de execução:

// application.js

import './app_helpers.js'
import './opentok_video.js'
import './opentok_screenshare.js'

Criação dos pacotes de JavaScript

Em cada subseção, criaremos os arquivos JavaScript que enumeramos acima.

O app_helpers.js Arquivo

O app_helpers.js arquivo conterá funções auxiliares genéricas que exportaremos para o restante do código, a fim de utilizá-las em todo o aplicativo. Vamos criar screenshareMode(), setButtonDisplay(), formatChatMsg()e streamLayout() funções.

A screenshareMode() função utilizará a Video API da Vonage e a API do Signal para enviar uma mensagem aos navegadores de todos os participantes, o que acionará uma window.location mudança. A API Signal é a mesma que usaremos para o chat de texto, que é seu caso de uso mais simples. No entanto, como veremos nesta função, a API Signal oferece uma maneira intuitiva e poderosa de direcionar o fluxo do seu aplicativo simultaneamente para todos os participantes, sem a necessidade de escrever muito código:

export function screenshareMode(session, mode) {
  if (mode == 'on') {
    window.location = '/screenshare?name=' + name;
    session.signal({
      type: 'screenshare',
      data: 'on'
    });
  } else if (mode == 'off') {
    window.location = '/party?name=' + name;
    session.signal({
      type: 'screenshare',
      data: 'off'
    });    
  };
};

A próxima função, setButtonDisplay() altera o estilo do elemento HTML que contém o botão “Ativar/Desativar o Modo de Observação” para que seja block ou none dependendo se o participante é o moderador ou não. Existem muitas outras maneiras de fazer isso, incluindo métodos mais seguros. No entanto, para manter as coisas simples neste aplicativo destinado a assistir a vídeos entre amigos, vamos manter a abordagem minimalista:

export function setButtonDisplay(element) {
  if (name == moderator_env_name) {
    element.style.display = "block";
  } else {
    element.style.display = "none";
  };
};

A formatChatMsg() função recebe como argumento a mensagem de texto enviada pelo participante e a formata para exibição no site. Essa função procura por qualquer texto entre dois dois pontos e tenta interpretar o texto entre esses dois pontos como um emoji. Ela também acrescenta o nome do participante a cada mensagem para que todos saibam quem está falando.

Para adicionar os emojis, precisamos instalar um pacote do Node chamado node-emoji e podemos fazer isso adicionando const emoji = require('node-emoji); no início do arquivo e executando yarn add node-emoji na linha de comando. A função utilizará match() uma expressão regular para procurar sequências de texto delimitadas por dois dois pontos e, se houver correspondência, invocará a emoji const que definimos para transformar essa sequência de caracteres em um emoji:

export function formatChatMsg(message) {
  var message_arr;
  message_arr = message.split(' ').map(function(word) {
    if (word.match(/(?:\:)\b(\w*)\b(?=\:)/g)) {
      return word = emoji.get(word);
    } else {
      return word;
    }
  })
  message = message_arr.join(' ');
  return `${name}: ${message}`
};

A última função dentro app_helpers.js precisamos criar é streamLayout() aquela que recebe como argumentos o elemento HTML e o número de participantes. A função adicionará ou removerá classes CSS do elemento, dependendo do número de participantes, a fim de alterar a apresentação do chat de vídeo para um formato de grade:

export function streamLayout(element, count) {
  if (count >= 6) {
    element.classList.add("grid9");
  } else if (count == 5) {
    element.classList.remove("grid9");
    element.classList.add("grid4");
  } else if (count < 5) {
    element.classList.remove("grid4");
  }
};

O chat.js Arquivo

O chat.js código vai criar a Chat classe usando um constructor(). Essa Chat classe será chamada e instanciada tanto na visualização do chat de vídeo quanto na de compartilhamento de tela:

// chat.js

import { formatChatMsg } from './app_helpers.js';

export default class Chat {
  constructor(session) {
    this.session = session;
    this.form = document.querySelector('form');
    this.msgTxt = document.querySelector('#message');
    this.msgHistory = document.querySelector('#history');
    this.chatWindow = document.querySelector('.chat');
    this.showChatBtn = document.querySelector('#showChat');
    this.closeChatBtn = document.querySelector('#closeChat');
    this.setupEventListeners();
  }

Atribuímos várias propriedades a Chat, principalmente com base em diferentes elementos no DOM e na sessão da Video API. A última, this.setupEventListeners() é a chamada de uma função que precisamos agora adicionar ao arquivo:

setupEventListeners() {
    let self = this;
    this.form.addEventListener('submit', function(event) {
      event.preventDefault();

      self.session.signal({
        type: 'msg',
        data: formatChatMsg(self.msgTxt.value)
      }, function(error) {
        if (error) {
          console.log('Error sending signal:', error.name, error.message);
        } else {
          self.msgTxt.value = '';
        }
      });
    });

    this.session.on('signal:msg', function signalCallback(event) {
      var msg = document.createElement('p');
      msg.textContent = event.data;
      msg.className = event.from.connectionId === self.session.connection.connectionId ? 'mine' : 'theirs';
      self.msgHistory.appendChild(msg);
      msg.scrollIntoView();
    });

    this.showChatBtn.addEventListener('click', function(event) {
      self.chatWindow.classList.add('active');
    });

    this.closeChatBtn.addEventListener('click', function(event) {
      self.chatWindow.classList.remove('active');
    });
  }
}

setupEventListeners() cria um EventListener botão para o chat de texto submit . Quando uma nova mensagem é enviada, ela é encaminhada à API do Signal para ser processada e enviada a todos os participantes. Da mesma forma, quando uma nova mensagem é recebida, uma nova <p> é adicionada ao elemento de bate-papo, e a janela de bate-papo do participante é rolada para exibi-la.

Os próximos dois arquivos que vamos criar desempenham funções semelhantes na criação de novas classes para a sessão de videochamada e para a visualização do compartilhamento de tela.

O party.js Arquivo

Neste arquivo, vamos criar a Party classe que será usada para instanciar novas instâncias do chat de vídeo:

// party.js

import { screenshareMode, setButtonDisplay, streamLayout } from './app_helpers.js';

export default class Party {
  constructor(session) {
    this.session = session;
    this.watchLink = document.getElementById("watch-mode");
    this.subscribers = document.getElementById("subscribers");
    this.participantCount = document.getElementById("participant-count");
    this.videoPublisher = this.setupVideoPublisher();
    this.clickStatus = 'off';
    this.setupEventHandlers();
    this.connectionCount = 0;
    setButtonDisplay(this.watchLink);
  }

A constructor() função recebe a sessão da Video API como argumento e a passa para this.session. As demais propriedades são definidas e recebem valores. A watchLink, subscribers, participantCount provêm dos elementos HTML, enquanto videoPublisher recebe uma função como seu valor, e clickStatus recebe o valor padrão off.

Vamos criar a setupVideoPublisher() função neste momento. A função chama a função do SDK JavaScript da Video API initPublisher() para iniciar a exibição do Video. Ela pode receber argumentos opcionais e, por isso, especificamos que o Video deve ocupar 100% da largura e da altura do seu elemento e deve ser anexado a ele:

setupVideoPublisher() {
    return OT.initPublisher('publisher', {
      insertMode: 'append',
      width: "100%",
      height: "100%"
    }, function(error) {
      if (error) {
        console.error('Failed to initialise publisher', error);
      };
    });
  }

Existem várias ações para as quais também precisamos criar ouvintes de eventos e adicioná-los à classe. Precisamos monitorar quando a sessão estiver conectada, quando um stream de Video for criado, quando uma conexão for adicionada e quando uma conexão for encerrada. Quando uma conexão é adicionada ou encerrada, incrementamos ou decrementamos a contagem de participantes e atualizamos o número de participantes no elemento de contagem de participantes <div> na página:

setupEventHandlers() {
    let self = this;
    this.session.on({
      // This function runs when session.connect() asynchronously completes
      sessionConnected: function(event) {
        // Publish the publisher we initialzed earlier (this will trigger 'streamCreated' on other
        // clients)
        self.session.publish(self.videoPublisher, function(error) {
          if (error) {
            console.error('Failed to publish', error);
          }
        });
      },

      // This function runs when another client publishes a stream (eg. session.publish())
      streamCreated: function(event) {
        // Subscribe to the stream that caused this event, and place it into the element with id="subscribers"
        self.session.subscribe(event.stream, 'subscribers', {
          insertMode: 'append',
          width: "100%",
          height: "100%"
        }, function(error) {
          if (error) {
            console.error('Failed to subscribe', error);
          }
        });
      },

      // This function runs whenever a client connects to a session
      connectionCreated: function(event) {
        self.connectionCount++;
        self.participantCount.textContent = `${self.connectionCount} Participants`;
        streamLayout(self.subscribers, self.connectionCount);
      },

      // This function runs whenever a client disconnects from the session
      connectionDestroyed: function(event) {
        self.connectionCount--;
        self.participantCount.textContent = `${self.connectionCount} Participants`;
        streamLayout(self.subscribers, self.connectionCount);
      }
    });

Por fim, adicionamos mais um ouvinte de evento. Esse ouvinte de evento é associado ao click ação do botão “Ativar/Desativar Modo de Visualização”. Quando clicado, ele leva à visualização de compartilhamento de tela, caso o status de clique estivesse desativado. Você deve se lembrar de que o status de clique recebe o valor padrão “desativado” na definição da classe:

this.watchLink.addEventListener('click', function(event) {
      event.preventDefault();
      if (self.clickStatus == 'off') {
        // Go to screenshare view
        screenshareMode(self.session, 'on');
      };
    });
  }
}

O screenshare.js Arquivo

A última classe que vamos criar é uma Screenshare classe que será responsável por definir o compartilhamento de tela de Video. A constructor() função recebe como argumentos a sessão da Video API e o nome do participante:

// screenshare.js

import { screenshareMode } from './app_helpers.js';

export default class Screenshare {
  constructor(session, name) {
    this.session = session;
    this.name = name;
    this.watchLink = document.getElementById("watch-mode");
    this.clickStatus = 'on';
  }

Ao contrário da Party classe, o clickStatus aqui vem por padrão como on já que queremos sair do compartilhamento de tela e voltar ao modo de chat por vídeo, caso o moderador clique no botão “Ativar/Desativar o modo de observação”.

Também utilizamos toggle() para compartilhar a tela do participante, caso ele seja o moderador, ou para que todos os demais participantes possam acompanhar o compartilhamento de tela:

toggle() {
    if (this.name === moderator_env_name) {
      this.shareScreen();
    } else {
      this.subscribe();
    }
  }

A shareScreen() função chamada na toggle() precisa ser definida:

shareScreen() {
    this.setupPublisher();
    this.setupAudioPublisher();
    this.setupClickStatus();
  }

Essa função, por si só, possui três funções que também precisam ser criadas. A primeira função exibirá a tela do moderador. No entanto, a exibição da tela, por si só, não inclui o áudio. Portanto, uma segunda função transmitirá o áudio do computador do moderador. Em seguida, a função final shareScreen() retornará à visualização do chat de Video caso o botão “Ativar/Desativar Modo de Assistir” seja clicado:

setupClickStatus() {
    // screen share mode off if clicked off
    // Set click status
    let self = this;
    this.watchLink.addEventListener('click', function(event) {
      event.preventDefault();
      if (self.clickStatus == 'on') {
        self.clickStatus = 'off';
        screenshareMode(self.session, 'off');
      };
    });
  }

  setupAudioPublisher() {
    var self = this;
    var audioPublishOptions = {};
    audioPublishOptions.insertMode = 'append';
    audioPublishOptions.publishVideo = false;
    var audio_publisher = OT.initPublisher('audio', audioPublishOptions,
      function(error) {
        if (error) {
          console.log(error);
        } else {
          self.session.publish(audio_publisher, function(error) {
            if (error) {
              console.log(error);
            }
          });
        };
      }
    );
  }

  setupPublisher() {
    var self = this;
    var publishOptions = {};
    publishOptions.videoSource = 'screen';
    publishOptions.insertMode = 'append';
    publishOptions.height = '100%';
    publishOptions.width = '100%';
    var screen_publisher = OT.initPublisher('screenshare', publishOptions,
      function(error) {
        if (error) {
          console.log(error);
        } else {
          self.session.publish(screen_publisher, function(error) {
            if (error) {
              console.log(error);
            };
          });
        };
      }
    );
  }

Tudo isso tem como objetivo criar o compartilhamento de tela para o moderador. Todos os demais usuários do aplicativo vão querer acompanhar esse compartilhamento de tela. Usaremos a subscribe() função para fazer isso. Essa será a última função dentro do arquivo:

subscribe() {
    var self = this;
    this.watchLink.style.display = "none";
    this.session.on({
      streamCreated: function(event) {
        console.log(event);
        if (event.stream.hasVideo == true) {
          self.session.subscribe(event.stream, 'screenshare', {
            insertMode: 'append',
            width: '100%',
            height: '100%'
          }, function(error) {
            if (error) {
              console.error('Failed to subscribe to video feed', error);
            }
          });
        } else if (event.stream.hasVideo == false ) {
          self.session.subscribe(event.stream, 'audio', {
            insertMode: 'append',
            width: '0px',
            height: '0px'
          }, function(error) {
            if (error) {
              console.error('Failed to subscribe to audio feed', error);
            }
          });
        };
      }
    });
  }
}

Agora estamos prontos para fazer com que todas essas classes que definimos funcionem no aplicativo, criando instâncias delas dentro do opentok_screenshare.js e opentok_video.js arquivos.

Criando opentok_video.js

O opentok_video.js arquivo criará uma nova experiência de bate-papo por Video. A maior parte do trabalho foi feita nas classes que definimos acima, portanto, este arquivo é relativamente pequeno. Primeiro, vamos importar o Chat e Party :

// opentok_video.js

import Chat from './chat.js'
import Party from './party.js'

Em seguida, definiremos uma variável global vazia para armazenar a sessão da Video API:

var session = ''

Em seguida, colocamos o restante do código dentro de três verificações para garantir que estamos no caminho correto do site, que o DOM está totalmente carregado e que o nome do participante não está vazio:

if (window.location.pathname == '/party') {
  document.addEventListener('DOMContentLoaded', function() {
    if (name != '') {

O restante do código inicia uma nova sessão da Video API, caso ainda não exista uma, e instancia um novo Chat e um novo Party. No final, também aguardamos que a API de Sinais envie uma screenshare mensagem de dados com o valor de on. Quando essa mensagem é recebida, o window.location é movido para /screenshare:

// Initialize an OpenTok Session object
      if (session == '') {
        session = OT.initSession(api_key, session_id);
      }

      new Chat(session);
      new Party(session);

      // Connect to the Session using a 'token'
      session.connect(token, function(error) {
        if (error) {
          console.error('Failed to connect', error);
        }
      });

      // Listen for Signal screenshare message
      session.on('signal:screenshare', function screenshareCallback(event) {
        if (event.data == 'on') {
          window.location = '/screenshare?name=' + name;
        };
      });
    };
  });
}

Criando opentok_screenshare.js

O último arquivo JavaScript que vamos criar é muito semelhante ao anterior. Ele é responsável pela visualização do compartilhamento de tela e utiliza o Screenshare e Chat que definimos anteriormente:

import Screenshare from './screenshare.js'
import Chat from './chat.js'

// declare empty global session variable
var session = ''

if (window.location.pathname == '/screenshare') {
  document.addEventListener('DOMContentLoaded', function() {
    // Initialize an OpenTok Session object
    if (session == '') {
      session = OT.initSession(api_key, session_id);
    }

    // Hide or show watch party link based on participant
    if (name != '' && window.location.pathname == '/screenshare') {
      new Chat(session);
      new Screenshare(session, name).toggle();

      // Connect to the Session using a 'token'
      session.connect(token, function(error) {
        if (error) {
          console.error('Failed to connect', error);
        }
      });

      // Listen for Signal screenshare message
      session.on('signal:screenshare', function screenshareCallback(event) {
        if (event.data == 'off') {
          window.location = '/party?name=' + name;
        };
      });
    }
  });
};

Antes de encerrarmos, por último, mas certamente não menos importante, precisamos definir o estilo do front-end do aplicativo. Todo esse código não serve para nada se não for acessível aos participantes.

Estilo do aplicativo

A folha de estilo deste aplicativo não teria sido criada sem a ajuda do meu amigo e ex-colega, Hui Jing Chen , que me ensinou muito sobre design front-end ao longo desse processo. O aplicativo utiliza principalmente Flexbox Grid para organizar os elementos.

Vamos começar criando um custom.css arquivo dentro de app/javascript/stylesheets. Queremos garantir que ele seja incluído em nosso aplicativo, então vamos adicionar uma linha de importação application.scss na mesma pasta, @import './custom.css';.

Primeiro, vamos adicionar o estilo básico em custom.css:

:root {
  --main: #343a40;
  --txt-alt: white;
  --txt: black;
  --background: white;
  --bgImage: url('~images/01.png');
  --chat-bg: rgba(255, 255, 255, 0.75);
  --chat-mine: darkgreen;
  --chat-theirs: indigo;
}

html {
  box-sizing: border-box;
  height: 100%;
}
 
*,
*::before,
*::after {
  box-sizing: inherit;
  margin: 0;
  padding: 0;
}
 
body {
  height: 100%;
  display: flex;
  flex-direction: column;
  background-color: var(--background);
  background-image: var(--bgImage);
  overflow: hidden;
}
 
main {
  flex: 1;
  display: flex;
  position: relative;
}

input {
  font-size: inherit;
  padding: 0.5em;
  border-radius: 4px;
  border: 1px solid currentColor;
}

button,
input[type="submit"] {
  font-size: inherit;
  padding: 0.5em;
  border: 0;
  background-color: var(--main);
  color: var(--txt-alt);
  border-radius: 4px;
}

header {
  background-color: var(--main);
  color: var(--txt-alt);
  padding: 0.5em;
  height: 4em;
  display: flex;
  align-items: center;
  justify-content: space-between;
}

Então, vamos adicionar o estilo para a página de destino:

.landing {
  margin: auto;
  text-align: center;
  font-size: 125%;
}

.landing form {
  display: flex;
  flex-direction: column;
  margin: auto;
  position: relative;
}

.landing input,
.landing p {
  margin-bottom: 1em;
}

.landing .error {
  color: maroon;
  position: absolute;
  bottom: -2em;
  width: 100%;
  text-align: center;
}

Também queremos definir o estilo do chat de texto, garantindo, principalmente, que ele permaneça no lugar e não role junto com o resto da página à medida que o chat avança:

.chat {
  width: 100%;
  display: flex;
  flex-direction: column;
  height: 100%;
  position: fixed;
  top: 0;
  left: 0;
  z-index: 2;
  background-color: var(--chat-bg);
  transform: translateX(-100%);
  transition: transform 0.5s ease;
}

.chat.active {
  transform: translateX(0);
}

.chat-header {
  padding: 0.5em;
  box-shadow: 0 1px 5px rgba(0, 0, 0, 0.12), 0 1px 3px rgba(0, 0, 0, 0.24);
  display: flex;
  justify-content: space-between;
}

.btn-chat {
  height: 5em;
  width: 5em;
  border-radius: 50%;
  box-shadow: 0 3px 6px 0 rgba(0, 0, 0, .2), 0 3px 6px 0 rgba(0, 0, 0, .19);
  position: fixed;
  right: 1em;
  bottom: 1em;
  cursor: pointer;
}

.btn-chat svg {
  height: 4em;
  width: 2.5em;
}

.btn-close {
  height: 2em;
  width: 2em;
  background: transparent;
  border: none;
  cursor: pointer;
}

.btn-close svg {
  height: 1em;
  width: 1em;
}

.messages {
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow-y: scroll;
  padding: 1em;
  box-shadow: 0 1px 5px rgba(0, 0, 0, 0.12), 0 1px 3px rgba(0, 0, 0, 0.24);
  scrollbar-color: #c1c1c1 transparent;
}

.messages p {
  margin-bottom: 0.5em;
}

.mine {
  color: var(--chat-mine);
}

.theirs {
  color: var(--chat-theirs);
}

.chat form {
  display: flex;
  padding: 1em;
  box-shadow: 0 1px 5px rgba(0, 0, 0, 0.12), 0 1px 3px rgba(0, 0, 0, 0.24);
}

.chat input[type="text"] {
  flex: 1;
  border-top-left-radius: 0px;
  border-bottom-left-radius: 0px;
  background-color: var(--background);
  color: var(--txt);
  min-width: 0;
}

.chat input[type="submit"] {
  border-top-right-radius: 0px;
  border-bottom-right-radius: 0px;
}

Agora, vamos criar o estilo para os elementos de chat por Video e compartilhamento de tela:

.videos {
  flex: 1;
  display: flex;
  position: relative;
}

.subscriber.grid4 {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(25em, 1fr));
}

.subscriber.grid9 {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(18em, 1fr));
}
 
.subscriber,
.screenshare {
  width: 100%;
  height: 100%;
  display: flex;
}
 
.publisher {
  position: absolute;
  width: 25vmin;
  height: 25vmin;
  min-width: 8em;
  min-height: 8em;
  align-self: flex-end;
  z-index: 1;
}

.audio {
  position: absolute;
  opacity: 0;
  z-index: -1;
}

.audio {
  display: none;
}

.dark {
  --background: black;
  --chat-mine: lime;
  --chat-theirs: violet;
  --txt: white;
}

Por fim, vamos adicionar uma consulta de mídia que manterá o chat de texto com as proporções corretas em telas menores:

@media screen and (min-aspect-ratio: 1 / 1) {
  .chat {
    width: 20%;
    min-width: 16em;
  }
}

É isso aí! O aplicativo, tanto o back-end quanto o front-end, já foi criado. Agora estamos prontos para juntar tudo.

Juntando tudo isso

Embora o aplicativo seja uma combinação de várias linguagens de programação, a saber, Ruby e JavaScript, com back-end e front-end interligados, é relativamente simples executá-lo. Isso porque o Rails nos permite integrar tudo perfeitamente com um único comando.

Na linha de comando, você pode executar bundle exec rails s e observar o servidor Rails iniciar. Você também verá a seguinte linha, quase mágica, na saída do console na primeira vez que executar o aplicativo:

[Webpacker] Compiling...

Na verdade, você perceberá isso sempre que fizer uma alteração em qualquer um dos seus pacotes de JavaScript ou CSS. Essa saída indica que o Rails está usando o Webpack para compilar e incorporar todos os seus pacotes ao aplicativo. Assim que o [Webpacker] Compiling... processo estiver concluído, você verá uma lista de todos os seus pacotes compilados:

Version: webpack 4.42.1 Time: 1736ms Built at: 05/01/2020 12:01:37 PM Asset Size Chunks Chunk Names js/app_helpers-31c49752d24631573287.js 100 KiB app_helpers [emitted] [immutable] app_helpers js/app_helpers-31c49752d24631573287.js.map 44.3 KiB app_helpers [emitted] [dev] app_helpers js/application-d253fe0e7db5e2b1ca60.js 564 KiB application [emitted] [immutable] application js/application-d253fe0e7db5e2b1ca60.js.map 575 KiB application [emitted] [dev] application js/chat-451fca901a39ddfdf982.js 103 KiB chat [emitted] [immutable] chat js/chat-451fca901a39ddfdf982.js.map 46.1 KiB chat [emitted] [dev] chat js/opentok_screenshare-2bc51be74c7abf27abe2.js 110 KiB opentok_screenshare [emitted] [immutable] opentok_screenshare js/opentok_screenshare-2bc51be74c7abf27abe2.js.map 51 KiB opentok_screenshare [emitted] [dev] opentok_screenshare js/opentok_video-15ed35dc7b01325831c0.js 109 KiB opentok_video [emitted] [immutable] opentok_video js/opentok_video-15ed35dc7b01325831c0.js.map 50.6 KiB opentok_video [emitted] [dev] opentok_video js/party-f5d6c0ccd3bb1fcc225e.js 105 KiB party [emitted] [immutable] party js/party-f5d6c0ccd3bb1fcc225e.js.map 47.5 KiB party [emitted] [dev] party js/screenshare-4c13687e1032e93dc59a.js 105 KiB screenshare [emitted] [immutable] screenshare js/screenshare-4c13687e1032e93dc59a.js.map 47.9 KiB screenshare [emitted] [dev] screenshare manifest.json 2.38 KiB [emitted]

Os nomes dos arquivos indicam que eles foram compactados, mas ainda é possível ver os nomes dos seus pacotes neles se você olhar com atenção, como opentok_screenshare, party, app_helpers, etc.

Executar seu aplicativo localmente é ótimo para você mesmo testá-lo, mas você provavelmente gostaria de convidar amigos para participar com você!

Você pode criar um link acessível externamente para o seu aplicativo em execução local usando uma ferramenta como o ngrok. Ela fornece uma URL externa para o seu ambiente local. A Nexmo Developer Platform disponibiliza um guia sobre começar a usar o ngrok que você pode seguir.

Se você quiser simplesmente começar a usar, também pode implantar esse aplicativo com um clique a partir do GitHub diretamente no Heroku.

Adoraria saber o que você desenvolveu usando a Video API da Vonage! Participe da conversa em nosso Slack da Comunidade e compartilhe sua história!

Compartilhar:

https://a.storyblok.com/f/270183/384x384/e5480d2945/ben-greenberg.png
Ben GreenbergEx-funcionários da Vonage

Ben é um desenvolvedor que mudou de carreira, tendo atuado anteriormente por uma década nas áreas de educação de adultos, organização comunitária e gestão de organizações sem fins lucrativos. Ele trabalhou como representante de desenvolvedores na Vonage. Escreve regularmente sobre a interseção entre desenvolvimento comunitário e tecnologia. Natural do sul da Califórnia e morador de longa data da cidade de Nova York, Ben reside atualmente perto de Tel Aviv, em Israel.