Rails 5 JSON API con RethinkDB

Con Rails 5 fuera y el nuevo modo API incluido en el framework, veamos que se necesita para configurar una nueva aplicación para que funja como una JSON API.

En esta guía se explica cómo crear y configurar una aplicación Rails con RethinkDB, de tal manera que funcione como una JSON API (puro back-end).

Una aplicación de este tipo se da muy bien para después emplearla con múltiples clientes, ya sean SPAs (Single Page Applications) hechas en Angular o aplicaciones móviles construidas con Ionic 2, wink-wink.

Se asume familiaridad con Rails, git y los códigos de estado HTTP.

Preliminares

Commit inicial

Para generar el proyecto y hacer el primer commit:

rails new json_api --api --skip-active-record
cd json_api
git init
git add .
git ci -m "Commit inicial."

La opción de –skip-active-record desactiva ActiveRecord porque usaremos RethinkDB con NoBrainer en lugar de una base de datos relacional como PostgreSQL.

Configuración inicial

Agrega estas gemas a tu Gemfile:

group :development, :test do
  #...
  # Framework para pruebas
  gem 'minitest-rails'
  # Para generar objetos de prueba
  gem 'fabrication'
  # Para generar datos aleatorios
  gem 'faker'
end
 
#...
# Para usar RethinkDB a través de NoBrainer
gem 'nobrainer'
# Para construir JSON APIs con facilidad
gem 'jbuilder', '~> 2.5'
# Para usar ActiveModel has_secure_password
gem 'bcrypt', '~> 3.1.7'

Después en la terminal:

bundle
rails g minitest:install
rails g nobrainer:install

Con esto se instalan las gemas que hagan falta, después se generan los archivos de configuración para Minitest y NoBrainer.
Al ejecutar el generador de Minitest, te va a preguntar si debe sobreescribir el archivo test_helper.rb, contesta sí.

Agrega este contenido al archivo config/application.rb:

config.generators do |g|
  # Framework de pruebas estilo BDD
  g.test_framework :minitest, spec: true, fixture_replacement: :fabrication
  g.fixture_replacement :fabrication, dir: 'test/fabricators'
end

En el archivo test/test_helper.rb, comenta la siguiente línea:

# fixtures :all

No tenemos disponible ActiveRecord, si no se comentan esas líneas al tratar de ejecutar las pruebas se lanzará una excepción.

En el archivo config/initializers/inflections.rb agrega:

ActiveSupport::Inflector.inflections( :en ) do |inflect|
  inflect.acronym 'API'
end

Esto permite que al generar los controladores se utilice API como nombre del módulo contenedor en lugar de Api.

Con la configuración inicial terminada, agrega los cambios al repo:

git add .
git ci -m "Configuración inicial."

Testing

TDD vs BDD

La diferencia entre los dos es el cómo te gusta pensar cuando modelas tu aplicación.

El TDD utiliza el es (assert), el BDD usa el debe ser (must). Básicamente es una cuestión de ser vs debe ser al modelar.

Behavior-driven development

Si lo tuyo es el TDD, pasa a la siguiente sección.

Genera tu primer controlador

rails g controller API::Users

Ejecuta y observa:

rake test

Edita test/controllers/api/users_controller_test.rb y cambia el test de ejemplo por:

it "#index debe ser exitoso" do
  get api_users_url
  response.status.must_equal 200
end

Ejecuta y observa:

rake test

Edita config/routes.rb para agregar el recurso:

namespace :api do
  resources :users
end

Ejecuta y observa:

rake test

Edita app/controllers/api/users_controller.rb y agrega:

def index
  render json: {}
end

Ejecuta y observa:

rake test

😀

Esta fue una muestra de un proceso muy básico de BDD para un controlador.

Genera tu primer modelo

rails g model User name:string email:string password_digest:string

Ejecuta y observa:

rake test

Abre el archivo test/models/user_test.rb y verás:

require "test_helper"
 
describe User do
  let(:user) { User.new }
 
  it "must be valid" do
    value(user).must_be :valid?
  end
end

Si analizamos un poco la situación y contemplamos nuestro modelo User, veremos que el usuario forzosamente necesita al menos un correo. Así, un usuario recién creado debería ser inválido.

Cambia el contenido de ese archivo por este:

require 'test_helper'
 
describe User do
  let(:user) { User.new }
 
  it 'no debe ser válido' do
    value(user).wont_be :valid?
  end
end

Ejecuta y observa:

rake test

Abre el archivo app/models/user.rb y agrega:

validates :email, presence: true

Ejecuta y observa:

rake test

😀

Esto fue una muestra de un proceso muy básico de aplicación de BDD para crear un modelo.

A continuación veremos cómo se haría con TDD.

Test-driven development

Para que los archivos de las pruebas se generen al estilo TDD cambia el valor de spec a false en el archivo config/application.rb:

config.generators do |g|
  # Framework de pruebas estilo TDD
  g.test_framework :minitest, spec: false, fixture_replacement: :fabrication
  g.fixture_replacement :fabrication, dir: 'test/fabricators'
end

Genera tu primer controlador

rails g controller API::Users

Ejecuta y observa:

rake test

Edita test/controllers/api/users_controller_test.rb y cambia el test de ejemplo por:

def test_index_action
  get api_users_url
  assert_response :success
end

Nota: El nombre de los métodos debe comenzar necesariamente con test_ para que sean ejecutados durante el proceso de pruebas.

El método anterior es equivalente a este:

test 'index action' do
  get api_users_url
  assert_response :success
end

Ejecuta y observa:

rake test

Edita config/routes.rb para agregar el recurso:

namespace :api do
  resources :users
end

Ejecuta y observa:

rake test

Edita app/controllers/api/users_controller.rb y agrega:

def index
  render json: {}
end

Ejecuta y observa:

rake test

😀

Esta fue una muestra de un proceso muy básico de TDD para un controlador.

Genera tu primer modelo

rails g model User name:string email:string password_digest:string

Ejecuta y observa:

rake test

Abre el archivo test/models/user_test.rb y verás:

require "test_helper"
 
