I have set a mailer:
def created_poll_email(poll)
#poll = poll
mail(subject: 'A new poll was created! ' + #poll.question)
end
Firstly I was calling it on polls controller:
def create
#poll = Poll.new(poll_params.merge(user: current_user))
if #poll.save
PollMailer.created_poll_email(#poll).deliver_now
And it was working fine.
Now I want to move it to a callback on model:
after_save :send_email
def send_email
PollMailer.created_poll_email(#poll).deliver_now
end
But now i get error undefined method `question' for nil:NilClass. I tried to set also other callbacks as after_create or after_commit but same result.
Why is this happening and how can I fix?
You should replace #poll, which isn't set inside your model, with self:
def send_email
PollMailer.created_poll_email(self).deliver_now
end
Related
I have a before action in a user mailer file, which is supposed to stop mailers sending if a column on user is set to true or false. However current user is currently unavailable. I understand why, but was wondering if there was a way to do this.
I want to avoid adding the check_if_users_can_receive_mailers at the top of each mailer method.
before_action :check_if_users_can_receive_mailers
#methods that send mailers
private
def check_if_users_can_receive_mailers
current_user.send_mailers?
end
You have to make the current user available as a attribute or class variable. The most straight forward method is something like this:
class MailerBase < ActionMailer::Base
before_action :check_if_users_can_receive_mailers
attr_accessor :user
def initialize(user)
#user = user
end
private
def check_if_users_can_receive_mailers
user.send_mailers?
end
end
class SomeMailerClass < MailerBase
end
In Rails only your controller and views are request aware. Mailers and models and other classes in your application are not and they cannot get the current user since they can't access the session nor the method current_user which is a helper method mixed into your controller (and the view context).
If your mailers need to know about the current user the most logical approach is to pass that information into the mailer:
class UserMailer < ApplicationMailer
def intialize(user)
#user = user
end
end
However a mailer should only have one job - to send emails and it shouldn't be questioning if it should do the job or not. Determining if you should send an email to the user should be done elsewhere. You can place this logic in the controller or even better in a service object:
# app/notifiers/user_notifier.rb
class UserNotifier
def initialize(user, event:)
#user = user
#event = event
end
def notify
if #user.wants_email?
spam_user!
end
send_in_app_notification
end
def self.notify(user, event:)
new(user, event:)
end
private
def spam_user!
# ...
end
def send_in_app_notification
# ...
end
end
class ThingsController
def create
#thing = Thing.new
if #thing.save
UserNotifier.notify(current_user, event: :thing_created)
redirect_to #thing
else
render :new
end
end
end
I am on rails 4.2.10. I need to trigger a job using sidekiq in after_save method. But the job is triggered, before the object is committed into the database, so I get the error, object not found with id=xyz.
So, I need to use
after_commit :method_name, :on => [:create, :update]
But the changes that I made in object doesn't show up in above method. I have an attribute email. When I was calling above method after_save, email_changed? return true. But if I call the same method using after_commit, email_changed? returns `false.
Is it because I am using object.save method and not create method?
Below is the method, which I am calling to trigger the job:
def update_or_create_user
if email_changed?
ServiceUpdateDataJob.perform_later action: 'update', data: {type: 'user', user_id: self.id}
end
true
end
I recognize this isn't exactly an answer to your question as stated. However...
IMO, you're overloading your model's responsibilities. I suggest you create a service that triggers the job when your model is saved. It might look something like:
class FooService
attr_accessor :unsaved_record
class << self
def call(unsaved_record)
new(unsaved_record).call
end
end
def initialize(unsaved_record)
#unsaved_record = unsaved_record
end
def call
kick_off_job if unsaved_record.save
!unsaved_record.new_record?
end
private
def kick_off_job
# job logic
end
end
You might use the service in a controller something like:
class FooController < ApplicationController
def create
#new_record = ModelName.new(record_params)
if FooService.call(#new_record)
# do successful save stuff
else
# do unsuccessful save stuff
end
end
...
end
I have a simple mailer
class ApplyMailer < ActionMailer::Base
def inform_teacher
end
def inform_division
end
def inform_everyone
inform_teacher.deliver
inform_division.deliver
end
end
Calling inform_teacher and inform_division everything works well. But when I try to call inform_everyone just one blank email arrives.
Is it possible to combine multiple email method though one method?
Found solution to this:
class ApplyMailer < ActionMailer::Base
def inform_teacher
end
def inform_division
end
def self.inform_everyone
ApplyMailer.inform_teacher.deliver
ApplyMailer.inform_division.deliver
end
end
I created a new model named PaypalOrder using:
rails generate model order_id:integer ip_address:string first_name:string last_name:string card_type:string card_expires_on:date
Then I ran rake db:migrate
Now my order model looks like:
class PaypalOrder < ActiveRecord::Base
belongs_to :order
attr_accessor :card_number, :card_verification
validate :validate_card, :on => :create
def purchase
#code
end
private
def validate_card
#code
end
def credit_card
#code
end
end
and the controller i created:
class PaypalOrdersController < ApplicationController
def new
#paypal_order = PaypalOrder.new
end
def create
#paypal_order = current_order.build_paypal_order(params[:paypal_order])
if #paypal_order.save
# ...
else
render :action => "new"
end
end
end
But I'm getting the following error:
NameError in PaypalOrdersController#create
undefined local variable or method `current_order' for #<PaypalOrdersController:0xf7b0a34>
Why am I not able to access the current_order and how can I successfully build paypal_order
EDIT: made the following change:
class PaypalOrdersController < Spree::BaseController
works fine now!!
Why not change line 8 of your controller to:
#paypal_order = PaypalOrder.new(params[:paypal_order])
?
If it is Rails 4 you'll have to do:
#paypal_order = PaypalOrder.new(params.require(:paypal_order).permit!)
As per the error, current_order method does not exist.
You need to create current_order method first before calling it.
I am assuming that your intention with current_order was to reuse the order instantiated by the new action...
Since instance variables don't live through more than one request you need to instantiate the paypalorder again.
#paypal_order = PaypalOrder.new(params[:paypal_order])
enter code here
Say user submits a form (creates new item in his account).
Before it goes to database - I want to do this:
params[:user] => current_user.id
# make a note of who is the owner of the item
# without people seing this or being able to change it themselves
# and only after that to save it to database
What's the best way to do it?
All I see in controller is this:
def create
#item = Item.new(params[:item])
...
end
And I'm not sure how to change values under params[:item]
(current_user.id is Devise variable)
Tried to do this:
class Item < ActiveRecord::Base
before_save :set_user
protected
def set_user
self.user = current_user.id unless self.user
end
end
And got an error:
undefined local variable or method `current_user'
It should be as simple as:
def create
#item = Item.new(params[:item])
#item.user = current_user
#...
end
You're getting an undefined local variable or method current_user error as current_user is not available in the model context, only controller and views.