Devise async with Mandrill mailer wrong number of arguments (1 for 2) - devise

I'm using delayed_job to handle background jobs. I recently came across the devse_async gem to integrated delayed_job and devise.
I am receiving a wrong number of arguments (1 for 2) error when delayed_job processes the email job in the queue. I should note I'm using Mandrill's API to send these emails.
delayed job console
Starting job worker
[Worker(host:MacBook-Pro.local pid:21007)] Job Devise::Async::Backend::DelayedJob#perform (id=1) RUNNING
[Worker(host:MacBook-Pro.local pid:21007)] Job Devise::Async::Backend::DelayedJob#perform (id=1) FAILED (0 prior attempts) with ArgumentError: wrong number of arguments (1 for 2)
[Worker(host:MacBook-Pro.local pid:21007)] 1 jobs processed at 30.3564 j/s, 1 failed
trace
wrong number of arguments (1 for 2)
/Users/Sites/app/mailers/devise_mailer.rb:6:in `confirmation_instructions'
devise_mailer
class DeviseMailer < MandrillMailer::TemplateMailer
include Devise::Controllers::UrlHelpers
include Devise::Mailers::Helpers
default from: 'no-reply#foobar.com'
def confirmation_instructions(record, token)
#resource = record
# Route mailer to send the proper subject and template
if #resource.pending_reconfirmation?
confirmation_template = 'Reconfirmation Instructions'
confirmation_subject = 'Confirm your new email'
else
confirmation_template = 'Confirmation Instructions'
confirmation_subject = 'Confirmation Email'
end
# Include proper greeting in email based on User type
recipient_name = nil
if #resource.type == "Business"
recipient_name = record.business_name
else
recipient_name = record.first_name
end
puts "sending confirmation email"
host = ActionMailer::Base.default_url_options[:host]
mandrill_mail template: confirmation_template,
subject: confirmation_subject,
from_name: 'foobar',
to: { email: 'contact#foobar.ca' },
vars: {
'FNAME' => recipient_name,
'LIST_COMPANY' => "foobar",
'HTML_LIST_ADDRESS_HTML' => "foobar",
'CONFIRMATION_LINK' => "%s/users/confirmation?confirmation_token=#{record.confirmation_token}" % ENV['MAIL_HOST']
# "http://0.0.0.0:3000/users/confirmation?confirmation_token=#{record.confirmation_token}"
},
async: true
end
end
registrations_controller.rb
def new
build_resource({})
resource.build_supp_form
respond_with self.resource
end
def create
super
end

The use of database encrypted tokens was introduced in devise 3.1 (https://github.com/plataformatec/devise/blob/master/CHANGELOG.md#310---2013-09-05), so your confirmation_instructions mailer method doesn't expect any token as the second parameter.
In fact you're not using that parameter anywhere in your method, notice you call record.confirmation_token.
Just remove the second parameter in the method signature and you should be good to go:
def confirmation_instructions(record)
...
end

Related

jwt error with method `verify_expiration'

