Undefined method `[]' for nil:NilClass when trying to pull value from hash - ruby

I'm new to Ruby, and for the most part it's starting to make sense, but then I came across this problem.
I searched for a user using:
user = User.find_by_id(user_id)
I can do user.inspect and it displays as:
#<User id: 1, username: "test_user", first_name: "test", last_name: "user">
However, when I try to do:
user[:id]
I get:
undefined method `[]' for nil:NilClass
What am I overlooking?
Here is the user.rb:
class User < ActiveRecord::Base
# Validations
validates :first_name, :last_name, :presence => true
validates :username, :uniqueness => {:allow_blank => true}
# Setup accessible (or protected) attributes for your model
default_fields = [:username, :first_name, :last_name]
attr_accessible *default_fields
# Instance Methods
def full_name
"#{first_name} #{last_name}"
end
end
Additionally, if I do:
user.to_s
it results in:
#<User:0x007fdad09717f8>
Which further leads me to believe that it's not nil, but yet I can't reference the hash values.

your first sentence
user = User.find_by_id(user_id)
returned a null object, so you need to test this in order to avoid the exception:
if( ! user.nil? )
# code with user[:id]
end

So the problem turns out not to be related to code, but to data in a Database table that I was using to get the 'user_id' value. Thats what I get for trusting that someone else would have written proper validations on all the models in the project.
Thats everyone for helping me resolve this, if I hadn't needed to create a minimum example that reproduced the issue I never would have stumbled across the bad entry in the database.
So to use Miguel Prz's Idea I ended up doing this:
user.nil? ? '' : user[:id]
This is further proof why I should never trust anyone else's work :)

Related

Rails returns an empty active record and failed to initialize the attributes in side the model

