Skip to content

Manual técnico para desarrolladores

Rodrigo Alfonso edited this page Apr 5, 2020 · 2 revisions

(Actualizado a Abril 2020)

Ver también: Manual técnico para usuarios y el README.md de cada proyecto.

Introducción

Arquitectura del proyecto

El proyecto consta de varios componentes:

Otros repositorios importantes:

  • gobstones-web-desktop: Acá se suben los binarios de la versión Desktop compilados por el servidor de integración contínua. Es un repositorio sin código, solo se usa la parte de Releases.
  • gobstones.github.io: Landing page del proyecto visible en https://gobstones.github.io
  • gobstones-issues: Issue tracker de usuarios. Desde Gobstones Web se pueden crear issues mediante un formulario y los mismos terminan creándose en este repositorio.
  • Repos con el slug gobstones-MODO-AMBIENTE: (ejemplo): Son páginas de Github Pages que redireccionan a la versión correcta de Gobstones Web, en el modo correcto (bloques, código o teacher) para un ambiente (si no dice -staging es producción).

El resto de los repositorios de la organización son viejos o pueden considerarse deprecados.

Tecnologías usadas

Los componentes de vista usan Web Components con el framework Polymer y manejan las dependencias usando Bower. Los deploys se hacen a Github Pages.

La versión desktop de Gobstones Web está hecha con Electron.

Todos los proyectos están escritos en JavaScript (ES6), salvo el tablero que usa CoffeeScript.

Manejo del proyecto

Hay dos issue trackers importantes:

Usamos Travis CI como servidor de integración contínua para automatizar los deploys y/o correr tests.

🌟 Toda propuesta de cambio debe ser realizada mediante un pull request, en donde se discutirá la solución entre todos los mantenedores.

Desarrollando

Instalando el entorno

Para empezar, tenemos que tener instalado node.js 8.17.0. En node >= 10 no anda, así que hasta que esté solucionado el error vamos a tener que usar la 8.17.0. Al ser una versión vieja, no conviene instalarlo directamente sino mediante nvm, un gestor de versiones de nodejs. Si usás Windows podés usar nvm-windows.

Una vez instalado node vamos a instalar algunas dependencias necesarias:

npm install -g gulp-cli grunt-cli polymer-cli bower yarn

Gobstones-Web (UI)

Instalando

Para empezar, clonamos el repositorio de Gobstones Web:

git clone https://github.com/gobstones/gobstones-web
cd gobstones-web/

Luego, instalamos las dependencias del cliente con:

npm install # dependencias para desarrollo
bower install # dependencias client side

Al ejecutar estos comandos se crearán los directorios node_modules/ y app/bower_components/. Una vez creados se podrá ejecutar:

gulp serve

...y la app estará ejecutando en http://localhost:5000 y deberías ver algo como esto:

image

Al modificar algún archivo, el servidor se reiniciará aplicando automáticamente los cambios.

⚠️ Algunas tecnologías utilizadas como Polymer 1.x, HTML Imports y Bower actualmente están siendo deprecadas. Se recomienda migrar a algo más reciente.

Introducción a Polymer 1.x

La idea de Polymer es que uno pueda crear sus propios componentes (con código JS, estilos CSS y maquetado HTML) y reutilizarlo en otras partes o incluso en otras aplicaciones web.

Para hacer andar Polymer en un sitio web, agregar:

<script src="bower_components/webcomponentsjs/webcomponents.min.js"></script>
<link href="bower_components/polymer/polymer.html" rel="import" />

Para crear un componente:

<dom-module id="greeting-box">
  <template>
   <!-- Estilos -->
    <style>
      .greetingDiv {
          background-color: red;
      }
    </style>
    
    <!-- Maquetado -->
    <div class="greetingDiv">{{greeting}}</div>
  </template>
  
  <!-- Código -->
  <script>
    Polymer({
      is: "greeting-box",
      properties: {
        greeting: {
          type: String,
          value: "Hello!"
        }
      }
    });
  </script>
</dom-module>

Esto tiene que ir en un archivo greeting-box.html.

Para usar nuestro componente custom, hacemos un HTML Import y lo agregamos como si fuese un tag HTML más:

