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