Criando eventos recorrentes com Recurrence


Leia em 2 minutos

Recurrence é uma biblioteca criada para gerar eventos recorrentes de maneira simples. Eu criei essa gem há tempos, mas nunca escrevi nada sobre ela. Eis que muitas pessoas começaram a me mandar e-mails perguntando como utilizá-la e chegou a hora de fazer um artigo mostrando seu uso na prática.

A primeira coisa que você precisa fazer é instalar a gem.

sudo gem install recurrence

Depois, basta adicionar à biblioteca.

require "rubygems"
require "recurrence"

A maneira mais simples de usar é instanciar um objeto através dos métodos de classe daily, weekly, monthly e yearly, que permitem criar eventos diários, semanais, mensais e anuais, respectivamente.

r = Recurrence.daily

Para retornar a próxima data, você deve utilizar o método Recurrence#next.

r.next
#=> Fri, 18 Dec 2009

r.next
#=> Fri, 18 Dec 2009

Você também pode utilizar o método Recurrence#next!, que altera a variável interna de data, como mostra o exemplo abaixo.

r.next!
#=> Fri, 18 Dec 2009

r.next!
#=> Sat, 19 Dec 2009

Se você quiser, pode especificar o intervalo de dias que o evento pode se repetir; basta utilizar a opção :starts e :ends.

# define the starting date to 2009-12-24
r = Recurrence.daily(:starts => Date.new(2009, 12, 24))

# define the ending date to 2009-12-31
r = Recurrence.daily(:ends => Date.new(2009, 12, 31))

Você também pode passar essas datas como strings, desde que elas sejam interpretadas pelo método Date#parse.

r = Recurrence.daily(:starts => "2009-12-24")

Os atalhos são uma excelente maneira de tornar o código mais semântico, mas em alguns casos é melhor seguir com o bom e velho método new.

r = Recurrence.new(:every => :week, :on => :saturday, :interval => 2)

Como você pode ver no exemplo acima, estamos criando um evento semanal, que acontece aos sábados, a cada duas semanas. Ao utilizar o método Recurrence#next! você terá algo como isso:

r.next!
#=> Sat, 19 Dec 2009

r.next!
#=> Sat, 02 Jan 2010

Você também pode especificar uma lista com os dias da semana que o evento deve ocorrer.

r = Recurrence.weekly(:on => [:saturday, :sunday])

r.next!
#=> Sat, 19 Dec 2009

r.next!
#=> Sun, 20 Dec 2009

r.next!
#=> Sat, 26 Dec 2009

Para acessar uma lista com todos os eventos disponíveis, basta utilizar o método Recurrence#events.

r.events
#=> [Sun, 20 Dec 2009, Sun, 27 Dec 2009]

Esse método fará irá guardar a lista de datas geradas, aumentando a performance se ele for acessado mais de uma vez. Se você quiser regerar essas datas, pode utilizar o método Recurrence#events!.

Também é possível verificar se uma data está no intervalo especificado no objeto Recurrence.

r = Recurrence.weekly(:on => :saturday)

r.include? "2009-12-19"
#=> true

Como usar na prática

Como você pode ver, o Recurrence é totalmente parametrizado. Essa abordagem permite que você armazene os itens necessários para montar sua recorrência em um banco de dados, por exemplo. Digamos que você tenha um modelo Event, com o seguinte schema:

class CreateEvents < ActiveRecord::Migration
  def self.up
    create_table :events do |t|
      t.string :title,        :null => false
      t.binary :meta,         :null => false
      t.date   :scheduled_to, :null => false
    end
  end
end

Os atributos que serão utilizados no Recurrence são :meta e :scheduled_to. O primeiro irá armazenar o tipo de recorrência e as informações como dia, intervalo, etc. Já o segundo, irá armazenar a próxima data do evento, para diminuir a geração de datas. O modelo Event pode armazenar os atributos como este exemplo:

class Event < ActiveRecord::Base
  serialize :meta, Hash

  validates_presence_of :meta, :title, :scheduled_to
end

Veja um exemplo de como cadastrar um evento “Pagamento”, que acontece todo mês no dia 5.

options = {:every => :month, :on => 5}
r = Recurrence.new(options)

event = Event.new(:title => "Pagamento", :meta => options, :scheduled_to => r.next)
event.save!

Na hora de montar o objeto de recorrência, você pode utilizar algo como isto:

event = Event.first

r = Recurrence.new(event.meta.merge(:starts => event.scheduled_to))
r.next
#=> Tue, 05 Jan 2010

Se você precisar exibir um evento mais de uma vez, utilize este mesmo processo e exiba os eventos com o método events.

Finalizando

Criar eventos recorrentes nem sempre é uma tarefa simples, principalmente no que diz respeito à performance. O José Valim fez uma série de contribuições que ajudaram a melhor esse ponto, além de outras funcionalidades como os atalhos.

Para conhecer mais sobre as opções disponíveis do Recurrence, acesse o README do projeto no Github.