How to update rails db records - ruby-on-rails

I have a rails app. I have a table User and a column Number which is a string. Some users saved their phone number with spaces (for example 1234 1234) and now I want to remove the space from their phone numbers.
I tried this but it didn't work:
space = " "
phones = User.where("number like ?", "%#{space}%").pluck(:number)
phones.each do |phone|
phone = phone.gsub(/\s+/, "")
phone.save
end
I got the error NoMethodError: undefined method 'save' How can I do this properly?

You need to have the user object to save it. Read inline comments below
space = " "
users = User.where("number like ?", "%#{space}%") # collect users with number having space character here.
# then iterate on those users
users.each do |user|
user.number = user.number.gsub(/\s+/, "") # notice here, changing the phone number of that user
user.save # and saving that user with the updated `number`
end

You pluck data from User table. Thus, phones variable contains a number array not USER objects. You can't use save on a array element. That's why the error occurs.
You can do the following:
space = " "
phones = User.where("number like ?", "%#{space}%")
phones.each do |phone|
phone.number = phone.number.gsub(/\s+/, "")
phone.save
end

The way you could do is create a rake task to update the existing records on the system.
namespace :update do
desc 'Strip space from existing numbers from Users'
task(:number => ::environment) do
space = ' '
numbers_with_space = User.where("number like ?", "%#{space}%")
numbers_with_space.each do |a|
a.number = a.number.gsub!(/\s+/, '')
a.save(validate: false) # You would like to use
# validate false in order
# to stop other validation from updating the record.
end
end
Then execute the rake task.
bundle exec rake update:number
Another way to handle this beforehand can be through reformatting number during validation. This way you'll not need to run the rake task or code to reformat and save when new data are entered in app.
class User < ApplicationRecord
before_validation :reformat_number, on: [:create, :update]
private
def reformat_number
self.number.gsub!(/\s+/, '')
end
end

Related

How to go through 2 models in a loop in ruby on rails ?

I am using MailKick gem and I am sending emails to users from id 1 to 1000 who have not opted out to get emails.
So I have
#users = User.where('id >= 1').where('id <= 1000')
#opt_out_users = Mailkick.opt_outs.where(active: true)
User model has id, email and name as fields.
MailKick model has id, email, user_id and active as fields.
Now I can run the code
#users.each do |user|
//Code to send emails to each user
end
but I am not able to figure out how to filter out those users who have opted out.
As documentation says, you should add mailkick_user to your model:
class User < ActiveRecord::Base
mailkick_user
end
Then scope .not_opted_out will be avaliable for you:
User.not_opted_out.where('id <= 1000').each { |user| user.do_smth }
You should be able to use
#users = User.where.not(
id: Mailkick.opt_outs.where(active: true).pluck(:user_id)
).where("id <= 1000")
The clause .where('id >= 1') is redundant.
You can do this in one command:
#users_who_have_not_opted_out = User.where(id: 1..1000)
.where.not( id: MailKick.op_outs.where(active: true).pluck(:user_id) )
The first where function gets all ids between 1 and 1000. The second where.not statement returns all ids that are not in the opt_out list. The pluck(:user_id) turns the MailKick.opt_outs.where(active:true) association into an array of user_ids
You can then run:
#users_who_have_not_opted_out do |user|
# Here you would execute your send email command on user
end
you can get all user ids who are not opted out like this,
#opt_out_users = Mailkick.opt_outs.where(active: true,:user_id=>{'$gte'=>1,'$lte'=>1000}).map(&:user_id);
The above statement will return the user ids in an array.
Next you can can search user object using those ids.
#user = User.where(:id=>{'$in'=> #opt_out_users});
And loop through each user
#user.each do |user|
#code for sending mails
end

Remove element from array unless has a certain value in Rails

I have an array of "names" and I would like to display the current user's name ONLY IF there it is the only name in the array. If there are other names in the array, I don't want to show the current user's name.
Currently, I am listing the names and excluding the current user's name, but don't want to exclude the current user's name if it is the only one in the array. I hope I explained that okay.
My code now:
module ConversationsHelper
def other_user_names(conversation)
users = []
conversation.messages.map(&:receipts).each do |receipts|
users << receipts.select do |receipt|
receipt.receiver != current_user
end.map(&:receiver)
end
users.flatten.uniq.map(&:name).join ', '
end
end
This should work:
def other_user_names(conversation)
# get all users (no duplicates)
users = conversation.messages.map(&:receipts).map(&:receiver).uniq
# remove current user if there are more than 1 users
users.delete(current_user) if users.many?
# return names
users.map(&:name).join(', ')
end
I would move the first line into a Conversation#users method.

Rake task - undefined method

