Desplegar una aplicación Node.js como servicio systemd (demonio)

En el mundo linux hay dos gestores de demonios que se suelen usar en casi todas las distribuciones. Upstart o systemd.

Si lo que quieres es crear un demonio para Ubuntu o cualquier otro SO que use Upstart, tienes que leer este articulo, y si lo que quieres es crear el demonio para Debian u otros que usen systemd, sigue leyendo este post.

Vamos al lio. Para nuestro demonio vamos a necesitar 3 cosas: forever, un script para crear el servicio y un script para avisar a monit para que mire nuestro servicio y lo reinicie si se cae.

El script del servicio

Como me gusta ponértelo fácil, voy a obviar la explicación y simplemente les digo que copies el siguiente código y lo pegues dentro de un archivo que se llame como el servicio que quieres crear dentro de /etc/init.d/my-service (por ejemplo).

Dentro de este archivo solo tienes que tocar dos o tres cosas al principio del archivo en la zona de configuración. Es fácil, básicamente es poner el nombre del servicio (en mi ejemplo es “my-service”), el directorio donde está alojado, y el nombre del archivo principal de la aplicación.
Hay algunas cositas mas que se pueden configurar, pero en general con esto basta.

#!/bin/bash
# This service requires you have forever app installed in your system.


### CONFIG ZONE
# This is the service name. It cant have any special char or space.
serviceName="my-service"

# Where is located your app
appFilePath="/var/node/my-service-node-app"

# Whats the name of your main app file.
appIndex="app.js"

enviroment=production
### END CONFIG ZONE


###### DO NOT EDIT BELOW THIS LINE ######
# Source function library.
. /lib/lsb/init-functions

pidFile="/var/run/$serviceName.pid"
logFile="/var/run/$serviceName.log"

command="node"
nodeApp="$appFilePath/$appIndex"
foreverApp="forever"

start() {
	echo "Starting $serviceName"
	PATH=/usr/local/bin:$PATH
	export NODE_ENV=$enviroment
	$foreverApp start --pidFile $pidFile -l $logFile -a -d -c "$command" $nodeApp
	RETVAL=$?
}

restart() {
	echo -n "Restarting $serviceName"
	$foreverApp restart $nodeApp
	RETVAL=$?
}

stop() {
	echo -n "Shutting down $serviceName"
	$foreverApp stop $nodeApp
	RETVAL=$?
}

status() {
   echo -n "Status $serviceName"
   $foreverApp list
   RETVAL=$?
}

case "$1" in
   start)
        start
        ;;
    stop)
        stop
        ;;
   status)
        status
       ;;
   restart)
   	restart
        ;;
	*)
       echo "Usage:  {start|stop|status|restart}"
       exit 1
        ;;
esac
exit $RETVAL

Tambien dejo el gist para que cualquier mejora que quieras añadir lo hagas por esta via.
https://gist.github.com/capynet/2de13de81ef8a71f2b2f

Ya tenemos servicio! ¿Y como lo usamos?. Pues como cualquier servicio:

sudo service my-service start
sudo service my-service stop
sudo service my-service restart
sudo service my-service status

Esto debería alcanzar, pero no. Este demonio no sabe volver a arrancarse si la aplicación de nodejs se para. Esa parte la vamos a solucionar usando monit.

Configurar monit

El rol de monit es el de “mirar” todo el tiempo al demonio que creamos anteriormente, y arrancarlo si este se muere.

En Debian y la mayoría de distros, a los archivos de configuración que tenemos que crear (uno por demonio) los pondremos en “/etc/monit/monitrc.d/“.
Yo por lo general creo un archivo con el formato monit-[MI-SERVICIO].conf.

Dentro de este archivo SIEMRPE vamos a usar el siguiente formato, así que simplemente cópialo, pon el nombre del demonio y el puerto, guarda los cambios, reinicia monit y listo.

check process my-service with pidfile "/var/run/my-service.pid"
        start program = "/etc/init.d/my-service start"
        stop program = "/etc/init.d/my-service stop"
        if failed
                port [THE NODEJS PORT HERE] protocol HTTP
                request / with timeout 10 seconds
        then restart

De nuevo, aqui está el gist para que contribuyas si quieres.
https://gist.github.com/capynet/c7aa09a996fe909a0960

