Microservicio de noticias en Rails 5. Parte 1
Rails 5 está pronto a salir y quiero aprovechar de documentar el desarrollo de un microservicio de noticias con el cual queremos entregar algunas informaciones en nuestra plataforma Bambinotes.
La idea es que los roles de usuarios de Bambinotes estén suscritos a un canal de noticias con el que puedan revisar algunos detalles de ésta, y que posteriormente puedan navegar a un blog más profesional si es necesario.
El repositorio del proyecto está en GitHub: https://github.com/lomefin/nms
Asumiremos que estamos desde un computador sin Rails 5, pero con RVM y Postgres ya instalado.
Primero, instalar ruby 2.3.1 -el más reciente al momento de escribir este artículo.
rvm use 2.3.1@nms --create
Luego instalar rails 5 (5.0.0.rc1)
gem install rails --pre --no-ri --no-rdoc
Luego que se instale todo eso, creo el nuevo proyecto llamado nms
.
rails new nms --database=postgresql
Con el nuevo proyecto, y ya configurado para Postgres, debo crear las bases de datos.
createdb nms_development
createdb nms_test
Luego agregué Pry para tener una mejor consola y rails_12factor para integrarme con Heroku
group :development do
gem 'pry-rails'
end
group :production do
gem 'rails_12factor'
end
Vamos a dejar todo listo para Heroku de una vez, eso significa tener un Procfile funcionando.
Procfile
web: bundle exec puma -t 5:5 -p ${PORT:-3000} -e ${RACK_ENV:-development}
Ahora hacemos bundle update y luego heroku local y tendremos una aplicación lista para andar. En paralelo abro una nueva terminal para tener la consola andando a su vez con rails c.
Luego vamos a crear el objeto Post que tendrá las noticias, siempre prefiero usar la clase News, pero como es un término plural se enreda todo, aparte Post es un elemento más genérico en los ejemplos.
rails g model Post channel:string uuid:uuid options:json message:text title:text
published_at:datetime meta:json url:string
Una de las novedades de Rails 5 es que el uso de rake disminuye en favor del uso de rails, por lo tanto para hacer esta migración hacemos
rails db:migrate
Ahora a implementarle algunos scopes.
class Post < ApplicationRecord
scope :of_channel, -> (channel) { where(channel: channel) }
scope :published, -> { where.not(published_at: nil) }
scope :incremental, -> { order(published_at: :desc) }
end
Listo nuestro modelo! Por ahora estaré usando el scope :published que luego será reemplazado por AASM. Ahora los controladores (en verdad, EL controlador)
rails g controller posts
El único controlador que tendremos hasta el momento será post_controller. Nota: En el código habrá un controlador de administración, pero no hablaré de él aún hasta que tenga una implementación definida.
Nuestro controlador tendrá esta forma
1
2
3
4
5
6
7
8
9
10
11
class PostsController < ApplicationController
def index
@posts = Post.of_channel(params[:channel]).published.incremental
end
def show
@post = Post.of_channel(params[:channel]).published.find_by_uuid params[:uuid]
end
end
Luego configuraremos las rutas para que podamos llegar a estos objetos.
1
2
3
4
5
Rails.application.routes.draw do
scope ':channel' do
resources :posts, only: [:show, :index], param: :uuid
end
end
Ya tenemos las rutas definidas, podemos revisarla en la consola de pry usando el comando show-routes.
Prefix Verb URI Pattern Controller#Action
posts GET /:channel/posts(.:format) posts#index
post GET /:channel/posts/:uuid(.:format) posts#show
admin_posts GET /admin/posts(.:format) admin/posts#index
POST /admin/posts(.:format) admin/posts#create
new_admin_post GET /admin/posts/new(.:format) admin/posts#new
edit_admin_post GET /admin/posts/:uuid/edit(.:format) admin/posts#edit
admin_post GET /admin/posts/:uuid(.:format) admin/posts#show
PATCH /admin/posts/:uuid(.:format) admin/posts#update
PUT /admin/posts/:uuid(.:format) admin/posts#update
DELETE /admin/posts/:uuid(.:format) admin/posts#destroy
Finalmente, para tener lista la lectura de los posts por canal implementamos las vistas tanto en html como en json.
index.html.erb
1
2
3
4
<h1><%= params[:channel] %></h1>
<ul class="nms-post-list">
<%= render partial: "post", collection: @posts %>
</ul>
_post.html.erb
1
2
3
4
5
<li class="nms-post-list-item">
<%= link_to post_path(uuid: post.uuid,channel: post.channel) do %>
<%= post.title %>
<% end %>
</li>
show.html.erb
1
2
3
4
5
6
7
8
9
10
11
<article class="nms-post" data-uuid="<%= @post.uuid %>">
<h1 class="nms-post-title"><%= @post.title %></h1>
<p class="nms-post-header">
<small class="nms-post-publication-date"
data-value="<%= @post.published_at %>">
<%= l @post.published_at %>
</small>
</p>
<p class="nms-post-message"><%= @post.message %></p>
</article>
index.json.jbuilder
json.array! @posts do |post|
json.uuid post.uuid
json.title post.title
json.message post.message
end
show.json.jbuilder
1
2
3
4
5
6
json.uuid @post.uuid
json.title @post.title
json.message @post.message
json.published_at @post.published_at
json.channel @post.channel
json.meta (if @post.meta then @post.meta else {} end)
Para hacer una prueba rápida crearemos un Post en la consola y veremos que entrega.
1
2
3
Post.create channel: "BNS", uuid: SecureRandom.uuid,
message: "Welcome to NMS, the new News Microservice",
title: "Welcome to NMS", published_at: Time.zone.now
Con esto ya podemos comenzar a ver el listado en ambos formatos y sus detalles. Seguiremos con el sistema para agregar los posts en la parte 2.