#320 Jbuilder
- Download:
- source codeProject Files in Zip (102 KB)
- mp4Full Size H.264 Video (20.9 MB)
- m4vSmaller H.264 Video (10.5 MB)
- webmFull Size VP8 Video (11.6 MB)
- ogvFull Size Theora Video (22.7 MB)
Si echamos un vistazo al Gemfile
de una aplicación Rails 3.2 veremos que en los comentarios se hace mención a una gema que no aparecía en las versiones anteriores, llamada Jbuilder.
# To use Jbuilder templates for JSON # gem 'jbuilder'
Jbuilder es un motor de plantillas que sirve para generar respuestas en JSON. Fue recientemente creada por David Heinemeier Hansson, pero en lugar de incluirla en Rails 3.2 David prefirió publicarla por separado en forma de gema. Este enfoque permite su uso con versiones anteriores. Jbuilder proporciona un DSL para generar ficheros JSON de forma similar a XML Builder, y en este episodio veremos cómo se usa.
Respuestas JSON desde la aplicación
Como demonstración de Jbuilder vamos a utilizar una aplicación sencilla de blog. La aplicación dispone de varios artículos, y querríamos proporcionar una representación en JSON de dichos artículos simplemente añadiendo .json
a la URL de cada artículo. Si ahora mismo hacemos esto veremos que aparece un error porque no hemos añadido esta funcionalidad.
Se puede añadir esta funcionalidad sin emplear Jbuilder añadiendo un bloque respond_to
a la acción show
en ArticlesController
.
def show @article = Article.find(params[:id]) respond_to do |format| format.html format.json { render json: @article } end end
Al recargar la página veremos la representación en JSON del artículo.
Personalización de la respuesta
El JSON que se devuelve incluye todos los atributos del artículo pero, ¿y si queremos personalizarlos? Aquí es donde se nos complican las cosas. Podemos invocar as_json
en el artículo para controlar lo que se devuelve. Supongamos que queremos mostrar los campos id
, name
y content
del artículo, así como su autor y los mismo tres campos de los comentarios.
def show @article = Article.find(params[:id]) respond_to do |format| format.html format.json { render json: @article.as_json(only: [:id, :name, :content], include: [:author, {comments: {only:[:id, :name, :content]}}]) } end end
Probémoslo recargando la página. Al hacerlo veremos la nueva respuesta en JSON que hemos personalizado incluyendo los registros Author
y Comment
.
Uso de Jbuilder
Aunque esto funciona el código que hemos terminado usando no es muy bonito. Podríamos redefinir as_json
en el modelo, pero tampoco quedaría mucho mejor. Aquí es donde entra en juego Jbuilder. Para su instalación simplemente tenemos que quitar el comentario en la línea correspondiente del Gemfile
y ejecutar bundle
.
# To use Jbuilder templates for JSON gem 'jbuilder'
Volviendo al controlador podemos eliminar la llamada a respond_to
y volver al comportamiento por defecto, que es buscar la plantilla correspondiente al formato solicitado.
def show @article = Article.find(params[:id]) end
A continuación vamos a crear una plantilla JSON en el directorio /app/views/articles
, en la que podremos usar código Ruby para definir la salida JSON. Tenemos acceso a un objeto json
sobre el que podemos definir de la siguiente manera:
json.id @article.id json.name @article.name
Tenemos que reiniciar el servidor después de haber instalado la gema, tras lo cual podremos recargar la página para ver la salida que hemos preparado.
Puede resultar complicado enumerar cada atributo por separado de esta forma. En vez de esto podemos llamar a extract!
sobre el objeto JSON y pasar el objeto y una lista de los métodos o atributos que queramos recuperar.
json.extract! @article, :id, :name, :published_at
Disponemos de una sintaxis alternativa para hacer esto:
json.(@article, :id, :name, :published_at)
Esto sólo funciona en Ruby 1.9 porque invoca a call
sobre el objeto en segundo plano pasándole el método extract!
. Una de las ventajas de mostrar JSON en la plantilla de la vista de esta forma es que tenemos acceso a los métodos helper, lo cual es especialmente útil para mostrar URLs. Supongamos que sólo queremos incluir la URL de edición en el JSON si el usuario es administrador. También tendremos acceso al método current_user
si la solución de autenticación que estamos usando lo incluye como método helper.
json.(@article, :id, :name, :published_at) json.edit_url edit_article_url(@article) if current_user.admin?
Como el usuario que tiene sesión iniciada es administrador, el JSON debería mostar el enlace.
Anidamiento
En nuestra aplicación un Article
pertenece a un Author
. Si queremos incluir los atributos de un autor, una de las formas de hacerlo es la siguiente:
json.(@article, :id, :name, :published_at) json.edit_url edit_article_url(@article) if current_user.admin? json.author @article.author, :id, :name
Con esto anidaremos los atributos de Author
tal y como queremos.
Podemos hacer cosas mas complejas, supongamos que queremos asignar una URL al autor, podemos pasar un bloque al author
de la siguiente forma:
json.(@article, :id, :name, :published_at) json.edit_url edit_article_url(@article) if current_user.admin? json.author do |json| json.(@article.author, :id, :name) json.url author_url(@article.author) end
Si ahora recargamos la página veremos los anidados los atributos del autor, incluyendo la URL.
Podemos hacer lo mismo con las asociaciones has_many
. Por ejemplo un artículo tiene muchos comentarios, y podemos añadirlos directamente y mostrar los atributos que queramos que sean visibles.
json.(@article, :id, :name, :published_at) json.edit_url edit_article_url(@article) if current_user.admin? json.author do |json| json.(@article.author, :id, :name) json.url author_url(@article.author) end json.comments @article.comments, :id, :name, :content
Con esto también se incluyen los comentarios.
El enfoque es un poco diferente si tenemos que usar la sintaxis de bloque porque tenemos un array de comentarios y tenemos que iterar sobre cada elemento. Lo que hacemos es pasar los objetos json
y comment
al bloque lo que nos dará el acceso necesario:
json.(@article, :id, :name, :published_at) json.edit_url edit_article_url(@article) if current_user.admin? json.author do |json| json.(@article.author, :id, :name) json.url author_url(@article.author) end json.comments @article.comments do |json, comment| json.(comment , :id, :name, :content) end
Esto hace básicamente lo mismo que el código anterior.
Parciales
Al estar rellenando el bloque de comentarios con bastante detalle puede ser que nos interese duplicar esta funcionalidad en algún otro sitio, para lo que podemos usar parciales. Funcionan de forma similar a las vistas normales, tenemos que llamar a partial!
en el objeto json
y pasar la ruta al parcial o tan sólo un objeto, en este caso un comentario.
json.comments @article.comments do |json, comment| json.partial! comment end
Esto hará que se busque en el directorio app/views/comments
un parcial llamado _comment.json.jbuilder
. En este parcial tenemos acceso al mismo objeto json
y podemos hacer lo mismo que haríamos en el bloque de comentario. También tenemos acceso al objeto comentario porque también lo hemos pasado en la llamada a partial!
.
json.(comment, :id, :name, :content)
Esto mostrará el mismo JSON que teníamos antes.
Alternativas
Jbuilder no es la única gema que hace este tipo de cosas. Al final de su README se enumeran varias alternativas a considerar. RABL es la más popular, por lo que la veremos en un próximo episodio.