Em um projeto pequeno, que precisava rodar em um desktop, eu precisei de um agendador de tarefas, o sistema precisava rodar em windows, linux ou mac, então utilizar o Cron para isto não era uma opção, e eu precisava de algo semelhante ao Cron, rodar algum código a cada X tempo não era o suficiente, ou pelo menos tornaria a minha vida muito mais difícil.
Eu também não queria ter que iniciar um processo separado da aplicação para cuidar disto pois eram tarefas simples, e depois de algumas pesquisas, encontrei o Rufus-Scheduler que resolve o problema que eu tinha, então resolvi escrever este post para que eu lembre dele caso precise novamente.
Sempre lembrando que aqui não serão mostrados todos do Rufus-Scheduler, apenas uma forma simples de usa-lo, se tiver dúvidas deixe um comentário que farei o melhor para esclarece-las.
Este exemplo vai começar com uma aplicação simples, um scaffold de uma entidade de nome SimpleTask com os seguintes parâmetros:
rails generate scaffold SampleTask name:string cron:string times:integer done:boolean job_id:string rake db:migrate
A premissa para este exemplo é a seguinte:
Quando o sistema for iniciado as tarefas ainda não completas tem que ser agendadas novamente, e toda vez que eu criar uma tarefa nova esta tem que ser agendada automaticamente.
No model criado adicionar o seguinte código:
class SampleTask < ActiveRecord::Base after_create :schedule_me after_destroy :unschedule_me def schedule_me MySampleScheduler.schedule read_attribute(:id) end def unschedule_me MySampleScheduler.unschedule read_attribute(:job_id) end end
Criar o arquivo lib/my_sample_scheduler.rb com o seguinte conteúdo:
class MySampleScheduler @@scheduler = Rufus::Scheduler::PlainScheduler.start_new def self.update_schedules @@scheduler.in '1m' do #delay initialization because of problems acessing rails models during rails initialization jobs = @@scheduler.cron_jobs tasks = SampleTask.all tasks.each do |task| job = jobs[task.job_id] if task.job_id job.unschedule if job && (task.done || job.cron_line!=task.cron) if !task.done schedule(task) end end end end def self.unschedule(job_id) @@scheduler.unschedule(job_id) end def self.schedule(task_id) task = SampleTask.find task_id job = @@scheduler.cron task.cron do |j| task = SampleTask.find task_id if task puts "#{task.name} executing at #{Time.now} ---- #{task.times}" task.times = task.times - 1 task.done = task.times==0 task.save unschedule task.job_id if task.done else puts "Task deleted #{task_id}" unschedule j.job_id end end task.job_id = job.job_id task.save end end
Rodar a aplicação e já deve ser possível cadastrar tarefas.
Quando a aplicação for finalizada e novamente inicializada, as tarefas pendentes terão um delay de 1 minuto para re-iniciarem, isto foi necessário porque se acessarmos os models diretamente durante a inicialização do rails, muitas coisas ruins podem acontecer.
Não se esqueça que para este exemplo funcionar você precisa ter o Rufus-Scheduler instalado. Se você seguiu todos os passos, um “bundle install” do diretório do projeto deve resolver o problema.
Se quiser o código da aplicação que eu escrevi, ele esta disponível neste endereço:http://github.com/urubatan/rails_schedule_samples
Se você já usa o GIT pode baixar o código com o comando: git clone git://github.com/urubatan/rails_schedule_samples.git
Tags: agendamento, dicas, rails, Ruby, scheduler
Hehehe…
Muito Facil ? Mais facil que o @Schedule do Java EE 6 não é…
Não precisa de toda essa parafernalha… apenas um @Schedule em cima do metodo e PUFT… tudo pronto
[Translate]