class UserTest < ActiveSupport::TestCase
  def user
    @user ||= User.new
  end
 
  def test_valid
    assert user.valid?
  end
end

Si analizamos un poco la situación y contemplamos nuestro modelo User, veremos que el usuario forzosamente necesita al menos un correo. Así, un usuario recién creado debería ser inválido.

Cambia el único test que existe en ese archivo por este otro:

def test_invalid
  assert_not user.valid?
end

Ejecuta y observa:

rake test

Abre el archivo app/models/user.rb y agrega:

validates :email, presence: true

Ejecuta y observa:

rake test

😀

Y esta fue una muestra de un proceso muy básico de TDD para un modelo.

TDD o BDD es una cuestión de gustos. En lo personal prefiero el BDD.

Espero esta guía te sea útil.

Repositorio en github

lobo-tuerto/rails_rethinkdb

Coach outlet

Cómo arreglar el doble ícono de Google Chrome en elementary OS

Al abrir Google Chrome aparece dos veces el ícono en el dock.

Para corregirlo, abre una terminal y teclea esto:

sudo nano /usr/share/applications/google-chrome.desktop

Inserta StartupWMClass=Google-chrome-stable bajo cada una de las tres secciones siguientes:

[Desktop Entry]
StartupWMClass=Google-chrome-stable
...
 
[NewWindow Shortcut Group]
StartupWMClass=Google-chrome-stable
...
 
[NewIncognito Shortcut Group]
StartupWMClass=Google-chrome-stable
...

Configuración de una aplicación Rails 4.2 con MongoDB para una JSON API

En esta guía se explica cómo crear y configurar una aplicación Rails con MongoDB, de tal manera que funcione como una JSON API, es decir, puro back end.

Una aplicación de este tipo se da muy bien para después conectarla con un cliente SPA (Single Page Application), digamos en AngularJS o para emplearla con una aplicación móvil hecha con Ionic, por ejemplo.

Se asume familiaridad con Rails, git y los códigos de estado HTTP.

Preliminares

Commit inicial

Para generar el proyecto y hacer el primer commit:

rails new json_api --skip-active-record
cd json_api
git init
git add .
git ci -m "Commit inicial."

La opción de –skip-active-record desactiva ActiveRecord porque usaremos MongoDB con Mongoid en lugar de una base de datos relacional como PostgreSQL o MySQL.

Configuración inicial

Agrega estas gemas a tu Gemfile:

group :development, :test do
  ...
  # Framework para pruebas
  gem 'minitest-rails'
  # Para generar objetos de prueba
  gem 'fabrication'
  # Para generar datos de prueba
  gem 'faker'
end
 
# Para usar MongoDB a través de Mongoid
gem 'mongoid'
# Para usar respond_with en los controladores
gem 'responders'
# Para usar ActiveModel#has_secure_password
gem 'bcrypt'

Después en la terminal:

bundle
rails g mongoid:config
rails g minitest:install

Con esto se instalan las gemas que hagan falta, después se generan los archivos de configuración para Mongoid y Minitest. No es necesario correr el generador para los responders.

Al ejecutar el generador de Minitest, te va a preguntar si debe sobreescribir el archivo test_helper.rb, contesta sí.

En el archivo config/mongoid.yml, quita el comentario de la línea que aparece a continuación y cambia el valor a false:

# raise_not_found_error: true
raise_not_found_error: false

No queremos que Mongoid levante una excepción toda vez que no encuentre un documento.

Es deseable tener más control sobre cuándo se lanza una excepción, de esta forma, cuando se busca un usuario para autenticación y no existe, en vez de un código de error 404 (Not Found) podemos mandar un 401 (Unauthorized).

Agrega este contenido al archivo config/application.rb:

config.generators do |g|
  # Framework de pruebas estilo BDD
  g.test_framework :minitest, spec: true, fixture: true, fixture_replacement: :fabrication
 
  # Estamos haciendo una API, no queremos assets, helpers, tampoco views
  g.assets false
  g.helper false
  g.template_engine nil
end
 
# Para manejar la presentación de errores 404 y 500 como JSON
config.exceptions_app = self.routes

En el archivo test/test_helper.rb, comenta las siguientes líneas:

# ActiveRecord::Migration.check_pending!
# fixtures :all

No tenemos disponible ActiveRecord, si no se comentan esas líneas al tratar de ejecutar las pruebas se lanzará una excepción.

En el archivo config/initializers/inflections.rb agrega:

ActiveSupport::Inflector.inflections( :en ) do |inflect|
  inflect.acronym 'API'
end

Esto permite que al generar los controladores se utilice API como nombre del módulo contenedor en lugar de Api.

Configuración de rutas y del controlador principal de la aplicación

En el archivo config/routes.rb agrega:

match '/404' => 'application#not_found', via: [:get, :post, :put, :patch, :delete]
match '/500' => 'application#exception', via: [:get, :post, :put, :patch, :delete]

Esto permite personalizar las respuestas de error cuando no se encuentra alguna ruta o cuando se tiene algún error en la aplicación.

Por defecto, en estos casos lo que se devuelve es HTML (y el código de estado HTTP adecuado), pero estamos interesados en siempre entregar JSON al cliente.

En el archivo app/controllers/application_controller.rb cambia :exception por :null_session y agrega:

protect_from_forgery with: :null_session
 
# Estos métodos se llaman desde las rutas
def not_found
  response = http_error( { status: '404', message: 'Not Found' } )
  render json: response, status: :not_found
end
 
def exception
  response = http_error( { status: '500', message: 'Internal Server Error' } )
  render json: response, status: :internal_server_error
end
 
 
private
# Método de utilería
def http_error( error )
  { error: { http: error } }
end

La línea de protect_from_forgery_with indica el tipo de protección que se utilizará como medida de prevención contra un ataque de tipo CSRF.

Cuando es :exception se lanza una excepción ante cualquier petición POST, PUT, PATCH o DELETE si no viene acompañada de un token especial que Rails normalmente inserta en las vistas. Siendo esta una aplicación que va a ser utilizada como API no cuenta con ellas.

De la documentación: ActionController::RequestForgeryProtection::ClassMethods

