I can use Devise helper methods in regular views but don't know how to use it within my Mailer. I need to determine if user is signed in to construct proper email message.
class UserMailer < ActionMailer::Base
def receipt
end
end
receipt.text.erb
<% if user_signed_in? %> #Error: undefined method `user_signed_in?' for #<#<Class:0x35695fc>
Secret link
<% end %>
Actually, you can't and most of all, you shouldn't use this kind of Devise helper on your mailer.
Why ? Well, if you search Devise's code base for the user_signed_in? helper, you will find it in Devise::Controllers::Helpers module, as you can see here. This means that it is supposed to be used in a controller context, as it uses method such as request and warden that is only available on controllers.
If you must make any decision in your mail view based on whether a user is signed in or not, I would recommend you to pass this information from your controller to your mailer:
Your controller:
class UserController < ApplicationController
def your_action
UserMailer.receipt(user_signed_in?).deliver
#....
end
end
Your mailer:
class UserMailer < ActionMailer::Base
def receipt(signed_in)
#signed_in = singed_in
#....
end
end
Your mailer view:
<% if #signed_in %>
Secret link
<% end %>
I hope it helps !
You can pass it over from helper. Something like this
class UserMailer < ActionMailer::Base
def receipt
#is_signed = user_signed_in?
end
end
and
<% if #is_signed %>
Secret link
<% end %>
Related
in views, there are idiom like this for #create and #update actions
<% if #article.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(article.errors.count, "error") %> prohibited
this article from being saved:
</h2>
<ul>
<% #article.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
Now, is there similarly a way to check in the Users::SessionsController < Devise::SessionsController, whether the user-login was successful or not?
There is a similar thread here Ruby on Rails Devise code after login but the answer by Peter P. Jan 18 '15 at 3:06 suggests, that exactly when the login is unsuccessful, then the before_action "will not be run", which is bad, because I need exactly this information, whether the login was unsuccessful.
The accompanying code is
# app/controllers/custom_sessions_controller.rb
class CustomSessionsController < Devise::SessionsController
## for rails 5+, use before_action, after_action
before_filter :before_login, :only => :create
after_filter :after_login, :only => :create
def before_login
end
def after_login
end
end
But obviously useless towards my problem... (?)
The suggestions are to use Warden hooks like this by User Mike Lapinskas, Oct 8 '16 at 5:33, but the content too doesn't seem to be an answer to my problem. It is about after login, not a test whether the login was unsuccessful
class User < ApplicationRecord
Warden::Manager.after_set_user do |user, auth, opts|
if (opts[:scope] == :user && opts[:event] == :set_user)
# < Do your after login work here >
end
end
end
The approach in this thread Devise: redirect on sign up failure? seems promising
The hint to Devise::FailureApp led me to https://gist.github.com/emilsoman/5604254, also see Custom Devise 401 unauthorized response
The responses mentioned there are all JSON. Is this mandatory? Can't I just call render on an html.erb-template/partial ?
Thanks
If you want to execute an post-sign in method, just create a custom controller, inherit Devise::SessionsController and pass a block to super. Devise will call that block after signing in successfully.
https://github.com/heartcombo/devise/blob/57d1a1d3816901e9f2cc26e36c3ef70547a91034/app/controllers/devise/sessions_controller.rb#L22
class CustomSessionsController < Devise::SessionsController
def create
super do |resource|
# do something after signing in successfully
end
end
end
To handle failure case, you can catch the exception which raised by warden's #authenticated! (←I'm wrong about this)
https://github.com/heartcombo/devise/blob/57d1a1d3816901e9f2cc26e36c3ef70547a91034/app/controllers/devise/sessions_controller.rb#L19
If your logic is complicated, and you need to custom a lot of code, just write your own #create without super, and use devise's provided method to achive your requirement. Keep in mind that devise has some callbacks which might affect your custom code.
class CustomSessionsController < Devise::SessionsController
def create
self.resource = warden.authenticate(auth_options) # Without `!`
if resource
# success
else
# failure
end
end
end
I am using custom mailer by overriding the devise mailer. It is working fine. But I need to pass some data to the mailer template so that when the confirmation email send to the user it contains some dynamic content. I have tried it using sessions,#resource and current_user method but both are not working. Is there any way to do that?
Custom mailer
class CustomMailer < Devise::Mailer
helper :application # gives access to all helpers defined within `application_helper`.
include Devise::Controllers::UrlHelpers # Optional. eg. `confirmation_url`
default template_path: 'devise/mailer' # to make sure that you mailer uses the devise views
def confirmation_instructions(record, token, opts={})
opts[:subject] = "Email Confirmation"
opts[:from] = 'no-reply#abc.com'
#data = opts[:custom_field]
super
end
end
in the controller
CustomMailer.confirmation_instructions(token, {custom_field: "abc"})
This is the code in template
We are happy to invite you as user of the <b> <%= #data %> </b>
Thanks.
First, read about Devise custom mailer to familiarize yourself with the process.
Briefly this is how you'd go about doing it:
in config/initializers/devise.rb:
config.mailer = "DeviseMailer"
Now you can just use DeviseMailer like you'd do for any other mailer in your project:
class DeviseMailer < Devise::Mailer
helper :application # gives access to all helpers defined within `application_helper`.
include Devise::Controllers::UrlHelpers # Optional. eg. `confirmation_url`
default template_path: 'devise/mailer' # to make sure that your mailer uses the devise views
...
def invite(sender, recipient)
#sender = sender
#recipient = recipient
mail( :to => recipient.email,
:subject => "Invite by #{sender.name}"
)
end
...
end
You can now call the invite in your project and pass whatever variable you want to be able to access in your template.
i.e:
Calling the invite method:
DeviseMailer.invite(current_user, newContact).deliver
So in your view you can then just call the variable:
invite.html.erb
<p>Hello <%= #recipient.email %></p>
<% if #sender.email? %>
<p> some additional welcome text here from <%= #sender.email %> </p>
<% end %>
EDIT
To answer your specific question here is what you want to override:
def confirmation_instructions(record, token, opts={})
headers["Custom-header"] = "Bar"
opts[:from] = 'my_custom_from#domain.com'
opts[:reply_to] = 'my_custom_from#domain.com'
super
end
Then call it anywhere you want:
DeviseMailer.confirmation_instructions(User.first, "faketoken", {})
I want to pass a method from the application controller to a mailer to send shopping cart contents to email.
Method in the application_controller.rb:
def current_order
if session[:order_id].present?
Order.find(session[:order_id])
else
Order.new
end
end
Mailer:
class CartMailer < ApplicationMailer
default from: "from#example.com"
def send_cart_contents
#order = current_order
mail(to: "to#example.com", subject: 'Order from the site')
end
end
And the view:
Order from the site
<% #order.order_items.each do |oi| %>
<%= oi.product.name %>
<% end %>
I'm getting an error: undefined local variable or method 'current_order'.
What am I doing wrong? Thank you.
UPDATE
If I'm passing it as a parameter:
# Preview all emails at http://localhost:3000/rails/mailers/cart_mailer
class CartMailerPreview < ActionMailer::Preview
def cart_mailer_preview
CartMailer.send_cart_contents(current_order)
end
end
I'm also getting NameError.
UPDATE 2
CartMailerPreview don't have access to the current_order, so to test it just pass an id with the parameter. When you use it normally all works well.
The CartMailer is not going to have visibility to the current_order defined in application_controller.rb. This is a good thing.
Best practices is to have the send_cart_contents method accept the order so that it can mail it out:
class CartMailer < ApplicationMailer
default from: "from#example.com"
def send_cart_contents(order)
#order = order
mail(to: "to#example.com", subject: 'Order from the site')
end
end
This way you can mail out a cart from a background job and isolates your mailers from your controllers. Relying on a global current_order is not a good practice.
You should pass current_order to the mailer as a parameter.
I am having a action in application controller
def is_customer_logged_in?
!!session[:customer_id]
end
And in my view am trying to access the application_controller action like this
<% unless is_customer_logged_in? %>
some functions
<% end %>
The above code is a partial layouts.
This is the error message I am facing
undefined method `is_customer_logged_in?' for #<#<Class:0xb51a5300>:0xb5616484>
You can define it to be a helper method and you should be able to access that method in the view.
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
def is_customer_logged_in?
!!session[:customer_id]
end
helper_method :is_customer_logged_in?
end
try helper_method: is_customer_logged_in?
I defined a helper class as below
module SessionsHelper
def current_user
#current_user= User.find_by_fbid(session[:fbid])
end
def sign_in(user)
session[:fbid] = user.fbid
#current_user = user
end
def signed_in?
!current_user.nil?
end
end
I included the Helper Class in my Application Controller
class ApplicationController < ActionController::Base
protect_from_forgery
include SessionsHelper
end
The sign in method gets called from Session Controller
class SessionsController < ApplicationController
def create
user = User.find_or_create_by_fbid(params[:user][:fbid])
user.update_attributes(params[:user])
sign_in(user)
redirect_to user_path(user)
end
end
However I am not able to access 'current_user' variable from users#show view.
<% if signed_in? %>
<p>
<b>Current User:</b>
<%= current_user.name %>
</p>
<% end %>
It says : undefined method `name' for nil:NilClass
Can anyone please advise ?
The method current_user does not get called at all from index.
Putting include SessionsHelper in your controller includes those module methods in the controller, so they are accessible in your controller methods. You want the helper methods available in your views, so you need to use helper SessionsHelper in your application controller.
That being said, I do agree with Jits that the methods you have in SessionsHelper really do belong in the controller instead of in a helper.
Generally you should have methods like current_user defined in your application_controller and then make them available as helpers in the views. This way the controllers have access to them (and trust me, you will most likely need access to things like that). Example:
def current_user
..
end
helper :current_user
What helped me:
Define methods to use in the controller in helper files
Define methods to use in the view in the relevant model file
Example
Suppose you had this in user_helper.rb
def something
2 + 2
end
simply move that code into
models/user.rb
and it will be accessible in the view without any further effort.