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
Related
I am developing a rails app. Most of the parts work fine, but I got one weird problem when I tried to calculate the time an user used to edit and submit one form.
I thought it would be good to do it in the following order:
1. in the controller "edit" method, record the time the user start to see the form.
2. in the "update" method, record the submit time, then do the math and get how long the user had spent on the form.
class Test
##start_time = 0
##end_time = 0
def edit
##start_time = Time.now
end
def update
##end_time = Time.now
time_used = ##end_time - ##start_time
puts time_used.to_i
end
end
The code above actually works fine while running on my own developing computer, the output is what I expected. But when I upload the code to the production environment(multicore cpus), sometime the output is right, sometime it is not. I debugged the code and found in some case, the ##start_time is set to 0 when submitting the form. I am confused what was going on, maybe I just misused the ## for the variable. Please help me out, any idea would be appreciated, thanks.
Edit:
The problem is solved by adding a virtual attribute to the model as hinted by Vishal. In addition, I added a hidden field in the submit form, and in the strong parameter part added the corresponding parameter to allow it to be passed from edit to update method.
Your solution will create conflicts when more than two users try to edit simultaneously, So basically what idea I have is:
Add one virtual attribute in your model edit_start_time You don't need attribute for endtime because it can be directly fetched by Time.now at any time.
Set edit_start_time value in edit method like:
#model.edit_start_time = Time.now.utc #you can use any
In update method directly calculate edit time like:
total_update_time = Time.now.utc - #model.edit_start_time.utc
If you are unaware of how to create virtual attributes then there are so many questions on StackOverflow as well as docs. I am not explaining how to do it here because its the different topic.
All the best
You're using class variables that can interfer with each other. Your Test class will only ever have one class variable called ##start_time associated with it.
This means if another user sees the form, they will reset the ##start_time for every user currently on it.
To prevent this, use instance varaibles. When a new user sees the form, they will make a new instance variable that is tied to their instance of the class, rather than the class itself. This will allow users to have different start and end times.0
All you need to do is change every ## to #. So instead of ##start_time', try#start_time` throughout your code, and the same for end_time.
I am trying to implement 2FA(two factor authentication) in my existing rails 4.2.10 application, I have configured many bits.
Issue I am facing is to get/retrieve a code which is valid for 5 minutes and send this code over to user on his defined phone number or email.
I did tried ROTP::TOTP.new(user.otp_secret).at(Time.now), guessing from gem's source code, which seems to work fine and give a valid otp_code in console, but in sessions_controler, as weird as it sounds, user.otp_secret is null, always...
I have posted an issue on the gem.
I don't think this can be bug, rather this is a functionality I want to build.
My stack:
Ruby: 2.4.2
Rails: 4.2.10
Devise: 4
attr_encrypted: 1.4(if it matters)
Additionally, I want to extend drift period(code acceptance time) to 5 minutes. I think that will be easy, but doing it for single code, not universally, or for all codes, this has me thinking for a while now.
My main issue is the first one, getting the code to send through SMS, this is a subproblem, which I think is doable, but if anyone has/had experience with this and can help, that will be great.
UPDATE: I updated attr_encrypted and restarted the system, it started working, also I realized there is a method current_otp in which devise_two_factor adds in the user model, so I started using that. BUT after a few minutes, it is also throwing the same issue of user.otp_secret being nil. Its getting weird...
UPDATE 2/Hacky solution: Weirdly enough, I had to add these 3 methods in user model and everything started working:
def encrypted_otp_secret
self[:encrypted_otp_secret]
end
def encrypted_otp_secret_iv
self[:encrypted_otp_secret_iv]
end
def encrypted_otp_secret_salt
self[:encrypted_otp_secret_salt]
end
As you can suspect, i got here by examining a behavior thatdoing user.encrypted_otp_secret was giving me nil while it was not, even after reloading user model. And doing user[:encrypted_otp_secret] was giving me the actual value.
It seems like a bug in attr_encrypted. I am not sure yet.
For anyone else that runs into this issue, I have found a next step needed to get the current_otp method to work. In the method pre_otp method call
> u = User.find_by(email: 'test#example.com')
> u.otp_required_for_login = true
> u.otp_secret = User.generate_otp_secret
> u.save!
and then you can call u.current_otp...
https://blog.tommyku.com/blog/integrating-two-step-two-factor-authentication-into-rails-4-project-with-devise/
I've looked everywhere for a similar error but couldn't find a solution, so in desperation I'm posting here.
My controller has this:
def add_upc
#upcs = Dvd.add_upc(params[:dogTag], params[:newUpc])
end
and in the Model we have:
def self.add_upc(dogTag, newUpc)
existingUpc = Dvd.find(dogTag).dvd_upc2title.find_by_upc(newUpc)
if existingUpc.nil?
createdUpc = Dvd.find(dogTag).dvd_upc2title.create(:upc => newUpc)
if createdUpc
upcs = createdUpc
else
upcs = 'Error: nothing was created'
end
end
end
I've set up a view page to see what's happening and I can see the object being created by createdUpc. I can also confirm that the parameters dogTag and newUpc are being passed correctly. Yet the record is not being added to the table.
Weirdly, this does work if I issue the Dvd.find(dogTag).dvd_upc2title.create(:upc => newUpc) command with the values substituted for the variables from the IRB.
Can't figure out why this is not working. I'm new to Rails so don't know what other error debugging I could use to figure out where the problem lies.
Ideas are welcome.
Thanks.
Edit:
Found the error thanks to RyanWilcox, it was the validation I had set up in the controller for UPC telling me that value already existed (even though UPCs are supposed to be unique. Is there a way to validate on a combination of 2 fields?
What I really like doing for situations like this ("why did this fail to save?") is using create! instead of create.
This will throw an exception on error, with the failed validation's text as the message of the exception. It makes problems like this obvious.
I have a user. A user can have many tables. Actual, only 5.
In my table model I have
validate :max_tables
def max_tables
if user.tables.count > 5
errors[:base] << "You already have 5 tables."
end
end
This works all good and if I try to create a table and my user already has 5, I get a page that says
ActiveRecord::RecordInvalid in TablesController#create
Validation failed: You already have 5 tables.
But I don't get redirected back to the new table page with errors displaying nicely like I do if other validations aren't met. For some reason I'm getting stuck on this hard error page.
Any ideas?
EDIT: SOLVED
I was generating a short url in an after_create callback, and in there I was calling save!
Once I fixed that, All was good. So thanks #house9!
Use errors.add_to_base "You already have 5 tables." - in my oldest and biggest app I use errors.add_to_base and return false - it could be that the return is not required, but I haven't tested it.
EDIT: SOLVED
I was generating a short url in an after_create callback, and in there I was calling save!
Once I fixed that, All was good. So thanks #house9!
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.