I tinkering my way into creating a rake task that grabs the amount of checkins for a given page throw facebook-graph. I usign the koala gem and rails.
I do this by creating a rake task:
task :get_likes => :environment do
require 'koala'
# Grab the first user in the database
user = User.first
# Loop throw every school & and call count_checkins
School.columns.each do |column|
user.facebook.count_checkins(column.name, user)
end
end
# Count like for every school else return 0
def count_checkins(name, u)
a = u.facebook.fql_query('SELECT checkins FROM page WHERE name = "' + name + '"')
if a[0].nil?
return 0
else
return b = a[0]["checkins"]
end
end
# Initialize an connection to the facebook graph
def facebook
#facebook ||= Koala::Facebook::API.new(oauth_token)
end
But I get a error:
private method `count_checkins' called for #<Koala::Facebook::API:0x007fae5bd348f0>
Any ideas or better way to code a rake task would be awesome!
Check the full error here: https://gist.github.com/shuma/4949213
Can't really format this properly in a comment, so I'll put it in an answer. I would put the following into the User model:
# Count like for every school else return 0
def count_checkins(name)
a = self.facebook.fql_query('SELECT checkins FROM page WHERE name = "' + name + '"')
if a[0].nil?
return 0
else
return b = a[0]["checkins"]
end
end
# Initialize an connection to the facebook graph
def facebook
#facebook ||= Koala::Facebook::API.new(oauth_token)
end
Then change the rake task to:
task :get_likes => :environment do
require 'koala'
# Grab the first user in the database
user = User.first
# Loop throw every school & and call count_checkins
School.columns.each do |column|
user.count_checkins(column.name)
end
end
That way count_checkins is defined on the user model, rather than trying to modify a class within Koala -- and you aren't duplicating work by having to pass around more User and Facebook parameters than are necessary.

Iterating through every record in a database - Ruby on Rails / ActiveRecord

n00b question. I'm trying to loop through every User record in my database. The pseudo code might look a little something like this:
def send_notifications
render :nothing => true
# Randomly select Message record from DB
#message = Message.offset(rand(Message.count)).first
random_message = #message.content
#user = User.all.entries.each do
#user = User.find(:id)
number_to_text = ""
#user.number = number_to_text #number is a User's phone number
puts #user.number
end
end
Can someone fill me in on the best approach for doing this? A little help with the syntax would be great too :)
Here is the correct syntax to iterate over all User :
User.all.each do |user|
#the code here is called once for each user
# user is accessible by 'user' variable
# WARNING: User.all performs poorly with large datasets
end
To improve performance and decrease load, use User.find_each (see doc) instead of User.all. Note that using find_each loses the ability to sort.
Also a possible one-liner for same purpose:
User.all.map { |u| u.number = ""; puts u.number }

How to store the result of my algorithm?

I have an algorithm that searches through all of my sites users, finding those which share a common property with the user using the algorithm (by going to a certain page). It can find multiple users, each can have multiple shared properties. The algorithm works fine, in terms of finding the matches, but I'm having trouble working out how to store the data so that later I'll be able to use each unit of information. I need to be able to access both the found users, and each of the respective shared properties, so I can't just build a string. This is an example of the output, being run from the perspective of user 1:
user 4
sharedproperty3
sharedproperty6
user 6
sharedproperty6
sharedproperty10
shareproperty11
What do I need to do to be able to store this data, and have access to any bit of it for further manipulation? I was thinking of a hash of a hash, but I can't really wrap my head around it. I'm pretty new to programming, and Ruby in particular. Thanks for reading!
EDIT - Here's the code. I'm fully expecting this to be the most incorrect way to do this, but it's my first try so be gentle :)
So if I'm understanding you guys correctly, instead of adding the interests to a string, I should be creating an array or a hash, adding each interest as I find it, then storing each of these in an array or hash? Thanks so much for the help.
def getMatchedUsers
matched_user_html = nil
combined_properties = nil
online_user_list = User.logged_in.all
shared_interest = false
online_user_list.each do |n| # for every online user
combined_properties = nil
if n.email != current_user.email # that is not the current user
current_user.properties.each do |o| # go through all of the current users properties
n.properties.each do |p| # go through the online users properties
if p.interestname.eql?(o.interestname) # if the online users property matches the current user
shared_interest = true
if combined_properties == nil
combined_properties = o.interestname
else
combined_properties = combined_properties + ", " + o.interestname
end
end
end
if shared_interest == true
matched_user_html = n.actualname + ": " + combined_properties
end
end
end
end
return matched_user_html
render :nothing => true
end
This returns an array of hashes with all users and their corresponding sharedproperties.
class User
def find_matching_users
returning Array.new do |matching_users|
self.logged_in.each do |other_user|
next if current_user == other_user # jump if current_user
# see http://ruby-doc.org/core/classes/Array.html#M002212 for more details on the & opreator
unless (common_properties = current_user.properties & other_user.properties).empty?
matching_users << { :user => other_user, :common_properties => common_properties }
end
end
end
end
end
In your view you can do something like this:
<%- current_user.find_matching_users.each do |matching_user| -%>
<%-# you can acccess the user with matching_user[:user] -%>
<%-# you can acccess the common properties with matching_user[:common_properties] -%>
<%- end -%>
You can use a hash table with the key being the user object and the value being an array of the shared properties . This is assuming that you first need to do a lookup based on the user .
Something like this :
#user_results = { user1 => [sharedproperty3,sharedproperty7] , user2 => [sharedproperty10,sharedproperty11,sharedproperty12]}
You can then acces the values like :
#user_results[user1]
or you can also iterate over all the keys using #user_results.keys

Resources