undefined method update_attributes in Rails 4 - update-attributes

i'm now using Rails 4
now i want to update my user record
i'm trying to use this command
#user=User.update_attributes(:name=> params[:name], :user=> params[:username], :pass=> params[:password])
OR
#user=User.update_attributes(name: params[:name], user: params[:username], pass: params[:password])
but always got the error
undefined method `update_attributes'
so how i can update my user
also i want to ask is it will update all the users in my DB ??
i think i must add some condition such as where id=#user.id but i don't know how i can do that in rails !!!

update_attributes is an instance method not a class method, so first thing you need to call it on an instance of User class.
Get the user you want to update :
e.g. Say you want to update a User with id 1
#user = User.find_by(id: 1)
now if you want to update the user's name and password, you can do
either
#user.update(name: "ABC", pass: "12345678")
or
#user.update_attributes(name: "ABC", pass: "12345678")
Use it accordingly in your case.
For more reference you can refer to Ruby on Rails Guides.
You can use update_all method for updating all records.
It is a class method so you can call it as
Following code will update name of all User records and set it to "ABC"
User.update_all(name: "ABC")

It seems that the "update_attributes" method is not working anymore!
You must use the "update" method like this:
#user=User.update(:name=> params[:name], :user=> params[:username], :pass=> params[:password])

Related

attr_accessor breaks 2 of my methods in Rails

I have a User model. One of its attributes is a string called :access which can be either nil, "admin", or "active".
Now inside the User model I have the following methods:
def admin?
self.access == "admin"
end
def active?
self.access == "active"
end
They work fine. But if I add attr_accessor :access to the model something breaks. My admin? and active? methods no longer work. When I go into rails console and get a User out of the database I can see that user = User.find(7) shows access is set to "admin". But if I type user.access it returns nil. user.admin? returns false.
attr_accessor is overwriting the default methods of reading and writing an attribute from db. Why do you need attr_accessor if it is already a column in db and Rails provides you with the read and write methods. Are you confusing it with attr_accessible?

creating stub for after_create hook in rspec

my model code is:
class User < ActiveRecord::Base
after_create :create_node_for_user
def create_node_for_user
UserGraph.create(user_id: self.id)
end
end
and test for User model:
it "create node in graph database on user creation" do
userr = FactoryGirl.build(:user)
UserGraph.should_receive(:create).with(user_id: userr.id)
userr.save
end
but my test is failing with message
Failure/Error: userr.save
<UserGraph (class)> received :create with unexpected arguments
expected: ({:user_id=>nil})
got: ({:user_id=>94})
what might be wrong?
The explanation given by Yves is correct: the user id is nil until the record is saved because it is autogenerated by the DB. Here's an alternate approach:
it "create node in graph database on user creation" do
userr = FactoryGirl.build(:user)
create_args = nil
UserGraph.should_receive(:create) { |*args| create_args = args }
userr.save
expect(create_args).to eq(:user_id => userr.id)
end
Essentially, this moves the expectation about what the arguments should be so that it comes after the record has been saved, when the record has an id.
The Problem is that the userr you build with FactoryGirl does not have an ID. Thats why the expectation tells you that you expected :user_id=>nil. The ID will be generated when AR saves the record, so there is no way that you can guess the generated ID ahead of time. You could use a less restrictive assertion on the mock:
UserGraph.should_receive(:create).with(hash_including(:user_id))
This will verify that a hash is passed with a :user_id key. You can find more about hash_including here: http://rubydoc.info/gems/rspec-mocks/RSpec/Mocks/ArgumentMatchers:hash_including
Another thing you can try (not sure if it works) is to match against the kind_of matcher of rspec. This would make sure that a number was passed with :user_id
UserGraph.should_receive(:create).with(:user_id => kind_of(Numeric))

Rails -- self vs. #

I am following Michael Hartl's RoR tutorial, and it is covering the basics of password encryption. This is the User model as it currently stands:
class User < ActiveRecord::Base
attr_accessor :password
attr_accessible :name, :email,: password, :password_confirmation
email_regex = /^[A-Za-z0-9._+-]+#[A-Za-z0-9._-]+\.[A-Za-z0-9._-]+[A-Za-z]$/
#tests for valid email addresses.
validates :name, :presence => true,
:length => {:maximum => 50}
validates :email, :presence => true,
:format => {:with => email_regex},
:uniqueness => {:case_sensitive => false}
validates :password, :presence => true,
:length => {:maximum => 20, :minimum => 6},
:confirmation => true
before_save :encrypt_password
private
def encrypt_password
self.encrypted_password = encrypt(password)
end
def encrypt(string)
string
end
end
I posted a previous question about before_save not working, and it turns out that what I had accidentally done is written my encrypt_password as:
def encrypt_password
#encrypted_password = encrypt(password)
end
I understand that if self.encrypted_password sets the encrypted_password attribute, but why does #encrypted_password not do that as well? In the response to the previous post about before_save not working someone said that the instance variable was "forgotten" after the method ended with the way I had originally coded it -- why was this the case? Can someone please explain how self and # work differently in the context of the code above?
NOTE: I already took a look at the posts here and here, but they both say that "self" is calling the attribute = method, and I don't even understand how that method could exist here since I never created it or declared the encrypted_password w/ attr_accessor. So I am still confused, and this is not a re-posting of those questions.
The accessors for encrypted_password have been automatically added by Rails for you because a field by that name exists in the users table.
Any field you add to a table will be automatically made available via self.field_name.
Here is where Michael Hartl's tutorial creates the encrypted_password field in the users table.
Also look at the user_spec.rb (Listing 7.3) in the linked page, where the author is testing for the presence of the encrypted_password field.
UPDATED:
As #mu points out, the # is used for Ruby instance variables (aka "iv"). But encrypted_password is an "attribute" defined by Rails, and is not an instance variable.
If you run User.find(1).instance_variables, you will see that there is an iv called #attributes, which is of type Hash.
Inside that iv is where the encrypted_password is stored. Rails has defined accessor methods for encrypted_password, which gets/sets the data for that
attribute in the #attributes Hash.
Note that you could also get/set the data via #attributes["encrypted_password"] called from within the User class (but the accessor methods are convenient way to do just that).
If you let me, I'd like to rephrase the answer.
I explained in this post, that as soon as you create a (rails-) Model with the same (singular) name as one of the (plural) tablenames of your database, the "magic" of rails will create setters and getters in order to modify your table's records.
This is because your model inherits all methods from the ActiveRecord::Base Class, which defines basic CRUD accessors (Create, Read, Update, Delete).
The key point related to your question, is that you don't know how rails implements the instance variable related to your database table column, And you shouldn't. :) All you have to know is that at that point, you have setters and getters available to CRUD (create, read, update, delete) your database column "encrypted_password".
In your example, maybe rails uses an instance variable called #encrypted_password, maybe rails uses an hash-instance-variable called #attributes["encrypted_password"], or maybe rails uses an instance variable called #you_will_never_guess_encrypted_password.
-
And that's a good point you don't know about the internal rails behavior with instance variables. In 2019 Rails further development may lead the framework to use #complicated-hash-instance-variable to store the encrypted_password value.
In fact the best approach is to let rails manage its "private" "affair" ;) with instance variables, and just use the getter and setter methods it provides to you.
So your application will still work with encrypted_password in the next century (I hope so ^^).
So if you use #encrypted_password it may work with some "imaginary" version of rails and it won't work anymore with other rails versions. Actually with a current version of rails it doesn't work.
-
The second key point is that when you want to use the getter "encrypted_password" Rails created for your encrypted_password database table column, you prefix it with "self" in order to tells Ruby : "ok I want to use the encrypted_password method of my User instance variable."
In Ruby, a method is called by passing its name to a receiver.
You write it like this :
my_receiver.my_method
In your case we pass the method encrypted_password to the User instance variable. But we don't know how this instance variable will be named, so we use the word self to tell Ruby : "I'm talking about any instance variable of the User class that calls the encrypted_password method".
For instance we could have named our instance variable "toto" :
toto = User.new
so toto.encrypted_password would display the encrypted password, and self in this very case in our code would reference toto.
However, thanks to Ruby, if you don't give any receiver when calling a method, Ruby will assume you pass it to self.
Reference : Pragmatic Programmer's guide
So in your example, you even don't need to put "self." as prefix.
You could have it written like this :
class User < ActiveRecord::Base
def encrypt_password
encrypted_password = encrypt(password)
end
end
I hope this helps to clarify this interesting subject.
TL;DR -
Always write self.widget_count = 123 if you intend to save widget_count back to the database.
(But please do read the long answers, as the reason why is valuable to know.)

How to magically supply Active Record scopes with arguments?

I'm not sure this is even possible, but let's see if one of you comes up with a solution. This is more or less about code quality in terms of readability and not an actual problem because I already have a solution. I have a friendship model and a user model. The friendship model is used to model friendships between two users:
class Friendship
def self.requested(user)
where(:user_id => user).where(:status => 'requested')
end
def self.pending(user)
where(:user_id => user).where(:status => 'pending')
end
def self.accepted(user)
where(:user_id => user).where(:status => 'accepted')
end
# ...
end
class User
has_many :friendships
# ...
end
Is it somehow possible to call the requested, pending or accepted scope through
the user model without providing an argument?
a_user.friendships.pending # this does not work, is there a way to get it working?
a_user.friendships.pending(a_user) # works of course!
I think this should work if you take the argument off. Calling pending off of the user object like this should already scope friendships to the appropriate user. Define the method like this:
def self.pending
where(:status => 'pending')
end
And call:
a_user.friendships.pending
Check the logs for the generated query if you're not sure it's working.
If you still want to call it by passing an argument I'd name that method Friendship.pending_for(user).

ROR Devise: allowing username or email logins: undefined local variable

I've been following the tutorial on the Devise github page here: https://github.com/plataformatec/devise/wiki/How-To:-Allow-users-to-sign_in-using-their-username-or-email-address
I'm in the second part of the tutorial where we allow users to recover their password using either their username or email and there's a large block of code that the tutorial says to copy into the User model. This is the line that throws an error:
def self.find_record(login)
where(attributes).where(["username = :value OR email = :value", { :value => login }]).first
end
This is the error I get:
NameError (undefined local variable or method `attributes' for #<Class:0xa70e1a8>):
app/models/user.rb:63:in `find_record'
app/models/user.rb:44:in `find_recoverable_or_initialize_with_errors'
app/models/user.rb:30:in `send_reset_password_instructions'
Anyone know why this error is showing up?
You're not passing in any attributes, and there's no 'attributes' class method, so where(attributes) doesn't work. It doesn't look like you need to either. Change your method to:
def self.find_record(login)
where(["username = :value OR email = :value", { :value => login }]).first
end
For what it's worth, I don't see how it would have worked in the tutorial either.

Resources