Update array in rails action? - ruby-on-rails

I have the following code in an action:
#user = #current_user
#user.votes[1430]='up'
#user.update_attributes(params[:user])
Votes is a string type, there is no default value - I just want to set it for the first time when this action occurs.
Unfortunately I get this error:
NoMethodError
You have a nil object when you didn't expect it!
The error occurred while evaluating nil.votes
any ideas what I'm doing wrong?

The cause of the error seems to be that #user is a nil reference.
You can confirm this by using logging and checking in your console window:
logger.info "user is nil" if #user.nil?
You are assigning #user to be the value of #current_user. I've seen this pattern before and usually current_user is a function, declared elsewhere, not an instance variable. If you are using that pattern, the line should be something like #user = current_user instead.
(Additionally, if votes is a string, your second line appears to be referring to index 1430 of that string, which is probably not what you want either.)

I'm splitting off the comments as I don't want to hijack Evil Trouts response and we seem to be veering away from what was initially spoken about.
Unfortunately I get this error: NoMethodError
You have a nil object when you didn't expect it! The error occurred while evaluating nil.votes
For this, have a look at the authlogic example application. My guess is that you don't have the appropriate before_filter set up and aren't requiring the user to be logged in on that action.
the 1430 is actually two seperate IDs
I don't fully follow this. It's a concatenation of two IDs? What are the IDs for? To be honest, I've never used an array as being a field type in my database so I don't know what the advantages of it are, but whenever I think to myself that an array would be a good idea, I usually question whether or not it wouldn't be better to just have it be a separate model, and hence, table.
It sounds like the situation you are describing might have a Question which a User can vote up on. If so, I might have a separate Voteable model which would join the users with the questions they can vote 'up' on.
Maybe if you provide some more insight into this side of things, I can make a better suggestion. Cheers.

Related

Independent ActiveRecord query inside ActiveRecord::Relation context

