Saltar al contenido
Development

Agregar búsqueda de imágenes de Pexels a mi blog manager

Por Victor Da Luz
railsrubypexelsdev-logblog-manager

Medium quiere una imagen de encabezado. Mi blog manager necesitaba una forma de elegir una sin salir de la app.

He estado construyendo una app en Rails que me ayuda a sindicar posts de mi blog en Astro hacia Medium y LinkedIn. El flujo de cross-posting ya estaba casi terminado; podía escanear un repositorio de GitHub en busca de archivos markdown, renderizarlos como HTML, y subirlos a Medium como borradores. Pero faltaba una pieza: Medium destaca una imagen de encabezado en la parte superior de cada post, y mi app no tenía forma de seleccionar una.

La elección obvia fue Pexels. Tienen una API limpia, un nivel gratuito generoso, y un requisito de atribución fácil de cumplir con un <figcaption>.

Lo que construí

La sección de Pexels vive en la página de detalle del post. Hay un formulario de búsqueda prellenado con el título del post, una grilla de resultados que carga sin recargar la página completa, y un botón “Select” por imagen. Al elegir una, la tarjeta del post se actualiza en el momento y el botón de importación a Medium se activa.

El enfoque de Turbo Frame hizo esto directo. El formulario de búsqueda apunta a un turbo_frame_tag "pexels_results", así que la respuesta reemplaza solo esa región de la página:

<%= form_with url: search_pexels_post_path(post), method: :get,
      data: { turbo_frame: "pexels_results" } do |f| %>
  <%= f.text_field :query, value: post.title.presence || post.slug %>
  <%= f.submit "Search" %>
<% end %>

<%= turbo_frame_tag "pexels_results" %>

Del lado del controlador, search_pexels renderiza el partial directamente dentro del frame. Rápido, sin transición de página, sin reseteo de scroll.

La particularidad de autenticación de Pexels

Todas las demás APIs que he integrado usan Authorization: Bearer {token}. Pexels no. Su header de autenticación es simplemente la API key sin adornos:

req["Authorization"] = @api_key   # correct
req["Authorization"] = "Bearer #{@api_key}"  # wrong - 401

Está documentado, pero es fácil pasarlo por alto al copiar de otro cliente.

Dónde guardar la API key

Por blog vs. global: tengo una sola cuenta de Pexels, así que hacerlo por blog significaría ingresar la misma key varias veces. En su lugar, agregué un modelo singleton AppSetting:

class AppSetting < ApplicationRecord
  encrypts :pexels_api_key

  def self.current
    first_or_create!
  end
end

Active Record Encryption (ya usado para los tokens de GitHub y Medium) maneja el cifrado en reposo. La página de configuración en /settings me da una UI para pegar la key sin tocar credentials.yml.enc.

Bloqueando la importación a Medium

El botón de importación a Medium ahora requiere que haya una imagen de Pexels seleccionada. La verificación vive tanto en la vista (tooltip deshabilitado) como en el controlador (redirect con alert); los bloqueos que solo existen en la vista se pueden saltear con un POST directo.

Cuando corre DraftCreator

DraftCreator antepone la imagen seleccionada como un <figure> antes del cuerpo HTML del post. El <figcaption> incluye la atribución del fotógrafo, cumpliendo los requisitos de Pexels. Medium lo renderiza en la parte superior del borrador.

Una trampa en los tests

minitest/mock se eliminó en Minitest 6.0.6; Minitest::Mock ya no existe. Descarté el test de controlador basado en stubs y me apoyé directamente en los tests unitarios de Pexels::Client.

Qué sigue

La acción search_pexels todavía lee de Rails.application.credentials; conectarla a AppSetting.current.pexels_api_key es el seguimiento obvio de una línea. Después de eso: cross-posting a LinkedIn, que ya tiene el token pero no el job.

Lecturas relacionadas

Development

El botón de commit del editor es un botón de deploy

Confirmar un borrador a main despliega el blog automáticamente. En cuanto eso quedó claro, sync vs. async dejó de ser una cuestión de estilo, más el caso especial de afiliado heredado que un validador nuevo casi rompió.

Leer