<link rel="import" href="../../bower_components/polymer/polymer.html">
<greeting-box greeting="Hola!!"></greeting-box>

Consideraciones:

  • No usar self-closing tags (<greeting-box /> en vez de <greeting-box></greeting-box>): No va a funcionar.
  • El nombre de los componentes custom siempre tiene que tener un guión (-). El componente del ejemplo no podría llamarse simplemente greeting.
  • Al compilar un proyecto Polymer se suele usar un binario llamado vulcanize que une todos los componentes en un solo archivo para evitar que el browser haga muchos requests. Podría decirse que vulcanize es el webpack de Polymer™.

➡️ Para más información leer la documentación y seguir los tutoriales.

Directorios importantes

  • build/: Archivos necesarios para la versión Desktop.
  • app/elements: Componentes de Polymer de la aplicación, uno por carpeta. El archivo elements.html contiene imports para todos ellos.
  • app/images: Imágenes
  • app/scripts: Archivos sueltos de JS. Como los HTML imports esperan, bueno... HTML, hay varios archivos que wrappean libraries externas como showdown.html o tweenjs.html. ¡Pero también hay archivos nuestros como stylist.html o el directorio loaders!
  • app/styles: Estilos que no pertenecen a ningún componente.

Mapa de componentes

select-mode main-app 1 main-app 2 main-app 3 main-app 4

Componentes importantes

gobstones-ide

El punto de entrada de la aplicación puede ser view-blocks, view-code o view-teacher. Luego, cada una de estos componentes renderiza un main-app, que se encarga de configurar el drawer y algunas cosas de layout globales, y por último dibuja un gobstones-ide. Este es el componente principal de la aplicación.

Acá hay cosas como:

  • Los modals
  • Parsers de URL
  • Carga inicial de ejercicios
  • Tour

event-bus

Los componentes de la aplicación se comunican (casi*) siempre por un event bus global definido en window.BUS.

Para emitir un evento se usa:

window.BUS.fire("nombre-del-evento", objetoConDatosOpcional);

Para recibirlo, los componentes deben implementar el BusListenerBehavior de la siguiente forma:

<link rel="import" href="{PATH_A_scripts}/behaviors/busListenerBehavior.html">
Polymer({
  is: 'componente-que-escucha-eventos',
  behaviors: [
    Polymer.BusListenerBehavior,
    ...
  ],
  ready: function() {
    this.subscribeTo("nombre-del-evento", datosOpcionales => {
      // ...
    })
  }
});

*casi = A veces se usa el bus y otras variables globales que comienzan con window.GBS_

Archivos importantes

app/scripts/loaders/loader.html

Un Loader es cualquier cosa que permita abrirse o guardarse dentro de Gobstones Web. Un entorno de Gobstones puede tener varios archivos como:

  • Texto
  • Bloques
  • Tableros iniciales
  • Metadata (configuración del ejercicio)
  • Biblioteca del docente

Cada uno de estos lo llamamos componente y hay loaders para cada uno de ellos en app/scripts/loaders/components/.

La mayoría hereda de TextLoader que es una versión simplificada de Loader con una interfaz más simple diseñada para archivos de texto. Los componentes que sean de un solo archivo usan el mixin SingleFileComponent.

app/scripts/loaders/project{Blocks|Teacher}Loader.html

Este es un loader que representa al proyecto, compuesto por otros loaders. Se encarga de delegar en cada componente la tarea de armar o procesar el archivo y codificar/decodificar el archivo ZIP del proyecto.

app/scripts/loaders/remote/{gitHub|url}Loader.html

Puede descargar una carpeta de un repositorio GitHub / una URL cualquiera, crear un archivo ZIP ficticio y pasárselo a un ProjectLoader para que siga su curso de carga normal.

⚠️ Las credenciales de GitHub están hardcodeadas. Apenas exista un Backend oficial de Gobstones hay que migrarlas allí.

app/scripts/loaders/remote/gitHubGuideLoader.html

Sabe leer guías y cursos de un repositorio de GitHub y armar un listado de ejercicios.

app/scripts/stylist.html

Clase que maneja estilos generales del editor de tableros y los paneles deslizables. Lo más importante acá es el escalado automático del tablero al tamaño de la pantalla.

app/scripts/behaviors/aOnFirstLoad.html

