diff options
author | Joachim Filip Ignacy Bartosik <jbartosik@gmail.com> | 2011-06-06 20:50:58 +0200 |
---|---|---|
committer | Joachim Filip Ignacy Bartosik <jbartosik@gmail.com> | 2011-06-10 18:11:42 +0200 |
commit | a79293f652cedf63f9f3cd88107e98b9b4da99f3 (patch) | |
tree | e4621b126b132c31c6d65e250f962fd1d7a6eb24 | |
parent | Bot obtains voters, agenda items and voting options lists from webapp (diff) | |
download | council-webapp-a79293f652cedf63f9f3cd88107e98b9b4da99f3.tar.gz council-webapp-a79293f652cedf63f9f3cd88107e98b9b4da99f3.tar.bz2 council-webapp-a79293f652cedf63f9f3cd88107e98b9b4da99f3.zip |
Send email reminders about meetings using delayed_job
-rw-r--r-- | site/Gemfile | 2 | ||||
-rw-r--r-- | site/Gemfile.lock | 8 | ||||
-rw-r--r-- | site/app/mailers/user_mailer.rb | 6 | ||||
-rw-r--r-- | site/app/models/agenda.rb | 43 | ||||
-rw-r--r-- | site/app/views/user_mailer/meeting_reminder.erb | 5 | ||||
-rw-r--r-- | site/config/environments/test.rb | 2 | ||||
-rw-r--r-- | site/config/initializers/custom_configs.rb | 4 | ||||
-rw-r--r-- | site/db/schema.rb | 21 | ||||
-rw-r--r-- | site/doc/sample_configs/reminders.yml | 1 | ||||
-rwxr-xr-x | site/script/delayed_job | 5 | ||||
-rw-r--r-- | site/spec/models/agenda_spec.rb | 41 | ||||
-rw-r--r-- | site/spec/models/user_mailer_spec.rb | 14 | ||||
-rw-r--r-- | site/spec/spec_helper.rb | 5 | ||||
-rw-r--r-- | site/spec/support/delayed_should_receive.rb | 11 | ||||
-rw-r--r-- | site/spec/support/should_have_text.rb | 8 |
15 files changed, 170 insertions, 6 deletions
diff --git a/site/Gemfile b/site/Gemfile index fa841ac..a95faa0 100644 --- a/site/Gemfile +++ b/site/Gemfile @@ -3,11 +3,13 @@ gem 'rails', '3.0.3' gem 'sqlite3-ruby', :require => 'sqlite3' gem 'devise' gem 'hobo_devise', '>=0.0.2' +gem 'delayed_job' group :development, :test do gem 'ruby-debug' gem 'rspec-rails' gem 'shoulda' + gem 'email_spec' gem 'cucumber-rails' gem 'capybara' diff --git a/site/Gemfile.lock b/site/Gemfile.lock index 4313a4a..aca3ead 100644 --- a/site/Gemfile.lock +++ b/site/Gemfile.lock @@ -57,7 +57,11 @@ GEM nokogiri (>= 1.4.4) rack-test (>= 0.5.7) culerity (0.2.15) + daemons (1.1.0) database_cleaner (0.6.6) + delayed_job (2.1.2) + activesupport (~> 3.0) + daemons devise (1.3.4) bcrypt-ruby (~> 2.1.2) orm_adapter (~> 0.0.3) @@ -69,6 +73,8 @@ GEM dryml (1.3.0.pre28) actionpack (>= 3.0.0) hobo_support (= 1.3.0.pre28) + email_spec (1.1.1) + rspec (~> 2.0) erubis (2.6.6) abstract (>= 1.0.0) factory_girl (1.3.3) @@ -207,7 +213,9 @@ DEPENDENCIES capybara cucumber-rails database_cleaner + delayed_job devise + email_spec factory_girl fuubar-cucumber hobo (>= 1.3.0.pre28) diff --git a/site/app/mailers/user_mailer.rb b/site/app/mailers/user_mailer.rb index c5c18f8..d9515f3 100644 --- a/site/app/mailers/user_mailer.rb +++ b/site/app/mailers/user_mailer.rb @@ -7,4 +7,10 @@ class UserMailer < ActionMailer::Base :to => user.email ) end + def meeting_reminder(user, agenda) + @user = user + @agenda = agenda + mail(:subject => "Upcoming meeting reminder - #{agenda.meeting_time.to_s}", + :to => user.email) + end end diff --git a/site/app/models/agenda.rb b/site/app/models/agenda.rb index 22414d7..ea58041 100644 --- a/site/app/models/agenda.rb +++ b/site/app/models/agenda.rb @@ -3,7 +3,8 @@ class Agenda < ActiveRecord::Base hobo_model # Don't put anything above this fields do - meeting_time :datetime + meeting_time :datetime + email_reminder_sent :boolean, :null => false, :default => false timestamps end @@ -101,7 +102,12 @@ class Agenda < ActiveRecord::Base end end - def self.voters + def time_for_reminders + offset = CustomConfig['Reminders']['hours_before_meeting_to_send_email_reminders'].hours + meeting_time - offset + end + + def self.voters_users # It's possible to rewrite this as SQL, but # * this method is rarely called # * it fetches little data @@ -109,7 +115,38 @@ class Agenda < ActiveRecord::Base # Joachim council = ::User.council_member_is(true) proxies = Agenda.current.proxies - [council - proxies.*.council_member + proxies.*.proxy].flatten.*.irc_nick + [council - proxies.*.council_member + proxies.*.proxy].flatten + end + + def self.voters + Agenda.voters_users.*.irc_nick + end + + def self.send_current_agenda_reminders + agenda = Agenda.current + + return if agenda.email_reminder_sent? + return if Time.now < agenda.time_for_reminders + + for user in Agenda.voters_users + UserMailer.delay.deliver_meeting_reminder(user, agenda) + end + + agenda.email_reminder_sent = true + agenda.save! + end + + before_save do |a| + return true if a.new_record? + return true unless a.meeting_time_changed? + a.email_reminder_sent = false + true + end + + after_save do |a| + if a.new_record? or a.meeting_time_changed? + Agenda.delay(:run_at => a.time_for_reminders).send_current_agenda_reminders + end end protected diff --git a/site/app/views/user_mailer/meeting_reminder.erb b/site/app/views/user_mailer/meeting_reminder.erb new file mode 100644 index 0000000..b49e7e6 --- /dev/null +++ b/site/app/views/user_mailer/meeting_reminder.erb @@ -0,0 +1,5 @@ +<%= @user %>, + +meeting will take place on <%= @agenda.meeting_time.to_s %>. You can view agenda for the meeting on: + + <%= agenda_url(@agenda) %> diff --git a/site/config/environments/test.rb b/site/config/environments/test.rb index c2fc237..944a63f 100644 --- a/site/config/environments/test.rb +++ b/site/config/environments/test.rb @@ -32,4 +32,6 @@ Council::Application.configure do # Print deprecation notices to the stderr config.active_support.deprecation = :stderr + + config.action_mailer.default_url_options = { :host => 'localhost', :port => '3000' } end diff --git a/site/config/initializers/custom_configs.rb b/site/config/initializers/custom_configs.rb index 599646f..dca866f 100644 --- a/site/config/initializers/custom_configs.rb +++ b/site/config/initializers/custom_configs.rb @@ -1,2 +1,4 @@ CustomConfig = {} -CustomConfig['Bot'] = YAML.load open('config/bot.yml').read +for conf in ['bot', 'reminders'] + CustomConfig[conf.camelize] = YAML.load open("config/#{conf}.yml").read +end diff --git a/site/db/schema.rb b/site/db/schema.rb index fec65de..071ff84 100644 --- a/site/db/schema.rb +++ b/site/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20110603133359) do +ActiveRecord::Schema.define(:version => 20110606170332) do create_table "agenda_items", :force => true do |t| t.string "title" @@ -30,12 +30,29 @@ ActiveRecord::Schema.define(:version => 20110603133359) do t.datetime "meeting_time" t.datetime "created_at" t.datetime "updated_at" - t.string "state", :default => "open" + t.string "state", :default => "open" t.datetime "key_timestamp" + t.boolean "email_reminder_sent", :default => false, :null => false end add_index "agendas", ["state"], :name => "index_agendas_on_state" + create_table "delayed_jobs", :force => true do |t| + t.integer "priority", :default => 0 + t.integer "attempts", :default => 0 + t.text "handler" + t.text "last_error" + t.datetime "run_at" + t.datetime "locked_at" + t.datetime "failed_at" + t.string "locked_by" + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "delayed_jobs", ["locked_by"], :name => "delayed_jobs_locked_by" + add_index "delayed_jobs", ["priority", "run_at"], :name => "delayed_jobs_priority" + create_table "participations", :force => true do |t| t.string "irc_nick" t.datetime "created_at" diff --git a/site/doc/sample_configs/reminders.yml b/site/doc/sample_configs/reminders.yml new file mode 100644 index 0000000..edf937a --- /dev/null +++ b/site/doc/sample_configs/reminders.yml @@ -0,0 +1 @@ +hours_before_meeting_to_send_email_reminders: 24 diff --git a/site/script/delayed_job b/site/script/delayed_job new file mode 100755 index 0000000..edf1959 --- /dev/null +++ b/site/script/delayed_job @@ -0,0 +1,5 @@ +#!/usr/bin/env ruby + +require File.expand_path(File.join(File.dirname(__FILE__), '..', 'config', 'environment')) +require 'delayed/command' +Delayed::Command.new(ARGV).daemonize diff --git a/site/spec/models/agenda_spec.rb b/site/spec/models/agenda_spec.rb index 2a08210..83b31a3 100644 --- a/site/spec/models/agenda_spec.rb +++ b/site/spec/models/agenda_spec.rb @@ -147,4 +147,45 @@ describe Agenda do (voters - nicks).should be_empty (nicks - voters).should be_empty end + + it 'should add Agenda.send_current_agenda_reminders to delayed jobs when created' do + Agenda.should_receive_delayed(:send_current_agenda_reminders) + Factory(:agenda) + end + + it 'should add Agenda.send_current_agenda_reminders to delayed jobs when meeting time changes' do + a = Factory(:agenda) + Agenda.should_receive_delayed(:send_current_agenda_reminders) + a.meeting_time = Time.now + 24.hours + a.save! + end + + it 'should set email_reminder_sent to false when time changes' do + a = Factory(:agenda, :email_reminder_sent => true) + lambda { + a.meeting_time = Time.now + 24.hours + a.save! + }.should change(a, :email_reminder_sent?).from(true).to(false) + end + + it 'should send reminders properly with send_current_agenda_reminders using delayed jobs' do + agenda = Factory(:agenda) + users = users_factory([:user] * 2) + council = users_factory([:council] * 2) + Factory(:proxy, :proxy => users.first, :council_member => council.first, :agenda => agenda) + UserMailer.should_receive_delayed(:deliver_meeting_reminder, council.last, agenda) + UserMailer.should_receive_delayed(:deliver_meeting_reminder, users.first, agenda) + + Agenda.send_current_agenda_reminders + + agenda.reload + agenda.email_reminder_sent.should be_true + end + + it 'should not send reminders with send_current_agenda_reminders if Agenda.current.email_reminder_sent is true' do + a = Factory(:agenda, :email_reminder_sent => true) + users = users_factory([:user] * 2) + UserMailer.should_not_receive(:delay) + Agenda.send_current_agenda_reminders + end end diff --git a/site/spec/models/user_mailer_spec.rb b/site/spec/models/user_mailer_spec.rb new file mode 100644 index 0000000..ea2c2bc --- /dev/null +++ b/site/spec/models/user_mailer_spec.rb @@ -0,0 +1,14 @@ +require 'spec_helper' +describe UserMailer do + it 'should send proper meeting reminders' do + user = Factory(:user) + agenda = Factory(:agenda) + reminder = UserMailer.meeting_reminder(user, agenda) + reminder.should deliver_to(user.email) + reminder.should deliver_from("no-reply@localhost") + reminder.should have_text(/meeting will take place on #{agenda.meeting_time.to_s}./) + reminder.should have_text(/You can view agenda for the meeting on:/) + reminder.should have_text(/http:\/\/localhost:3000\/agendas\/#{agenda.id}/) + reminder.should have_subject("Upcoming meeting reminder - #{agenda.meeting_time.to_s}") + end +end diff --git a/site/spec/spec_helper.rb b/site/spec/spec_helper.rb index a8b8aea..47ed37e 100644 --- a/site/spec/spec_helper.rb +++ b/site/spec/spec_helper.rb @@ -15,3 +15,8 @@ RSpec.configure do |config| config.mock_with :rspec config.use_transactional_fixtures = true end + +RSpec.configure do |config| + config.include(EmailSpec::Helpers) + config.include(EmailSpec::Matchers) +end diff --git a/site/spec/support/delayed_should_receive.rb b/site/spec/support/delayed_should_receive.rb new file mode 100644 index 0000000..0fbbe27 --- /dev/null +++ b/site/spec/support/delayed_should_receive.rb @@ -0,0 +1,11 @@ +class Object + def should_receive_delayed(method, *args) + m = RSpec::Mocks::Mock.new('proxy') + if args.empty? + m.should_receive(method) + else + m.should_receive(method).with(*args) + end + self.should_receive(:delay).and_return(m) + end +end diff --git a/site/spec/support/should_have_text.rb b/site/spec/support/should_have_text.rb new file mode 100644 index 0000000..3016c13 --- /dev/null +++ b/site/spec/support/should_have_text.rb @@ -0,0 +1,8 @@ +module Mail + class Message + # emailspec doesn't add this, so we have to + def has_text?(text) + not body.to_s.match(text).nil? + end + end +end |