How to filter out inactive emails from an array of emails in a rails application? - ruby-on-rails

I am currently working on a ticket where it asks me to filter out any inactive email to be sent to the recipient. Here is the method I am working on:
def self.delivering_email(message)
return if email_to_be_delivered?(message.subject)
email_list = message.to
if email_list.is_a?(String)
email_list = email_list.split(",").map(&:strip)
end
email_list.each { |email|
identity = Identity.find_by(email: email)
next if identity.nil?
# email_list.delete(email) unless identity.try(:preferred_user).active?
email_list.select(email) if identity.try(:preferred_user).active?
}
message.to = email_list
message.perform_deliveries = !email_list.empty?
end
the "# email_list.delete(email) unless identity.try(:preferred_user).active?" I commented out because the QA mentioned that ONLY one inactive email filters out and does not fully filter other inactive emails in the array. I assumed instead of .delete I have to use .select but don't know if it works because I don't have any way to test and reproduce the error on my end, or how to implement it the right way.
Any help will be appreciated.

You're trying to modify an array while you're iterating over it, that may lead to weird behavior. One option is to just use a separate array.
Since you are already iterating with email_list.each you can call next if the current email does not satisfy you, like you already do for identity.nil?.
So it may look smth like
valid_emails = []
email_list.each { |email|
identity = Identity.find_by(email: email)
next if identity.nil? || !identity.try(:preferred_user).active?
valid_emails << email
end
message.to = valid_emails

Related

How do I loop through a model and save that to an array?

I have a mailer that checks for all users that meet a certain criteria.
If that criteria is true, I'm trying to do a do loop, and return the result into an array.
I'm then trying to use that to loop through that array and send an email to each email in the array.
I tried assigning the variable 'emails' to this array where I'm passing this code in
emails = [User.where(:state => 'Arizona').each]
Which isn't working because when I do the following.. the emails aren't sent
emails.each do |email|
new_request(email,row).deliver_now
end
Then I tried to do a loop and save those results to an variable
User.where(:state => 'Arizona').each do |u|
emails = u.email
end
Yet again, when I do this following code the emails aren't sent
emails.each do |email|
new_request(email,row).deliver_now
end
FYI - everything else is working just fine with the rest of my program, the emails are definitely going out fine when I don't use this code. For instance, if I do this:
emails = ['a#gmail.com','b#gmail.com']
the array works fine, and then I can do this..
emails.each do |email|
new_request(email,row).deliver_now
end
and the code email is sent to a#gmail.com and b#gmail.com. So again, the real question is, how do I loop through those users where that criteria is true, and save to an array like this so I can run this emails.each do code and get it work?
User.where(:state => 'Arizona').each do |u|
emails = u.email
end
In the above logic, in every iteration, the emails will be overwritten with the iteration email.
You have to insert the emails in an array to make the logic as expected.
emails = []
User.where(state: 'Arizona').each do |u|
emails << u.email # To insert the user email into the array
end
You can also try to debug the values in the array whenever you face issues in logic.
User.where(:state => 'Arizona').each do |u|
emails = u.email
p emails, "emails" # To check the value stored in the emails.
end
Just use pluck
User.where(:state => 'Arizona').pluck(:email)
=> ["a#gmail.com", "b#gmail.com"]

How do I enter a record for every user where condition is true in Rails

So I have app that has political candidates.
When a new political candidate is entered, I want to enter a notification into the notifications table for every user that's state is equal to the state of the new candidate being entered.
Ultimately, I want to enter in records to the notification table for every single user where that condition is met.
I know I'm way off, but here's where I'm at now. I'm trying to loop through each user and then enter this record when that condition is true.
def create
#candidate = Candidate.new(candidate_params)
if #candidate.save
User.each do |u|
if Candidate.state == User.state
#notification = Notification.new(:message => 'Message', :user_id => U.id)
#notification.save
else
end
end
else
render('new')
end
end
The candidate is created with this code, but the notifications aren't working. Basically I have two users where their state equals "Arizona" and I would expect if I create a new candidate where the state is "Arizona" that I should get two record into notifications, one with each user ID.
I think you got a bit mixed up between classes and instances. Here's the relevant bit:
#candidate = Candidate.new(candidate_params)
...
User.each do |u|
if Candidate.state == User.state
...
end
end
In your code Candidate is a class, and #candidate holds the recently created instance of a Candidate. Likewise, User is a class and u holds a User instance (on each loop iteration). Your comparison should actually use the instances rather than the classes:
if #candidate.state == u.state
Having sorted that, it's worth noting that your code has a couple of other errors -- User.each won't work. You need to specify a selector to get a list of User objects to loop through. One way would be to call User.all.each (which looking at your code is probably what you were trying). That pulls all User objects. But, since users can be from anywhere, if you do that you will cycle through a lot of users you don't need to.
Since all you need is users whose state matches the new candidate, you can use the where() method to pre-filter the list you are looping through. That way you don't need the if at all.
#candidate = Candidate.new(candidate_params)
...
User.where(state: #candidate.state).each do |u|
#notification = Notification.new(message: 'Message', user: u)
#notification.save
end
The other problem in your code is in the line to create a notification. You use U.id but the loop variable is lower case u. As an added tip, you don't need to set the object ID specifically. If you just pass the User object (as in the code above), Rails is smart enough to figure out the rest.
For performance don't iterate all users, you can search users that match the candidate's state then create notification for each user.
def create
#candidate = Candidate.new(candidate_params)
if #candidate.save
users = User.where(state: #condidate.state)
users.each { |user| #notification = Notification.create(:message => 'Message', :user_id =user.id } if users
else
render 'new'
end
end

Ruby passing parameters

What is the proper way to pass the parameters to a function?
For example:
def self.find_by_example(username, email)
user = User.find_by_username(username) || User.find_by_email(email)
end
I would like to find the user by his username or email but if a create a function passing the 2 parameters Rails shows
(wrong number of arguments (given 1, expected 2))
When I call User.find_by_example('example')
I still don't get it, the parameters passed in must not be the attribute?
and why does it say "given 1"?
You must be calling the function like `User.find_by_example("name to find") and the function expects two arguments (name and email). You could define the function as:
def self.find_by_example(term)
user = User.find_by_username(term) || User.find_by_email(term)
end
And call it User.find_by_example("Name to find") or User.find_by_example("email#to_find.com")
This does not work ok if you have users with a username like an email. And it is not much efficient if you wish to search by other fields. SO you could also:
def self.find_by_example(terms)
if terms[:email]
user = User.find_by_email(terms[:email])
elsif terms[:username]
user = User.find_by_username(terms[:username])
elsif terms[:id]
user = User.find_by_id(terms[:id])
elsif terms[:document]
user = User.find_by_document(terms[:document])
end
end
And call the method User.find_by_example(:email => "email#example.com"). This is similar to the find_by method that Active Record already provides (but allows many arguments), so no need to implement it.
The proposed and accepted answer is not really the equivalent of the code asked in the question. It is accepted, so one might assume that it guessed the OP intent correctly. But I think it can be useful for (especially junior) programmers to think about the problem more deeply.
Think of what method should do
(not just if it immediately gives you result you wish to see, there can be surprises in the edge cases)
The original code
def self.find_by_example(username, email)
user = User.find_by_username(username) || User.find_by_email(email)
end
Could be used this way x.find_by_example(nil, 'test#example.com').
If we assume there can't be users with NULL username (which IMO is a reasonable assumption), the call would result in finding an user strictly by email.
The proposed solution does not give you this possibility:
def self.find_by_example(term)
user = User.find_by_username(term) || User.find_by_email(term)
end
x.find_by_example('test#example.com') would return user with such username if exists, and (possibly other) user with such e-mail otherwise.
In other words - you have less control which field is used to find a user (which can be correct, if that's really what you need)
So it depends on the OP intent.
If one want to retain how the original method works, but improve the interface, it could be done like this:
def self.find_by_example2(username: nil, email: nil)
user = User.find_by_username(username) || User.find_by_email(email)
end
And calling x.find_by_example2(email: 'test#example.com') is equivalent to x.find_by_example(nil, 'test#example.com') but looks better.
Bonus: Performance implications
The proposed solution
def self.find_by_example(term)
user = User.find_by_username(term) || User.find_by_email(term)
end
makes second query when the user is not find by username. You can improve it as well if you wish to employ some sql magic:
def self.find_by_example(term)
user = User.where("username = ? OR (username IS NULL and email = ?)", term, term).first
end
There's another possibility (though not 100% equivalent to the accepted solution):
def self.find_by_example(term)
user = User.where("username = ? OR email = ?", term, term).first
end
(I'll leave as an exercise the answer how those are different, to keep this post short...ish)
Bonus 2: flexibility
This
def self.find_by_example(terms)
if terms[:email]
user = User.find_by_email(terms[:email])
elsif terms[:username]
user = User.find_by_username(terms[:username])
elsif terms[:id]
user = User.find_by_id(terms[:id])
elsif terms[:document]
user = User.find_by_document(terms[:document])
end
end
is a waste of your time, because rails gives you already better interface to do this.
Instead of calling
x.find_by_example(document: 'foo')
you could just do
User.find_by(document: 'foo')
There's really no need to implement it that way, it's basically crippled version of ActiveRecord interface, that you have to maintain as you add new fields to User model.

How to retrieve sixth record from database in rails using Where query

I want to retrieve sixth, seventh and eighth record from database using ruby on rails, but it's possible only till fifth, after that undefined method sixth is coming. Please suggest me if there is any possible way.
Following is my code which I tried:
#reporting_masters_travel_requests4 = ReportingMastersTravelRequest.where(travel_request_id: #travel_request.id,status: nil).fifth
if #reporting_masters_travel_requests2 = ReportingMastersTravelRequest.where(travel_request_id: #travel_request.id,status: nil)[1]
ReportingMastersTravelRequest.where(reporting_master_id: #reporting_masters_travel_requests2.reporting_master_id).update_all(status: "true",daily_bill_comment: #comment)
TravelRequest.where(id: #travel_request.id).update_all(reporting_master_id: #reporting_masters_travel_requests3.reporting_master_id)
flash[:notice] = 'Daily Bill Request Send To Higher Authority For Approval'
elsif #reporting_masters_travel_requests3 = ReportingMastersTravelRequest.where(travel_request_id: #travel_request.id,status: nil)[2]
ReportingMastersTravelRequest.where(reporting_master_id: #reporting_masters_travel_requests3.reporting_master_id).update_all(status: "true",daily_bill_comment: #comment)
TravelRequest.where(id: #travel_request.id).update_all(reporting_master_id: #reporting_masters_travel_requests4.reporting_master_id)
flash[:notice] = 'Daily Bill Request Send To Higher Authority For Approval'
elsif #reporting_masters_travel_requests4 = ReportingMastersTravelRequest.where(travel_request_id: #travel_request.id,status: nil)[3]
ReportingMastersTravelRequest.where(reporting_master_id: #reporting_masters_travel_requests4.reporting_master_id).update_all(status: "true",daily_bill_comment: #comment)
TravelRequest.where(id: #travel_request.id).update_all(reporting_master_id: #reporting_masters_travel_requests5.reporting_master_id)
flash[:notice] = 'Daily Bill Request Send To Higher Authority For Approval'
else
flash[:alert] = 'No Reporting Manager is present'
end
I have used the above code and it works perfectly,but its not so dynamic as i manually need to specify for how many time if want to check in table via array [4].But problem is what if 10 records are present in database then for this the above logic will fail.
You are using where to get records from database and it returns array of objects so you can use something like this:
#reporting_masters_travel_requests4 = ReportingMastersTravelRequest.where(travel_request_id: #travel_request.id,status: nil)[5]
You can do as follows:
#reporting_masters_travel_requests = ReportingMastersTravelRequest.where(travel_request_id: #travel_request.id,status: nil)
#reporting_masters_travel_requests.each do |record|
#Something like record.update record.save
end
#set a msg like no more records after loop
msg="No more records"

How to set up usernames from email of all users?

I would like to create rake task to set the username of all users' without a username to the part before the '#' in their email address. So if my email is test#email.eu, my username should become test. If it's not available, prepend it by a number (1).
So i have problem witch checking uniqness of username. Code below isn`t working after second loop ex: when i have three emails: test#smt.com, test#smt.pl, test#oo.com username for test#oo.com will be empty.
I have of course uniqness validation for username in User model.
desc "Set username of all users wihout a username"
task set_username_of_all_users: :environment do
users_without_username = User.where(:username => ["", nil])
users_without_username.each do |user|
username = user.email.split('#').first
users = User.where(:username => username)
if users.blank?
user.username = username
user.save
else
users.each_with_index do |u, index|
pre = (index + 1).to_s
u.username = username.insert(0, pre)
u.save
end
end
end
end
Other ideas are in Gist: https://gist.github.com/3067635#comments
You could use a simple while loop for checking the username:
users_without_username = User.where{ :username => nil }
users_without_username.each do |user|
email_part = user.email.split('#').first
user.username = email_part
prefix = 1
while user.invalid?
# add and increment prefix until a valid name is found
user.username = prefix.to_s + email_part
prefix += 1
end
user.save
end
However, it might be a better approach to ask the user to enter a username upon next login.
if i understand your code correct, you are changing the username of existing users in the else branch? that does not look as if it's a good idea.
you should also use a real finder to select your users that don't have a username. otherwise you will load all the users before selecting on them.
i don't know if it "matches your requirements" but you could just put a random number to the username so that you do not have the problem of duplicates.
another thing that you can use is rubys retry mechanism. just let active-record raise an error and retry with a changed username.
begin
do_something # exception raised
rescue
# handles error
retry # restart from beginning
end
In your query User.find_by_username(username), you only expect 1 record to be provided. So you don't need any each. You should add your index in another way.

Resources