Turn on request forgery protection. Bear in mind that GET and HEAD requests are not checked.

Con :null_session se indica que no habrá estado del lado del servidor, por tanto, no existe una sesión que mantega el estado de conectado dentro del sistema. La autenticación se logra por medio de un token especial que se enviará desde el cliente en la cabecera de la petición. Más adelante veremos cómo.

Otros errores que sería bueno atrapar

Igual, en el archivo app/controllers/application_controller.rb agrega:

# Para cuando lancemos la excepción de documento no encontrado en MongoDB
rescue_from Mongoid::Errors::DocumentNotFound, with: :document_not_found
# Para cuando el tipo de contenido solicitado no sea application/json
rescue_from ActionController::UnknownFormat, with: :not_acceptable
# Para cuando hagan falta parámetros requeridos en algún modelo
rescue_from ActionController::ParameterMissing, with: :unprocessable_entity
 
def document_not_found
  response = http_error( { status: '404', message: 'Document Not Found' } )
  render json: response, status: :not_found
end
 
def not_acceptable
  response = http_error( { status: '406', message: 'Not Acceptable' } )
  render json: response, status: :not_acceptable
end
 
def unprocessable_entity
  response = http_error( { status: '422', message: 'Unprocessable Entity' } )
  render json: response, status: :unprocessable_entity
end
 
def unauthorized
  response = http_error( { status: '401', message: 'Unauthorized' } )
  render json: response, status: :unauthorized
end

Con la configuración inicial terminada, agrega los cambios al repo:

git add .
git ci -m "Configuración inicial."

Testing

TDD vs BDD

La diferencia entre los dos es el cómo te gusta pensar acerca del modelado de la aplicación.

El TDD utiliza el es (assert, afirma), el BDD usa el debe (must, debe). Básicamente es una cuestión de es vs debe ser.

Probemos primero con un poco de BDD

Genera tu primer controlador

rails g controller API::Users

Ejecuta y observa:

rake test

Edita test/controllers/api/users_controller_test.rb y cambia el test de ejemplo por:

it "#index debe ser exitoso" do
  get :index
  response.status.must_equal 200
end

Ejecuta y observa:

rake test

Edita config/routes.rb para agregar el recurso:

namespace :api do
  resources :users
end

Ejecuta y observa:

rake test

Edita app/controllers/api/users_controller.rb y agrega:

def index
  render json: {}
end

Ejecuta y observa:

rake test

😀

Esta fue una muestra de un proceso muy básico de BDD para un controlador.

Genera tu primer modelo

rails g model User name:string email:string password_digest:string

Ejecuta y observa:

rake test

Abre el archivo test/models/user_test.rb y verás:

require 'test_helper'
 
describe User do
  let( :user ) { User.new }
 
  it "must be valid" do
    user.must_be :valid?
  end
end

Si analizamos un poco la situación y contemplamos nuestro modelo User, veremos que el usuario forzosamente necesita al menos un correo. Así, un usuario recién creado debería ser inválido.

Cambia el único test que existe en ese archivo por este otro:

it 'no debe ser válido' do
  user.wont_be :valid?
end

Ejecuta y observa:

rake test

Abre el archivo app/models/user.rb y agrega:

validates :email, presence: true

Ejecuta y observa:

rake test

😀

Y esta fue una muestra de un proceso muy básico de BDD para un modelo.

A continuación veremos cómo se haría con TDD.

Probemos ahora con un poco de TDD

Para que las pruebas se generen al estilo TDD cambia spec a false:

g.test_framework :minitest, spec: false, fixture: false

Genera tu primer controlador

rails g controller API::Users

Ejecuta y observa:

rake test

Edita test/controllers/api/users_controller_test.rb y cambia el test de ejemplo por:

def test_index_action
  get :index
  assert_response :success
end

Nota: El nombre de los métodos debe comenzar necesariamente con test_ para que sean ejecutados durante el proceso de pruebas.

El método anterior es equivalente a este:

test 'index action' do
  get :index
  assert_response :success
end

Ejecuta y observa:

rake test

Edita config/routes.rb para agregar el recurso:

namespace :api do
  resources :users
end

Ejecuta y observa:

rake test

Edita app/controllers/api/users_controller.rb y agrega:

def index
  render json: {}
end

Ejecuta y observa:

rake test

😀

Esta fue una muestra de un proceso muy básico de TDD para un controlador.

Genera tu primer modelo

rails g model User name:string email:string password_digest:string

Ejecuta y observa:

rake test

Abre el archivo test/models/user_test.rb y verás:

require 'test_helper'
 
class UserTest < ActiveSupport::TestCase
 
  def user
    @user ||= User.new
  end
 
  def test_valid
    assert user.valid?
  end
 
end

Si analizamos un poco la situación y contemplamos nuestro modelo User, veremos que el usuario forzosamente necesita al menos un correo. Así, un usuario recién creado debería ser inválido.

Cambia el único test que existe en ese archivo por este otro:

def test_invalid
  assert_not user.valid?
end

Ejecuta y observa:

rake test

Abre el archivo app/models/user.rb y agrega:

validates :email, presence: true

Ejecuta y observa:

rake test

😀

Y esta fue una muestra de un proceso muy básico de TDD para un modelo.

En lo personal prefiero el BDD, siento que en ese estilo se expresa mejor la intención de lo que quieres programar.

Finalmente

Más adelante publicaré una guía acerca de cómo hacer pruebas de integración y cómo utilizar fabricators de manera efectiva en las pruebas de modelos y controladores.

Espero esta guía te sea útil.

Repositorio en github

lobo-tuerto/rails_json_api

Levantar aplicación Rails para producción en Ubuntu con Unicorn y Nginx

Normalmente para levantar una aplicación Rails en producción, utilizamos este esquema:

  • Ubuntu como el Sistema Operativo de nuestro Servidor.
  • Unicorn como Servidor de Aplicaciones Rails.
  • Nginx como Servidor HTTP corriendo como un Front-End Reverse Proxy
  • Postgres como el manejador de nuestra Base de Datos.

