Delayed_job ActionMailer caching? - ruby-on-rails-3

I currently call a delayed_job (gem delayed_job_mongoid) to send a confirmation email. However, it doesn't seem to use the latest data I'm passing, and instead uses a cached version. See below:
My controller:
...
_user.calculate_orders
_user.save
_user.reload
Mailer.delay.order_reported(_user)
...
The Mailer
class Mailer < Devise::Mailer
def order_reported(to_user)
#to_user = to_user
email_with_name = "#{#to_user.name} <#{#to_user.email}>"
mail(:to => email_with_name, :subject => "Test email")
end
end
For example, if an attribute _user.total_orders = 3 gets updated to 5 and saved. It's correctly reflected in the database, the delayed job DB record contains the updated info of 5, but when email is sent, it uses cached info of 3.
I've also tried calling the method via rails console:
This works and uses the parameter that's passed and updated info
Mailer.order_reported(u).deliver
This doesn't work and uses cached data
Mailer.delay.order_reported(u)

I had a very similar problem with Sidekiq recently, related to passing in a serialized version of my User object instead of just an ID.
I would refactor slightly to only pass simple objects as parameters in the delayed method. So, you could do this instead:
Mailer.delay.order_reported(_user.id)
and then update the method to read as follows:
class Mailer < Devise::Mailer
def order_reported(to_user_id)
#to_user = User.find(to_user_id)
email_with_name = "#{#to_user.name} <#{#to_user.email}>"
mail(:to => email_with_name, :subject => "Test email")
end
end
That would ensure you always get a fresh user object when your mailer is actually delivered.

Related

Rails 3 and update_attributes

I am moving an application from Rails 2.2.2 to Rails 3. I have a form that is used to update a user's info that was fully functional in Rails 2 but breaks in Rails 3
When the form is submitted, a method is called that creates the user object and then tries to do the update like this:
if #user.update_attributes params[:user] ## line 126
Then the controller throws this exception:
undefined method `update_attributes' for #<ActiveRecord::Relation:0xacfc178>
in: app/controllers/admin_controller.rb:126:in `save_user'
So it looks like ActiveRecord in Rails 3 is returning a different type of object? One that doesn't inherit update_attributes? How do I fix this?
Here is the full controller method in question:
def save_user
#needs_password_gen = "YES"
#user = B2bUser.where("id = ?",params[:id])
#needsAPICredentials = false
##### Make sure thay gave us an email address
if !params[:user][:email] || !validEmailAddress(params[:user][:email].to_s)
flash[:warning] = "Valid email address is required."
redirect_to :controller => "admin/edit_user/#{#user.id}" and return
end
#user.first.update_attributes params[:user]
end
THANKS
Looks like your #user object may be an array. You are probably using :where to query and are forgetting to pop it off the array. Try this:
#user.first.update_attributes params[:user]

Access a mailer method without sending the email

