Ruby why is my object saving without an ID? - ruby-on-rails

I feel like I'm missing something very basic, and someone can look at this and very quickly telling me what that is...
def create
#user = User.find_by_id(create_params[:user_id])
#plan = #user.plans.new(create_params)
if #plan.save
puts #plan.inspect
end
end
Right now the puts statement returns this:
#<Plan id: nil, zipcode: "02114", selected_plan: 5, user_id: 1>
So basically.. all the attributes are there, but there's no id...
More info:
create_params = {zipcode:"02114", selected_plan:"5", user_id: 1}
And if it's helpful to know, save! does work and successfully saves the plan with an id... and plan.save == true returns true

Your code should have the following pattern.
#user = User.find(create_params[:user_id]) # see changes
#plan = #user.plans.new(create_params)
if #plan.save
puts #plan.reload.inspect #reload object to see if the object has been saved!
else
puts #plan.errors.messages.inspect # if object is not saved there should be Object#errors.messages should have messages
end

Related

making an each in the right way

In movie_controller I added this
def the_followers
#movie = Movie.find(params[:id])
#followings = #movie.followings.where(user_id: params[:user_id])
.page(params[:page]).per(10)
render layout: nil
end
the_followers is the name of the file in view/movies/the_followers
in movie (view) I added this. but It did not work. Why?
<% #followings.each do |following| %>
Username: <%= following.user.username %>
<% end %>
I have a blank list. Did I miss anything?
You may have different issues providing the list empty. Try following steps to isolate the problem.
remove scopes
#followings = Following.all.paginate(page: 1, per_page: 100)
Add them one by one
#followings = #movie.foollowings.all.paginate(page: 1, per_page: 100)
then try
#followings = #movie.foollowings.where(user_id: params[:user_id]).paginate(page: 1, per_page: 100)
finally
#followings = #movie.followings.where(user_id: params[:user_id])
.page(params[:page]).per(10)
See for yoursellf, at what step you'r array becomes empty.
You can use the simplest debugging method to locate the problem.
def the_followers
#movie = Movie.find(params[:id])
# Check movie followings array is empty?
puts "---movie following num: #{#movie.followings.count}---"
# Check the user_id param is present?
puts "---user_id: #{params[:user_id]}---"
# Check movie followings with user_id is empty?
puts "---movie following with user_id num: #{#movie.followings.where(user_id: params[:user_id]).count}---"
#followings = #movie.followings.where(user_id: params[:user_id])
.page(params[:page]).per(10)
# Check the result is empty?
puts "---result num: #{#followings.count}---"
render layout: nil
end
If all puts correct, then check the views code.

Strong parameters in test issue

Ruby 2.1.1p76 on Rails 4.1.1.
Please check out my controller:
def update
begin
current_user.update_settings user_settings_params unless params[:user_setting].blank?
current_user.update_attribute :district_id, params[:user][:district_id] unless params[:user].blank? || params[:user][:district_id].blank?
flash[:success] = "Preferencje zostały zaktualizowane"
redirect_to subscription_index_path
rescue UserLevelException => exception
flash[:alert] = "Sprytnie, za karę zostałeś wylogowany ;)"
session[:user_id] = nil
redirect_to root_path
return
end
end
private
def user_settings_params
params.require(:user_setting).permit(
:inquiry_subject, :inquiry_body,
:offer_subject, :offer_body,
:only_companies_with_email,
{:district_ids => []},
# {:district_ids => params[:user_setting][:district_ids].try(:keys)},
:delivery_address,
)
end
See the commented line? In the form above - user_settings_params will not return :district_ids array of ids, and this is fine, since I can use the line below instead to have them (got it from guides).
The problem I have is when running this test:
test 'should set user level10 districts' do
user = login_user :paid10
post :update, :user_setting => {:district_ids => [districts(:zachodniopomorskie).id, districts(:slaskie).id]}
assert_equal nil, flash[:alert]
assert_equal 'Preferencje zostały zaktualizowane', flash[:success]
db_user_districts = User.find(user.id).settings.districts.all
assert db_user_districts.include? districts(:zachodniopomorskie)
assert db_user_districts.include? districts(:slaskie)
assert_equal 2, db_user_districts.count
end
It passes. When debugging user_settings_param has :district_ids available as if strong parameters were disabled or something. I wanted to submit an issue to rails but most probably I'm doing something wrong and can't figure it out.
I've found it - it was because of quirky way I was creating checkboxes for HABTM
= check_box_tag "user_setting[district_ids][#{district.id}]", district.id, user.settings.district_ids.include?(district.id)
= label_tag "user_setting[district_ids][#{district.id}]", district.name
For no particular reason I've inserted ids into params keys AND values. And because of that those were passed to params object as hash. In test though those were sent as array. So it was the view to blame.
= check_box_tag "user_setting[district_ids][]", district.id, user.settings.district_ids.include?(district.id)
= label_tag "user_setting[district_ids][]", district.name