En palabras más sencillas, Unicorn se encargará de interpretar todo nuestro código Ruby, Nginx de servir nuestro HTML, CSS, JS e imágenes y Postgres de leer y almacenar nuestra información. Todo esto vivirá en un servidor Ubuntu en la nube que operaremos desde la consola.

  1. Primero necesitas hacerte de un buen servidor e instalarle Ubuntu Server, recomiendo el servicio que da Linode porque el precio es muy accesible, nunca me ha fallado el servicio, son máquinas muy potentes y te dan control completo de tu servidor para instalar y desinstalar lo que tu quieras.
  2. Una vez que tengas tu servidor, necesitas instalarle Ruby, para ello puedes auxiliarte instalando rbenv con esta guía.
  3. Después instalale Nginx con estos comandos:
    sudo apt-get update
    sudo apt-get install nginx

    Por defecto Ubuntu lo activa, intenta visitar tu servidor desde un navegador, de esta forma:
    http://el_dominio_o_IP_de_tu_servidor

    Deberías ver una página como esta:
    nginx inicial

    Si no la ves, puedes iniciar Nginx manualmente desde tu servidor, los comandos para apagar, iniciar o reiniciar Nginx son lo siguientes:

    sudo service nginx stop
    sudo service nginx start
    sudo service nginx restart

    Para arrancarlo al iniciar Ubuntu ejecuta este comando:

    sudo update-rc.d nginx defaults

    En versiones recientes de Ubuntu te puede decir que ya existía el registro para iniciarlo automáticamente.

  4. Ahora instalaremos Unicorn:
    gem install unicorn

    ¡Así de fácil!

  5. Sube el código de tu aplicación al servidor. Puedes usar github, bitbucket o simplemente subir un zip si no tienes un manejador de versiones, normalmente subo mis aplicaciones Rails a un directorio apps en el home del usuario que vamos a usar para hacer los deploys, de esta forma:
    deployer@miservidor:~$ mkdir apps
    deployer@miservidor:~$ cd apps
    deployer@miservidor:~/apps$ pwd
    /home/deployer/apps/

    Para dejarlo así:

    /home/deployer/apps/mi_aplicacion
  6. Vamos a configurar un poco tu aplicación para producción:
    • Asegurate de agregar unicorn a tu Gemfile:
      gem 'unicorn'
    • Si usas Rails 4, asegurate de tener un valor en el archivo config/secrets.yml para el apartado de production, por ejemplo:
      # Do not keep production secrets in the repository,
      # instead read values from the environment.
      production:
        secret_key_base: b1329441746d18...4b14d78d744650450113242c

      La llave fue cortada a propósito, básate en las que trae tu aplicación para development y test; puedes sólo cambiarle un caracter a esas para que sea distinta la de producción.

    • Asegurate de tener configurada la Base de Datos para producción en tu archivo config/database.yml, utiliza el manejador de Base de Datos que más te agrade, por ejemplo:
      production:
        adapter: sqlite3   
        pool: 5
        timeout: 5000
        database: db/production.sqlite3
      Si en lugar de Sqlite deseas usar Postgres, basate en esta guia para instalarlo y configurarlo.
    • Instala las gemas necesarias para tu aplicación con el comando bundle
      bundle install --deployment --binstubs

      La opción binstubs genera los ejecutables en el directorio de tu aplicación ./bin para las gemas en el bundle, de esta forma cualquier herramienta como unicorn o cron asegura la versión de Ruby a usar y la versión de las gemas a usar.

    • Crea tu Base de Datos, ejecuta las migraciones y corre los seeds para el ambiente de producción:
      rake db:create RAILS_ENV=production
      rake db:migrate RAILS_ENV=production
      rake db:seed RAILS_ENV=production
    • rake db:create es solo necesario ejecutarlo una vez para crear la base de datos

    • Precompila los assets para que se compriman y optimicen:
      rake assets:precompile RAILS_ENV=production

    Nota: Si deseas saber en que manejador de base de datos estás trabajando, entra a la consola con rails c production y escribe la siguiente sentencia:

    ActiveRecord::Base.connection.adapter_name

    En mi caso salio lo siguiente:

     => "PostgreSQL"
    
  7. Ahora vamos a configurar unicorn dentro de tu aplicación, para esto necesitamos crear dos archivos:
    • El primer archivo se ubicará en la ruta config/unicorn_init.sh dentro de tu aplicación, y tendrá el siguiente código:
      #!/bin/sh
      ### BEGIN INIT INFO
      # Provides:          unicorn
      # Required-Start:    $remote_fs $syslog
      # Required-Stop:     $remote_fs $syslog
      # Default-Start:     2 3 4 5
      # Default-Stop:      0 1 6
      # Short-Description: Manage unicorn server
      # Description:       Start, stop, restart unicorn server for a specific application.
      ### END INIT INFO
      set -e
       
      # Feel free to change any of the following variables for your app:
      TIMEOUT=${TIMEOUT-60}
      APP_ROOT=/home/deployer/apps/mi_aplicacion
      PID=$APP_ROOT/tmp/pids/unicorn.pid
      CMD="cd $APP_ROOT; bundle exec unicorn -D -c $APP_ROOT/config/unicorn.rb -E production"
      AS_USER=deployer
      set -u
       
      OLD_PIN="$PID.oldbin"
       
      sig () {
        test -s "$PID" && kill -$1 `cat $PID`
      }
       
      oldsig () {
        test -s $OLD_PIN && kill -$1 `cat $OLD_PIN`
      }
       
      run () {
        if [ "$(id -un)" = "$AS_USER" ]; then
          eval $1
        else
          su -c "$1" - $AS_USER
        fi
      }
       
      case "$1" in
      start)
        sig 0 && echo >&2 "Already running" && exit 0
        run "$CMD"
        ;;
      stop)
        sig QUIT && exit 0
        echo >&2 "Not running"
        ;;
      force-stop)
        sig TERM && exit 0
        echo >&2 "Not running"
        ;;
      restart|reload)
        sig HUP && echo reloaded OK && exit 0
        echo >&2 "Couldn't reload, starting '$CMD' instead"
        run "$CMD"
        ;;
      upgrade)
        if sig USR2 && sleep 2 && sig 0 && oldsig QUIT
        then
          n=$TIMEOUT
          while test -s $OLD_PIN && test $n -ge 0
          do
            printf '.' && sleep 1 && n=$(( $n - 1 ))
          done
          echo
       
          if test $n -lt 0 && test -s $OLD_PIN
          then
            echo >&2 "$OLD_PIN still exists after $TIMEOUT seconds"
            exit 1
          fi
          exit 0
        fi
        echo >&2 "Couldn't upgrade, starting '$CMD' instead"
        run "$CMD"
        ;;
      reopen-logs)
        sig USR1
        ;;
      *)
        echo >&2 "Usage: $0 <start|stop|restart|upgrade|force-stop|reopen-logs>"
        exit 1
        ;;
      esac

      Este archivo se encargará de apagar, encender y reiniciar un proceso de Unicorn para nuestra aplicación, asegúrate de personalizar la ruta que establece el valor de APP_ROOT con la ruta en la cual viva tu aplicación, y de poner tu usuario con el que accedes al servidor en la variable AS_USER.

      Debemos darle permiso de ejecución:

      chmod go+x unicorn_init.sh
    • El siguiente archivo se ubicará en config/unicorn.rb y tendrá la siguiente información:
      root = "/home/deployer/apps/mi_aplicacion"
      working_directory root
      pid "#{root}/tmp/pids/unicorn.pid"
      stderr_path "#{root}/log/unicorn.log"
      stdout_path "#{root}/log/unicorn.log"
       
      listen "/tmp/unicorn.miaplicacion.sock"
      worker_processes 2
      timeout 30

      Este archivo define los parámetros que le daremos a Unicorn como el timeout, el numero de workers, etcétera. También se define la ubicación del socket con el que se comunicará con Nginx. Asegúrate de personalizar el root y el listen según tu ambiente.

    • Crea el directorio pids dentro de la carpeta tmp de tu proyecto, porque lo va a necesitar Unicorn, ubicándote en la carpeta de tu aplicación, ejecuta este comando:
      mkdir tmp/pids

      Para que te cree este directorio:

      /home/deployer/apps/mi_aplicacion/tmp/pids/
    • Ahora vamos a crear un acceso directo del primer archivo que creamos para tener a la mano una ubicación desde donde levantar y apagar todos nuestros unicorns, primero nos movemos al siguiente directorio:
      cd /etc/init.d/

      Y creamos un acceso directo a nuestro archivo (puedes escoger el nombre que gustes para el acceso directo, yo tiendo a ponerles unicorn_elnombredelaapp para que sea fácil recordarlo):

      sudo ln -s /home/deployer/apps/mi_aplicacion/config/unicorn_init.sh unicorn_mi_aplicacion

      ¡Y ahora procedemos a levantar Unicorn!

      sudo /etc/init.d/unicorn_mi_aplicacion start

      Recuerda que puedes apagar y reiniciar Unicorn con los siguientes comandos:

      sudo /etc/init.d/unicorn_mi_aplicacion stop
      sudo /etc/init.d/unicorn_mi_aplicacion restart

      Cada que hagas cambios en tu código, deberás reiniciar Unicorn; si llegas a agregar alguna gema, es posible que restart no sea suficiente, sino que necesites darle stop y después start.

    • Para que se ejecute automáticamente cada que se reinicie el servidor, ejecutamos el siguiente comando:
      sudo update-rc.d /etc/init.d/unicorn_mi_aplicacion defaults
  8. Ahora vamos a realizar la conexión entre Nginx y Unicorn, primero nos movemos a nuestra carpeta de la aplicación:
    ~/apps/mi_aplicacion

    Y creamos el archivo config/nginx.conf con el siguiente contenido:

    upstream miapliacion {
      server unix:/tmp/unicorn.mi_aplicacion.sock fail_timeout=0;
    }
     
    server {
      listen 80;
      server_name www.miaplicacion.com miaplicacion.com;
      root /home/deployer/apps/mi_aplicacion/public;
     
      location ^~ /assets/ {
        gzip_static on;
        expires max;
        add_header Cache-Control public;
      }
     
      try_files $uri/index.html $uri @miaplicacion;
      location @miaplicacion {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_redirect off;
        proxy_pass http://miaplicacion;
      }
     
      error_page 500 502 503 504 /500.html;
      client_max_body_size 4G;
      keepalive_timeout 10;
    }

    Asegúrate de personalizar el valor de upstream, en el server_name poner los dominios o subdominios a los cuales quieres que responda tu aplicación, en root poner la ruta de la carpeta public de tu aplicación, y en el try_files location y proxy_pass escribir lo mismo que pusiste en upstream agregandole una arroba al inicio.

    Para que funcione tu dominio/subdominio debes apuntarlos primero a la IP de tu servidor; esto lo puedes hacer desde antes de hacer esta guía. Esto quiere decir que en este punto de la guía, al visitar tu dominio o subdominio, deberías ver la pantalla inicial de Nginx; sino, quiere decir que no has configurado dichos dominios con tu proveedor, esto se hace desde su panel de administración, por ejemplo en GoDaddy estableces los NameServers de Linode y en tu Linode agregas dichos dominios/subdominios para que al visitarlos desde tu navegador veas tu servidor.

    Muévete al directorio de los sitios disponibles de Nginx:

    cd /etc/nginx/sites-enabled/

    Crea un acceso directo como lo hicimos con unicorn:

    sudo ln -s /home/deployer/apps/mi_aplicacion/config/nginx.conf mi_aplicacion

    ¡Y reinicia Nginx!

    sudo service nginx restart

    ¡Ya deberías ver tu sitio online en ambiente productivo!

    Si te salen errores no olvides revisar los logs de producción en tu carpeta de la aplicación, los logs de unicorn igualmente dentro de tu carpeta, o los logs de nginx si te has equivocado en la configuración.