Se llama así ¯_(ツ)_/¯. Contiene helpers varios de JS.

gs-board (Tablero)

Instalando

Si instalaste el entorno para Gobstones Web, deberías tener casi todo listo. Cloná el proyecto e instalá las dependencias locales:

git clone https://github.com/gobstones/gs-board
cd gs-board/
npm install
bower install

Ahora ya podés correr el proyecto con:

grunt

...y deberías tener el servidor corriendo, si no usás Windows ⚠️.

Lamentablemente, el servidor de desarrollo usa comandos de Linux para arrancar. Una de las tareas pendientes es adaptarlo para que levante ahí también.

Una vez instalada la distro de Linux (?) podés modificar los archivos que quieras. No hay muchos, pero están distribuídos de una forma que no es la estándar de Polymer. Se dividen en:

  • component.json: Nombre e imports
  • script.coffee: Código CoffeeScript 1.x
  • style.scss: Estilos en SASS
  • template.html: El markup

Dominio

Los componentes son:

  • gs-board: Tablero con muchas celdas
  • gs-cell: Celda con 4 bolitas
  • gs-stone: Bolita clickeable con el número adentro.
  • key-tracker: Detecta teclas presionadas para poder hacer CTRL+click y cambiar el cabezal.
  • cell-dresser: Vestidor con las vestimentas para las celdas - en realidad, no - Es un mal nombre. Es una clase que sabe detectar la regla más adecuada de la vestimenta para una celda.
  • board-demo: Demo para desarrollo.

Deploy

Ejecutar grunt dist y se generarán los archivos compilados. Estos se commitean en el control de versiones. Luego, crear un tag con ./tag.sh <VERSION_DESEADA> y el script hará el script, actualizando también el wrapper en Ruby utilizado por Mumuki.


🛈 ¡Todo esto también aplica para gobstones-code-runner!


gs-element-blockly (canvas de bloques)

Instalando

Cloná el proyecto e instalá las dependencias locales:

git clone https://github.com/Program-AR/gs-element-blockly
cd gs-element-blockly/
npm install
bower install

Ahora ya podés correr el proyecto con:

polymer serve

Estructura

No hay. Son tres archivos de muchísimas líneas de código con customizaciones de Blockly para que tenga bloques que generen código Gobstones.

En gobstones-blocks.js están definidos los bloques, gobstones-language-generator.js el generador que traduce de Bloques a código Gobstones, y en gs-element-blockly.html el wrapper de Polymer que interactúa con la aplicación.

Esta integración usa una sublibrary llamada proceds-blockly que monkey-patchea Blockly para mejorar y agregar features a los bloques de Procedimientos y Funciones.

Podés aprender el resto en la documentación.

Deploy

Ejecutando tag.sh <VERSIÓN_DESEADA> se generará un tag y Travis hará el resto. "El resto" no aplica para Gobstones Web, solo para Mumuki. Es decir, que una vez creado el tag ya se puede actualizar la dependencia en Gobstones Web.

FAQ

¿Cómo es la interfaz de gobstones-interpreter?

Lo más fácil es probarlo en un REPL, pero también podés leer la documentación de la interfaz.

¿Para qué sirve cada dependencia de gobstones-web?

{
    "iron-elements": "Elementos varios para Polymer",
    "neon-elements": "Elementos varios para Polymer",
    "page": "Ruteador",
    "paper-elements": "Elementos varios para Polymer",
    "platinum-elements": "Elementos varios para Polymer",
    "polymer": "El framework de vista",
    "ace-widget": "Wrapper del editor de texto Ace para Polymer",
    "gs-board": "Tablero de Gobstones con soporte de vestimentas",
    "lodash": "La standard library que le falta a js",
    "webcomponentsjs": "Polyfill de webcomponents v0",
    "jquery": "Library de UI",
    "jquery-ui": "Library de UI",
    "app-localize-behavior": "Localizador de strings de Polymer",
    "jszip": "Compresor y descompresor de archivos ZIP utilizados por los proyectos",
    "file-saver": "Permite guardar archivos de la misma forma en todos los browsers. No estoy seguro de que se esté usando",
    "app-router": "Ruteador de Polymer",
    "async": "Helpers para código asincrónico utilizado en los Loaders",
    "gs-element-blockly": "Editor de Blockly para bloques Gobstones",
    "showdown": "Parser de Markdown",
    "js-emoji": "Colección de emojis",
    "js-yaml": "Parser de YAML",
    "material-walkthrough": "Library para el tour inicial",
    "tweenjs": "Library de animaciones para scrollear suavemente a los errores de bloques",
    "juicy-jsoneditor": "Editor de JSON. Creo que está sin uso",
    "simplemde": "Editor de Markdown usado por Gobstones Teacher",
    "gobstones-code-runner": "Botón y lógica para correr ejercicios de Gobstones Web",
    "paper-tristate-checkbox": "Checkbox de 3 estados usado por el blocks-toolbox-selector",
    "ace-builds": "Highlighter de Gobstones para el editor de texto Ace"
  }

