YACSSTooltip: un plugin gratuito y Open Source para crear tooltips de imágenes con jQuery

17/12/2018
Artículo original

Si eres seguidor habitual de este blog, habrás notado que desde hace unos días las imágenes muestran la información de una manera más clara, en concreto utilizando un "tooltip" o capa de información que persigue al cursor y que muestra el texto asociada a la misma de una forma visible. Por ejemplo:

En este caso es una animación, pero entra en cualquier otro post reciente y la verás en acción en tu navegador de escritorio.

Este tooltip es una capa especial creada con JavaScript y CSS y para que funcione así he creado un plugin para jQuery que funciona con cualquiera de sus versiones y que permite generar ese tooltip para cualquier elemento que disponga de un atributo title o alt, pero que está pensado fundamentalmente para imágenes.

Este tooltip persigue al cursor mientras esté encima del elemento, se adapta a un ancho máximo para que no se generen líneas excesivamente largas y tiene en cuenta los límites de la página para no salirse ni por encima ni por debajo y que por lo tanto no se fuerce la aparición de barras de desplazamiento. En la animación anterior ves que cuando se acerca al borde derecho o al de abajo, el tooltip modifica su posición relativa respecto al cursor.

Para el texto que se muestra en el tooltip utiliza el texto del atributo title de la imagen o elemento en caso de estar presente, sino utiliza el valor del atributo alt. Si no tienes establecido ninguno  de los dos, entonces no actúa. Solo añade texto plano y no HTML (esto no es un festival: es un componente funcional), así que tenlo claro.

Por defecto, las imágenes en HTML muestran un tooltip nativo (el típico horrible de color amarillo en Windows) para lo que hayamos establecido en el atributo title, que además no se mueve del sitio, pareciendo en donde hayas puesto el cursor, por ejemplo así:

Para evitar que salga "esto" al mismo tiempo que el tooltip que genera este plugin lo que hace es deshabilitarlo y volver a restablecerlo cuando se saca el cursor de encima de la imagen/elemento.

Uso del plugin

Vete al repositorio de Github en el que lo he albergado y descarga la última versión del plugin. La puedes encontrar en la zona de "releases" del repositorio. Dentro del ZIP encontrarás un archivo .min.js que es la versión del plugin minimizada y lista para usar en producción. Pesa tan solo 833 bytes si el servidor la comprime con GZIP (lo más habitual al servirla).

Para utilizarla en tu página, añádela en cualquier parte del código después de haber añadido a jQuery, claro:

<script src="js/jquery.min.js"></script>
<script src="js/jquery.YACSSTooltip.min.js"></script>

Ahora lo único que tienes que hacer es seleccionar con jQuery los elementos a los que quieres aplicar el tooltip llamando al método addTooltip(). Por ejemplo, si lo haces cuando la página esté lista, usando para ello el método $ de jQuery en su variante que recibe una función (si no sabes de qué te hablo ya estás tardando en aprender en serio JavaScript y jQuery), sería:

$(function() {
$('.article img').addTooltip();
});

Lo que hace este fragmento es, tras haberse cargado la página, localiza todos los elementos de imagen dentro de los elementos con la clase .article y les aplica el tooltip.

¡Listo!

Por supuesto, este plugin juega con las reglas de jQuery por lo que puedes seguir encadenando llamadas a métodos para hacer más cosas con el conjunto de elementos seleccionados tras haberles aplicado el tooltip. Por ejemplo:

$(function() {
$('.article img').addTooltip().css('border', '5px solid red');
});

En este caso, además, le ponemos un borde gordo de color rojo a todas esas imágenes.

Modificar el aspecto por defecto

Para generar estos tooltip, independientemente del número de imágenes/elementos sobre los que actúe, se utiliza un único elemento HTML subyacente que el plugin añade a la página. Es decir da igual que haya 1.000 imágenes en tu web o app que van a mostrar este tipo de tooltip: solo se usa un elemento, por lo que no se carga la página. Además se le asigna un identificador aleatorio que evita que pueda colisionar con cualquier otro elemento que tengas en la página.

El aspecto por defecto del tooltip es el que se muestra en la imagen anterior, al principio del artículo, pero si necesitas cambiarle ligeramente el aspecto lo único que tienes que hacer es añadir una definición de clase al CSS de tu página, con el nombre .YACSSTooltip. Cualquier propiedad que pongas ahí tendrá efecto sobre el tooltip, aunque seguramente tendrás que utilizar el modificador !important para asegurarte de que actúa sobre el mismo y que no hay conflictos con otros estilos más específico que pudieras tener en tu página.

Por ejemplo, si quieres que el tooltip tenga los bordes planos y no redondeados como se ven por defecto, puedes añadir este selector:

.YACSSTooltip {
border-radius: 0;
}

Como ves es extremadamente sencillo de usar junto con jQuery.

Dentro de unos días os hablaré sobre otro plugin más complejo que he creado y que he añadido también a este blog: el que te permite hacer zoom y pan sobre las imágenes que no caben en el espacio disponible, incluso desde móviles.

Hasta entonces... ¡espero que este plugin te resulte útil!

FIX: Planes de mantenimiento de SQL Server - no se borran las copias de seguridad antiguas

17/12/2018
Artículo original

Una buena política a la hora de trabajar con nuestras bases de datos es sacar partido a los planes de mantenimiento de SQL Server para crear flujos de trabajo que nos ayuden a realizar todo tipo de tareas del día a día en las bases de datos, pero automatizándolos para no tener que estar pendientes.

Una de las tareas más comunes es la de realizar copias de seguridad. Podemos realizar backups de muchas maneras, incluso manualmente en versiones como SQL Server Express (gratuita) que no soportan el programador de tareas de SQL Server, el Agente de SQL Server. Pero sin duda la manera más sencilla y visual es utilizar los planes de mantenimiento. Gracias a ellos podemos simplemente arrastrar cajas especializadas en un diseñador visual y unirlas mediante flechas para crear secuencias y conseguir lo que necesitemos:

La interfaz de un plan de mantenimiento de SQL Server, con un plan de backups completos con retención quincenal

En el caso de la figura, por ejemplo, la tarea realiza las siguientes sub-tareas:

  1. Verificar la integridad de todas las bases de datos y sus índices.
  2. Realizar un backup completo de todas las bases de datos. Se hará semanalmente ya que forma parte de un plan con varias otras tareas que se encargan de hacer backups diferenciales y backups de logs mucho más a menudo.
  3. Borrado de copias de seguridad antiguas.
  4. Borrado de histórico de operaciones antiguas.
  5. Si cualquier paso falla notificará automáticamente por correo electrónico a las personas responsables del proceso.

