Saltar para a documentação
Ruby

Paginação

Uma página, todas as páginas ou um item de cada vez, em todas as listas paginadas.

list, list_all e iterate

Todas as listas paginadas têm três métodos. list obtém uma página e devolve uma OpenEmail::Page. list_all segue o cursor por todas as páginas e devolve um único Array. iterate percorre as mesmas páginas um item de cada vez: passa cada item a um bloco, ou devolve um Enumerator quando não lhe dá nenhum. Os três aceitam os filtros da lista, limit:, cursor: e api_key:.

three_ways.rb
page = client.emails.list(status: "failed", limit: 50)page.items.each { |email| puts "#{email[:id]} #{email[:lastError]}" } failures = client.emails.list_all(status: "failed") client.emails.iterate(status: "failed") do |email|  puts email[:id]end puts failures.size, page.has_more?

Os mesmos três nomes repetem-se sempre que um espaço de nomes tem mais do que uma lista, com o nome da lista que percorrem: list_events, list_all_events e iterate_events em emails, list_deliveries, list_all_deliveries e iterate_deliveries em webhooks, e assim por diante.

OpenEmail::Page

itemsArray<Hash>
As linhas desta página, retiradas do envelope `data` da API, cada uma um Hash com chaves Symbol. Vazio quando a página não contém nada.
has_more?Boolean
Se há outra página a seguir. `has_more` sem o ponto de interrogação lê o mesmo valor. Quando a API não envia `hasMore`, é true exatamente quando existe um `next_cursor`.
next_cursorString or nil
O que deve devolver como `cursor:` para obter a página seguinte, e nil na última.

Uma página é um objeto Data do Ruby, por isso está congelada, compara-se por valor e converte-se num Hash com to_h.

Um bloco ou um Enumerator

Com um bloco, iterate percorre todas as páginas nesse momento e passa-lhe cada item. Sem bloco, devolve um Enumerator e não obtém nada até o consumir. Em ambos os casos só pede a página seguinte depois de entregues todos os itens da atual, por isso tudo o que para mais cedo também para os pedidos: first(10) só lê as páginas de que dez itens precisam, find para na correspondência e break num bloco termina o percurso.

enumerator.rb
latest = client.emails.iterate(status: "failed", limit: 100).first(10) invoice = client.emails.iterate(status: "failed").find do |email|  email.dig(:tags, :invoice) == "inv_2026_09_4192"end from_api = client.emails.iterate(status: "bounced").lazy.select { |email| email[:source] == "api" }.first(5) p latest.size, invoice&.fetch(:id), from_api.map { |email| email[:id] }

Um método de Enumerable que precisa de todos os itens, como select, map ou count chamado diretamente sobre o Enumerator, lê todas as páginas antes de devolver, tal como list_all. Ponha lazy à frente para os encadear e continuar a poder parar mais cedo.

Um Enumerator recomeça o seu percurso cada vez que é consumido, por isso chamar first(10) duas vezes sobre o mesmo obtém a primeira página duas vezes. Guarde o resultado, e não o Enumerator, quando precisar dele de novo.

Retomar a partir de um cursor

Um cursor é opaco. Guarde o next_cursor da última página que leu e devolva-o como cursor: para continuar a partir daí, num pedido posterior ou noutro processo. list_all e iterate também aceitam cursor: e começam o seu percurso a seguir a ele.

resume.rb
first_page = client.emails.list(status: "failed", limit: 25)saved = first_page.next_cursor if saved  rest = client.emails.list_all(status: "failed", cursor: saved)  puts rest.sizeend

Um cursor pertence à lista e aos filtros de onde veio, por isso envie os mesmos filtros com ele. Um que a lista não consiga situar é recusado com invalid_cursor, e a solução é então recomeçar sem cursor.

limit:

