Desarrolle un cliente API en Ruby puro (sin frameworks) aplicando SOLID en cada clase: Single Responsibility con clases separadas para HTTP, paginacion, formateo y manejo de errores. Incluye retry con exponential backoff (3 intentos con delay incremental), jerarquia de excepciones custom para cada tipo de error, y suite completa de tests con Minitest + WebMock que mockean todas las llamadas HTTP sin depender de la API real.
EasyBrokerClient: clase principal que expone metodos de alto nivel — list_properties(limit:), get_property(id:), search_properties(query:) — delegando la logica HTTP a clases internas
HttpClient: wrapper sobre Net::HTTP que centraliza headers (X-Authorization, Content-Type, Accept), base URL y manejo de respuestas — una sola clase responsable de la comunicacion HTTP
Paginacion automatica: el metodo list_properties itera sobre todas las paginas de la API siguiendo el campo next_page de la respuesta, acumulando resultados hasta completar el limit o agotar paginas
Serializacion de respuestas: cada response de la API se parsea a objetos Ruby con atributos tipados (Property, PropertyList) en vez de trabajar con hashes crudos
Retry con exponential backoff: ante errores 5xx o timeouts, reintenta la request hasta 3 veces con delays de 1s, 2s, 4s — evita sobrecargar la API en momentos de degradacion
Backoff configurable: el delay base, multiplicador y maximo de reintentos se pueden ajustar por instancia del cliente
Circuit breaker basico: si los 3 reintentos fallan, lanza la excepcion original con contexto del intento fallido para debugging
Jerarquia de excepciones custom: EasyBrokerError (base) → AuthenticationError (401), NotFoundError (404), RateLimitError (429), ServerError (5xx), TimeoutError
Cada excepcion incluye el status code HTTP, el body de la respuesta y un mensaje descriptivo — facilita el debugging sin inspeccionar logs de red
Manejo diferenciado: errores 4xx no se reintentan (son del cliente), solo errores 5xx y timeouts activan el retry
Single Responsibility: HttpClient (HTTP), Paginator (paginacion), PropertyFormatter (formateo de output), ErrorHandler (mapeo de errores)
Open/Closed: nuevos endpoints se agregan creando metodos en el cliente sin modificar las clases de infraestructura
Dependency Inversion: el cliente recibe el HttpClient por constructor, permitiendo inyectar un mock en tests
Interface Segregation: cada clase expone solo los metodos que sus consumidores necesitan — Paginator no conoce los detalles de HTTP
Suite completa con Minitest: tests unitarios para cada clase del cliente, validando happy paths y edge cases
WebMock para stubbing de HTTP: cada test registra respuestas predefinidas sin hacer llamadas reales a la API de EasyBroker
Tests de retry: verifica que el cliente reintenta ante 503, que respeta el backoff, y que lanza excepcion tras agotar intentos
Tests de paginacion: simula respuestas con next_page para verificar que el cliente recorre todas las paginas correctamente
Tests de errores: verifica que cada status code HTTP se mapea a la excepcion custom correspondiente
PropertyFormatter: formatea propiedades para output en consola con titulo, precio, ubicacion y URL — util para scripts CLI
Script ejecutable: ruby main.rb lista las primeras 15 propiedades publicadas formateadas en tabla
FLUJO — Listado con paginacion automatica: [main.rb] EasyBrokerClient.new(api_key) → client.list_properties(limit: 15) → [Paginator] GET /properties?page=1&limit=4 → response {content: [...], pagination: {next_page: "/properties?page=2"}} → acumula 4 propiedades → GET next_page → acumula 4 mas (total 8) → GET next_page → acumula 4 mas (total 12) → GET next_page → acumula 3 mas (total 15, alcanza limit) → retorna array de 15 Property objects
FLUJO — Retry con exponential backoff: [HttpClient] GET /properties → response 503 Service Unavailable → intento 1 fallido → sleep(1s) → GET /properties → response 503 → intento 2 fallido → sleep(2s) → GET /properties → response 200 OK → exito, retorna body parseado. Si el 3er intento tambien falla: sleep(4s) → GET → 503 → lanza ServerError con mensaje "3 intentos agotados"
FLUJO — Mapeo de errores por status code: [HttpClient] recibe response → [ErrorHandler] evalua status → 200-299: retorna body → 401: lanza AuthenticationError("API key invalida") → 404: lanza NotFoundError("Propiedad no encontrada") → 429: lanza RateLimitError("Limite de requests excedido") → 500-599: lanza ServerError("Error del servidor") + activa retry → Timeout: lanza TimeoutError + activa retry
FLUJO — Request completo: [EasyBrokerClient] llama metodo de negocio → [HttpClient] construye request (URL + headers + API key) → Net::HTTP.get(uri) → [ErrorHandler] evalua response → si error retriable: [RetryStrategy] aplica backoff → si error de cliente: lanza excepcion inmediata → si exito: JSON.parse(body) → [Serializer] mapea a Property/PropertyList objects → retorna al caller
FLUJO — Test con WebMock: [Test setup] WebMock.stub_request(:get, "api.easybroker.com/properties").to_return(status: 200, body: fixture.json) → [Test] client.list_properties(limit: 4) → HttpClient hace GET real → WebMock intercepta y retorna fixture → assertions sobre el resultado → WebMock.assert_requested verifica que la request se hizo con los headers correctos