Run a method after 5 minutes of creation of record in model rails - ruby

I'm new to rails,
If I send a request to any user and in case if there is not any response from there with in 5 minutes then request send to another user.
How can I do this in rails please suggest me.

You can make use of run_at option provided by delayed job.
class RecordModel < ActiveRecord::Base
after_create :make_delayed_entry
def make_delayed_entry
Delayed::Job.enqueue(SendMailAndWaitJob.new(parameters), 0, 5.minutes.from_now, :queue => "queue_name")
end
end
SendMailAndWaitJob its the class file(lib file) where you can check request accepted or not.

Related

Rails 6 Active Job. Why I can't send async tasks?

I was following the Action Mailer guideon https://guides.rubyonrails.org/action_mailer_basics.html#calling-the-mailer
I did almost the same of what tutorial show. The controller:
def create
#user = User.create(user_params)
if #user && UserMailer.with(user: #user).welcome_email.deliver_later
token = encode_token({ user_id: #user.id })
render json: { token: token }, status: :created
else
render json: #user.errors.messages, status: :bad_request
end
end
The Mailer:
class UserMailer < ApplicationMailer
default from: 'notifications#example.com'
def welcome_email
#user = params[:user]
#url = 'http://example.com/login'
mail(to: #user.email, subject: 'Welcome to My Awesome Site')
end
end
But when I make the request Active Job yells:
ActiveJob::SerializationError => "Unsupported argument type: User"
It works correctly using deliver_now
This shows that we have a problem with :async adapter. But as the guide says:
Active Job's default behavior is to execute jobs via the :async adapter. So, you can use deliver_later to send emails asynchronously. Active Job's default adapter runs jobs with an in-process thread pool. It's well-suited for the development/test environments, since it doesn't require any external infrastructure, but it's a poor fit for production since it drops pending jobs on restart. If you need a persistent backend, you will need to use an Active Job adapter that has a persistent backend (Sidekiq, Resque, etc).
So what I'm missing here?
It's not working because ActiveJob doesn't support the objects. To make it accessible you have to either convert it into json string and then deserialise in the mailer method.
Try sending the object as json string and retrieve the id value, then use:
#user = User.find(params[:user_id])
Another approach is to use Resque or Sidekiq to process these jobs. They come really handy.
Another sources to help people out:
https://stackoverflow.com/a/40896988/5832250
If you want to go with rails guides and learn more:
https://guides.rubyonrails.org/active_job_basics.html#globalid

creating stub for after_create hook in rspec

my model code is:
class User < ActiveRecord::Base
after_create :create_node_for_user
def create_node_for_user
UserGraph.create(user_id: self.id)
end
end
and test for User model:
it "create node in graph database on user creation" do
userr = FactoryGirl.build(:user)
UserGraph.should_receive(:create).with(user_id: userr.id)
userr.save
end
but my test is failing with message
Failure/Error: userr.save
<UserGraph (class)> received :create with unexpected arguments
expected: ({:user_id=>nil})
got: ({:user_id=>94})
what might be wrong?
The explanation given by Yves is correct: the user id is nil until the record is saved because it is autogenerated by the DB. Here's an alternate approach:
it "create node in graph database on user creation" do
userr = FactoryGirl.build(:user)
create_args = nil
UserGraph.should_receive(:create) { |*args| create_args = args }
userr.save
expect(create_args).to eq(:user_id => userr.id)
end
Essentially, this moves the expectation about what the arguments should be so that it comes after the record has been saved, when the record has an id.
The Problem is that the userr you build with FactoryGirl does not have an ID. Thats why the expectation tells you that you expected :user_id=>nil. The ID will be generated when AR saves the record, so there is no way that you can guess the generated ID ahead of time. You could use a less restrictive assertion on the mock:
UserGraph.should_receive(:create).with(hash_including(:user_id))
This will verify that a hash is passed with a :user_id key. You can find more about hash_including here: http://rubydoc.info/gems/rspec-mocks/RSpec/Mocks/ArgumentMatchers:hash_including
Another thing you can try (not sure if it works) is to match against the kind_of matcher of rspec. This would make sure that a number was passed with :user_id
UserGraph.should_receive(:create).with(:user_id => kind_of(Numeric))

Rails select Empty method?

I have the following select in a helper for my Rails app:
def unit_select
Unit.all.map{|unit| unit.calls.empty? ? [unit.unit_name, unit.id] : ["#{unit.unit_name} (on call)", unit.id] }
end
What this does is look for a unit that has a call and if that unit has a call append (on call) next to the unit in a form. The problem I'm seeing with this is when it goes to look for unit.calls.empty? it's taking into account call records with a status of "closed" which should not be taken into account.
Is there another method I can use (or write) that will allow me to look at unit.calls with passing whether or not the call is in call_status open?
In your Unit model you can override the empty? method on the calls association:
class Unit < ActiveRecord::Base
has_many :calls do
def empty?
self.where(:call_status => :open).any?
end
end
end

Sinatra + Rack::Test + Rspec2 - Using Sessions?

It's the first time I'm working with Sinatra and I just can't get sessions to work in my tests. I have enable :sessions in my app.
I tried:
get "/controller/something", {}, "rack.session" => {:session => "Aa"}
or
get "/controller/something", {}, "session" => {:session => "Aa"}
But no session variables are being set in my request. I've looked around the web and tried several suggestions but nothing seems to work. Am I missing something?
Thanks!
Rack doesn't support passing in sessions via the request anymore (Rack >= v1.0). Read this post for more detailed information on that.
The best way to set a session variable in your app is to call an action inside of your application that will set the session variable. For instance, if you have a route inside your app that sets a session variable like this:
post '/set_sess_var/:id'
session[:user_id] = params[:id]
end
Let's pretend there's another route that you actually wanted to test which is using the session variable like this:
get '/get_user_attributes'
User.find(session[:user_id]).attributes
end
Then in your tests, you should first call the route which sets the session, then go onto another route which uses it. Here is rspec notation, since that is what I use for testing:
it "should print out user attributes" do
user_id = 1
post '/set_sess_var/' + user_id
get '/get_user_attributes'
last_response.body.should == User.find(user_id).attributes
end
If you were going to be using the route frequently in your tests, then you could write a method to accomplish this in your test file (if you're using Rspec, then this method could go in your spec_helper.rb or in your controller_spec.rb file):
def set_session_var(user_id)
post '/set_sess_var/' + user_id
end
and then call it in your tests when you needed it to be set:
it "should print out user attributes" do
set_session_var(1)
get '/get_user_attributes'
last_response.body.should == User.find(1).attributes
end
You need to use the keys that will end up in env:
get "/controller/something", {}, "rack.session" => {:session => "Aa"}

Why is this an endless loop? ActiveRecord

class Account < ActiveRecord::Base
after_update :give_user_credit, :on => :update
def give_user_credit
credit = User.current_user.credit + 3.8
User.current_user.update_attribute(:credit, credit)
end
end
When I use this the server hangs and when I come back to the application after a full reboot my credit is in the £1000's.
Whats going on here..
Thanks :D
Looks to me like you are setting the :give_user_credit callback to run every time that the record is updated.
But since the callback updates the record, it then triggers the callback again, which will continue on and on...
You could also use this private method:
model.credit = 10
model.send(:update_without_callbacks)

Resources