Como vemos todo muy visual y sencillo. Posteriormente, cuando tengamos todo definido, las programaremos para que se ejecuten periódicamente de forma automática, de lo cual se encargará el agente de SQL Server (SQL Server Agent).

El borrado de backups no funciona

Bien, lo anterior era tan solo para que conocieses esta interesante herramienta en caso de que no fuese así. El objetivo de este post, sin embargo, es comentar un problema que ocurre a veces y que puede darte más de un dolor de cabeza.

Uno de los pasos del flujo anterior, el 4, se encarga de borrar todas las copias de seguridad antiguas. De este modo tenemos un cierto periodo de retención de los datos (por ejemplo 15 días) durante el cual podemos volver a cualquier punto anterior de una base de datos en dicho tiempo (siempre y cuando coordinemos bien los diferentes tipos de copia de seguridad: completa, diferencial y la frecuente de los logs), pero a la vez mantenemos bajo control el espacio de almacenamiento, ya que estos backups pueden llegar a ocupar mucho espacio en disco.

Si abrimos la edición de ese paso 4 de borrado de datos, veremos una pantalla como esta:

Realmente es muy sencilla: indicamos qué queremos borrar, en dónde están las copias de seguridad, qué extensión tienen, y el periodo de retención que queremos. En este caso se borrarán los archivos que tengan más de 15 días de antigüedad.

El problema es que, dependiendo de cómo tengas las copias organizadas, no te funcionará. Y esto hace que puedan acumularse muchos GB de datos hasta que se te llene el disco y te quedes sin copias recientes. ¡Ouch!

Una cuestión muy habitual es que las copias de seguridad se guarden dentro de subcarpetas, una por cada base de datos. En este caso lo que debes hacer es marcar la casilla "Incluir subcarpetas de primer nivel" que se ve en la figura anterior. Pues bien, dependiendo de cómo hayas escrito los campos anteriores, te funcionará o no el borrado de estos archivos.

En primer lugar, si usas el botón de los tres puntos "..." para elegir la ruta en la que están guardadas las copias de seguridad, se produce una cosa extraña. Si eliges una subcarpeta en un disco, por ejemplo "K:\SQLBackups", esta ruta se mostrará en el cuadro de texto sin la barra inclinada final, tal y como la he puesto yo en la figura. Pero si tienes una unidad de disco entera dedicada a backups (y por lo tanto esas subcarpetas están en la raíz de ésta), te escribirá algo como "K:\", es decir, le pone la barra inclinada al final. En condiciones normales esto no es ningún problema, pero SQL Server tiene un bug (no lo he encontrado reflejado en ninguna parte, es un bug sacado de la (amarga) experiencia) que hace que sea muy diferente ponerle o no esta barra.

Si le dejas la barra del final a una unidad raíz y marcas lo de incluir las subcarpetas, la tarea no funcionará. Lo peor es que no producirá ningún error y no te enterarás tampoco. Por lo tanto, es muy importante que le quites a mano la barra inclinada final que te pone la UI de SQL Server Management Studio.

Es un detalle tonto pero muy importante. Con esto te funcionará como cabe esperar y tus backups antiguos se irán eliminando para no ocupar espacio innecesario.

Otro detalle tonto pero importante: la extensión del archivo debe ir sin el punto delante. Si se lo pones "tragará" con él sin problemas y no te advertirá de nada, pero tampoco te funcionará el proceso. Así que debe ir como se ve en la figura, simplemente bak (o la extensión que corresponda según el caso) sin el punto.

Como ves los detalles son importantes...

Nota: lo anterior funciona bien como he indicado con SQL Server 2017. No lo he probado en versiones anteriores, pero es muy probable que ocurra lo mismo pues esto no tienen pinta de haberlo tocado mucho en años. Si este post te ayuda y usas una versión anterior déjalo en los comentarios para que sepamos para qué versiones sirve. Gracias.

Puede que te interese también:

¡Espero que te sea útil!

Fukuoka Ruby Award 2019 - Programas juzgados por Matz

15/12/2018
Artículo original

Estimados Entusiastas de Ruby,

Al gobierno de Fukuoka, Japón junto con “Matz” Matsumoto les gustaría invitarlo a participar en la siguiente competencia de Ruby. Si ha desarrollado un programa interesante en Ruby, lo animamos a competir.

Fukuoka Ruby Award 2019 - Premio Mayor - 1 Millon de Yenes!

Fecha límite para participar: Enero 31, 2019

Fukuoka Ruby Award

Matz y un grupo de panelistas elegirán los ganadores de la Competencia Fukuoka. El premio mayor de la Competencia Fukuoka es 1 millon de yenes. Los ganadores del premio mayor anteriores incluyen a Rhomobile (USA) y al Centro Climático APEC (Korea).

http://myfukuoka.com/category/news/ruby-news/

Los programas que entren en la competencia no tiene que estar escritos enteramente en Ruby pero deben aprovechar las ventajas de las características únicas de Ruby.

Los proyectos deben haber sido desarrollados o completados en los últimos 12 meses para ser elegibles. Por favor visite el siguiente sitio web de Fukuoka para ver detalles adicionales o para ingresar:

http://www.digitalfukuoka.jp/events/185 or http://myfukuoka.com/events/2019-fukuoka-ruby-award-guidelines-for-applicants/

http://www.digitalfukuoka.jp/uploads/event_detail/file/465/RubyAward_ApplicationForm_2019.doc

Por favor envíe el formulario de aplicación por correo a award@f-ruby.com

Este año, hay los siguientes premios especiales:

El ganaor del Premio AWS recibirá:

  • Amazon Echo (sujeto a cambio)
  • Consulta técnica sobre arquitectura AWS

El ganador del Premio GMO Pebabo recibirá:

  • Lolipop! servicio de hospedaje compartido: suscripción gratuita por 10 años en el Plan Estándar, o cupon por JPY 100,0000 para el plan con medida de la Nube Administrada.
  • Servicio de registro de Dominio DNS Muumuu: suscripción gratuita por 10 años a un dominio (por un dominio que cueste JPY 10,000 al año o menos)

El ganador del premio IIJ GIO recibirá:

  • Cupón gratuito IIJ GIO equivalente a 500,000 yenes (Hasta por 6 meses)

