1

I am trying to send mail from a file in lib directory. I used Action Mailer Basics to help me configure this. I can successfully send mail from my controller so I think my problem is not properly requiring the mailer in my lib file "filename.rb".

I run ruby filename.rb and get

/pathtofile/lib/otherfile.rb:37:in `alert': uninitialized constant UserMailer (NameError)
from filename.rb:47:in `block in <main>'
from filename.rb:35:in `secondly_loop'
from filename.rb:47:in `<main>'

filename.rb

alert

otherfile.rb

def alert
  #send email http://www.gotealeaf.com/blog/handling-emails-in-rails
  UserMailer.mailer_method().deliver_now
  puts "Sent Email"
end #end def alert

app/mailers/user_mailer.rb

class UserMailer < ApplicationMailer
  default from: 'email@email.com'

  def mailer_method(a)
    @a = a
    mail(to: 'email@email.com', subject: "Hello you have new mail")
  end
end

app/views/mailer_method.html.erb

<!DOCTYPE html>
<html>
  <head>
    <meta content='text/html; charset=UTF-8' http-equiv='Content-Type' />
  </head>
  <body>
    <h2>Daniel, </h2>
    <p>Your email is boring and has arrived now.<br><br>
      Sincerely,<br>
      Daniel</p>
  </body>
</html>
Leo Brito
  • 2,053
  • 13
  • 20
Daniel
  • 2,950
  • 2
  • 25
  • 45

3 Answers3

0

Rails automagically loads models for you. Just calling ruby filename.rb won't load UserMailer for you, so you'll get an uninitialized constant error because there is no UserMailer defined in the current namespace.

EDIT Here's an SSCCE that allows you to call alert: notice that it is within the namespace of MyAlert; Rails automagically loaded UserMailer so that it is accessible within MyAlert's namespace.

I've slightly changed some definitions for the sake of shortness and clarity.

my_alerts.rb

class MyAlerts
  def self.alert
    UserMailer.mailer_method("It worked")
    puts "Sent Email"
  end 
end

application_mailer.rb

class ApplicationMailer < ActionMailer::Base
  default from: "from@example.com"
  layout 'mailer'
end

user_mailer.rb

class UserMailer < ApplicationMailer
  default from: 'email@email.com'

  def self.mailer_method(a)
    puts a
  end
end

Output:

lbrito@lbrito:~/Documents/rails/textgen$ rails c
Loading development environment (Rails 4.2.4)
2.2.1 :001 > MyAlerts.alert
It worked
Sent Email
 => nil 
Leo Brito
  • 2,053
  • 13
  • 20
  • I can't figure out how to call `alert` and even if it does work in `rails console` I don't understand how rails interacts with lib – Daniel Sep 21 '15 at 23:11
  • @Daniel I've provided a SSCCE that hopefully will make things clear. – Leo Brito Sep 21 '15 at 23:32
  • It does help a lot, but I still get the same error message. I even did this: – Daniel Sep 22 '15 at 02:31
  • `filename.rb` => `MyAlerts.alert` And I reorganized my code to include the changes that you suggested. I need to have a cron job call `ruby filename.rb` I guess I could have a cron job run `MyAlerts.alert` instead.... Is there anyway to call `ruby filename.rb` and load the Mailer? – Daniel Sep 22 '15 at 02:38
  • Having another process call `ruby filename.rb` will incur in the same problem I mentioned: when executing `alert`, `UserMailer` is undefined. I suggest you use something like Resque (https://github.com/resque/resque) to execute cron jobs from within Rails. – Leo Brito Sep 22 '15 at 11:56
  • Thanks I will check out resque and it looks like a good way of solving my problem. For the question, is it even possible to call UserMailer in the lib directory? – Daniel Sep 22 '15 at 20:25
  • I want to add that these q/a's includes all of the mailer parts in one file, but I wanted to have them place in the proper rails directories http://stackoverflow.com/questions/4951310/actionmailer-3-without-rails http://stackoverflow.com/questions/741989/actionmailer-and-ramaze – Daniel Sep 22 '15 at 20:45
0

This is an updated example of using require. Note I don't recommend actually doing this as this should be set up as a rake task, so this is purely an example of using require:

lib/filename.rb

#!/usr/bin/env ruby

require_relative './my_alert'

MyAlert.alert

lib/my_alert.rb

require_relative '../app/mailers/user_mailer'

class MyAlert
  def self.alert
    #send email
    UserMailer.mailer_method("foo").deliver_now
    puts "Sent Email"
  end
end

app/mailers/user_mailer.rb

require 'action_mailer'

class UserMailer < ActionMailer::Base
  default from: 'email@email.com'

  def mailer_method(a)
    @a = a
    mail(to: "mail@email.com", subject: "Hello you have new mail")
  end
end

I have gotten this to work in a test app so that it can access UserMailer, but as this is not actually running as a rails app, you will need to add some more code to let ActionMailer know where to look for the template. Making a rake task will actually run it through Rails which would be preferred.

MikeS
  • 117
  • 3
  • I couldn't get any combination of require/require_relative '../app/mailers/user_mailer.rb' to work – Daniel Sep 22 '15 at 02:11
  • When you require, you need to omit the .rb extension, so, you would use: require_relative '../app/mailers/user_mailer' NOT require_relative '../app/mailers/user_mailer.rb' – MikeS Sep 22 '15 at 15:14
  • When I had tried earlier I had tried various combinations of require/require_relative and '../app/mailers/user_mailer' or '~/app/mailers/user_mailer' 'user_mailer' (with and without .rb) .... 12 combos I think – Daniel Sep 22 '15 at 20:21
  • @Daniel If you want to be able to call the UserMailer from the command line, you should define a rake task, so you can run the rake task instead of trying to call a ruby script directly. See example at: http://stackoverflow.com/questions/21456886/how-to-include-actionmailer-class-in-rake-task – MikeS Sep 24 '15 at 14:14
  • Thanks good suggestion and it looks like a good solution; I could use a cron job to call the rake command. I will play with that to get it to work. However it still does not answer the question I asked. Do you know why you might not be able to call the UserMailer from inside of lib? – Daniel Sep 24 '15 at 19:06
  • I have updated with an example that I have working in a test app (at least for requiring UserMailer. It is tricky because you want to call a ruby script directly that is not actually running within the Rails framework, so you don't have a bunch of load paths automatically set up for you. As to why you cannot get the require_relative to work, I would have to see the whole app. – MikeS Sep 24 '15 at 21:59
  • Whats weird is the rake takes longer to run than ruby 'filename.rb' takes. I can't get it to work tho. My whole question is pretty much summed up on your comment haha "you will need to add some more code to let ActionMailer know where to look for the template" – Daniel Sep 25 '15 at 18:48
0

If you are using the rails lib folder then you should use rails to run your code. You are trying to run ruby code that includes rails components outside the context of rails. When you run ruby filename.rb you are by passing all the magic that goes into rails autoloading. ActionMailer is a gem and usually that gem is loaded for you via bundler when you run your rails server. Look at your config/applicarion.rb to understand what I am taking about. And if you are still confused read: http://guides.rubyonrails.org/autoloading_and_reloading_constants.html

I think what you want to do is build a rails engine. That way you can keep your code modular but still have access to all the various rails components and gems such as ActionMailer. There are a million amazing tutorials on how to build a rails engine to get you started, but I would start with reading the ruby on rails official guides.

Davey
  • 1,093
  • 1
  • 13
  • 25