limit: é o tamanho de cada página, não um total. Em list é quantas linhas voltam. Em list_all e iterate é quantas cada pedido pede, por isso um valor maior significa menos idas e voltas para as mesmas linhas. Cada lista tem o seu próprio intervalo e valor predefinido, quase sempre de 1 a 100 com 25 se não enviar nenhum, e um valor fora do intervalo é recusado em vez de ajustado. A página de cada lista indica o seu intervalo.

Quando um percurso para

  • Quando uma página diz que has_more? é false.
  • Quando uma página não traz next_cursor, já que uma página que diz haver mais sem indicar nenhum cursor ficaria em ciclo para sempre.
  • Quando a API devolve o mesmo cursor que acabou de receber, pelo mesmo motivo.

Cada página é um GET, por isso é repetida por si só, como qualquer leitura, antes de se lançar o que quer que seja. Uma falha que sobreviva às repetições é lançada a partir de list_all, e os itens já obtidos são descartados. Em iterate, os itens das páginas anteriores já foram entregues nessa altura, por isso torne seguro executar duas vezes o que o bloco faz, ou pagine com list e guarde cada next_cursor para que uma segunda tentativa possa começar onde a primeira parou.

Conversas e rascunhos

threads.list e drafts.list, com os seus list_all e iterate, paginam com o pageToken e o nextPageToken da API em vez de um cursor. A gem esconde a diferença: passe o token como cursor: e leia-o de next_cursor.

page_token.rb
page = client.threads.list(folder: "inbox", limit: 50)later = client.threads.list(folder: "inbox", limit: 50, cursor: page.next_cursor) if page.has_more? p page.items.size, later&.items&.size

O servidor oferece um token sempre que uma página volta cheia, por isso has_more? pode ser true naquela que acaba por ser a última página, e a chamada seguinte não devolve então nenhum item.

Páginas que trazem mais

Algumas listas respondem com mais do que linhas, e devolvem um objeto Data próprio em vez de OpenEmail::Page.

MétodoDevolveO que acrescenta
addresses.listOpenEmail::AddressBookPageaddresses em vez de items, mais unrestricted e domains, com has_more? e next_cursor.
addresses.list_allOpenEmail::AddressBookTodos os endereços em addresses, com unrestricted e domains tal como a última página os indicou. É o único list_all que devolve o livro de endereços inteiro em vez de um Array. addresses.iterate entrega apenas os endereços.
contacts.list_peopleOpenEmail::PeoplePageseen, false quando a chave não consegue ler os endereços vistos no correio. list_all_people e iterate_people devolvem apenas as pessoas.
temp_mail.list_messagesOpenEmail::TempMessagesPageexpires_at, o momento em que a caixa expira. list_all_messages e iterate_messages devolvem apenas as mensagens.
templates.list_sendsOpenEmail::TemplateSendsPaginado por número em vez de por cursor: items, total, page e page_size. Peça a página seguinte com page:.
emails.send_batchOpenEmail::BatchResultNão é uma página: items, um para cada mensagem que enviou, com as contagens sent e failed.

Listas que devolvem o Hash da API

Algumas listas paginam por deslocamento, por número de página ou por um cursor numérico próprio, e devolvem o corpo analisado tal como chegou, um Hash com data, em vez de uma OpenEmail::Page. Não têm list_all nem iterate, por isso tem de as paginar você mesmo.

MétodoPagina comO que volta
exports.listlimit: e offset:data, total e hasMore.
imports.list_failuresafter: e limit:data, e nextCursor, um Integer a devolver como after: que é nil na última página.
subscriptions.list e subscriptions.list_domainslimit: e offset:data, total, counts e hasMore.
billing.list_invoicespage: e limit:data, total, page, limit, hasMore e metered.
offset_paging.rb
offset = 0 loop do  batch = client.subscriptions.list(status: "active", limit: 50, offset:)  batch[:data].each { |row| puts "#{row[:senderEmail]} #{row[:total]}" }   break unless batch[:hasMore]   offset += batch[:data].sizeend

Uma lista que não é paginada, como languages.list, labels.list_colors ou roles.list_permissions, devolve logo um Array.