El ganador del Premio Money Forward recibirá:

  • cena con los contribuidores Ruby de Money Forward,
  • un tiquete gratuito por 10 años para el plan premium de nuestro servicio de administración de finanzas personales “Money Forward”.

El ganador del Premio Salesforce recibirá:

  • productos novedosos de salesforce.com

“Matz estará probando y evaluando su código fuente a fondo, ¡así que es muy significativo aplicar! La entrada a la competencia es gratuita.”

Gracias!

Publicado por Fukuoka Ruby el 2018-11-29
Traducción de vtamara

Publicado Ruby 2.6.0-rc1

15/12/2018
Artículo original

Nos complace anunciar la publicación de Ruby 2.6.0-rc1.

Ruby 2.6.0-rc1 es el primer candidato a publicación de Ruby 2.6.0. Introduce algunas características nuevas y mejoras en desempeño, por ejemplo:

JIT

Ruby 2.6 introduce una implementación inicial de un compilador JIT (Just-in-time).

Un compilador JIT busca mejorar el desempeño de cualquier ejecución de un programa en Ruby. A diferencia de los compiladores JIT ordinarios para otros lenguajes, el compilador JIT de Ruby hace la compilación JIT de manera única, emitiendo código C en un disco y lanzando el proceso ordinario de compilación en C para generar código nativo. Ver también: Organización MJIT por Vladimir Makarov.

Forma de uso: Sólo especifique --jit en la línea de comandos o en la variable de entorno $RUBYOPT. Al especificar --jit-verbose=1 se presentará información básica de la compilación JIT. Vea más opciones con ruby --help.

El propósito principal de esta versión JIT es darle oportunidad de comprobar que funciona en su plataforma y encontrar riesgos de seguridad antes de la versión 2.6. El compilador JIT es soportado cuando Ruby se construye con GCC, Clang o Microsoft VC++, que debe estar disponible en tiempo de ejecución. De lo contrario no podrá usarlo por el momento.

Con Ruby 2.6.0 preview3, hemos alcanzado una mejora en velocidad de 1.7 veces sobre Ruby 2.5 con una carga de trabajo intensiva en el uso de la CPU, denominada Optcarrot https://gist.github.com/k0kubun/d7f54d96f8e501bbbc78b927640f4208. También vamos a mejorar el desempeño de cargas de trabajo intensivas en el uso de memoria tales como las aplicaciones Rails.

Este pendiente de la nueva era en el desempeño de Ruby.

RubyVM::AbstractSyntaxTree [Experimental]

Ruby 2.6 introduce el módulo RubyVM::AbstractSyntaxTree.

Este módulo tiene un método parse que puede reconocer código ruby en una cadena y retornar nodos del AST (Abstract Syntax Tree - Árbol de sintaxis abstracta), y el método parse_file que reconoce código ruby en un archivo y retorna los nodos del AST. También se introduce la clase RubyVM::AbstractSyntaxTree::Node. Puede obtener información de la localización y los nodos hijos de objetos Node. Esta característica es experimental. No se garantiza compatibilidad de la estructura de los nodos AST.

