10. Biblioteca estándar

data:: · Índice · Siguiente: Comandos del CLI →

Métodos de listas y de cadenas, y las funciones globales disponibles en cualquier parte del script.


10.1 Métodos de lista

Recorrer

lista.each { |item| ... }

Ejecuta el bloque para cada elemento.

check "each" {
  doc.pages.each { |page|
    assert page.width > 0, "página #{page.number} sin anchura"
  }
}

lista.each_with_index { |item, i| ... }

Como each, pero el segundo parámetro recibe la posición (empezando en 0).

check "each_with_index" {
  doc.fonts.each_with_index { |font, i|
    print("fuente", i + 1, "de", doc.fonts.length, ":", font.name)
  }
}

Comprobar

lista.all { |item| ... }

Verdadero si todos los elementos cumplen la condición. Una lista vacía devuelve verdadero.

check "all" {
  require doc.fonts.all { |f| f.is_embedded }
  require doc.pages.all { |p| p.has_trim_box }
}

lista.any { |item| ... }

Verdadero si algún elemento la cumple. Una lista vacía devuelve falso.

check "any" {
  assert doc.pages.any { |p| p.extract_text() != "" },
    "el documento entero está sin texto"
}

lista.contains(valor)

Verdadero si el valor está en la lista.

check "contains" {
  require [1, 2, 3].contains(2)
  require prepress::detect_spot_colors().contains("Barniz")
}

Transformar

lista.filter { |item| ... }

Nueva lista solo con los elementos que cumplen la condición.

check "filter" {
  malas = doc.images.filter { |img| img.dpi < 300 }
  assert malas.length == 0,
    "#{malas.length} imagen(es) con resolución baja"

  // Encadenando: filtrar y después transformar
  nombres = doc.fonts.filter { |f| !f.is_embedded }.map { |f| f.name }
  print("fuentes sueltas:", nombres.join(", "))
}

lista.map { |item| ... }

Nueva lista con el resultado del bloque para cada elemento.

check "map" {
  numeros = doc.pages.map { |p| p.number }
  anchuras = doc.pages.map { |p| p.width }
  print("páginas:", numeros.join(", "))
}

Acceder

lista.length

Cantidad de elementos. Funciona como propiedad o como método: lista.length y lista.length() son equivalentes.

check "length" {
  require doc.pages.length == doc.page_count
  print("fuentes:", doc.fonts.length)
}

lista.get(n)

El enésimo elemento, 1-based. Da un error claro si el índice no existe.

check "get" {
  fila = data::load_dataset("datos/lotes.csv").get(2)   // segunda fila
  print("primera columna:", fila.get(1))
}

lista.first() y lista.last()

Primer y último elemento. Devuelven null en una lista vacía (sin error).

check "first y last" {
  primera = doc.pages.first()
  ultima = doc.pages.last()
  print("de la página", primera.number, "a la", ultima.number)

  // Seguro en lista vacía: null es falso
  spots = prepress::detect_spot_colors()
  assert !spots.first() || spots.first() == "Barniz",
    "tinta especial inesperada: #{spots.first()}"
}

lista.join([separador])

Une los elementos en un texto. Separador por defecto: ", ".

check "join" {
  print(doc.fonts.map { |f| f.name }.join(", "))
  print(prepress::get_page_boxes(1).join(" | "))
  print([1, 2, 3].join(" -> "))
}

10.2 Métodos de cadena

Método Qué hace
texto.contains(sub) ¿Contiene el fragmento?
texto.starts_with(sub) ¿Empieza por?
texto.ends_with(sub) ¿Termina en?
texto.trim() Quita los espacios de los extremos
texto.to_uppercase() Todo en mayúsculas
texto.to_lowercase() Todo en minúsculas
texto.length Cantidad de caracteres
check "Métodos de cadena" {
  titulo = doc.title

  require titulo.length > 0
  require titulo.trim() == titulo          // sin espacios sobrantes
  assert !titulo.to_lowercase().contains("borrador"),
    "el título sigue marcado como borrador"

  codigo = codes::decode_barcode(1)
  assert codigo.starts_with("789"), "GTIN no brasileño"

  archivo = doc.filename
  assert archivo.ends_with(".pdf"), "extensión inesperada"
}

Diferencia entre el contains de cadena y el de lista:

check "contains en cada tipo" {
  // cadena: busca un FRAGMENTO dentro del texto
  require "documento final".contains("final")

  // lista: busca un ELEMENTO entero
  require ["a", "b"].contains("a")
  require !["ab"].contains("a")      // "a" no es un elemento de la lista
}

10.3 Funciones globales

min(a, b) y max(a, b)

check "min y max" {
  anchuras = doc.pages.map { |p| p.width }
  // Reduciendo una lista con each
  menor = 99999
  doc.pages.each { |p| menor = min(menor, p.width) }
  print("página más estrecha:", menor, "pt")
}

abs(x)

Valor absoluto: imprescindible para comparar dimensiones con tolerancia.

check "abs" {
  const A4_ANCHURA = 595.0
  const TOLERANCIA = 5.0

  doc.pages.each { |page|
    // "la diferencia, por arriba o por abajo, es menor que la tolerancia"
    assert abs(page.width - A4_ANCHURA) < TOLERANCIA,
      "la página #{page.number} está fuera del A4: #{page.width}pt"
  }
}

round(x)

Redondea al entero más próximo. Útil para que los mensajes queden legibles.

check "round" {
  doc.images.each { |img|
    // sin round: "217.4453125 DPI" — con round: "217 DPI"
    assert img.dpi >= 300,
      "imagen en la página #{img.page_number}: #{round(img.dpi)} DPI"
  }

  mb = struct::file_size() / 1024 / 1024
  print("tamaño:", round(mb), "MB")
}

print(...)

Imprime los valores separados por espacios. Sale por stderr, así que no ensucia el informe del stdout: puedes usar > informe.json sin mezclarlos.

check "print" {
  print("documento:", doc.filename)
  print("páginas:", doc.page_count, "| fuentes:", doc.fonts.length)

  // Útil para investigar antes de escribir la validación definitiva
  doc.images.each { |img|
    print("imagen", img.width, "x", img.height, "@", round(img.dpi), "DPI")
  }
}

region(x, y, anchura, altura [, nombre])

Crea una región. Documentada en el capítulo 2.


10.4 Patrones útiles

Contar cuántos elementos fallan

check "Recuento de problemas" {
  malas = doc.images.filter { |i| i.dpi < 300 }
  assert malas.length == 0,
    "#{malas.length} de #{doc.images.length} imágenes por debajo de 300 DPI"
}

Listar en el mensaje los elementos que fallaron

check "Lista en el mensaje" {
  // Los encadenamientos van en la misma línea: el punto tiene que venir
  // justo después del valor anterior, sin salto de línea en medio.
  problemas = doc.pages.filter { |p| !p.has_trim_box }.map { |p| p.number }

  assert problemas.length == 0,
    "páginas sin TrimBox: #{problemas.join(", ")}"
}

Validar con tolerancia

function cerca_de(valor, objetivo, tolerancia) {
  abs(valor - objetivo) < tolerancia
}

check "Con tolerancia" {
  doc.pages.each { |page|
    assert cerca_de(page.width, 595.0, 2.0),
      "página #{page.number}: anchura #{page.width}pt (se esperaba 595 ± 2)"
  }
}

Evitar errores en un documento vacío

check "Defensivo" {
  // El cortocircuito evita evaluar first() en una lista vacía
  assert doc.pages.length == 0 || doc.pages.first().width > 0,
    "la primera página no tiene anchura"
}

data:: · Índice · Siguiente: Comandos del CLI →