Why is my update failing in a each loop? - ruby-on-rails

users = User.all()
user.each do |u|
b = get_id_blah()
u.some_id = b.id
u.save
end
I get the error:
ruby-1.8.7-p302#rails3/gems/activemodel-3.0.1/lib/active_model/attribute_methods.rb:364:in `method_missing': private method `update' called for #<User:0x1017b8188> (NoMethodError)
Should I be calling save outside of the loop?

This might just be a typo, but it could explain your no-method-error
users = User.all()
user**s**.each do |u|
end
It should be something like
users = User.all
users.each do |user|
....
end
Or simply
User.all.each do |user|
...
end

Are you sure you this is the exact code? It sounds like you are calling object.update instead of just calling object.save
By the way, you don't need parentheses if you're calling functions in Ruby :-)

Related

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.

undefined method `empty?' for nil:NilClass how to avoid it

Hi Together I've got this code:
#coursesFound = #user.available_courses
#courses = []
for course in #coursesFound do
#courseInGroups = course.user_groups
for group in #courseInGroups do
#group = UserGroup.find group.id
if #group.users.map { |u| u.id }.include? #user.id
#courses << course
break
end
end
end
# Wenn ein Kurs keiner Gruppe hinzugefĆ¼gt wurde
if #courseInGroups.empty?
#courses << course
end
on my debian vm it works fine but on my live system I got this error:
undefined method `empty?' for nil:NilClass
How can I avoid this?
If this #coursesFound = #user.available_courses returns an empty activerecord relation.
Then this won't execute
for course in #coursesFound do
#courseInGroups = course.user_groups
for group in #courseInGroups do
#group = UserGroup.find group.id
if #group.users.map { |u| u.id }.include? #user.id
#courses << course
break
end
end
end
Which means when you get here #courseInGroups is nil
if #courseInGroups.empty?
#courses << course
end
So your quick fix would be
if #courseInGroups && #courseInGroups.empty?
#courses << course
end
You can use the try method to Avoid this error:
#courseInGroups.try(:empty?)
This won't throw an error if #courseInGroups was nil.
And don't forget blank? when using rails. Here you find a good overview of all methods with or without rails.
I did not analyze your code, it's just for you, me and others that do not use this methods often, mix them up and then come here - just to remember: empty? is not blank?.
You need to properly initialize your object as well.
#courseInGroups = course.user_groups || []
You won't get nil:NilClass error any more if you initialize properly.
To get rid of nil:NilClass error you can use other answer. like try etc.
You can put the ? before the dot of the empty:
if #courseInGroups?.empty

Error deleting record in console ruby on rails

I am trying to delete a record using the console. I have a model for "User". I tried several methods in the console:
a = User.where(:id => '18')
a.destroy
a.delete
User.where(:id => '18').destroy
User.where(:id => '18').delete
Using all of these methods, I got the same error: "Wrong number of arguments (0 for 1)"
Does anyone know what I am doing wrong?
Thx!
Try:
a = User.find(18)
a.destroy
When we use where, result will be ActiveRecord::Relation, means multiple records, on which you can't call destroy directly. You will need to call destroy by iterating over the result.
users = User.where(:id => 18)
users.each do |user|
user.destroy
end
I can add something here, The issue with your code that you are passing string while it expects an integer 'Number'
Your code should be as the following:
a = User.where(:id => 18).first
a.destroy
Without using first array of object will be returned and you can't use destroy method directly on it, in case you don't want to add first then your code should be like:
a = User.where(:id => 18)
a.each do |obj|
obj.destroy
end

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 }

Rails find_or_create_by where block runs in the find case?

The ActiveRecord find_or_create_by dynamic finder method allows me to specify a block. The documentation isn't clear on this, but it seems that the block only runs in the create case, and not in the find case. In other words, if the record is found, the block doesn't run. I tested it with this console code:
User.find_or_create_by_name("An Existing Name") do |u|
puts "I'M IN THE BLOCK"
end
(nothing was printed). Is there any way to have the block run in both cases?
As far as I understand block will be executed if nothing found. Usecase of it looks like this:
User.find_or_create_by_name("Pedro") do |u|
u.money = 0
u.country = "Mexico"
puts "User is created"
end
If user is not found the it will initialized new User with name "Pedro" and all this stuff inside block and will return new created user. If user exists it will just return this user without executing the block.
Also you can use "block style" other methods like:
User.create do |u|
u.name = "Pedro"
u.money = 1000
end
It will do the same as User.create( :name => "Pedro", :money => 1000 ) but looks little nicer
and
User.find(19) do |u|
..
end
etc
It doesn't seem to me that this question is actually answered so I will. This is the simplest way, I think, you can achieve that:
User.find_or_create_by_name("An Existing Name or Non Existing Name").tap do |u|
puts "I'M IN THE BLOCK REGARDLESS OF THE NAME'S EXISTENCE"
end
Cheers!

Resources