Question

I'm trying to create a simple form to collect an email address and then send an email to me.

I'm randomly getting uninitialized constant Marketing::InviteController::MarketingMailer, sometimes on the first time the form is submitted, but ALWAYS the second time is submitted, so if you go to localhost:3000/request-invite, enter an email and submit the form, then enter an email and submit the form again, you will get this error.

Any ideas why i'm getting this error even?

/controllers/marketing/invite_controller.rb

class Marketing::InviteController < ApplicationController

  layout 'marketing/layouts/layout'

  # GET /request-invite
  def new
    @invite = Invite.new

    respond_to do |format|
      format.html # new.html.erb
    end
  end

  # POST /request-invite
  def create
    @invite = Invite.new(params[:invite])

    respond_to do |format|
      if @invite.save
        MarketingMailer.invite(@invite.email).deliver
        format.html { redirect_to request_invite_path, notice: 'Success' }
      else
        format.html { render action: "new", error: 'An error has occurred' }
      end
    end
  end

end

/views/marketing/invite/new.html.erb

<%= render :partial => 'shared/messages', :locals => {:object => @invite} %>
                        <%= render :partial => 'shared/object_errors', :locals => {:object => @invite} %>

                        <%= form_for @invite, :url => request_invite_path, :method => :post do |f| %>

                            <%= f.text_field :email, :placeholder => 'Email' %>    
                            <%= f.submit 'Send Request', :class => 'btn' %>
                        <% end %>

/mailers/marketing/marketing_mailer.rb

class Marketing::MarketingMailer < ActionMailer::Base

    require 'mail'
    address = Mail::Address.new "test@test.com" # ex: "john@example.com"
    address.display_name = "Test" # ex: "John Doe"
    # Set the From or Reply-To header to the following:
    address.format # returns "John Doe <john@example.com>"
    default from: address

    # Sends an email when they request an invite
    def invite(to)
        @to = to
        mail(:subject => "Jobfly Invite Request", :to => 'test@test.com', :reply_to => @to)
    end
end

/views/marketing/marketing_mailer/invite.html.erb

<!DOCTYPE html>
<html>
  <head>
    <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
  </head>
  <body>
    <p>The following email has requested an invite for jobfly</p>
    <p>Email: <%= @to %></p>
  </body>
</html>

Log sample:

Started GET "/request-invite" for 127.0.0.1 at 2013-12-10 17:41:44 -0600
Processing by Marketing::InviteController#new as HTML
  Rendered shared/_messages.html.erb (0.1ms)
  Rendered shared/_object_errors.html.erb (0.1ms)
  Rendered marketing/invite/new.html.erb within marketing/layouts/layout (3.0ms)
Completed 200 OK in 72ms (Views: 71.3ms | ActiveRecord: 0.0ms)


Started POST "/request-invite" for 127.0.0.1 at 2013-12-10 17:41:49 -0600
Processing by Marketing::InviteController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"RTVaxb3jK9VwS0/SZ1e6JjMeA6XVqBof44m04Wsvbd8=", "invite"=>{"email"=>"jeff@gmail.com"}, "commit"=>"Send Request"}
   (0.2ms)  BEGIN
  Invite Exists (0.3ms)  SELECT 1 AS one FROM `invites` WHERE `invites`.`key` = 'SZNu9vbl' LIMIT 1
  SQL (33.8ms)  INSERT INTO `invites` (`created_at`, `email`, `key`, `updated_at`) VALUES ('2013-12-10 23:41:49', 'jeff@gmail.com', 'SZNu9vbl', '2013-12-10 23:41:49')
   (0.4ms)  COMMIT
Completed 500 Internal Server Error in 43ms

NameError - uninitialized constant Marketing::InviteController::MarketingMailer:
...
Was it helpful?

Solution

It sounds like autoloading issues. Have you tried referencing the MarketingMailer constant using the full name? ::Marketing::MarketingMailer ?

Rails doesn't load all classes in development. Instead, when a class is missing, Rails tries to guess where in the project to find the class, based on a set of autoloading paths.

For instance, all of the folders in your app folder are added to the autoloading paths. So if you reference Foobar.new, Rails will look in your app/models or app/controllers for a foobar.rb file, that it expects to contain a class Foobar definition. However, when you use modules, the waters become more muddy.

class Foo::Bar
  # some code
  Baz.new
end

Rails will now think that the Baz class is defined in a baz.rb class that is located in subfolders, so it will look in the autoload folders and search for subfolders named according to the enclosing modules, checking app/models/foo/bar/baz.rb and app/controllers/foo/bar/baz.rb

When it doesn't find anything, Rails has different ways of trying to resolve it which I don't fully understand. However, if you prefix your class reference with two colons, that means you are referencing from the root namespace and Rails should not try to search relative to your current module, so if we take the above example again:

class Foo::Bar
  ...

  Baz.new # Searches for foo/bar/baz.rb
  ::Baz.new # Searches for baz.rb
  ::My::Baz.new # Searches for my/baz.rb
  My::Baz.new # Searches for foo/bar/my/baz.rb

  ...
end

In production mode, most of these problems disappear, because Rails preloads all source files in your project, so if Baz has been defined anywhere in any folder, Rails doesn't have to make guesses about where to find it. It already exists, because it has been loaded.

OTHER TIPS

i think you should refer your mailer just as you defined it in your code Marketing::MarketingMailer, not MarketingMailer

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top