I had a question when I try to use JWT to decode the token that is sent back from the frontend. When the token is decoded to be “2”, which is a user id for me to grab the user in the backend, I got this error: “NoMethodError: undefined method `include?’ for 2:Integer”, which is from the following codes in JWT source codes:
def verify_expiration
return unless #payload.include?('exp')
raise(JWT::ExpiredSignature, 'Signature has expired') if #payload['exp'].to_i <= (Time.now.to_i - exp_leeway)
end
What should I do to fix this?
my application controller file looks like this:
class ApplicationController < ActionController::API
def encode_token(payload)
JWT.encode(payload, 'secret')
end
def auth_header_token
request.headers['Authorization'].split(' ')[1]
end
def session_user
binding.pry
decoded_hash = decoded_token
if !decoded_hash.empty?
user_id = decoded_hash[0]["user_id"]
user = User.find_by :id=>user_id
end
end
def decoded_token
if auth_header_token
begin
JWT.decode(auth_header_token, 'secret',true, algorithm: 'HS256')
rescue JWT::DecodeError
[]
end
end
end
end
my session_controller looks like this:
class SessionController < ApplicationController
def login
user = User.find_by :email=>params[:email]
if user && user.authenticate(params[:password])
payload = user.id
token = encode_token(payload)
render json: {
user:user, include: ['order', 'favorites','orders.dishes','favorites.dishes'],
jwt:token
}
else
render json: {status: "error", message: "We don't find such an user according to your information,please try again."}
end
end
def auto_login
if session_user
render json: session_user
else
render json: {errors: "No User Logged In."}
end
end
end
Should I change the way I encode user_id?
Just found that the problem is I need to encode user_id as an object, not just the id itself, because JWT can check it as an object, and that is where the error message comes from. So, instead of
payload = user.id
token = encode_token(payload)
should have set:
payload = {user_id: user.id}
token = encode_token(payload)

How to Rspec system that ActionCable broadcast messages appear in the view

I want to test that messages that are broadcast when some background jobs are completed are actually appearing in the view.
I have unit tests for this which work fine. I would actually like to ensure that the JS gets run so that the view is updated with the correct message.
So far I have not been able to find any way to do this.
Here is the test I have where I would like to add the expectation for the broadcast message:
require 'rails_helper'
require 'sidekiq/testing'
RSpec.describe 'sending a quote request', js: true do
let(:quote_request_form) { build(:quote_request_form) }
before do
create(:job_rate, :proofreading)
create(:proofreader_with_work_events)
end
it 'shows the user their quotation' do
visit new_quote_request_path
fill_in("quote_request_form_name", with: quote_request_form.name)
fill_in("quote_request_form_email", with: quote_request_form.email)
attach_file('customFile','/Users/mitchellgould/RailsProjects/ProvenWordNew/spec/test_documents/quote_request_form/1.docx', make_visible: true)
click_on "Submit"
Sidekiq::Testing.inline! do
page.execute_script("$('#invisible-recaptcha-form').submit()")
expect(current_path).to eq(quote_confirm_path)
#add expectation here:
expect(page).to have_content("Calculating Time Required")
page.execute_script("window.location.pathname = '#{quotation_path(Quotation.first)}'")
expect(current_path).to eq(quotation_path(Quotation.first))
expect(page).to have_content("Here is your quotation")
end
end
end
Here is my .coffee file:
$(document).on 'turbolinks:load', ->
if $("meta[name='current_user']").length > 0
App.notification = App.cable.subscriptions.create "NotificationChannel",
connected: ->
# Called when the subscription is ready for use on the server
disconnected: ->
# Called when the subscription has been terminated by the server
received: (data) ->
$('.background_message').html(data.content)
if data.head == 302 && data.path
window.location.pathname = data.path
else if App.notification
App.quotation.unsubscribe()
delete App.notification
Here is one of the background jobs that broadcasts a message when its done:
class CreateParagraphDetailsJob < ApplicationJob
queue_as :default
after_perform :broadcast_message, :calculate_proofreading_job_duration
def perform(document, proofreading_job_id, current_user_id)
document.create_paragraph_details
end
private
def calculate_proofreading_job_duration
CalculateDurationJob.set(wait: 1.seconds).perform_later proofreading_job_id, current_user_id
end
def broadcast_message
ActionCable.server.broadcast "notification_channel_user_#{current_user_id}", content: "Analyzed writing quality of paragraphs"
end
def document
self.arguments.first
end
def proofreading_job_id
self.arguments.second
end
def current_user_id
self.arguments.last
end
end
Any ideas on how to do this?

Loading ISSUE in rails

I'm facing a problem with Loading a Constant in Rails console (rails console). Here how my structure look like this
- app
- controllers
- models
- earning
- daily_earning.rb
- monthly_earning.rb
- weekly_earning.rb
- yearly_earning.rb
- views
Some more information
I also have a rake which look like this
namespace :past_days do
desc "Past 7 Days Earning"
task :earning => :environment do
puts $:.select { |i| i=~ /models/ }.to_yaml
7.downto(1).each do |i|
start_date = i.days.ago.beginning_of_day
puts "====== Dumping past #{start_date.strftime('%A')} earning ====="
end_date = start_date.end_of_day
Performer.top_daily_earners(start_date,end_date)
puts "====== Dumped #{start_date.strftime('%A')} earning !!! ======="
puts
end
end
end
And the top_daily_earners method look like this If you check this #klass = DailyEarning
def top_daily_earners(start_date=nil,end_date=nil)
unless start_date or end_date
date = 1.day.ago
#start_date,#end_date = date.beginning_of_day,date.end_of_day
end
if start_date and end_date
#start_date,#end_date = start_date,end_date
end
#klass = DailyEarning
#earning_performers = retrieve_earnings
puts "COUNT -----"
puts #earning_performers.count
puts ""
store_earning
end
Question :
Now when I run rake task bundle exec rake past_days:earning (Rake run without any error) all work fine but when I run this
rails console see attach screenshot
I get errors NameError: uninitialized constant DailyEarning and I have manually require the file as can be seen the above screenshot
So the POINT of all the above question is why the error on rails console (NameError: uninitialized constant DailyEarning) and why not the error in
rake task
Attaching DailyEarning Code based on #dtt comment
puts 'DailyEarning'
class DailyEarning
include Mongoid::Document
store_in session: "writeable"
field :performer_id, :type => Integer
field :user_id,:type => Integer
field :stage_name,:type => String
field :full_name,:type => String
field :start_date,:type => DateTime
field :end_date,:type => DateTime
field :amount,:type => BigDecimal
before_create :other_details
## Please avoid using default scope because it AFAIK it make the date parameter as static
class << self
def default_scoping
where(:start_date.gte => 1.day.ago.beginning_of_day).and(:end_date.lte => 1.day.ago.end_of_day)
end
end
private
def other_details
## Fetch from Mongo Instead of Mysql to avoid the slow sql query
performer_source = PerformerSource.where(performer_id: performer_id).only([:stage_name,:user_id]).first
self.user_id = performer_source.user_id
self.stage_name = self.stage_name
#self.full_name = self.full_name
end
end
My understanding is that to autoload a model in a folder you would need to namespace it:
to autoload the model in app/models/earning/daily_earning.rb
class Earning::DailyEarning
end
it may be that instead you could use:
module Earning
class DailyEarning
end
end

Rails 3 - wrong number of arguments (0 for 1)

I'm trying to build an authentication system for my application
to register the user fills in a form which then submitted calls user#signup
def signup
#user = User.new(params[:user])
#user.password(params[:user][:password])
#user.save
end`
My user model contains
def password(pass)
#password=pass
self.salt = User.random_string(10)
self.hashed_password = User.encrypt(#password, self.salt)
end
When trying to debug, I checked what is contained in my #user hash:
After #user = User.new(params[:user]) the hash contains my parameter exept 'id', 'hashed_password' and 'salt' (plus the timestamps) which are empty
After #user.password(params[:user][:password]) the hash now contains values for 'hashed_password' and 'salt'
#user.save returns
ArgumentError in UsersController#signup
wrong number of arguments (0 for 1)
Stack trace
Started POST "/users/signup" for 127.0.0.1 at Fri Oct 05 14:23:49 +1000 2012
Processing by UsersController#signup as HTML
Parameters: {"user"=>{"last_name"=>"last", "first_name"=>"first", "login"=>"myusername", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "email"=>"me#gmail.com", "dob(1i)"=>"1980", "dob(2i)"=>"4", "dob(3i)"=>"2"}, "authenticity_token"=>"R8/BNPox9F9rkUXZQ84xjnsplRjqLJYe35EtGjKEAWk=", "utf8"=>"✓", "commit"=>"Create User"}
(0.1ms) begin transaction
(0.0ms) rollback transaction
Completed 500 Internal Server Error in 10ms
ArgumentError (wrong number of arguments (0 for 1)):
app/controllers/users_controller.rb:17:in `signup'
app/controllers/users_controller.rb:16:in `signup'
Rendered /Library/Ruby/Gems/1.8/gems/actionpack-3.2.8/lib/action_dispatch/middleware/templates/rescues/_trace.erb (1.1ms)
Rendered /Library/Ruby/Gems/1.8/gems/actionpack-3.2.8/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb (1.1ms)
Rendered /Library/Ruby/Gems/1.8/gems/actionpack-3.2.8/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb within rescues/layout (7.8ms)
Any idea?
I'm guessing you have a column called password and/or you have a validation referencing :password.
In that case, the problem is that you've overridden password -- the implicit zero-parameter getter -- with password(pass), a one-parameter setter. You should instead move your setter to password=, and make a getter that always returns nil:
def password
nil
end
def password=(pass)
#password = pass
self.salt = User.random_string(10)
self.hashed_password = User.encrypt(#password, self.salt)
end
As a bonus, this means you can eliminate the explicit password-setting call, since User.new(:password => 'xyz') will call password= automatically.

Google task API authentication issue ruby

I am having the problem to authenticate a user for google tasks.
At first it authenticates the user and do things perfect. But in the second trip it throws an error.
Signet::AuthorizationError - Authorization failed. Server message:
{
"error" : "invalid_grant"
}:
following is the code:
def api_client code=""
#client ||= (begin
client = Google::APIClient.new
client.authorization.client_id = settings.credentials["client_id"]
client.authorization.client_secret = settings.credentials["client_secret"]
client.authorization.scope = settings.credentials["scope"]
client.authorization.access_token = "" #settings.credentials["access_token"]
client.authorization.redirect_uri = to('/callbackfunction')
client.authorization.code = code
client
end)
end
get '/callbackfunction' do
code = params[:code]
c = api_client code
c.authorization.fetch_access_token!
result = c.execute("tasks.tasklists.list",{"UserId"=>"me"})
unless result.response.status == 401
p "#{JSON.parse(result.body)}"
else
redirect ("/oauth2authorize")
end
end
get '/oauth2authorize' do
redirect api_client.authorization.authorization_uri.to_s, 303
end
What is the problem in performing the second request?
UPDATE:
This is the link and parameters to user consent.
https://accounts.google.com/o/oauth2/auth?
access_type=offline&
approval_prompt=force&
client_id=somevalue&
redirect_uri=http://localhost:4567/oauth2callback&
response_type=code&
scope=https://www.googleapis.com/auth/tasks
The problem is fixed.
Solution:
In the callbackfunction the tokens which are received through the code provided by the user consent are stored in the database.
Then in other functions just retrieve those tokens from the database and use to process whatever you want against the google task API.
get '/callbackfunction' do
code = params[:code]
c = api_client code
c.authorization.fetch_access_token!
# store the tokens in the database.
end
get '/tasklists' do
# Retrieve the codes from the database and create a client
result = client.execute("tasks.tasklists.list",{"UserId"=>"me"})
unless result.response.status == 401
p "#{JSON.parse(result.body)}"
else
redirect "/oauth2authorize"
end
end
I am using rails, and i store the token only inside DB.
then using a script i am setting up new client before calling execute, following is the code.
client = Google::APIClient.new(:application_name => 'my-app', :application_version => '1.0')
client.authorization.scope = 'https://www.googleapis.com/auth/analytics.readonly'
client.authorization.client_id = Settings.ga.app_key
client.authorization.client_secret = Settings.ga.app_secret
client.authorization.access_token = auth.token
client.authorization.refresh_token = true
client.authorization.update_token!({access_token: auth.token})
client.authorization.fetch_access_token!
if client.authorization.refresh_token && client.authorization.expired?
client.authorization.fetch_access_token!
end
puts "Getting accounts list..."
result = client.execute(:api_method => analytics.management.accounts.list)
puts " ===========> #{result.inspect}"
items = JSON.parse(result.response.body)['items']
But,it gives same error you are facing,
/signet-0.4.5/lib/signet/oauth_2/client.rb:875:in `fetch_access_token': Authorization failed. Server message: (Signet::AuthorizationError)
{
"error" : "invalid_grant"
}
from /signet-0.4.5/lib/signet/oauth_2/client.rb:888:in `fetch_access_token!'
Please suggest why it is not able to use the given token? I have used oauth2, so user is already authorized. Now i want to access the api and fetch the data...
===================UPDATE ===================
Ok, two issues were there,
Permission is to be added to devise.rb,
config.omniauth :google_oauth2, Settings.ga.app_key,Settings.ga.app_secret,{
access_type: "offline",
approval_prompt: "" ,
:scope => "userinfo.email, userinfo.profile, plus.me, analytics.readonly"
}
refresh_token must be passed to the API call, otherwise its not able to authorize.
I hope this helps to somebody, facing similar issue.