Exibindo calendários com o plugin has_calendar no Ruby on Rails


Leia em 2 minutos

O novo Spesa está sendo desenvolvido em ritmo acelerado — isso significa que ando trabalhando muito nas minhas horas vagas — e diversas funcionalidades que estou implementando estão sendo extraídas na forma de plugins.

O mais recente deles é o has_calendar, que permite exibir calendários com eventos de maneira muito simples.

A minha idéia, já que não uso Windows nem para desenvolvimento, nem para (sic) produção, foi utilizar o comando cal, disponível em sistemas *nix, evitando toda a complexidade de ter que se trabalhar com datas. Para deixar explicíto, este plugin não irá funcionar no Windows.

Usando o plugin

Primeiro, você terá que instalar o plugin. Para isso, execute o comando script/plugin install git://github.com/fnando/has_calendar.git.

Se você quiser exibir um calendário sem eventos, pode simplesmente chamar o helper calendar.

<%= calendar %>

Se quiser especificar um mês específico, pode passar um hash com algumas opções.

<%= calendar :year => 2008, :month => 9 %>

O dia atual é diferenciado e não exibe o número; em vez disso o texto "TODAY" é colocado no lugar. Se quiser substituir esta mensagem, utilize a opção :today.

<%= calendar :today => 'HOJE' %>

Para adicionar eventos, você deve passar um bloco, que receberá um objeto Date referente ao dia do calendário. Assim, você pode realizar consultas relacionadas a esta data e exibí-las da maneira que achar melhor.

<% calendar do |date| %>
  <% for schedule in Schedule.by_date(date) %>
    <%= link_to schedule.title, schedule_path(schedule) %>
  <% end %>
<% end %>

Como você pode perceber, isso faria até 31 consultas ao banco de dados (uma para cada dia da semana) se você não otimizasse seu código (embora eu tenha escrito o artigo assim para facilitar, minha idéia de uso ia ser totalmente diferente). Pensando nisso após ler o comentário do Carlos, decidi que seria melhor implementar uma forma onde apenas o resultado de uma consulta fosse informado.

No template, você pode definir a opção :events, passando o resultado de sua consulta. O bloco, que antes recebia a data, agora receberá também todos os registros específicos daquele dia.

<% calendar :events => Schedule.all, :field => :scheduled_at do |date, events| %>
  <% for schedule in events %>
    <%= link_to schedule.title, schedule_path(schedule) %>
  <% end %>
<% end %>

Melhorando a apresentação do calendário

Para formatar o calendário, você pode usar este CSS como ponto de partida.

#calendar {
  border-collapse: collapse;
  width: 100%;
}

#calendar td,
#calendar th {
  color: #ccc;
  font-family: "Lucida Grande",arial,helvetica,sans-serif;
  font-size: 10px;
  padding: 6px;
}

#calendar th {
  border: 1px solid #ccc;
  background: #ccc;
  color: #666;
  text-align: left;
}

#calendar td {
  background: #f0f0f0;
  border: 1px solid #ddd;
}

#calendar span {
  display: block;
}

#calendar td.events {
  background: #fff;
}

#calendar td.today {
  background: #ffc;
  color: #666;
}

#calendar caption {
  display: none;
}

Ele irá se parecer com isto:

Imagem do has_calendar com estilo CSS

Mais simples, impossível! ;)

NOTA: O Mac OS X não permite iniciar a semana pela segunda-feira. Sendo assim, se você desenvolve neste sistema operacional, a semana começará no domingo. Em sistemas Linux, onde a maioria das aplicações são executadas, a semana iniciará na segunda-feira.

NOTA 2: Se você quiser utilizar este plugin no Windows, certifique-se de que exista um comando cal no seu PATH, retornando exatamente a saída do comando para *nix. Ele deve ser executado no formato cal -m 12 2008. Teoricamente funciona. :)

UPDATE: Este artigo foi atualizado para mostrar a nova opção :events.

UPDATE 2: O Carlos sugeriu adicionar a chamada ao método da data no próprio helper. Claro que fica melhor! Use a opção :field.