¿Cómo pruebo Gobstones Desktop?

En la consola podés ejecutar:

yarn start

...y se abrirá una ventana de Electron con Gobstones Web adentro, ligeramente modificado.

Las modificaciones son:

  • En vez de GithubGuideLoader se usará DesktopGuideLoader y en vez de GitHubLoader o UrlLoader se usará FsLoader, que carga los ejercicios de disco en vez de por red.
  • El selector de cursos (course-selector) tendrá una UI distinta y permitirá descargar los cursos al disco.
  • Cualquier otra que en el código aparezca como if (window.GBS_DESKTOP) ...
  • Las partes de código que dependan de node.js usan la función window.GBS_REQUIRE(...) y pueden incluir cualquiera de los módulos definidos en el package.json de la aplicación.

¿Cómo actualizo una dependencia?

Pedile a bower que la actualice (bower update gs-board, por ejemplo). Luego, borrá los directorios que cachean las libraries (rm -rf .tmp dist) y corré la aplicación de vuelta (gulp serve).

¿Cómo hago un deploy?

  • Correr gulp a secas va a generar el sitio en la carpeta dist/.
  • Para deployar a GitHub Pages manualmente se puede usar el script deploy.js.
  • Para deployar una versión Desktop manualmente se puede usar el script release_desktop.sh pasándole como argumento un token de GitHub.

...pero todo esto no es necesario. Travis se encarga de deployar a staging en cada push a master, y a producción cuando se crea un nuevo tag en el repositorio. Además, crea las versiones Desktop y las sube a la parte de Releases de gobstones-web-desktop.

¿Cómo funcionan la ejecución paso a paso, el resaltado de bloques y la atomicidad?

  1. Se le pide a gs-element-blockly que genere código a partir de los bloques: gsElementBlockly.generateCode(true);. El true significa "mandámelo con regions".
  2. Lo anterior devuelve algo como:
/*@BEGIN_REGION@),95gMaKm~w]!oI=YevV@*/
program {
  /*@BEGIN_REGION@1Z?hy-;8RDDM=zXZT/Dy@*/
  Poner(/*@BEGIN_REGION@J4~RIna`ugqcJqueI6o$@*/Rojo/*@END_REGION@*/)
  /*@END_REGION@*/
  /*@BEGIN_REGION@FQc1]toJiq{-nGOjvmxH@*/
  Sacar(/*@BEGIN_REGION@|1OU_Nx}4:[L7awoeT8M@*/Verde/*@END_REGION@*/)
  /*@END_REGION@*/
}
/*@END_REGION@*/
  1. Se le pide a gobstones-code-runner que corra ese código.
  2. Este último le pide a gobstones-interpreter que parsee e interprete el programa. El mismo devuelve un resultado final y un array de snapshots con estados intermedios de esta forma.
  3. Por cada estado intermedio, gobstones-code-runner produce un onResult(...) (un callback pasado cuando se lo invoca por primera vez) por cada snapshot recibido (con cierto delay según la velocidad del paso a paso). De cada estado intermedio se sabe el estado del tablero y su región, así que con eso se puede ir resaltando cada bloque con gsElementBlockly.highlightBlock(regionName).
  4. Si la ejecución de un procedimiento está marcada como atómica (esto ocurre por default en la biblioteca del docente pero se puede sobreescribir), algunos snapshots se omitirán en el paso a paso. Esta lógica se encuentra acá.