Resend Confirmation instructions to the user using devise - ruby-on-rails

I want to resend confirmation instructions to the user if the user has not confirmed his account after sign up. I am using rails 4. I have this code but its not working.
def resend_confirmation_instructions
users = User.where('confirmed_at IS ?', nil)
users.each do |user|
if (user.created_at < (Date.today - 8))
end_date = (user.created_at.to_date)
start_date = Date.today()
date_diff = (start_date - end_date).to_i
if (date_diff == 3) or (date_diff == 6)
user.send_confirmation_instructions
end
end
end
end
I am using whenever gem to schedule this task every day.

Your code seems alright, but you might have a performance problem when you do users.each with too many users.
Basically users will be an ActiveRecord::Relation, that is an object containing a collection of query parameters. Calling each on it will convert it to an Array as cause the query to be executed. If you have too many objects, that might kill your DB or ruby process.
What about using find_each, to see if it makes a difference?
You are also loading a lot of users that you will probably don't need, as you're executing this check inside the loop:
user.created_at < (Date.today - 8)
That's something else that can affect performance and kill your process. It would be better to add that filter to the active record query.
Try with:
def resend_confirmation_instructions
today = Date.today
users = User.where(confirmed_at: nil)
.where('created_at < ?', (today - 8))
users.find_each(batch_size: 100) do |user|
end_date = user.created_at.to_date
date_diff = (today - end_date).to_i
if date_diff == 3 || date_diff == 6
user.send_confirmation_instructions
end
end
end

Related

Limit requests that users can send to 3 every 30 days. Ruby on Rails

I am learning to program and I am trying with Ruby on rails. I would like to restrict one functionality that allows users to send me introduction requests (3 introduction request per 30 days). I am not sure if I have to create a method first, for example:
def month
where("created_at <?", Date.today - 30.days))
end
I don't know if that method is correct and if I can integrate it within this piece of code:
def create
#introduction = CompanyIntroduction.create(intro_params)
if current_user #Admin can edit the user name and email
#introduction.user = current_user
#introduction.user_name = current_user.full_name
#introduction.user_email = current_user.email
end
if #introduction.save
flash[:success] = "Thank you. Your request is being reviewed by TechIreland."
else
flash[:error] = #introduction.errors.full_messages
end
redirect_back(fallback_location: user_companies_path)
end
You're close. You have the time comparison backwards though, and the method (assuming it's on your model) should be a class method or a scope.
scope :in_the_last_month, -> { where('created_at > ?', Date.today - 30.days) }
# or more elegantly
scope :in_the_last_month, -> { where(created_at: 30.days.ago..) }
Then in the controller you can check how many requests were made recently.
if CompanyIntroduction.in_the_last_month.count >= 3
# give some error
else
# continue
end
This code is simple enough that you don't actually need to make it into a method, just the code in the controller is probably fine.
if CompanyIntroduction.where(created_at: 30.days.ago..).count >= 3

Rails Query a List for a CRON Job

I'm a complete novice with CRON jobs but I think I have that set up correctly.
Ultimately what I'm trying to do is send an email every day at 8:00 am to users (and a couple others) that have not logged in within the last 3 days, have not received the email, AND are marked as active OR temp as a status.
So from querying the db in console I know that I can do:
first = User.where(status: 'active').or(User.where(status: 'temp'))
second = first.where("last_login_at < ? ", Time.now-3.days)
third = second.where(notified: false)
That's not certainly clean but I was struggling with finding a contained query that grabbed all that data. Is there a cleaner way to do this query?
I believe I have my cron job set up correctly using a runner. I have whenever installed and in my schedule.rb I have:
every 1.day, at: '8:00 am' do
runner 'ReminderMailer.agent_mailer.deliver'
end
So under app > mailer I created ReminderMailer
class ReminderMailer < ApplicationMailer
helper ReminderHelper
def agent_reminder(user)
#user = user
mail(to: email_recipients(user), subject: 'This is your reminder')
end
def email_recipients(agent)
email_address = ''
email_addresses += agent.notification_emails + ',' if agent.notification_emails
email_addresses += agent.manager
email_address += agent.email
end
end
Where I'm actually struggling is where I should put my queries to send to the mailer, which is why I built a ReminderHelper.
module ReminderHelper
def applicable_agents(user)
agent = []
first = User.where(status: 'active').or(User.where(status: 'temp'))
second = first.where("last_login_at < ? ", Time.now-3.days)
third = second.where(notified: false)
agent << third
return agent
end
end
EDIT: So I know I could in theory do a chain of where queries. There's gotta be a better way right?
So what I need help on is: do I have the right structure in place? Is there a cleaner way to query this data in ActiveRecord for the CRON job? Is there a way to test this?
Try combining them together as if understand the conditions correct
Have not logged in within the last 3 days,
Have not received the email
Are marked as active OR temp as a status
User.where("last_login_at < ? ", 3.days.ago).
where(notified: false).
where(status: ['active', temp])
module ReminderHelper
def applicable_agents(user)
User.where("last_login_at < ? ", 3.days.ago).
where(notified: false).
where(status: ['active', temp])
end
end
You don't need to add/ assign them to array. Because this relation is already like an array. You can use .to_a if you need array. If you just want to iterate over them then users.each should work fine.
Update
class User
scope :not_notified, -> { where(notified: false) }
scope :active_or_temp, -> { where(status: ['active', 'temmp']) }
scope :last_login_in, -> (default_days = 3) { where("last_login_at < ?", default_days.days.ago) }
end
and then use
User.not_notified.active_or_temp.last_login_in(3)
Instead of Time.now-3.days it's better to use 3.days.ago because it keeps time zone also in consideration and avoids unnecessary troubles and failing test cases.
Additionally you can create small small scopes and combine them. More read on scopes https://guides.rubyonrails.org/active_record_querying.html