Recursos

How To Install Nginx On Ubuntu 14.04lts
How To Deploy Rails Apps Using Unicorn And Nginx On CentOS 6.5
How To Deploy with Rbenv

Mejores mensajes de los commits en GIT

Es importante la calidad de los mensajes, para cuidar la ortografía y la longitud de líneas prefiero usar vim, por lo que veremos a continuación.

Lo instalamos en Ubuntu:

sudo apt-get install vim

Le decimos a Git que lo use:

git config --global core.editor "vim"

Creamos archivo “~/.vimrc” con lo siguiente:

autocmd Filetype gitcommit setlocal spell textwidth=72

Al hacer un commit, no usar opción -m para que lance la ventana de vim:

git commit

vim inicio

Vim tiene dos modos: el de edición y el de comando.

Al entrar a vim se estará en modo de comando por lo que al escribir algo o dar enter no se reflejará en pantalla, para ello hay que pasarse al modo texto presionando una sola vez la tecla “i”, esto hará que se pueda escribir normalmente, para saber que lo hicimos bien, podemos ver que hasta abajo dice “– INSERTAR –“.

vim insertar

Escribimos el texto del commit, por la configuración que agregamos vim automáticamente pasará a una nueva línea cuando el mensaje sea demasiado largo, además de revisar que no tengamos errores de ortografía.

