How to populate empty attributes with random values from seeds.rb - ruby-on-rails

I've generated User model with attributes: first_name, last_name, city. Then, I've created 200 instances of the class using seed.rb and gem 'faker'. After all, I have added one more attribute to this model - age:string, so now in every instance of the class the age = 'nil'. Now, I want to populate every single user with randomly generated number from range 10..99.
Here is my code from seed.rb:
users = User.all
users.each do |element|
element.age = rand(10..99).to_s
end
When I type rake db:seed it seems to be successfull but when I check the database, each age:string is still 'nil'. Do you have any idea what might have gone wrong?

You are assigning the value, but you don't save it. You should change your code to
users = User.all
users.each do |element|
element.age = rand(10..99).to_s
element.save!
end
or just
users = User.all
users.each do |user|
user.update_attribute(:age, rand(10..99))
end
If you don't need to validate the record, the following is even faster
users = User.all
users.each do |user|
user.update_column(:age, rand(10..99))
end

#Simone Carletti's answer is good... maybe you'd benefit from find_each:
User.find_each do |user|
user.update_attribute :age, rand(10..99)
end

Also, you can combine Faker with Fabrication for more cool seeds.
Faker::Name.name #=> "Christophe Bartell"
Faker::Internet.email #=> "kirsten.greenholt#corkeryfisher.info"
You can use it to generate random data from db/seed.rbrunning rake db:seed.

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

How to make my seeds file Idempotent?

In my deployment process I am running my seeds file. I want this to be Idempotent so I can run it multiple times without any issue.
Currently I get PG primary key errors if I run it multiple times.
My seeds pattern looks like this:
user = User.create(.....)
user.save!
foo = Foo.create(....)
foo.save!
How can I make this Idempotent?
Is this the best way?
if( user.exists?(some_column: some_value) )
else
# do insert here
end
I believe you can make use of first_or_create
User.where(email: "email#gmail.com").first_or_create do |user|
user.name = "John"
end
This will only create User with email = "email#gmail.com" if it doesn't exist or it will return you the instance of existing User.
This way you can avoid the Unique Key Violation
You can try :
unless user.find_by(some_column: some_value)
user.save!
end

Rails 5 ActiveRecord - check if any results before using methods

In my Rails 5 + Postgres app I make a query like this:
user = User.where("name = ?", name).first.email
So this gives me the email of the first user with the name.
But if no user with this names exists I get an error:
NoMethodError (undefined method `email' for nil:NilClass)
How can I check if I have any results before using the method?
I can think if various ways to do this using if-clauses:
user = User.where("name = ?", name).first
if user
user_email = user.email
end
But this does not seem to be the most elegant way and I am sure Rails has a better way.
You can use find_by, returns the object or nil if nothing is found.
user = User.find_by(name: name)
if user
...
end
That being said you could have still used the where clause if you're expecting more than one element.
users = User.where(name: name)
if users.any?
user = users.first
...
end
Then there is yet another way as of Ruby 2.3 where you can do
User.where(name: name).first&.name
The & can be used if you're not sure if the object is nil or not, in this instance the whole statement would return nil if no user is found.
I use try a lot to handle just this situation.
user = User.where("name = ?", name).first.try(:email)
It will return the email, or if the collection is empty (and first is nil) it will return nil without raising an error.
The catch is it'll also not fail if the record was found but no method or attribute exists, so you're less likely to catch a typo, but hopefully your tests would cover that.
user = User.where("name = ?", name).first.try(:emial)
This is not a problem if you use the Ruby 2.3 &. feature because it only works with nil object...
user = User.where("name = ?", name).first&.emial
# this will raise an error if the record is found but there's no emial attrib.
You can always use User.where("name = ?", name).first&.email, but I disagree that
user = User.where("name = ?", name).first
if user
user_email = user.email
end
is particularly inelegant. You can clean it up with something like
def my_method
if user
# do something with user.email
end
end
private
def user
#user ||= User.where("name = ?", name).first
# #user ||= User.find_by("name = ?", name) # can also be used here, and it preferred.
end
Unless you really think you're only going to use the user record once, you should prefer being explicit with whatever logic you're using.

How to compare new data with existing in bd before save

Every time, when I'm getting the data from the API request, I require to compare and update records, if any changes was there.
for example i have saved User
user = User.first
user.name => 'name_one'
when i calling to api,api returns me User but name was cahnged to 'name_two'
so i need compare existing user with newly arrived and if name changed replace it
example of calling api
url= 'my api str'
result = Curl.get(url)
JSON.parse(result.body_str).each do |key, value|
value["commissions"].each do |k, v|
User.create(name: v["name"],etc... )
end
end
I will be glad for any suggestions.
You can also try the following:
u = User.first
u.name = "name_two"
u.name_changed? #=> true/false
u.name_was #=> "name_one" if it was "name_one"
These are called dirty method and very helpful for this kind of task.
Thanks
Try this code
u = User.first
u.name = "name_two"
u.save if u.changes.include?("name")
I believe you shall use active record's private method #read_attribute to compare current updated values with stores ones:
if send(:read_attribute, :attr) == self.attr
# value isn't changed
end
Additional info you can read here:
Validation from controller
You shall validate newly created record from controller:
v['name'] = 'User 1'
user = User.create(name: v['name'])
user.name # => # User 1

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 }

Resources