Rails - 'can't dump hash with default proc' during custom validation

I have 2 models. User and Want. A User has_many: Wants.
The Want model has a single property besides user_id, that's name.
I have written a custom validation in the Want model so that a user cannot submit to create 2 wants with the same name:
validate :existing_want
private
def existing_want
return unless errors.blank?
errors.add(:existing_want, "you already want that") if user.already_wants? name
end
The already_wants? method is in the User model:
def already_wants? want_name
does_want_already = false
self.wants.each { |w| does_want_already = true if w.name == want_name }
does_want_already
end
The validation specs pass in my model tests, but my feature tests fail when i try and submit a duplicate to the create action in the WantsController:
def create
#want = current_user.wants.build(params[:want])
if #want.save
flash[:success] = "success!"
redirect_to user_account_path current_user.username
else
flash[:validation] = #want.errors
redirect_to user_account_path current_user.username
end
end
The error I get: can't dump hash with default proc
No stack trace that leads to my code.
I have narrowed the issue down to this line:
self.wants.each { |w| does_want_already = true if w.name == want_name }
if I just return true regardless the error shows in my view as I would like.
I don't understand? What's wrong? and why is it so cryptic?
Thanks.
Without a stack trace (does it lead anywhere, or does it just not appear?) it is difficult to know what exactly is happening, but here's how you can reproduce this error in a clean environment:
# initialize a new hash using a block, so it has a default proc
h = Hash.new {|h,k| h[k] = k }
# attempt to serialize it:
Marshal.dump(h)
#=> TypeError: can't dump hash with default proc
Ruby can't serialize procs, so it wouldn't be able to properly reconstitute that serialized hash, hence the error.
If you're reasonably sure that line is the source of your trouble, try refactoring it to see if that solves the problem.
def already_wants? want_name
wants.any? {|want| want_name == want.name }
end
or
def already_wants? want_name
wants.where(name: want_name).count > 0
end

Add value to :params[]