ortografia

Para salir y guardar, en modo comando escribir:

:wq

Para cancelar:

:q!

Si usan español en los mensajes y no les corrige bien la ortografía, pasen al modo de comando con la tecla “ESC” y escriban el siguiente comando:

:setlocal spell spelllang=es

Les preguntará si desean crear un directorio, y si desean descargar los diccionarios, a todo digan que sí:

descargar corrector ortografia

Si siempre usan español, pueden cambiarlo en el archivo ~/.vimrc:

autocmd Filetype gitcommit setlocal spell spelllang=es textwidth=72

Recursos:
How do I make git use the editor of my choice for commits?
5 Useful Tips For A Better Commit Message
Vim en dos minutos

Teclado personalizado en Ubuntu 14.04

Hace poco tuve la necesidad de comprar un teclado USB para una laptop que tenía algunas teclas que no funcionaban.

Desafortunadamente, ningún mapeo se ajustaba por defecto a los dibujitos que tenía en mi teclado.

entradas

El más cercano era el de español de España, pero tenía los acentos invertidos (la tecla con la tilde normal la ponía invertida y viceversa).

Así que tuve que modificar el mapeo manualmente. Para esto se necesitan abrir los mapeos del español con permiso de administrador. Desde una terminal teclea:

cd /usr/share/X11/xkb/symbols/
sudo gedit es

En mi caso intercambié las entradas:

key <AD11> { [dead_grave, dead_circumflex, bracketleft, dead_abovering ] };
key <AC11> { [dead_acute, dead_diaeresis, braceleft, braceleft ] };

Dejándolas como:

key <AC11> { [dead_grave, dead_circumflex, bracketleft, dead_abovering ] };
key <AD11> { [dead_acute, dead_diaeresis, braceleft, braceleft ] };

Y lo mismo para las teclas que deseen cambiar. Después de guardar, hay que regenerar la configuración:

sudo dpkg-reconfigure xkb-data

Al lanzar la aplicación Entrada de Texto de Ubuntu ya deberían ver los cambios reflejados en el vistazo del teclado que aparece ahí.

Entrada De Texto

teclado

Después de cambiar todo lo necesario, para que los cambios se vieran en el editor de texto que ya tenía abierto, yo tuve que decirle a Ubuntu que cambiará el mapa de caracteres con el atajo Super+Espacio, osea la tecla Windows y el espacio.

Y ya se puede usar la nueva configuración personalizada en todas las aplicaciones.

Mi Ubuntu Desktop — Puesta a punto de una máquina de escritorio

Ubuntu recién instalado

Abre una terminal y teclea:

echo "" >> ~/.bashrc
echo "export EDITOR=nano" >> ~/.bashrc

Esto pone a nano como el editor por defecto, en lugar de vi.

Para activar el firewall, teclea en la terminal:

sudo ufw enable

Cambia los servidores de DNS a los de Google: Configure your network settings to use Google Public DNS.

Para activar los espacios de trabajo, busca en Unity: Apariencia → Comportamiento y da clic sobre Activar las áreas de trabajo.

Para modificar el número de áreas de trabajo (ej: 3) en la horizontal:

gsettings set org.compiz.core:/org/compiz/profiles/unity/plugins/core/ hsize 3

Para modificar el número de áreas de trabajo (ej: 2) en la vertical:

gsettings set org.compiz.core:/org/compiz/profiles/unity/plugins/core/ vsize 2

Busca e instala actualizaciones

Ya sea con la aplicación de Actualización de software o con:

sudo apt-get update
sudo apt-get upgrade

Ten en cuenta que la lista de los paquetes por actualizar en Actualización de software y apt-get upgrade puede variar un poco debido a: Phasing of Stable Release Updates (a veces en Actualización de software dice que está todo al día, pero al ejecutar apt-get upgrade te da una lista de paquetes que si necesitan actualización).

Ubuntu en una MacBook Pro

Si estás instalando Ubuntu en una MacBook Pro encontrarás útil lo que viene a continuación.

Ajusta el teclado

Para intercambiar las teclas Command y Alt (para que queden al estilo de un teclado de PC):

Con efecto inmediato pero temporal:

echo 1 | sudo tee /sys/module/hid_apple/parameters/swap_opt_cmd

Para hacerlo permanente:

echo options hid_apple swap_opt_cmd=1 | sudo tee -a /etc/modprobe.d/hid_apple.conf
sudo update-initramfs -u -k all

Para cambiar el comportamiento de las teclas de función (para bajar el brillo ahora tendrás que presionar FN + F1):

Con efecto inmediato pero temporal:

echo 2 | sudo tee /sys/module/hid_apple/parameters/fnmode

Para hacerlo permanente:

echo options hid_apple fnmode=2 | sudo tee -a /etc/modprobe.d/hid_apple.conf
sudo update-initramfs -u -k all

Pega con una tecla

Si vas a usar sólo el teclado de la laptop, recomiendo mapear alguna de las teclas menos usadas (como el Command de la derecha) para que funcione como si le hicieras clic con el botón medio del ratón.