I have a working mailer in Rails 3.
I want to have some logic inside the mailer methods which will decide if to email it or not.
I cannot have this logic outside the mailer, since I am using Delayed_job with a delay. I delay the email for an hour. If a condition hasn't been met, I send the email.
1. Is there a way to enter the mailer method and not complete the sending (gracefully)
2. Is there a better way of achieve what I am trying?
Code Sample
This is the mailer
class MessagesMailer < ActionMailer::Base
def message_notification(user, message)
#user = user
#msg = message
mail(to: #user.email, from: "#{#msg.sender.name} <me#myapp.com>", subject: "You have recieved a new message from #{#msg.sender.name}")
end
end
MessagesMailer.delay(:run_at => 5.minutes.from_now).message_notification(#user, #message) is being called throughout the app, particularly from MessagesController
I want to check if #user has already read that message. If so, not send the email.
Delay_job would perform message_notification in 5 minutes, but once that method is complete the email is sent regardless of what happens inside of it.
Is there a way to cancel delivery upon a condition (#user read the message)?

No POST from facebook using real-time updates in Rails, Heroku and Koala

This question is an expanded version of Facebook Real-time updated does not call our servers, that seems to be dead. Also, Realtime updates internal server error on Heroku using Koala is not helpful because I'm subscribing from the heroku console as pjaspers suggested.
I have an app (ruby 1.9.2p290 and Rails 3.1.3) that connects to facebook to get data from the current user. Everything is working ok with the koala gem (v1.2.1), but I'm polling the fb servers every time the users logs in. I would like to use facebook real-time updates, and I have read the following:
Koala manual on fb realtime updates: https://github.com/arsduo/koala/wiki/Realtime-Updates
Facebook page on realtime: https://developers.facebook.com/docs/reference/api/realtime/
I have set up the system in test mode and deployed to heroku successfully. I can subscribe to the user object and I get the GET request to my server, but no POST with updated information is ever received from facebook. If I issue a POST to my server manually everything works.
More information:
routes.rb
get '/realtime' => 'realtime#verify'
post '/realtime' => 'realtime#change'
generating
realtime GET /realtime(.:format) {:controller=>"realtime", :action=>"verify"}
POST /realtime(.:format) {:controller=>"realtime", :action=>"change"}
The controller (mock version, only to test if it's working):
class RealtimeController < ApplicationController
def verify
render :text => params["hub.challenge"]
end
def change
puts params.inspect
render :nothing => true
end
end
The subscription from the heroku console:
irb(main):004:0> #updates = Koala::Facebook::RealtimeUpdates.new(:app_id => ENV['FACEBOOK_APP_ID'], :secret => ENV['FACEBOOK_APP_SECRET'])
=> #<Koala::Facebook::RealtimeUpdates:0x00000004f5bca8 #app_id="XXXXXXX", #app_access_token="XXXXXXX", #secret="XXXXXXX", #graph_api=#<Koala::Facebook::API:0x00000004a8d7a8 #access_token="XXXXXXX">>
irb(main):005:0> #updates.list_subscriptions
=> [{"object"=>"user", "callback_url"=>"http://blah-blah-0000.herokuapp.com/realtime", "fields"=>["education", "email", "friends", "name", "website", "work"], "active"=>true}]
I don't know what to do next...
Maybe I am not triggering the correct changing events?
How do I see the list of users of my app? (right now it's a test app and the only user would be me)
Anyone with this kind of issue?
Is something wrong in the code?
Is facebook down? Is it the end of Internet?
Thank you for the help :)
You need to respond to the GET request with a challenge response. I have the same route for both POST and GET requests and use the following code:
route:
match "facebook/subscription", :controller => :facebook, :action => :subscription, :as => 'facebook_subscription', :via => [:get,:post]
controller:
def realtime_request?(request)
((request.method == "GET" && params['hub.mode'].present?) ||
(request.method == "POST" && request.headers['X-Hub-Signature'].present?))
end
def subscription
if(realtime_request?(request))
case request.method
when "GET"
challenge = Koala::Facebook::RealtimeUpdates.meet_challenge(params,'SOME_TOKEN_HERE')
if(challenge)
render :text => challenge
else
render :text => 'Failed to authorize facebook challenge request'
end
when "POST"
case params['object']
# Do logic here...
render :text => 'Thanks for the update.'
end
end
end
That should get you going with things... Note that to make a subscription I am using this:
#access_token ||= Koala::Facebook::OAuth.new(FACEBOOK_API_KEY,FACEBOOK_API_SECRET).get_app_access_token
#realtime = Koala::Facebook::RealtimeUpdates.new(:app_id => FACEBOOK_API_KEY, :app_access_token => #access_token)
#realtime.subscribe('user', 'first_name,uid,etc...', facebook_subscription_url,'SOME_TOKEN_HERE')
I think the key is that you properly respond to the GET request from Facebook. They use this to verify that they are contacting the correct server prior to sending confidential info about their users.
Also -- its been a while since I've looked at this, but if I remember correctly, I seem to recall having issues with anything besides default protocol port specifications within the callback URL. Ex: http://www.something.com:8080/subscription did not work -- it had to be http://www.something.com/subscription
Not sure if this might be the case with you, but make sure that you have permissions to access the object -user, permissions, page- properties (location, email, etc.)
In my case, I was trying to get notifications for changes on the location property without my app requiring the user_location permission. For the user object, look at the Fields section in this page https://developers.facebook.com/docs/reference/api/user/
Does Facebook know where your application is? Does it have a URL that it can resolve?

pass extra instance vars to devise_invitable email template

I'm overriding the devise_invitable controller and in my create method I'd like to pass extra values to the invitations_instructions email template. For example group name, has anyone been successful at this, if so please give me some clues here.
what I've tried...
my #group in my Users::InvitesController < Devise::InvitationsController create method is undefined in the email template.
tried to add :skip_invitation => true in my create and then send the email manually like...
self.resource = resource_class.invite!(params[resource_name], current_inviter, :skip_invitation => true)
::Devise.mailer.invitation_instructions(self.resource).deliver
but this gives the wrong number of arguments so there is something I'm not reading correctly from the documentation.
UPDATE - possible solution
The only way appears to be this, but I'm curious if there is a better way that uses the templates provided and devise mailer
in my /app/controller/users/InvitesController#create
(inherits from InvitationsController)
self.resource = resource_class.invite!(params[resource_name], current_inviter) do |u|
u.skip_invitation = true
end
UserMailer.invitation_instructions(self.resource, current_inviter, #object).deliver
where UserMailer is my general (standard) action mailer and goes something like...
def invitation_instructions(resource, inviter, object)
#resource = resource
#object = object
#inviter = inviter
mail(:to => resource.email, :subject => 'New invitation from ' + inviter.first_name)
end
There is a cleaner way to achieve the solution that you're looking for, and that is to use Devise's own procedures for overriding mailer templates.
First create a custom mailer that extends from Devise::Mailer:
class MyMailer < 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
end
Then, in config/initializers/devise.rb, set config.mailer to "MyMailer". This is going to allow you to override ANY email that devise sends out and customize to your liking.
Then for you, you can override invitable_instructions like this:
def invitation_instructions(record, token, opts={})
# Determine a way to set object -- likely through a query of some type
#object = get_object_for(record)
opts[:subject] = 'New invitation from ' + inviter.first_name
super
end
The main sticking point from your example was passing in extra data to set #group/#object. To do that, I would personally go with some type of query within the mailer (not clean, but it is encapsulated and therefore less "magical") to retrieve those objects.
Additionally, if you wanted to use custom email templates instead of devise's, you can simply add them to the app/views/my_mailer/ directory and devise will prefer emails in that directory over emails from the gem.

Rails 3: Manipulating devise "resource" in controller?

I'm using devise & devise_invitable in a rails 3 project, and I'm trying to manipulate some of the 'User' object fields in the devise controller.
The action is question is this:
def update
self.resource = resource_class.accept_invitation!(params[resource_name])
resource.first_name = 'Lemmechangethis'
if resource.errors.empty?
set_flash_message :notice, :updated
sign_in(resource_name, resource)
respond_with resource, :location => after_accept_path_for(resource)
else
respond_with_navigational(resource){ render_with_scope :edit }
end
end
I'd have thought that the (commented out) resource.first_name call would influence resource in much the same way as a model - but it doesn't seem to. I'm still getting a 'blank' validation error on this form.
So, the question is, how do I specify values to the User model in devise (and/or devise_invitable) that will actually be subject to verification?
Any suggestions appreciated,
John
resource does return a User models instance. So the resource.first_name = 'Lemmechangethis' statement does change your User models instance but it doesnot trigger your User models validations, which is probably why resource.errors always returns an empty array. One way to trigger the User models validation is to call resource.valid? for example. You can then check the resource.errors array for specific error messages.