Nuevas características

  • Se agregó el alias then a Kernel#yield_self. [Característica #14594]

  • else sin rescue ahora causa un error de sintaxis. [EXPERIMENTAL]

  • Los nombres de constantes pueden comenzar con una letra mayúscula fuera de la tabla ASCII. [Característica #13770]

  • Rangos no acotados [Característica #12912]

    Se introduce un rango no acotado (1..). Funciona como si no terminara. A continuación se presenta un uso típico:

    ary[1..]                          # igual a ary[1..-1] sin el mágico -1
    (1..).each {|index| ... }         # ciclo infinito desde el índice 1
    ary.zip(1..) {|elem, index| ... } # ary.each.with_index(1) { ... }
    
  • Añadido Binding#source_location. [Característica #14230]

    Este método retorna la localización en la fuente de una unión, un arreglo de 2 elementos __FILE__ y __LINE__. Tradicionalmente, la misma información podría recuperarse con eval("[__FILE__, __LINE__]", binding), pero planeamos cambiar este comportamiento para que Kernel#eval ignore la localización de uniones en las fuentes [Falla #4352]. Así que los usuarios deberían emplear este método recién introducido en lugar de Kernel#eval.

  • Añadida la opción :exception para que Kernel.#system lance un error en lugar de retornar false. [Característica #14386]

  • Agregado el modo oneshot [Característica #15022]
    • Este modo revisa “si cada línea fue ejecutada al menos una vez o no”, en lugar de “cuantas veces fue ejecutada cada línea”. Un gancho para cada línea es disparado al menos una vez, y después de que es disparado se elimina la bandera del gancho, i.e., corre sin gastos adicionales.
    • Agregada el argumento de palabra clave :oneshot_lines a Coverage.start.
    • Agregadas el argumento de palabra clave :stop y :clear a Coverage.result. Si clear es verdadero, deja el contador en cero. Si stop es verdadero, deshabilita las mediciones de cobertura.
    • Coverage.line_stub, que es una función auixiliar que crea la “colilla” para cobertura de líneas de una código fuente dado.
  • FileUtils#cp_lr. [Característica #4189]

Mejoras en desempeño

  • Mejorada la velocidad de Proc#call porque ya no necesitamos preocuparnos por $SAFE. [Característica #14318]

    Con la prueba de referencia lc_fizzbuzz que usa Proc#call repetidas veces podemos medir mejoras de 1.4 veces [Falla #10212].

  • Mejorada velocidad de block.call cuando block es un parámetro de bloque pasado a una función. [Característica #14330]

    Ruby 2.5 mejora el desempeño al pasar bloques. [Característica #14045] Además, Ruby 2.6 mejora el desempeño al llamar bloques pasados. Con micro-pruebas de referencia podemos observar una mejora de 2.6 veces.

  • Se introduce la estructura de datos Montón transitorio (Transient Heap - theap). [Falla #14858] [Característica #14989] theap es un montón usado para objetos en memoria de corta duración que son apuntados por clases específicas (Array, Hash, Object, and Struct). Por ejemplo, hacer objetos Hash pequeños y de corta duración es 2 veces más rápido. Con la prueba de referencia rdoc, observamos una mejora en desempeño de entre 6 y 7%.

Otros cambios notables desde 2.5

  • $SAFE es el estado global del proceso y podemos ponerlo nuevamente en 0. [Característica #14250]

  • Es obsoleto pasar safe_level a ERB.new. Los argumentos trim_mode y eoutvar se cambian a argumentos de palabra clave (keyword arguments). [Característica #14256]

  • La versión de Unicode soportada se ha actualizado a la 11. Se planea actualizar a 12 y 12.1 en las futuras versiones menores (TEENY) de Ruby 2.6.

  • Mezclado RubyGems 3.0.0.beta3. Se eliminaron las opciones --ri y --rdoc. Por favor use las opciones --document y --no-document en lugar de esas.

  • Mezclado Bundler como una gema por omisión.

Vea detalles en NOTICIAS o en la bitácora de commits.

Con esos cambios, cambiaron 6376 archivos, 227364 inserciones(+), 51599 eliminaciones (-) desde Ruby 2.5.0!

Disfrute programar con Ruby 2.6.0-rc1!

Descargas

  • https://cache.ruby-lang.org/pub/ruby/2.6/ruby-2.6.0-rc1.tar.gz

    SIZE:   16823448 bytes
    SHA1:   889db7731fd43f6dbf7f31ffdb0a29bba1a8d356
    SHA256: 6d6183639ed9c02320d7132e97c65489a39e24d8b55fc4ed35ac53d1189cb61d
    SHA512: ad101adee5c43f3645561e73970f15d4e9141f707da69a92e224575c665949e18ca53389e5315fca2ea393 4d77967a59e304353cde4a915537e7c4e4ee20be73
    
  • https://cache.ruby-lang.org/pub/ruby/2.6/ruby-2.6.0-rc1.zip

    SIZE:   20737499 bytes
    SHA1:   457e39aee1978da5e42af42a6ad230421544aa07
    SHA256: 2bcdf468de499e4d6983d60d63dcc883f4c54fdc05a08a54eb93d315477bc4cc
    SHA512: 0842fae8a199f6c1e76f5d775edbf468e18a54f0419324eb73595e0268c728c71733371d71dc2fa342105dbc487987ca5556948a9ef067276a7b5f552462802a
    
  • https://cache.ruby-lang.org/pub/ruby/2.6/ruby-2.6.0-rc1.tar.bz2

    SIZE:   14607078 bytes
    SHA1:   269fe9d414d7731e4a63959fadffe5c50c08ce0e
    SHA256: b4e9c0e8801946e9f0baba30948955f4341e9e04f363c206b7bd774208053eb5
    SHA512: cbd6281b2aab6fbce3f699c1ab57e5423304dca7a547a0b3cd4e8e980326dc7b85b2ca2bfaf3f3a648d40f4222fdf1740d81d422790ee7ae1ba1ed33eb11e3e8
    
  • https://cache.ruby-lang.org/pub/ruby/2.6/ruby-2.6.0-rc1.tar.xz

    SIZE:   11851908 bytes
    SHA1:   3b93fdf1c5bd969ab4fe0a8176a6cf64e4597e6e
    SHA256: 21d9d54c20e45ccacecf8bea4dfccd05edc52479c776381ae98ef6a7b4afa739
    SHA512: 3d93d8d80e4900e8b3a27f904ed60581cebc6c55f4ab7acafc81e95001f92f3ea4ddec2da6169b1ed5e0146f7b7c35c1c13b3243955d5825c72170834fe933f3
    

Publicado por naruse el 2018-12-06
Traducción de vtamara

FRIKADAS: Sketch2Code convierte tus bocetos a mano en interfaces HTML válidas y funcionales

14/12/2018
Artículo original

El proceso de diseño de interfaces de usuario suele empezar con el dibujo de bocetos para tratar de encajar todas las piezas de la interfaz. Estos bocetos se pueden llevar a cabo con algún software especializado (es la mejor manera) pero también es cierto que, en muchas ocasiones, acabamos haciéndolos en un folio o incluso en una servilleta de papel, según en donde nos surja la inspiración. De ese boceto inicial a tener una interfaz HTML + CSS que represente lo que hemos dibujado, normalmente hay un proceso más o menos largo que implica convertir el diseño inicial a HTML, colocar todos los controles, hacerlo "responsive" y dotarlo de una funcionalidad mínima como por ejemplo validaciones, sobre todo si son formularios.

Desde luego, sería mucho más cómodo poder trabajar todos los involucrados (diseñadores, desarrolladores e incluso clientes) en un diseño inicial, afinar el diseño en un papel y obtener los cambios en HTML + CSS + JavaScript el navegador en tiempo real, listos para ser utilizados. Esto es precisamente lo que pretende conseguir el proyecto del laboratorio de Inteligencia Artificial de Microsoft Research con su proyecto Sketch2Code.

Esta herramienta Web permite proporcionar un diseño previo hecho a mano en un papel y convertirlo en una interfaz Web válida y funcional:

Esta animación muestra el funcionamiento resumido de Sketch2Code

El proceso es el siguiente:

  • Digitalizas la imagen y la subes al sitio web de la herramienta.
  • Un modelo de Inteligencia Artificial entrenado para la tarea analiza visualmente el dibujo y trata de identificar los diferentes controles HTML que hay en el boceto.
  • Otro modelo especializado en reconocimiento de escritura, lee los textos de los controles, incluyendo etiquetas, cuadros de texto, etc...
  • Un algoritmo de maquetación genera una rejilla apropiada para encajar todos los controles en una disposición lo más parecida al boceto.
  • A partir de la información generada en los pasos anteriores se genera código HTML para obtener la página final.

Esquema del proceso seguido para generar la UI a partir del boceto

Para realizar su trabajo, el producto utiliza varios componentes estándar de Microsoft (el servicio de visión artificial de Azure, funciones de Azure, sitios web de Azure y almacenamiento Blob de Azure) combinados con un algoritmo de visión artificial personalizado para la tarea en cuestión.

Se trata de un experimento interesante que muestra toda la potencia de la inteligencia artificial y los procesos en la nube para crear aplicaciones que hace tan solo unos años serían inviables para una sola persona o un equipo pequeño. Y además puede resultarte muy útil.

Desde la página del proyecto Sketch2Code puedes utilizarlo y también puedes acceder al código fuente completo en GitHub en el que encontrarás no solo el código y la solución de Visual Studio, sino también explicaciones sobre la arquitectura, el proceso seguido, etc. por lo que puede ser muy interesante también para aprender.

El snap official de ruby está disponible

11/12/2018
Artículo original

Publicado el paquete snap oficial del lenguaje Ruby.

https://snapcraft.io/ruby

Snap es un sistema de paquetes desarrollado por Canonical. Le permite distribuir un programa con sus dependencias para muchas distribuciones de Linux diferentes. Esto resuelve el problema de los usuarios que no pueden instalar la versión más reciente de Ruby del repositorio por omisión de su sistema con rpm o apt.

En Ubuntu 16.04 o posterior, puede usar el snap de Ruby con el siguiente comando:

sudo snap install ruby --classic

(Si usa otras distribuciones de Linux, por favor consulte https://docs.snapcraft.io/installing-snapd/6735)

Nuestro snap usa la característica “channel” para publicar varias series de Ruby de manera concurrente. Por ejemplo, si no se especifica un canal, se instalará la versión 2.5.3. Pero si desea usar Ruby 2.4, especifique el canal 2.4 así:

sudo snap install ruby --classic --channel=2.4/stable

También puede usar múltiples canales. El siguiente comando cambia a Ruby 2.3:

sudo snap switch ruby --channel=2.3/stable
sudo snap refresh

Nuestro snap establece $HOME/.gem en las variables de ambiente GEM_HOME y GEM_PATH. Así que si quiere ejecutar comandos instalados por rubygems tales como rails y rspec sin usar bundle exec, debe añadir la siguiente línea al archivo rc de su interprete de comandos (como .bashrc):

eval `ruby.env`

Como $HOME/.gem es compartido por diversas versiones, si cambia la versión de ruby y la usa, necesitará recompilar las extensiones de C usando el comando gem pristine --extensions.

La versión inicial del snap oficial de Ruby ha sido publicada durante la cumbre de Snapcraft, que se llevó a cabo en las oficinas de Canonical en Londres el 6.Nov.2018. Toda retroalimentacioń es bienvenida en https://github.com/ruby/snap.ruby.

¡Disfrutelo!

Publicado por Hiroshi SHIBATA el 2018-11-08
Traducción de vtamara

TRUCO: Desarrollo Web - Cómo "congelar" en el tiempo una página Web para depurar el HTML/CSS

10/12/2018
Artículo original

El truco de hoy es corto y muy sencillo, pero al mismo tiempo poco intuitivo, y puede resultar útil en ciertas ocasiones.

Por ejemplo, estás depurando una página web y necesitas examinar el árbol del DOM de la página para ver las propiedades de un elemento concreto. El problema es que ese elemento se genera dinámicamente, mediante JavaScript, y en cuanto usas el típico menú de "Examinar elemento" y se abren las herramientas del desarrollador, el elemento desaparece.

Si se tratase de un elemento con la pseudo-clase :hover aplicada, es decir, aquellos que se hacen visibles o cambian sus propiedades cuando se pasa el ratón por encima, es algo muy fácil ya que los navegadores (y en especial Chrome, que es del que me voy a ocupar hoy) ofrecen facilidades para simular esto sin necesidad de realmente pasarle por encima al elemento (esto lo veremos en el vídeo a continuación, hacia el final). Sin embargo, si el elemento se genera mediante un script que luego lo hace desaparecer, no tendremos tanta suerte.

Por fortuna, en Chrome existe una forma muy sencilla de conseguir "congelar" la página en un momento determinado y por lo tanto evitar que esos elementos desaparezcan, pudiendo examinarlos, modificarlos, etc...

Te lo cuento en este vídeo corto a continuación:

[youtube:]

¡Espero que te resulte útil!

Mejores prácticas para crear Dockerfiles excelentes

04/12/2018
Artículo original

Nota: este artículo es una traducción del artículo original, de Jakub Skalecki, y ha sido traducido con su permiso expreso. Puedes seguir a Jakub en Twitter y en LinkedIn. Debes saber también que tenemos disponible un magnífico curso de Docker y Kubernetes en nuestro catálogo, por si te interesa aprender la herramienta a fondo.

Imagen ornamental - Operando con contenedores en un puerto - (c) José M. Alarcón 2018

¡Hola! Hace tiempo que trabajo con Docker. La creación de Dockerfiles es una parte esencial del proceso, y quería compartir algunos consejos sobre cómo mejorarlo.

Los objetivos:

  • Queremos minimizar el tamaño de la imagen, el tiempo de compilación y el número de capas.
  • Queremos maximizar el uso de la caché de compilación y la legibilidad del Dockerfile.
  • Queremos que trabajar con nuestro contenedor sea lo más agradable posible.

TL;DR - Para los que tienen prisa

Este artículo está lleno de ejemplos y descripciones detalladas, así que, aquí tienes un resumen rápido:

  • Escribir archivo .dockerignore
  • El contenedor debe hacer una sola cosa
  • ¡Debemos entender el almacenamiento en caché de Docker! Usa los comandos COPY y RUN en el orden correcto a la hora de utilizarlo.
  • Fusionar varios comandos RUN en uno solo.
  • Eliminar archivos innecesarios después de cada paso.
  • Usar una imagen base adecuada (las versiones Alpine deberían ser suficientes)
  • Configurar WORKDIR y CMD
  • Utilizar ENTRYPOINT cuando se tenga más de un comando y/o se necesite actualizar archivos usando datos en tiempo de ejecución.
  • Usar exec dentro del script del punto de entrada.
  • Mejor usar COPY que ADD
  • Especificar variables de entorno, puertos y volúmenes predeterminados dentro de Dockerfile

Ejemplo práctico

Así que, acabas de leer mis consejos. ¡Todo genial...! Pero te preguntarás, ¿cómo introducirlos en mis Dockerfiles y en qué se notará el hacerlo?

He preparado un pequeño archivo Dockerfile, con casi todos los posibles errores que se me ocurren. Ahora lo arreglaremos. Supongamos que queremos dockerizar una pequeña aplicación web Node.js. Aquí está (es complicada y probablemente no funcione, pero es tan sólo un ejemplo):

FROM ubuntu 

ADD . /app 

RUN apt-get update RUN apt-get upgrade -y 
RUN apt-get install -y nodejs ssh mysql 
RUN cd /app && npm install 

# esto debería iniciar tres procesos, mysql y ssh 
# en segundo plano y node app en primer plano 
# no es tremendamente bonito? <3 
CMD mysql & sshd & npm start

Podríamos construirlo usando docker build -t wtf.

¿Puedes detectar todos los errores que tiene? ¿No? Vamos a arreglarlos juntos, uno por uno.

1. Escribir .dockerignore

Al crear una imagen, Docker debe preparar el context antes de nada: recopilar todos los archivos que se pueden utilizar en un proceso. El contexto predeterminado contiene todos los archivos de un directorio Dockerfile. Normalmente no queremos incluir ahí el directorio .git, las bibliotecas descargadas ni los archivos compilados. El archivo .dockerignore es exactamente igual a .gitignore, por ejemplo:

.git/ 
node_modules/ 
dist/

2. El contenedor debe hacer una sola cosa

Técnicamente, PUEDES iniciar múltiples procesos dentro de un contenedor Docker. PUEDES poner aplicaciones de base de datos, frontend y backend, ssh, y supervisor en una imagen de docker. Pero muchas cosas no te irán bien:

  • Los tiempos de compilación serán largos (un cambio en, por ejemplo, el frontend te obligará a volver a compilar todo el backend)
  • Las imágenes serán muy grandes
  • Tendrás un registro duro de datos desde muchas aplicaciones (no un simple stdout)
  • Escalado horizontal innecesario
  • Problemas con los procesos "zombies" - Tendrás que acordarte de cuál es el proceso init adecuado

Mi consejo es que prepares una imagen Docker separada para cada componente y que utilices Docker Compose para iniciar fácilmente varios contenedores al mismo tiempo.

Eliminemos los paquetes innecesarios de nuestro Dockerfile. SSH puede ser reemplazado por docker exec.

FROM ubuntu 

ADD . /app 

RUN apt-get update
RUN apt-get upgrade -y
  
# deberíamos quitar ssh y mysql, y usar 
# contenedor separado para la base de datos  
RUN apt-get install -y nodejs # ssh mysql 
RUN cd /app && npm install 

CMD npm start

3. Fusionar varios comandos RUN en uno solo

Docker está totalmente basado en capas. El conocimiento de cómo funcionan es esencial.

  • Cada comando en Dockerfile crea una capa.
  • Las capas se almacenan en caché y se reutilizan.
  • Invalidar la caché de una sola capa invalida todas las capas subsiguientes.
  • La invalidación ocurre después de un cambio de comando, si los archivos copiados son diferentes, o si la variable de compilación es diferente a la anterior.
  • Las capas son inmutables, así que, si añadimos un archivo a una capa y lo eliminamos en la siguiente, la imagen TODAVÍA contiene ese archivo (¡simplemente no estará disponible en el contenedor!)

Me gusta comparar las imágenes de Docker con una cebolla:

Imagen de una cebolla - Dominio Público

Las dos te hacen llorar... esto no :) Ambas tienen capas. Para acceder y modificar la capa interna, debes eliminar todas las anteriores. Recuerda esto y todo irá bien.

Vamos a optimizar nuestro ejemplo. Estamos fusionando todos los comandos RUN en uno, y eliminando la actualización apt-get, ya que hace que nuestra compilación no sea determinista (dependemos de las actualizaciones de nuestra imagen base):

FROM ubuntu 

ADD . /app 

RUN apt-get update \ 
    && apt-get install -y nodejs \ 
    && cd /app \ 
	&& npm install 
	
CMD npm start

Se debe tener en cuenta que se deben fusionar comandos que tienen una probabilidad similar de ser modificados o de sufrir cambios. Actualmente, cada vez que nuestro código fuente se modifique, necesitamos reinstalar Node.js por completo. Así que, una opción mejor es:

FROM ubuntu 

RUN apt-get update && apt-get install -y nodejs 
ADD . /app 
RUN cd /app && npm install 

CMD npm start

4. No utilices la etiqueta de imagen base latest

La etiqueta latest es la que se utiliza por defecto, cuando no se especifica ninguna otra etiqueta. Así que nuestra instrucción FROM ubuntu en realidad hace exactamente lo mismo que FROM ubuntu:latest. Pero la etiqueta latest apuntará a una imagen diferente cuando se publique una nueva versión, y tu build puede romperse. Por lo tanto, a menos que estés creando un Dockerfile genérico que tengas que estar actualizado con la imagen base, deberás proporcionar una etiqueta específica.

En nuestro ejemplo, usemos la etiqueta 16.04:

FROM ubuntu:16.04 # ¡es así de fácil! 

RUN apt-get update && apt-get install -y nodejs 
ADD . /app 
RUN cd /app && npm install 

CMD npm start

5. Eliminar archivos innecesarios después de cada paso de RUN

Por lo tanto, supongamos que hemos actualizado las fuentes apt-get, instalado algunos paquetes necesarios para compilar otros, y hemos descargado y extraído los archivos. Obviamente no los necesitamos en nuestras imágenes finales, así que, mejor hagamos una limpieza. ¡El tamaño importa!

En nuestro ejemplo podemos eliminar las listas apt-get (creadas por apt-get update):

FROM ubuntu:16.04
 
RUN apt-get update \ 
	&& apt-get install -y nodejs \ 
    # líneas añadidas 
    && rm -rf /var/lib/apt/lists/* 
    
ADD . /app 
RUN cd /app && npm install

CMD npm start

6. Usar una imagen base adecuada

En nuestro ejemplo estamos usando ubuntu. Pero, ¿por qué? ¿Realmente necesitamos una imagen de base de propósito general, cuando sólo queremos ejecutar una aplicación de Node.js? Una mejor opción es usar una imagen especializada con Node.js ya instalado:

FROM node ADD . /app 

# ya no se necesita instalar
# node ni usar apt-get 
RUN cd /app && npm install 

CMD npm start

O mejor aún, podemos elegir la versión Alpine (Alpine es una distribución Linux muy pequeña, de unos 4 MB de tamaño. Esto lo convierte en el candidato perfecto para una imagen base)

FROM node:7-alpine 

ADD . /app 
RUN cd /app  && npm install 

CMD npm start

Alpine tiene un gestor de paquetes, llamado apk. Es un poco diferente a apt-get, pero aún así es bastante fácil de aprender. Además, tiene algunas características realmente útiles, como las opciones --o-cache y --virtual. De esta manera, elegimos exactamente lo que queremos en nuestra imagen, nada más. Tu disco te va a amar :)

7. Configurar WORKDIR y CMD

El comando WORKDIR cambia el directorio por defecto, donde ejecutamos nuestros comandos RUN / CMD / ENTRYPOINT.

CMD es una ejecución de comandos por defecto después de crear un contenedor sin otro comando especificado. Por lo general, es la acción que se realiza con más frecuencia. Añadámoslos a nuestro Dockerfile:

FROM node:7-alpine 

WORKDIR /app 
ADD . /app 
RUN npm install 

CMD ["npm", "start"]

Debes poner tu comando dentro de la matriz, una palabra por elemento (más en la documentación oficial)

8. Usa ENTRYPOINT (opcional)

No siempre es necesario, es opcional, ya que Entrypoint, o punto de entrada, añade complejidad. ¿Cómo funciona este sistema?

Entrypoint es un script, que se ejecutará en lugar de un determinado comando, y recibirá éste como un argumento. Es una excelente forma de crear imágenes ejecutables Docker:

#!/usr/bin/env sh
# $0 is a script name, 
# $1, $2, $3 etc are passed arguments  # $1 is our command 
CMD=$1
  
case "$CMD" in  
    "dev" ) 
		npm install 
		export NODE_ENV=development 
		exec npm run dev 
		;;
		
	"start" ) 
	# we can modify files here, using ENV variables passed in 
	# "docker create" command. It can't be done during build process.  
	echo  "db: $DATABASE_ADDRESS" >> /app/config.yml 
	export NODE_ENV=production 
	exec npm start 
	;;

	* ) 
	 # Run custom command. Thanks to this line we can still use 
	 # "docker run our_image /bin/bash" and it will work  
	 exec $CMD ${@:2} 
	 ;; 
esac

Guárdalo en tu directorio raíz, con el nombre entrypoint.sh. Así es su uso en Dockerfile:

FROM node:7-alpine 

WORKDIR /app 
ADD . /app 
RUN npm install 

ENTRYPOINT ["./entrypoint.sh"] 
CMD ["start"]

Ahora, valga la redundancia, podemos ejecutar esta imagen en un formato ejecutable:

docker run nuestra-app dev
docker run nuestra-app start

docker run -it nuestra-app /bin/bash` # Este también funcionará.

9. Usar exec dentro del script entrypoint

Como puedes ver en el ejemplo de entrypoint, estamos usando exec. Sin él, no podríamos detener nuestra aplicación de forma elegante (SIGTERM es engullido por el script bash). Exec básicamente reemplaza el proceso de script con uno nuevo, por lo que todas las señales y códigos de salida funcionan como se había previsto.

10. Mejor COPY que ADD

COPY es más sencillo. ADD tiene cierta lógica para descargar archivos remotos y extraer archivos (más en la documentación oficial). Sólo tienes que quedarte con COPY.

Nota: este punto necesita una explicación adicional. ADD puede ser útil si la compilación depende de recursos externos y quieres que la invalidación de la caché de compilación sea la adecuada en caso de cambio. No es una buena práctica, pero a veces es la única manera de hacerlo.

Vamos a AÑADIR... uups, COPIAR esto en nuestro ejemplo:

FROM node:7-alpine 

WORKDIR /app 

COPY . /app 
RUN npm install 

ENTRYPOINT ["./entrypoint.sh"] 
CMD ["start"]

11. Optimizar COPY y RUN

Deberíamos poner los cambios que se producen con menor frecuencia en la parte superior de nuestros Dockerfiles para aprovechar el almacenamiento en caché.

En nuestro ejemplo, el código cambiará a menudo, y no queremos reinstalar paquetes cada vez. Podemos copiar el package.json antes del resto del código, instalar dependencias y luego añadir otros archivos. Apliquemos esa mejora a nuestro Dockerfile:

FROM node:7-alpine 

WORKDIR /app 

COPY package.json /app 
RUN npm install 
COPY . /app 

ENTRYPOINT ["./entrypoint.sh"] 
CMD ["start"]

12. Especificar variables de entorno, puertos y volúmenes predeterminados

Probablemente necesitemos algunas variables de entorno para ejecutar nuestro contenedor. Es una buena práctica establecer valores predeterminados en Dockerfile. Además, debemos mostrar todos los puertos utilizados y definir los volúmenes.

Veamos la siguiente mejora aplicada a nuestro ejemplo:

FROM node:7-alpine 

# env variables required during build 
ENV PROJECT_DIR=/app 

WORKDIR $PROJECT_DIR 

COPY package.json $PROJECT_DIR 
RUN npm install 
COPY . $PROJECT_DIR
 
# env variables that can change  
# volume and port settings  
# and defaults for our application 
ENV MEDIA_DIR=/media \ 
	NODE_ENV=production \ 
	APP_PORT=3000 

VOLUME $MEDIA_DIR 
EXPOSE $APP_PORT 

ENTRYPOINT ["./entrypoint.sh"] 
CMD ["start"]

Estas variables estarán disponibles en el contenedor. Si necesitas variables de compilación solamente, usa build args en su lugar.

13. Añadir metadatos a la imagen usando LABEL

Hay una opción para añadir metadatos a la imagen, como información sobre quién es el encargado de mantenerla o una descripción ampliada. Necesitamos la instrucción LABEL para ello (antes podíamos usar la opción MAINTAINER, pero ahora está obsoleta). Los metadatos son utilizados a veces por programas externos, por ejemplo nvidia-docker requiere la etiqueta com.nvidia.volumes.needed para funcionar correctamente.

Ejemplo de un metadato en nuestro Dockerfile:

FROM node:7-alpine 
LABEL maintainer "jakub.skalecki@example.com" 
...

14. Añadir HEALTHCHECK

Podemos iniciar el contenedor docker con la opción --restart always (reiniciar siempre). Después de un fallo del contenedor, el "demonio" de Docker intentará reiniciarlo. Es muy útil si tu contenedor tiene que estar operativo todo el tiempo. Pero, ¿qué pasa si el contenedor se está ejecutando, pero no está disponible (bucle infinito, configuración no válida, etc.)? Con la instrucción HEALTHCHECK podemos decirle a Docker que compruebe periódicamente el estado de salud de nuestro contenedor. Puede ser cualquier comando, devolviendo 0 como código de salida si todo está bien, y 1 en el caso contrario.

Último cambio a nuestro ejemplo:

FROM node:7-alpine 
LABEL maintainer "jakub.skalecki@example.com" 

ENV PROJECT_DIR=/app 
WORKDIR $PROJECT_DIR 

COPY package.json $PROJECT_DIR 
RUN npm install 
COPY . $PROJECT_DIR 
ENV MEDIA_DIR=/media \ 
		 NODE_ENV=production \ 
		 APP_PORT=3000 

VOLUME $MEDIA_DIR 
EXPOSE $APP_PORT 
HEALTHCHECK CMD curl --fail http://localhost:$APP_PORT || exit 

ENTRYPOINT ["./entrypoint.sh"] 
CMD ["start"]

curl --fail devuelve el código de salida que no es cero si la petición falla.

Para usuarios avanzados

Este post se está haciendo muy largo, así que, aunque tenga algunas ideas más, no las expondré aquí. Si quieres saber más, echa un vistazo a las instrucciones de STOPSIGNAL, ONBUILD y SHELL. Además, algunas opciones muy útiles durante la compilación son --no-cache (especialmente en un servidor de integración continua, si quiere estar seguro de que la compilación se puede hacer en una nueva instalación de Docker), y --squash (más aquí).

Diviértete :)

Conclusión

Esto es todo. Un post largo, pero creo que contiene información útil. Si tienes tus propios consejos que dar sobre Dockerfiles, compártelos en comentarios. ¡Todos los comentarios son bienvenidos!

Publicado Ruby 2.3.8

01/12/2018
Artículo original

Se ha publicado Ruby 2.3.8

Esta versión incluye diversas correcciones de seguridad. Por favor revise los temas siguientes para ver detalles.

Por razones de mantenimiento, esta versión también incluye una corrección no relacionado con seguridad para soportar Visual Studio 2014 con la actulización de Octubre de 2018 de Windows 10.

Ruby 2.3 ahora está en fase de mantenimiento de seguridad, hasta el final de marzo de 2019. Después de esa fecha, terminará el mantenimiento de Ruby 2.3. Le recomendamos comenzar a planear la migración a las versiones más recientes de Ruby, tales como 2.5 o 2.4.

Descargas

  • https://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.8.tar.bz2

    TAMAÑO:   14418609 bytes
    SHA1:   91b31abdba00a346c155fd32bd32d3cec3b73bc4
    SHA256: 4d1a3a88e8cf9aea624eb73843fbfc60a9a281582660f86d5e4e00870397407c
    SHA512: 6d79e0d25757fd37188a8db3e630a52539bce7927fcb779a2ce9a97b9e5f330753035c16843552f1a1fb6c9a1e5c0f916b3cc8b5c0bfe81e20f35f8442e40ae8
    
  • https://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.8.tar.gz

    TAMAÑO:   17858806 bytes
    SHA1:   69311991a9cd2c8e3c86a0dbbaaf915ae91f0bec
    SHA256: b5016d61440e939045d4e22979e04708ed6c8e1c52e7edb2553cf40b73c59abf
    SHA512: 43b02f2f9de6baf281f09a49fd07367127b4de1fb14473380d06bfa7667990d8f722ae2d33cf7d15b02f7e799515f21aebd308897c4c2a5461ebab4049d6c7ef
    
  • https://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.8.tar.xz

    TAMAÑO:   11465792 bytes
    SHA1:   9771acdad851bbf0ef349bb7da5d0ffc91a860ed
    SHA256: 910f635d84fd0d81ac9bdee0731279e6026cb4cd1315bbbb5dfb22e09c5c1dfe
    SHA512: 06373050e6c1af9cb6a5863aef878b21c8a45fd0e68414e3d546cb73ec3028207d3acc0a9326428f172b9347a30bbe69a16f9dc0bdb739161d677adb2d888095
    
  • https://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.8.zip

    TAMAÑO:   19911652 bytes
    SHA1:   ad9e0ec7c874701832c9e224eb5b9e2258f3a041
    SHA256: ec9792d0473a22954ad25cd0c531fc672679c1a5eaeefa08caf9e1288852796f
    SHA512: 732d69cd55f1c273a02005306720fd8fc9d77398177db9509452be31820976b54b30319d9e6bc36fb6bcd7af656f807ef6c26d8955012c8b20424912a2f51bf8
    

Comentarios de la versión

Agradecimientos a todos los que ayudaron con esta versión.

Publicado por usa el 2018-10-17
Traducción de vtamara

Publicado Ruby 2.5.2

01/12/2018
Artículo original

Se ha publicado Ruby 2.5.2

Esta versión incluye diversas correcciones así como correcciones de seguridad.

También se corrigieron fallas. Ver detalles en las bitácoras de commits.

Descargas

  • https://cache.ruby-lang.org/pub/ruby/2.5/ruby-2.5.2.tar.bz2

    TAMAÑO:   13592827 bytes
    SHA1:   562d6b8be5a0804ed7617bb0465b288d44b2defc
    SHA256: ea3bcecc3b30cee271b4decde5e9ff3e17369d5fd1ed828d321c198307c9f0df
    SHA512: 9f9388a162a3ae9c14ec8999fa3b12ff5397de14f55996cc8761d21c757113db37ace4d326b9606de7ad3a5875aa94fec900dd9b81b2fb0dff558c39422f4aa1
    
  • https://cache.ruby-lang.org/pub/ruby/2.5/ruby-2.5.2.tar.gz

    TAMAÑO:   15600481 bytes
    SHA1:   7e503e75621b69cedb1d8b3fa2bee5aef2f1a714
    SHA256: b32340e64a0c7ecbf31486c41fe429a55c7984d980eca7a78138367d9209f471
    SHA512: 9aee69d2ac6aefe2d81649055ba7b99e4e58cf203ac75083ba1b35b3a4fd7f72ee257e26ca80460da5c2a7817fd507aecec9c143f170e16980625e95eeb31686
    
  • https://cache.ruby-lang.org/pub/ruby/2.5/ruby-2.5.2.tar.xz

    TAMAÑO:   11071052 bytes
    SHA1:   ea352c9bcaa47ab094cdec0f4946c62b1a1769d7
    SHA256: 8be6b6afdf09957a6e2c2a6ada4b1982a391a828b34e49072c4beb60febb678d
    SHA512: b6b805b18ba6da7b28c7e2bdf3da7eaf1dcc15ae22744228d032e8ddec2fbba4cc4fb822b9ef7f6b561052113a4f28dc50ccfa4f00e3728a35ce27137f4a70e6
    
  • https://cache.ruby-lang.org/pub/ruby/2.5/ruby-2.5.2.zip

    TAMAÑO:   18786735 bytes
    SHA1:   98fdbae195bbbc3f131d49d9e60bf3fbb8b56111
    SHA256: f148947fee070f30826ef0bda77228b9c374b388050db81ad07f5cd8608e3624
    SHA512: 1b804337099ecfa045eecf1a4e3f35fa786bd6e835dc50267d6a3792a782b193ec9708564e3ac5169a95ef4afc2c131782af937dafd8122117e8cff577736c0f
    

Comentarios de la versión

Muchos contribuidores, desarrolladores y usuarios que reportaron errores nos ayudaron a hacer esta publiación. Gracias por sus contribuciones.

Publicado por nagachika el 2018-10-17
Traducción de vtamara

Página Siguiente