What i have: (Action in Controller)
def create
#test = Test.new(params[:test])
#test.save
devicefiles = params[:devicefiles]
if devicefiles != nil
devicefiles.each do |attrs|
devicenote = Testdevicenote.new(attrs, :test_id => #test.id)
devicenote.save
end
end
end
This controller action does not show any error message and is rendering the view, but :test_id is not being saved in the database. How can i solve this?
EDIT: Ok whoops, I see it now...
Models only take one hash on initialize, not 2.
Testobjectnote.new(attrs.merge(:test_id => #test.id))
In short no one here has any clue, because that's not enough information. We dont know how your models are setup.
But when debugging models that "won't save" it's often good to use the bang version save, save!. save returns true or false letting you know if it was able to save the record. But save! will raise exceptions when the model can't be saved, and the exception will tell you why.
That exception will likely tell you why the record is not being saved.
Also, its usually better to use the associations, rather than manage the ids yourself.
def create
#test = Test.new(params[:test])
if params[:devicefiles]
params[:devicefiles].each do |attrs|
#test.testdevicenotes << Testdevicenotes(attrs)
end
end
#test.save
end
It's hard to say because you didn't post your view with the form that is posting to the create action, but if it's a typical Rails form, it should probably look like:
def create
#test = Test.new(params[:test])
#test.save
devicefiles = params[:test][:devicefiles]
if devicefiles != nil
devicefiles.each do |attrs|
devicenote = Testdevicenote.new(attrs, :test_id => #test.id)
devicenote.save
end
end
objectfiles = params[:test][:objectfiles]
if objectfiles != nil
objectfiles.each do |attrs|
objectnote = Testobjectnote.new(attrs, :test_id => #test.id)
objectnote.save
end
end
end
This assumes that :devicefiles and :objectfiles are inside the form :test

Rails Unit Testing won't update database

I'm trying to run the following unit-test:
def test_passwordchange
# check success
assert_equal #longbob, Usuario.autenticar("longbob", "longtest")
#change password
#longbob.password = "nonbobpasswd"
#longbob.password_confirmation = "nonbobpasswd"
assert #longbob.save!
#new password works
assert_equal #longbob, Usuario.autenticar("longbob", "nonbobpasswd")
#old pasword doesn't work anymore
assert_nil Usuario.autenticar("longbob", "longtest")
#change back again
#longbob.password = "longtest"
#longbob.password_confirmation = "longtest"
assert #longbob.save!
assert_equal #longbob, Usuario.autenticar("longbob", "longtest")
assert_nil Usuario.autenticar("longbob", "nonbobpasswd")
end
However, it throws error on the 1st line that contains "assert_equal" that says:
<#<Usuario ID: 1000003, login: "longbob", hashed_password: "078cf6ae2de80ed6c004c8c8576a5572e077a52c", salt: "1000", nombre: nil, apellido: nil, email: "lbob#mcbob.com", telefono: nil, tipo_usuario: nil, foto: nil, bol_activo: nil>> expected but was <nil>.
Here's my authenticate method:
def self.authenticate (login, pass)
u=find(:first, :conditions=>["login = ?", login])
return nil if u.nil?
return u if Usuario.encrypt(pass, u.salt)==u.hashed_password
nil
end
Also, I defined the following:
def password=(pass)
#password=pass
self.salt = Usuario.random_string(10) if !self.salt?
self.hashed_password = Usuario.encrypt(#password, self.salt)
end
So, I guess that should update the hashed_password every time I reassigned something to "password"... right?
Whats happening?
Thx.
UPDATE: I noticed that if I change:
assert_equal #longbob,
Usuario.autenticar("longbob",
"nonbobpasswd")
to
assert_equal #longbob2,
Usuario.autenticar("longbob",
"nonbobpasswd")
It passes that test, however it fails in the following line... Trowing the same error... What's up with that?
To answer after my comment, we find out that the problem is the call to save does not update the record in the database. I have suggested you to write another test to see this behaviour and be able to fix it, the test you have writed start to be too long and in fact the bug does not have anything related to the authenticate mechanism.
Here is the test I would write :
def test_change_password_save
old_hash = #longbob.hashed_password
#longbob.password = "nonbobpasswd"
#longbob.password_confirmation = "nonbobpasswd"
#longbob.save
assert_not_equal(old_hash, #longbox.reload.hashed_password)
end
If this test failed, I would suggest you to write another question in stackoverflow for this bug.
You're probably getting a validation error. Check the contents of #longbob.errors.
What happens if you split this into two separate statements? (Which is good practice anyway)
#longbob.password = #longbob.password_confirmation = "nonbobpasswd"
See, #password_confirmation= is actually a method, which might not return the value that was passed to it, depending on how the method was implemented.
Is it possible that changing the password variables doesn't update the hashed_password field in the database.
You probably need something like this in Usuario:
before_save :rehash
def rehash
self.hashed_password = ??? #replace ??? with whatever your hashing logic is
end

Resources