I am getting an empty active record when i initialize new model object.
2.3.0 :030 > user = User.new
=> #<User >
2.3.0 :031 >
when i access the attributes present in the model it returns an error like
2.3.0 :031 > user.UsersName
NoMethodError: undefined method `UsersName' for #<User >
User Model:
class User < ActiveRecord::Base
attr_accessible :UserName, :Password, :RoleId, :EmployeeId, :Password_confirmation
end
Please be sure either one of these is true:
Your model User is inherited from ActiveRecord::Base and you have a column UserName inside your users table. Though, by convention, we should not have attribute names started with capital letter.
EDIT- You can skip 'User' from 'UserName' as it is redundant and replace it with user.name. For that you should have column name inside users table and nothing else need to be defined inside model.
If above is not the case, then you must have an attr_accessor :UserName Or
you must have a method def UserName. Not sure if capital letters work here.
Also, i suggest you to define all your fields starting with lowercase letters.
Your model declares that UserName is accessible yet you're calling UsersName (user.UsersName).
Have you tried user.UserName?

RSpec Testing Controller with Model.create

I'm trying to test a controller to ensure that only an authorized party can view the correct child object using RSpec. I cant figure out what I'm doing wrong as I'm getting this error:
ActiveRecord::RecordInvalid: Validation failed: Company can't be blank
I have a Plan object and a Company object. The Store can have many plans (think of a pest control Company). I want to test that given a known scenario I can retrieve the plan fo the Company (assuming there is only one).
The Plan looks like this:
class Plan < ActiveRecord::Base
before_save :default_values
# Validation
validates :amount, :presence => true
validates :company, :presence => true
# Plans belong to a particular company.
belongs_to :company, :autosave => true
scope :find_all_plans_for_company, lambda {
|company| where(:company_id => company.id)
}
# Other code ...
end
The Company looks like this:
class Company < ActiveRecord::Base
validates :name, :presence => true
validates :phone1, :presence => true
validates_format_of :phone1, :phone2,
:with => /^[\(\)0-9\- \+\.]{10,20}$/,
:message => "Invalid phone number, must be 10 digits. e.g. - 415-555-1212",
:allow_blank => true,
:allow_nil => true
has_many :users
has_many :plans
end
.. controller looks like this
def index
#plans = Plan.find_all_plans_for_company(current_user.company)
respond_to do |format|
format.html # index.html.erb
format.json { render json: #plans }
end
end
.. and my RSpec test looks like this (excuse me if its full of gimmickery, I'm just splunking around with it and cannot get it to work).
describe PlansController do
def valid_attributes
{
:company_id => 1,
:amount => 1000
}
end
describe "GET index" do
it "should return the Plans for which this users company has" do
#company = mock_model(Company, :id => 1, :name => "Test Company", :phone1 => "555-121-1212")
Company.stub(:find).with(#company.id).and_return(#company)
controller.stub_chain(:current_user, :company).and_return(#company)
plan = Plan.create! valid_attributes
get :index, {}
assigns(:plans).should eq([plan])
end
# Other tests ...
end
end
The problem is, when I try this (or any of the crazy other variants I've tried) I get this error:
ActiveRecord::RecordInvalid: Validation failed: Company can't be blank
I'm not sure why this is happening as I thought the Company.stub call would handle this for me. But apparently not.
What am I missing here and what am I doing wrong? How can I get this test to pass?
Let's peel back the layers on this spec, to make sure things make sense (and to make sure I understand what's going on). First, what are you testing?
it "should return the Plans for which this users company has" do
...
assigns(:plans).should eq([plan])
So you want to check that the plans associated with the company of the current user are assigned to #plans. We can stub or mock out everything else.
Looking at the controller code, we have:
def index
#plans = Plan.find_all_plans_for_company(current_user.company)
What do we need to get this to work, without hitting the database and without depending on the models?
First of all, we want to get a mock company out of current_user.company. This is what these two lines in your spec code do:
#company = mock_model(Company, :id => 1, :name => "Test Company", :phone1 => "555-121-1212")
controller.stub_chain(:current_user, :company).and_return(#company)
This will cause current_user.company to return the mock model #company. So far so good.
Now to the class method find_all_plans_for_company. This is where I'm a bit confused. In your spec, you stub the find method on Company to return #company for id = 1.
But really, wouldn't it suffice just to do something like this in your controller code?:
#plans = current_user.company.plans
If you did it this way, then in your test you could just mock a plan, and then return it as the plans association for your mock company:
#plan = mock_model(Plan)
#company = mock_model(Company, :plans => [ #plan ])
controller.stub_chain(:current_user, :company).and_return(#company)
Then the assignment should work, and you don't need to actually create any model or hit the database. You don't even need to give your mock company an id or any other attributes, which anyway are irrelevant to the spec.
Maybe I'm missing something here, if so please let me know.
Why do you need to mock?
My standard testing setup is to use Database Cleaner which clears out the database from any records created during tests. In this way, the tests are run with real database records which are consequently deleted from the test database after each test.
You might also like taking a look at Factory Girl for creating instances of your models during testing (makes it easy to create 10 company records, for example).
See:
http://rubygems.org/gems/database_cleaner
http://rubygems.org/gems/factory_girl
I have three thoughts coming up that could resolve your issue:
Try adding attr_accessible :company_id to Plan class.
Because mock_model does not actually save to the database when you create a Plan with company_id of 1 it fails validation since it is not present in the database.
Ensure before_save :default_values in Plan class does not mess with company_id attribute of the newly created instance.

cant use has_secure_password, password_digest error

Good evening. I have a problem. i am using has_secure_password
and cause of this i have an error undefined methodpassword_digest=' for #`,
but i dont have this method!! Please help, dont know what to do. I read how to fix this problem but it didnt help me(
Here is my User model. Please help if you can.
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation
has_secure_password
validates_presence_of :password, :on => :create
before_create { generate_token(:auth_token) }
def send_password_reset
generate_token(:password_reset_token)
self.password_reset_sent_at = Time.zone.now
save!
UserMailer.password_reset(self).deliver
end
def generate_token(column)
begin
self[column] = SecureRandom.urlsafe_base64
end while User.exists?(column => self[column])
end
end
You may have forgotten to make sure your migration backing the user model has a column for password_digest. Make sure the column exists and that it's a string. If it doesn't, create a migration to add the column.
Models having has_secure_password store password in password_digest column instead of password column.
In fact password column is not needed.
> u=User.create!(email: 'user#gmail.com', password: '12345678')
> u
#<User:0x007fc794be9278> {
:id => 1,
:email => "user#gmail.com",
:password_digest => "$2a$10$S82GVFR..yO9jihgIoeMj.7dNMWtbCUZpWDKvH0tyMs1SYlfdefmW"
}
I had the same problem, I was following http://www.railstutorial.org/book/modeling_users
and my app/Controllers/users_controllers.rb didn't have a method to create the attribute, I also used git to share the working code between portable laptop for the train and larger home, this created the migration file but didnt apply it, my working user controller is below.
class UsersController < ApplicationController
def new
attr_accessor :name, :email, :password
def initialize(attributes = {})
#name = attributes[:name]
#email = attributes[:email]
#password = attributes[:password]
end
def formatted_email
"#{#name} <#{#email}>"
end
end
Hey I'm following RoR too and come into the same problem. The trick here is your bundle exec rake db:migrate fails and therefore the password_digest column hasn't been added into the database. My console complains that database for User already exists. I delete the db/development.sqlite3 manully with "SQLite Browser". After running bundle exec rake db:migrate, every test passes

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.)

activerecord validation - validates_associated

I'm unclear on what this method actually does or when to use it.
Lets say I have these models:
Person < ...
# id, name
has_many :phone_numbers
end
PhoneNumber < ...
# id, number
belongs_to :person
validates_length_of :number, :in => 9..12
end
When I create phone numbers for a person like this:
#person = Person.find(1)
#person.phone_numbers.build(:number => "123456")
#person.phone_numbers.build(:number => "12346789012")
#person.save
The save fails because the first number wasn't valid. This is a good thing, to me. But what I don't understand is if its already validating the associated records what is the function validates_associated?
You can do has_many :phone_numbers, validate: false and the validation you're seeing wouldn't happen.
Why use validates_associated then? You might want to do validates_associated :phone_numbers, on: :create and skip validation on update (e.g. if there was already bad data in your db and you didn't want to hassle existing users about it).
There are other scenarios. has_one according to docs is validate: false by default. So you need validates_associated to change that.

Resources