rake task to expire customers points balance

i am trying to work out how to write a rake tasks that will run daily and find where the days remaining is 0 to update the column amount to zero.
I have the following methods defined in my model, though they don't exactly appear to be working as I am getting the following error in the view
undefined method `-#' for Mon, 27 Jun 2016:Date
def remaining_days
expired? ? 0 : (self.expire_at - Date.today).to_i
end
def expired?
(self.expire_at - Date.today).to_i <= 0
end
def expire_credits
if expired?
self.update(:expire_at => Date.today + 6.months, :amount => 0)
end
end
with the rake tasks i have never written of these and i thought i would be able to call a method of StoreCredit that would expire the points if certain conditions are met but i am not sure how this all works
task :expire_credits => :environment do
puts 'Expiring unused credits...'
StoreCredit.expire_credits
puts "done."
end
# model/store_credit.rb
# get all store_credits that are expired on given date, default to today
scope :expire_on, -> (date = Date.current) { where("expire_at <= ?", date.beginning_of_day) }
class << self
def expire_credits!(date = Date.current)
# find all the expired credits on particular date, and update all together
self.expire_on(date).update_all(amount: 0)
end
end
Since it's a rake task, I think it's more efficient to update all expired ones together
#rake file
result = StoreCredit.expire_credits!
puts "#{result} records updated"
Retrieve Record Count Update
class << self
def expire_credits!(date = Date.current)
# find all the expired credits on particular date, and update all together
records = self.expire_on(date)
records.update_all(amount: 0)
records.length
end
end
You call class method but define instance method. You will need to define class method:
def self.expire_credits

Execute before_filter every 5min , in Rails 4

I have multiple (3) before_filter functions in Application_controller.
They look similar to this:
def give_vip_recomend
#list = List.where("billing_id =?", 5).first
if #list.present?
#v = Girl.where("country_id=?",#list.country_id).where("vip_recomend >= ?", Time.now).count
#b = Girl.where("country_id=?",#list.country_id).where("recomend >= ?", Time.now).count
#count = #v+#b
if #count < 8 #pārbauda recomended anketas jau ir 4
#advertisement = Girl.find_by_id(#list.advertisement_id)
#advertisement.vip_highlight = Time.now + 2.days
#advertisement.paid = true
#advertisement.save
#list.delete
flash[:notice] = "Izdevās piešķirt vip_highlight - gaidīšanas sarakstā esošam"
end
end
end
At this point I can't see performance issues, but later I believe it will cause some headaches.
Question:
Is it possible to execute those before_filters one by one. For example:
every 5 minutes - execute "X" before_filter
every 7 minutes - execute "Y" before_filter
Thanks in advance for your time.
Best regards
This is not something that should be considered a part of your controllers' jobs.
Consider looking at the whenever gem and processing jobs like this via cron.

updating a model attribute that will only change once

I have a subject model with attributes including a start_date and end_date - as well as a completed boolean attribute.
In subject.rb, I have a method to find how many weeks are remaining for a given subject:
def weeks_left
time = (self.end_date.to_time - Date.today.to_time).round/1.week
if time < 0
"completed"
elsif time < 1
"less than 1 week"
elsif time == 1
"1 week"
else
"#{time} weeks"
end
end
I want to tick the completed attribute if self.weeks_left == "completed" and the best way to do that seems like a call back, but I'm a bit unsure about using after_find - in general it seems like it would be too many queries, and indeed too big of a pain (especially after reading this) - but in this case, once a subject is complete, it's not going to change, so it seems useless to check it's status more than once - what's the best way to handle this?
Why dont you make a scope for this?
scope :completed, ->{where("end_date <= ?", Time.now)}
and a method
def completed?
self.weeks_left == "completed"
end
Looks like you need ActiveRecord::Callbacks. You can see more information here or on rails guide
before_save :update_completed
def update_completed
if (end_date_changed?)
time = (self.end_date.to_time - Date.today.to_time).round/1.week
self.complete = time < 0
end
end
This way you update the complete flag whenever end_date changes and it would always be in sync.
However because this is a calculated value you could also not store it as an attribute and simply define a method to get it
def complete
time = (self.end_date.to_time - Date.today.to_time).round/1.week
return time < 0
end

Resources