sudo apt-get install xkbset
echo "" >> ~/.bashrc
echo "xmodmap -e \"keycode 134 = Pointer_Button2\"; xkbset m" >> ~/.bashrc

Ajusta la escala de la UI

La pantalla Retina hace que todo se vea demasiado pequeño, para escalarla:

Busca en Unity: Monitores → Escala de menús y barras de título y ponle un valor como 1.25 o 1.5.

Ajusta el touchpad

Si el puntero salta de un lado a otro mientras arrastras algo por la pantalla, será mejor que instales libinput:

sudo apt-get install xserver-xorg-input-libinput

Para personalizar el comportamiento del trackpad necesitas modificar el archivo:
/usr/share/X11/xorg.conf.d/60-libinput.conf

Log out, log in… enjoy! 🙂

Software de cajón

Algunas cosas útiles que he encontrado a través de los años y que siempre termino instalando:

sudo apt-get install alpine testdisk mlock gddrescue transmission byobu \
comix gimp gimp-ufraw gnote inkscape anki fail2ban logwatch chkrootkit rkhunter nethogs \
unrar curl nmap htop mtr-tiny pdftk wkhtmltopdf imagemagick poppler-utils \
pandoc gdal-bin network-manager-openvpn-gnome libgeoip-dev libav-tools python-dev \
freeglut3-dev

Guía para rkhunter.

sudo rkhunter --propupd
sudo rkhunter --checkall

Navegador

Mi navegador preferido para desarrollo web: Google Chrome

Si tienes problemas para instalar Chrome, revisa este enlace: Cómo instalar Google Chrome en Ubuntu

Si quieres instalar la versión beta o la de desarrollo: Chrome Release Channels

Para activar el WebGL a veces es necesario entrar a: chrome://gpu/
Instalar chat de voz y video para gmail: Chat de vídeo y voz de Google

Para activar el click to play para Flash:
ConfiguraciónMostrar configuración avanzada…PrivacidadConfiguración de contenidoComplementosPermitirme elegir cuándo ejecutar contenido de complementos

Plugins

ColorZilla: Advanced Eyedropper, Color Picker, Gradient Generator and other colorful goodies.
Postman – REST Client: Postman helps you be more efficient while working with APIs.
EditThisCookie: EditThisCookie is a cookie manager. You can add, delete, edit, search, protect and block cookies!
uBlock Origin: An efficient blocker. Easy on CPU and memory.
JSON Formatter: Makes JSON easy to read. Open source.
Google Cast: Permite enviar contenido al Chromecast y a otros dispositivos que admitan Google Cast (versión BETA).
Videostream for Google Chromecast: Play your downloaded videos on the Chromecast™ (any file type).
ClearBrowserData: Clear all browser data with one simple click.

Activa TRIM para mejorar el rendimiento de tu disco duro de estado sólido

Si cuentas con un SSD, lo más recomendable es que pongas un trabajo cron a realizar las tareas de mantenimiento que necesita este tipo de disco duro:
Enable TRIM on SSD in Ubuntu for better performance (Using a daily cron job – recommended)

Verifica que el comando fstrim funciona adecuadamente en tu equipo:

sudo fstrim -v /

Si todo sale bien, verás algo como:
/: 2.5 GiB (2688892928 bytes) trimmed

Entonces, teclea en la terminal:

sudo nano /etc/cron.daily/trim

Agrégale este contenido:

#!/bin/sh
LOG=/var/log/trim.log
echo "*** $(date -R) ***" >> $LOG
fstrim -v / >> $LOG

Si tienes otras particiones en el SSD agrégalas al final del script anterior.

No olvides hacer ejecutable el script:

sudo chmod +x /etc/cron.daily/trim

Ubuntu para desarrollo de software

git es mi herramienta predilecta para control de versiones, y la necesito para instalar mi ambiente de trabajo: rbenv, nvm, pyenv, etc.

Aquí la guía de: Cómo instalar git

Para facilitar los cambios de directorio en la terminal: z

git clone https://github.com/rupa/z ~/z
echo ". ~/z/z.sh" >> ~/.bashrc

Aquí la guía de: Cómo instalar Ruby

Si te interesa el gamedev en Ruby, checa los prerequisitos para Ubuntu aquí: Getting Started on Linux

Y estas las gemas necesarias para darle rienda suelta al hobby:

gem install gosu chipmunk texplay lotu

Este es un mini demo que hice inspirado en el venerable Missile Command:

gem install missile-command-ruby

Instala Elixir con: Installing Elixir.

Para el manejador de versiones de Python: pyenv.

Instala Node.js con esta guía.
Instala PostgreSQL con esta guía.
Instala RethinkDB con esta guía.
Instala Redis con esta guía.

Instala OpenJDK Java con esta guía u Oracle Java con esta otra.

Las IDEs de JetBrains como IntelliJ (y demás) necesitan que se configure de manera particular el Inotify Watches Limit:

sudo apt-get install inotify-tools

Ubuntu desktop y entretenimiento

Instala calibre.

Para ajustar los tonos del monitor de acuerdo a la hora del día

Antes recomendaba f.lux, pero ya no hay repo actualizado y al parecer no funcionaba en algunos equipos. He probado Redshift y jala bastante bien.

sudo apt-get install redshift-gtk geoclue-hostip

Durante la noche, teclea en la terminal:

redshift-gtk &

Para escuchar música

Un muy buen reproductor de música: Clementine

sudo apt-get install clementine

Para descargar videos de youtube

Instala youtube-dl

Después de instalar git, clona en un directorio, entra a él y después ejecuta:

make clean
make all
sudo make install

Servidor de medios

Instala Plex Media Server.
Guía de instalación.

Para poder conectar algún dispositivo como una Smart TV a tu servidor Plex, debes abrir el puerto 32400 en el firewall. Teclea en la terminal:

sudo ufw allow from 192.168.x.x to any port 32400

Cambia 192.168.x.x por la dirección IP de tu Smart TV.

Si tus videos y películas están en otro disco duro, recuerda darle permisos 755 al directorio /media/$USER:

sudo chmod 755 /media/$USER

De otra forma Plex no podrá leer tus archivos.

Cómo instalar git en Ubuntu 17.04