There is some ruby on rails code
class User < ActiveRecord::Base
def self.all_users_count
User.all
end
end
User.all_users_count
returns, for example, 100
User.limit(5).all_users_count
Now it return 5 because of ActiveRecord::Relation context, in despite of i wroute name of class User.all instead simple all
(.to_sql show that query always contains limit or where id or other things in other cases)
So, how can i make context-independent AR queries inside model methods? like User.all and others?
Thank you!
Ps. Or maybe my code has an error or something like this, and in fact User.all inside any methods and context always must returns correct rows count of this model table
This is very weird and unexpected (unfortunately I can't confirm that, because my computer crashed, and have no rails projects at hand).
I would expect
User.all
to create a new scope (or as you call it - context)
Try working around this with
User.unscoped.all
Edit:
I tried it out on my project and on clean rails repo, and the results are consistent.
And after thinking a bit - this is maybe not even an issue - I think your approach could be faulty.
In what scenario would you chain User.limit(2).all_users_count ?? I can't think of any. Because either you need all users count, and you call User.all_usert_count (or just User.count)
... or you need something else and you call User.limit(2).where(...) - there's no point in calling all_users_count in that chain, is it?
And, when you think of it, it makes sense. Imagine you had some different method like count_retired, what would you expect from such call:
User.limit(2).count_retired ?
The number of retired users not bigger than 2, or the number of all retired users in the system? I would expect the former.
So I think one of two possibilities here:
either you implemented it wrong and should do it in a different way (as described above in the edit section)
or you have some more complex issue, but you boiled your examples down to a point where they don't make much sense anymore (please follow up with another question if you please, and please, ping me in the comment with a link if you do, because it sounds interesting)

Rails .update_attribute not updating

I notice this question pops up a lot, but after trying several recommended solutions I found I still can't figure out what is wrong. I have a model called sample and a user model as well. When a sample is approved the hours on the sample are supposed to be added to the users total hours, but the users value is never updated. Each user has a unique email which is stored in the sample when it is submitted for approval. I checked in the database to make sure it wasn't an issue with accessing the value, and no error is being thrown so I am not really sure what is happening. I'm pretty new to ruby and rails so any help is appreciated. My samples_controller.rb contains the function:
def approve
#sample = Sample.find(params[:id])
#sample.update(sample_status:1)
#user = User.find(Sample.email)
hours_update = #user.hours + #sample.volunteer_hours
#user.update_attributes(:hours, hours_update)
redirect_to samples_adminsamples_path
end
Edit: thanks for the help everyone, turns out I needed to use the command
#user = User.find_by(email: #sample.email)
in order to get the proper user.
Can you please give some more data like db structure of Sample and User tables.
From the limited information, I think the line number 4 (#user = User.find(Sample.email)) is the problem.
.find() tries to query the DB on id and Sample.email would be giving user's email and not the id of the corresponding user in db.
I am also guessing that in your controller, you are suppressing the thrown exception some where using begin-rescue block because .find() throws ActiveRecord::RecordNotFound exception if it fails to find the resource.
Alternatively, if it is fetching the user correctly, you can also try update_column to update the values.
You are using incorrect format for update_attributes
It should be
#user.update_attributes(hours: hours_update)
or
#user.update_attribute(:hours, hours_update)
NOTE: update_attribute doesn't triggers the callbacks

Why isn't there a NilReferenceError in ruby?

Why is NoMethodError not differentiated for nil in Ruby?
Calling a method on nil is an extremely common error and is usually caused by incorrect data being provided to the program. A NoMethodError on any other class usually implies an error in the code itself (e.g. why were you calling reconnect on a Document? There is likely an error in the code).
What problems are created if I add the following code to my project?
NilReferenceError = Class.new(NoMethodError)
class NilClass
def method_missing(symbol, *args)
raise NilReferenceError, "undefined method `#{symbol}' for nil:NilClass", caller
end
end
I want to do this because when I am triaging exceptions, a NilReferenceError is likely to be caused by bad data and the root cause is likely in another location (validation of input, for example). By contrast, a NoMethodError is likely to be a programming error rooted exactly at the line of the exception (easier to fix and also highly likely to happen 100% of the time).
What are the negative effects of adding code like that to my project?
I think this is just habits from other programming languages. In ruby, nil is a first class object, like an integer, a hash or your own class object.
After you see the "NoMethodError: undefined method xxx for nil:NilClass" error once or twice, you get used to it.
There is nothing wrong with monkeypatching nil to show a more descriptive error message, but it's not going to solve the root cause of the problem, which is coding practice that permits and propagates nil values.
Consider the following very contrived example:
def do_something(input)
object = fetch_something_with(input[element])
do_something_with(object)
end
A couple of ways this might blow up:
input hash does not contain element, and passes nil into fetch_something_with
fetch_something_with returns nil (by design or upon failure), which gets passed into do_something_with
An alternative approach might be:
def do_something(input)
object = fetch_something_with(validated_input)
object && return do_something_with(object)
end
def validated_input(input)
input.fetch(element) # fetch raises an exception if the value is not present
end
A bit more code, but it gives us peace of mind that this code isn't going to silently pass nil down the line to fail at some later point.
Of course, it doesn't make sense to be this paranoid in every single method, but it is good practice to have well thought out boundaries in your code, either at method or object level. Having nil slip by frequently is a sign that these borders need some strengthening.
Do you mean that, when doing something like b=nil; b.say_hello;, ruby will give you "undefined method `say_hello' for nil:NilClass (NoMethodError)" instead of something like (as in your claim) "undefined method `say_hello' for nil:NilClass (NilReferenceError)"?
Ruby is not wrong for this since nil is an object like other objects. It has methods like to_s, so ruby can't ban any call by just raising an exception saying "because it is nil, you cannot do anything. I will give you a NilReferenceError".
You can surely do as your code above if you know that what you are doing may prevent ruby's default behavior.

Ruby array include? method weird behaviour

I'm using the cancan gem, and in ability.rb, I wanted to check if a user with a certain role can update another object if and only if the user 'is part of' the other object (that is, there is an association between the models, and the user is found in the object's users method).
I wanted to use the include? method to see if #current_user is found in the resulting User array.
It always returns false when testing the code through ability.rb. If I run the same code in the rails console, it works fine.
I found out that include? does not work if the Array objects are not of the same class as the passed object, but I printed their class through class.name, and they are the same.
I ended up using detect and comparing id's, but I wanted to know if anyone has had this kind of issue before, and if you could shed some light on this.
Something similar happened with delete_if, where after deleting 20 out of a 100, count was still returning 100, but an each loop printed only the ones that were supposed to be there (20). This happened when executing within the RoR environment, but the rails console with the same code behaved as expected (array.count # => 20).
EDIT 20131007
A bit of code regarding the include? issue.
This bit returns false all the time, even though the user IS in the resulting array from course.institution.users.
can [:read, :update], Course do |course|
val = course.institution.users.include? user
end
If I take that same line, and try it for a given course and user in the rails console, like so:
Course.find(course_id).institution.users.include? User.find(user_id)
It works as it's supposed to, returning true then the user is found in the array and false if not.
So even, even if the == operator was weird in some way for this particular model, I'm dealing with the same arrays in both cases, so it should either bust or work well in both cases, not only within the cancan ability.rb... right?
Cancan has a built-in mechanism to query associations. Without knowing your exact setup, this should be close to how you could set this up in your Ability class:
can :view, MyObject, users: { id: user.id }

Rails 2: Model.find(1) gives ActiveRecord error when id 1 does not exist

I am using Rails 2.3.5 and in that if I give Model.find(1) and if 1 is not in the database, it returns ActiveRecord error. Should it just be returning nil as in the case of Model.find_by_column('..')?
This is the expected behavior. I think David explains this the best himself, so here is a quote from Ruby, S., Thomas, D. & Hansson, D.H., 2009. Agile Web Development with Rails, Third Edition Third Edition., Pragmatic Bookshelf (p.330).
When you use a finder driven by
primary keys, you’re looking for a
particular record. You expect it to
exist. A call to Person.find(5) is
based on our knowledge of the people
table. We want the row with an id of
5. If this call is unsuccessful—if the record with the id of 5 has been
destroyed—we’re in an exceptional
situation. This mandates the raising
of an exception, so Rails raises
RecordNotFound.
On the other hand,
finders that use criteria to search
are looking for a match. So,
Person.find(:first,
:conditions=>"name=’Dave’") is the
equivalent of telling the database (as
a black box) “Give me the first person
row that has the name Dave.” This
exhibits a distinctly different
approach to retrieval; we’re not certain up front that we’ll get a result.
It’s entirely possible the result set
may be empty. Thus, returning nil in
the case of finders that search for
one row and an empty array for finders
that search for many rows is the
natural, nonexceptional response.
If you really don't want the exception, you can use find_by_id:
# #user = User.find(params[:id]) # original code
#user = User.find_by_id(params[:id])
if #user
# found!
else
# not found
end
This should be faster than a separate exists? check.
EDIT: Note that as #Miguelgraz commented, in Rails 4 you should instead say User.find_by(id: params[:id]). Same functionality, but now the implementation won't need method_missing.
throwing the exception is the expected behavior.
in fact in the normal course of events if you let the exception go unhandled your rails server will return the proper 404 page not found error.
if you'd like for it to return nil you can catch it yourself:
begin
#model = Model.find(id_provided)
rescue ActiveRecord::RecordNotFound => e
#model = nil
end
If you want the exception to be thrown in find_by_attributes flavors of the finder methods, you can use the bang! version of the method.
For example,
Model.find_by_category!(a_category_value)
will throw RecordNotFound if no match is found.
I found this to be DRY in scenarios like RESTful controllers, where I have a common error handler for the exception and I want my actions to behave consistently when a resource matching the given parameters is not found.
You can check if the record exists before fetching it.
#model = Model.find(id) if Model.exists?(id)
Rails 4 Method
if user = User.find_by(id: params[:id])
#do something with user
else
#throw error or redirect
raise ActiveRecord::RecordNotFound
end
You can use find_by with the required attribute (in your case the id) this will return nil instead of giving an error if the given id is not found.
Model.find_by_id(id_value)
You could also use where but you have to know that where return an active record relation with zero or more records you need to use first to return only one record or nil in case zero records return.
Model.where(id: id_value).first
You can simply use:
user = User.find(10) rescue nil

Resources