NOTA: En algunas instalaciones de monit, hay que tocar su archivo de configuración general para que incluya los archvos de monitorización que añadamos. El archivo es /etc/monit/monitrc y tienes que editarlo como sudo y añadir al final “include/etc/monit/monitrc.d/*” si no lo tuviera ya puesto.

Bueno si a estas alturas todavía no tienes instalado forever, instalalo con sudo npm install forever -g

Hemos acabado, ya puedes arrancar monit o reiniciarlo si ya estaba corriendo. Monit va a usar el script que creamos para el para ver si el servicio está corriendo, si no es así, el solito se encarga de levantarlo y mantenerlo en funcionamiento inclusive después de reiniciar el servidor.

Chau!

CSS & maquetación

Front-end: Maquetar un buscador (input + botón) para que sea 100% fluido / responsive

La idea detrás de esto fue lograr que el buscador esté siempre al 100% del espacio que tenga disponible y no tener que preocuparme de dimensionar los input para que encajen y que el botón no caiga a dos lineas cuando se queda sin espacio.

Pueden ver como funciona si re dimensionan la ventana del navegador:

See the Pen vJBnL by Marcelo Tosco (@capynet) on CodePen.

CSS & maquetación

Front-end: Títulos multi linea con padding por cada una de las lineas

Así es como vemos un titulo cuando tiene aplicado un padding sin mas

texto-multilinea-error

Y asi es como queremos que se vea

titulo-con-padding-en-cada-linea

No voy a explicar toda la lógica que hay detrás de esto. Vayamos a la solución.

Este es el html. en div.titulo tenemos el background y en .highlight el background del texto, el padding que queremos que tenga y el hack mediante la propiedad box-shadow que es la que nos termina agregando los pedacitos de padding que nos falta entre lineas.

<div class="titulo">
    <span class="highlight">Títulos multi linea con padding por cada linea</span>
</div>
.titulo {
    padding-top: 32px;
    width: 700px;
    height: 200px;
    text-align: center;
    font-family: arial, helvetica, sans-serif;
    background: url('fondo.jpg') no-repeat 50% 50%;
}

.highlight {
    font-size: 50px;
    text-transform: uppercase;
    text-align: center;
    color: #fff;

    /*Esto es lo importante*/
    display: inline; /* Aseguramos que el elemento sea inline */
    background: black; /* Color de fondo del texto */
    line-height: 1.6em; /* Espaciado entre lineas */
    padding: 7px 0; /* padding deseado */
    box-shadow: 14px 0 0 black, -14px 0 0 black;
}

Del CSS que puse solo hagan caso a las lineas que tienen “/*Esto es lo importante*/“. El resto es decoración.

No comenté el box-shadow porque prefiero hacerlo aquí: La magia se da en box-shadow. Solo tienes que tocar dos parámetros. El color cámbialo de black al que hayas puesto en la propiedad background, y los valores 14px y -14px ajustalos para arriba o para abajo hasta que te guste el padding lateral que hay en cada una de las lineas.

Listo!

Artículo original: css-tricks.com

CSS & maquetación

CSS: Centrar vertical y horizontalmente un elemento sin saber su tamaño

Centrar un elemento cuando se sabe el tamaño que tiene es fácil. Pero y si el elemento que queremos centrar tiene un tamaño expresado en % o no tiene un tamaño especificado, ya no es tan fácil centrarlo.

Ahora que prácticamente IE8 ya no se usa, lo podemos quitar de la ecuación y usar CSS un poco mas avanzado.
Dicho lo anterior, ahora que podemos usar la propiedad transform: translate();, ya podemos centrar vertical y horizontalmente cualquier elemento.

<div class="centrado-porcentual">
  No nos hace falta conocer el tamaño de esta caja para poder centrarla vertical y horizontalmente :)
</div>
.centrado-porcentual {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    -webkit-transform: translate(-50%, -50%);
}

Resultado:
centrado-vert-hoz-transform

NOTA: si el elemento que quieres centrar está dentro de otro, recuerda que el padre tiene que tener una posición relativa.

Chau!

Idea original: css-tricks.com

Druplicon

Drupal 7: Controlar completamente los template de Field collection

El muy jodido tiene mas vueltas que una oreja cuando de theming se trata.

NOTA: todos los templates que hay que crear ponlos en tu theme. Lo ideal es dentro del directorio tu_theme/templates/field_collection o algo similar.

Primero creamos el tpl field-collection-view.tpl.php que es el wrapper que hay entre el html del field y el del field collection. Pon esto dentro:

<?php print $element['#children']; ?>

Luego field-collection-item.tpl.php que es el tpl que controla el render de cada uno de los item individuales de la colección:

<?php print render($content); ?>