Unos artículos atrás había comentado ya, un poco acerca de git y cuan grandioso es. 🙂

Instalación

Abre una terminal y teclea:

sudo apt-get install git

Listo, ya puedes usar git.

Para probar tu instalación teclea esto:

git --version

En mi caso presenta: git version 2.11.0.

Configuración

Es recomendable ejecutar los siguientes comandos para propósitos de identificación. Estos datos serán utilizados para marcar los commits que realices.

git config --global user.name "Pon aquí tu nombre"
git config --global user.email "pon@aqui.tu.correo"

Para tener texto en color:

git config --global color.ui "auto"

Para habilitar la detección automática del número de hilos a usar al empacar repositorios (bueno para computadoras multi-CPU o multi-núcleo):

git config --global pack.threads "0"

Aquí algunos alias que tengo dados de alta en mi ~/.gitconfig, ayudan a teclear menos en la terminal:

[alias]
    l = log --oneline --decorate --graph
    co = checkout
    ci = commit
    man = help
    h = help
    a = add
    f = fetch
    d = diff
    dc = diff --cached
    dt = difftool
    dtc = difftool --cached
    ds = diff --stat
    dsc = diff --stat --cached
    s = status --short --branch
    b = branch
    lg = log --graph --abbrev-commit --decorate --date=relative --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(bold yellow)%d%C(reset)'
    lgb = log --graph --abbrev-commit --decorate --date=relative --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(bold yellow)%d%C(reset)' --branches
 
[credential]
    helper = cache
 
[diff]
    algorithm = patience

Para tener un .gitignore global:

git config --global core.excludesfile ~/.gitignore_global

Para ignorar los archivos de las IDEs de JetBrains:

echo ".idea/" >> ~/.gitignore_global

Para el adecuado manejo de los comienzos de línea:

git config --global core.autocrlf input
# Configure Git on Linux to properly handle line endings

GitHub Social Code Hosting

Si tienes un proyecto open source y quieres un servicio de hospedaje de código y control de versiones premium sin pagar un centavo, prueba con GitHub.
También cuentan con soporte para proyectos privados en planes.

Tutoriales

Configuración de una aplicación Rails 4.2 tradicional

Actualmente existen dos enfoques para desarrollar una aplicación web, el tradicional, que es impulsado por frameworks como Rails y el de las JSON API + SPA.

En el enfoque tradicional tenemos todo nuestro código en una aplicación Rails. En ella toda la lógica de negocios está en los modelos, el cómo la operamos en los controladores y la interfaz de usuario en las vistas. Programamos las vistas con Slim, Ruby y Sass, usamos gemas como simple_form para crear fácilmente formularios en Ruby.

El enfoque JSON API + SPA, por otra parte, se cuenta con dos componentes: La aplicación servidor (API) y un cliente Javascript (o móvil) para la vista.

En principio, puede parecer atractiva la idea de hacer las vistas con Ruby y sus amigotes, pero la verdad es que no hay como trabajar en el ambiente natural del lenguaje donde se despliegan nuestras aplicaciones: usualmente Javascript.

En principio, también, puede parecer meh trabajar con HTML y amigos, sin embargo cuando descubres cosas como Grunt, Gulp, Yeoman, Bower, AngularJS y demás, —Y— lo que te permiten hacer en cuestión de organización de tu proceso de desarrollo, simplemente no puedes dar marcha atrás.

El desarrollo del cliente (UI) es en sí, un universo propio, y requiere de herramientas adecuadas para poderlo desarrollar de adecuadamente.

Ambos enfoques me agradan y he usado ambos en varios proyectos. Sin embargo, me siento más atraído hacia las JSON API + SPA debido a la clara separación entre front end y back end.

Preliminares

Commit inicial

Para generar el proyecto y hacer el primer commit:

rails new rails_app --database=postgresql
cd rails_app
git init
git add .
git commit -m "Commit inicial."

Configuración inicial

Agrega estas gemas a tu Gemfile:

group :development, :test do
  ...
  # Framework para pruebas
  gem 'minitest-rails'
  # Para generar objetos de prueba
  gem 'fabrication'
  # Para generar datos de prueba
  gem 'faker'
end
 
# Escribe las vistas en Slim en vez de HTML
gem 'slim-rails'
# Crea formularios de manera concisa
gem 'simple_form'

Después en la terminal:

bundle
rails g minitest:install
rails g simple_form:install

Modifica config/application.rb:

config.time_zone = 'Mexico City'
config.i18n.default_locale = 'es-MX'
 
config.generators do |g|
  g.test_framework :minitest, :spec => true, :fixture => false
end

Crea un usuario y una base de datos para esta aplicación, revisa ls guía: Cómo instalar PostgreSQL en Ubuntu

Configura el host, username y password en config/database.yml:

default: &default
  adapter: postgresql
  encoding: unicode
  host: localhost
  user: usuario_ejemplo
  password: xxx

Ejecuta en la terminal:

rake db:create

Descarga el archivo es-MX.yml y ponlo en: config/locales/es-MX.yml
Lista de localizaciones para Rails

Modifica config/initializers/inflections.rb:

ActiveSupport::Inflector.inflections(:en) do |inflect|
  inflect.clear
 
  inflect.plural(/$/, 's')
  inflect.plural(/([^aeéiou])$/i, '\1es')
  inflect.plural(/([aeiou]s)$/i, '\1')
  inflect.plural(/z$/i, 'ces')
  inflect.plural(/á([sn])$/i, 'a\1es')
  inflect.plural(/é([sn])$/i, 'e\1es')
  inflect.plural(/í([sn])$/i, 'i\1es')
  inflect.plural(/ó([sn])$/i, 'o\1es')
  inflect.plural(/ú([sn])$/i, 'u\1es')
 
  inflect.singular(/s$/, '')
  inflect.singular(/es$/, '')
 
  inflect.irregular('el', 'los')
end

Realiza el commit con los cambios en la configuración.

git add .
git ci -m "Configuración inicial."

¡Listo, a desarrollar! 😀

Recursos

Inflexiones en español para Rails
Configurar usuario y base de datos en PostgreSQL