0

I have a method that i created called get_primary_email(user) and it's in my application_controller.rb file.

Now i use this method in both my app, as well as a cron job that calls a method which uses get_primary_email(user)

My problem is, if i leave the method defined as this:

def get_primary_email(user)
end

It works just fine in my app, but the cron job errors out.

If i define the method as this:

def self.get_primary_email(user)
end

my cron job works, but every method that calls get_primary_email(user) through a browser fails.

How can i use this same method in both places?

EDIT

schedule.rb

set :output, "#{path}/log/cron.log"

every 1.day, :at => "8:00pm" do
  runner "BirthdayRemindersController.send_birthday_email_reminders"
end
Catfish
  • 18,876
  • 54
  • 209
  • 353
  • Are you defining 'get_primary_email' in a model? Can you show us the code and how you're setting up the cron job? – agmin Jan 14 '13 at 23:39
  • No it's my application_controller.rb file. I'm using as a helper method to get the users primary. each user can have up to 3 emails and there is a boolean as field for each of those emails. – Catfish Jan 15 '13 at 00:06
  • The best option is probably to move the method to a separate class and call it there. It's common to put something like this on a model, but it will work just fine as a standalone class. – James Mason Jan 15 '13 at 00:16
  • Do you understand the difference between instance methods and class (static) methods? You're call it as an instance method in the app and as a class method from cron. This is your problem. I would recommend moving this method to the model, and then creating a user presenter to expose the functionality to both controller and cron – quandrum Jan 15 '13 at 00:43
  • I'm a little familiar with the difference between instance methods and class methods. Can you explain what you mean by a "user presenter". – Catfish Jan 15 '13 at 03:14
  • @James can you elaborate? Particularly with some code? – Catfish Jan 15 '13 at 03:14
  • @Catfish, I posted an answer with some code snippets. – James Mason Jan 15 '13 at 19:48
  • Catfish - I would set up a Jobs Queue to handle this (Resque or DelayedJob). Use the cron job to populate the queue. Use the queue to send out emails. The way you're using controllers is not the way they were meant to be used. Controllers are meant to handle requests and responses, user state. This is simply a scheduled process. You should be able to get the attribute from user, send it to the Mailer. Job Queues also handle errors well. – Mark Swardstrom Jan 16 '13 at 19:37
  • @Swards can you post a simple example as an answer? – Catfish Jan 16 '13 at 19:41
  • I added some in my answer below. – Mark Swardstrom Jan 16 '13 at 20:21

2 Answers2

2

This is often done by setting up a cron job to call a rake task. In the rake task you can call Rails code easily.

There are lots of examples on the web. Here's one from SO:

A cron job for rails: best practices?

EDIT -- adding Resqueue code and Mailer examples

user.rb

class User

  scope :birthday_today, lambda { where('birthday = ?', Date.today) }

  def self.send_birthday_alerts
    self.birthday_today.each do |user|
      BirthdayAlertJob.enqueue(user.id)
    end
  end

  def get_primary_email
    ...
  end

end

jobs/birthday_alert_job.rb

class BirthdayAlertJob
  @queue = :birthday_alert

  def self.perform(u_id)
    user = User.find(u_id)
    UserMailer.birthday_alert(@user).deliver
  end
end

schedule.rb

every 1.day, :at => "8:00pm" do
  rake "users:send_birthday_alerts"
end

users.rake

namespace :users do
  desc "Queue up Birthday Alerts"
  task :send_birthday_alerts => :environment do
    User.send_birthday_alerts
  end
end

user_mailer.rb

class UserMailer < ActionMailer::Base

  def birthday_alert(user)
    mail(to: user.primary_email, subject: "Happy Birthday")
    ...
  end
end

This is close, untested, etc. One thing I know you'll need to do, is make sure the user hasn't already been sent an email. Not sure how you're doing this now. Another column could hold the last time the alert was queued.

Community
  • 1
  • 1
Mark Swardstrom
  • 17,217
  • 6
  • 62
  • 70
  • I've looked at that link a couple of times. In fact i'm using the second example in my code. I've updated my question with code from my schedule.rb file which is from the whenever gem. – Catfish Jan 15 '13 at 03:12
1

Based on what you've posted, I would suggest something like this:

lib/birthday_reminders.rb:

class BirthdayReminders
  def self.send_birthday_email_reminders
    # code ...
  end

  def self.primary_email_for(user)
    # code ...
  end
end

controllers/application_controller.rb:

class ApplicationController < ActionController::Base
  def get_primary_email(user)
    BirthdayReminders.primary_email_for(user)
  end
end

SOMEWHERE/schedule.rb:

set :output, "#{path}/log/cron.log"

every 1.day, :at => "8:00pm" do
  runner "BirthdayReminders.send_birthday_email_reminders"
end
James Mason
  • 4,246
  • 1
  • 21
  • 26