Por ultimo necesitamos field–field-collection.tpl.php, un tpl para el field que contiene todo lo que field collection entregue:

<ul class="coleccion de items">
  <?php foreach ($items as $delta => $item): ?>
    <li><?php print render($item); ?></li>
  <?php endforeach; ?>
</ul>

Y listo! ya tenes control a todos los niveles del markup que va a entregar un field collection.

Chau!

Druplicon

Drupal 7 : Crear un template block–bean–type-ENTITY-TYPE.tpl.php para bundles del módulo Bean

Caso de uso. Creaste un bean del tipo “banner” y quieres que el block.tpl.php tenga un marcado específico para este tipo de contenidos. O lo que es lo mismo que decir que quieres tener un block–bean–type-banner.tpl.php

La solución es poner este preprocess:

<?php
/**
 * Implements hook_preprocess_block().
 */
function TU_THEME_O_MODULO_preprocess_block(&$vars) {
  // Añadimos theme suggestions por tipo de bean.
  if ($vars['block']->module == "bean") {
    /** @var $bean Bean */
    $bean = bean_load_delta($vars['block']->delta);
    $vars['theme_hook_suggestions'][] = 'block__bean__type_' . $bean->type;
  }
}

Y ya podés copiar block.tpl.php a tu theme, renombrarlo a block–bean–type-banner.tpl.php, personalizarlo y borrar el cache.

Chau!

Druplicon

Drupal 7: Hacer bypass a page.tpl.php programáticamente

¿Y si necesitaras entregar una página sin los CSS ni JS ni el HTML (cabeceras, footer, sidebars) que viene por defecto?

Solo hay que hacer dos cosas:

1. Añadir la propiedad ‘delivery callback’ a tu menu callback

<?php
$items['factura/%node'] = array(
  'title' => 'Entrega una factura',
  'page callback' => 'generar_factura',
  'delivery callback' => 'mi_delivery_page',
  'access callback' => TRUE,
);

2. Añadir esta función.

No hace falta modificarla. Si querés cambiarle el nombre no te olvides de modificar también el nombre en el delivery callback del menú.

<?php
/**
 * Delivery callback.
 *
 * Entrega el contenido así como viene sin pasar ni
 * por page.tpl.php ni por html.tpl.php
 *
 */
function mi_delivery_page($page_callback_result) {

  if (isset($page_callback_result) && is_null(drupal_get_http_header('Content-Type'))) {
    drupal_add_http_header('Content-Type', 'text/html; charset=utf-8');
  }

  global $language;
  drupal_add_http_header('Content-Language', $language->language);
  print $page_callback_result;
  drupal_page_footer();
}

Borra cache y listo.

Chau!

Herramientas

Crear un bash alias que reciba parámetros

Por ejemplo si queremos tener un alias que nos permita importar la db de un proyecto:

En el archivo .bash_aliases o .bashrc de tu home:

# 1: creamos una función:
importar_db_demo() {
    mysql -uusuario_demo -p labasededatosdedestino < $1
}

# 2: Declaramos el alias
alias importar_db=importar_db_demo

Listo. ya podemos usarlo en la consola:

importar_db mydb.sql

Por ultimo aclarar que podes pasar la cantidad de parámetros que quieras. estos parámetros llegan a la función por medio de $1 para el primero $2 para el segundo, etc.

Druplicon

BoF de Context vs panels

El Martes 22 a las 19hs España (GMT +1) junto a estoyausente vamos a dar una pequeña charla sobre el eterno dilema de cual usar.. Context? Panels?… pues eso.

Personalmente voy a tratar de hacerlo lo mejor posible. Hablando en publico soy medio boludo a veces XD

Este BoF se va a hacer por Hangouts para los que entren, y como este tiene una capacidad limitada, todo aquel que no pueda participar activamente, puede seguirlo por Youtube (via streaming) ;)

Se va a pasar el link de Hangouts y Youtube por el Twitter de la Asociacion española de Drupal

De que vamos a hablar?
Hemos elegido una página que presenta las problematicas que todo drupalero se encuentra cuando tiene que por ejemplo:

  • Gestion de layouts.
  • Contenidos condicionales.
  • Que tipo de markup genera cada modulo y posibles mejoras (no hace falta decir cual de los módulos hace eso XD).
  • Exponer contenidos relacionados y aprovecharlos para mezclarlo todo en una sola pantalla
    (Por ejemplo: node -> author -> Profile2 -> apellido).
  • Soporte para i18n,
  • Y algunos etc, etc, etc.

Ya saben.

Chau!