Couldn't find User without an ID (Devise user model) - ruby-on-rails

I am new to rails. I have a Devise user model. I am trying to get an input from user and compare it with a string and redirect to another page if same. But there is some problem with #user. It will be a simple problem but I am stuck for hours. Please help.
questions_controller
class QuestionsController < ApplicationController
def qn1
#user = User.find_by_email(params[:email])
end
def submit
#user = User.find_by_email(params[:user])
if #user.answer == 'blank'
render root_path
else
render 'qn1'
end
end
end
qn1.html.erb
<%= form_for(#user, :url => {:controller => "questions", :action => "submit"}) do |f| %>
<%= f.label :answer %>
<%= f.text_field :answer %>
<br>
<%= f.submit "Submit", class: "btn btn-large btn-primary" %>
<% end %>

You should retrieve a param sent by a form_for using it's multidimensional array, like this:
params[:user][:attribute]
You told us that you have Devise installed and running. If you're logged in, the most easy and secure way to access the current user is with the following method (automatically provided by devise on login)
current_user
So, if you want to compare the answer sent with the current user one, you could do something like this:
# QuestionsController
def submit
if current_user.answer == params[:user][:answer]
redirect_to root_path
else
render 'qn1'
end
end
Or, if it is just a matter of compare the answer sent with some arbitrary string, you don't even need the User model:
# QuestionsController
def submit
if params[:user][:answer] == 'blank'
redirect_to root_path
else
render 'qn1'
end
end
You shouldn't retrieve a user, or instantiate a new one, just to set it's answer to the one sent by the form and compare it with a string. It's absolutely unnecessary.

Related

Getting my Ruby on Rails page to save the user's email without using devise

I am building a one page website where visitors will simply be able to submit their email address. The only goal in the database is to get an email (no name, etc). There is only one page visible at first, which is the homepage. If the user submits an email already in use, it sends the user to an error page. If the email is not in use, it sends the user to a success page.
I have asked a question about this previously, and after a lot of comments and trial and error, it appeared that it worked and then it stopped working. When I do Rails C, there is only one user in the system and that user doesnt have an email...
Here is what my user migration looks like :
class CreateUsers < ActiveRecord::Migration[5.2]
def change
create_table :users do |t|
t.string :email
t.timestamps
end
add_index :users, :email, unique: true
end
end
Here is what my user model looks like:
class User < ApplicationRecord
end
Here is what users/new.html.erb looks like:
<%= form_for #user, as: :post, url: users_path do |f| %>
<div class="wrapper">
<%= f.email_field :email , id: "search", class:"search input" %> <br />
<%= f.submit "yep", class: "submit input" %>
</div>
<% end %>
Here is my user controller:
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(params[:email])
if #user.save
redirect_to '/users/success'
else
redirect_to '/users/error'
end
end
def show
end
end
Here are my routes:
Rails.application.routes.draw do
root "users#new"
resources :users
end
When i run the code, it renders the homepage but when i click on submit, it sends me on a page called show.html.erb with http://localhost:3000/users/error on my brownser. No users are being saved in the console.
EDIT:
My model is
class User < ApplicationRecord
validates_uniqueness_of :email
end
It is still not working....
change new.html.erb as
<%= form_with(model: #user, local: true) do |f| %>
<div class="wrapper">
<%= f.email_field :email , id: "search", class:"search input" %> <br />
<%= f.submit "yep", class: "submit input" %>
</div>
your controller will be
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
redirect_to user_path(#user), notice: "yeh!!!!"
else
redirect_to new_user_path, notice: "email already registered"
end
end
def show
#user = User.find(params[:id])
end
private
def user_params
params.require(:user).permit(:email)
end
end
add
<p id="notice"><%= notice %></p> to your application.html.erb in layouts
rest as your question
There are a couple things wrong here.
You're so close, but you're misusing the as: attribute of form_for. Perhaps you think that will send as a POST request, but instead that is actually wrapping your form params in an object called "post". I saw this in the comments on another thread.
Remove the as: attribute and the helper will again wrap your params in the user object. While we're at it, you should also be able to remove the url: attribute as well since Rails form helpers are smart enough to infer that this is a new resourceful record and output the create URL as well as the POST action accordingly.
You need your controller to expect a whole "user" object instead of just checking for the email param. ALSO, assuming you're on Rails 4 or higher, you need to permit the email attribute to be mass-assigned on your User object. See the code.
def create
#user = User.new(params.require(:user).permit(:email)) # Not params[:email]
if #user.save
redirect_to '/users/success'
else
redirect_to '/users/error'
end
end
Also be careful about duplicate emails with different cases. The default in Rails is case-sensitive validation which means "JIM#gmail.com" would not trigger a validation error against "jim#gmail.com". You can fix this with.
class User < ApplicationRecord
validates :email, uniqueness: { case_sensitive: false }
end
BONUS!
Nowadays, it's better to move over to form_with (instead of form_for). It's on its way to becoming the new Rails standard and also makes a few of these things easier. The one point you'll want to keep in mind is that with form_with (and general Rails assumptions), forms are remote by default. So if you want to trigger a full page submit/refresh, add local: true to your form_with helper.
<%= form_with model: #user, local: true do |f| %>
<div class="wrapper">
<%= f.email_field :email , id: "search", class:"search input" %> <br />
<%= f.submit "yep", class: "submit input" %>
</div>
<% end %>
As you are using resources in routes so def show is called when route is /users/:id. That's why its calling show.html.erb file.
When you try to validate an email, then in model write the validation for it
class User < ApplicationRecord
validates_uniqueness_of :email
end
Hope this helps.
Try to add validates_uniqueness_of in your model
class User < ApplicationRecord::Base
attr_accessor :email
validates_uniqueness_of :email
end
And
def show
#user = User.find(email: params[:email])
end
And if you wanna check all
def show
#user = User.all
end
Please try this.
I hope that helpful

cannot update attribute within users model in custom action

I am using the Gem Devise for users in my application. I added the role attribute within users that is set to nil. After I sign in, within the application controller I have a redirect that goes to a custom action within the users controller and view called binary_selection if current_user.role = nil. The code is below
application_controller.rb:
def after_sign_in_path_for(resource)
if current_user.role.nil? ## temporary solution
#update_path(resource)
binary_selection_path(resource)
else
root_path(resource)
end
end
users_controller.rb:
def binary_selection
#user = current_user
respond_to do |format|
if #user.update_attributes(params[:user][:role])
format.html { redirect_to root_url, notice: "#{#user.name} was updated." }
else
format.html { render action: "edit" }
end
end
end
views/users/binary_selection.html.erb:
<%= form_for #user, url: binary_selection_path(#user), html: { method: :patch } do |f|
%><%= current_user %>
<div class = "form-group">
<%= f.label :role %>
<%= f.text_field :role, class: 'form-control', placeholder: "Enter wiki title", id: 'wiki_title' %>
</div>
<div class = "form-group">
<%= f.submit class: 'btn btn-success' ,id: 'wiki_submit' %>
</div>
<% end %>
config/routes.rb:
Rails.application.routes.draw do
devise_for :users
get "/users" => "users#binary_selection", as: 'binary_selection'
resources :users
end
When I get redirected to the binary_selection view I get this error:
NoMethodError in UsersController#binary_selection
undefined method `[]' for nil:NilClass
where it highlights this line within the users controller:
if #user.update_attributes(params[:user][:role])
I also noticed it has this for the params on the error page:
{"format"=>"13"}
You redirect your user after sign_in to the binaray_selection_path. This will result in a HTTP GET request. In your users_controller you try to get a attribute from the params that simple does not exist. There is no params[:user], because the form was not sent yet.
You need to redirect the user to a page were the form is rendered.
I advise to read this guide: http://guides.rubyonrails.org/getting_started.html#getting-up-and-running
Your form is not submitting the values properly may be. So inspect your params in the binery select action.
record#update_attributes doesn't take a single value, it accepts hash. So the proper code should be something like:
rails version < 4
if #user.update_attributes(params[:user])
in rails 4.*
if #user.update_attributes(user_params)
in controller make sure you have permitted the user parameters.

Updating model data from form value ruby on rails

Hello I'm very new to ruby on rails. Here i have form
<%= form_tag do %>
<%= text_field_tag :fullname, params[:fullname], placeholder: 'fullname' %>
.
.
.
<%= submit_tag "save" %>
<% end %>
Those form is to update model data. In my controller I have
def updateUser
user = Users.find(session[:user_id])
user.fullname = params[:fullname]
user.save
render 'profile'
end
It's not working (data doesn't updated), but when I tried
def updateUser
user = Users.find(session[:user_id])
user.fullname = 'david'
user.save
render 'profile'
end
It's working (the data updated). I don't know where did I go wrong, please kindly help me. Sorry for asking such easy question, I'm a newbie to Ruby (and so does Rails), I searched but didn't get a suitable answer for this case. Thank you
I'm very new to ruby on rails
Welcome - let me give you some ideas!
--
Form
Firstly, your form_tag is not created properly. You need to put a route in this (so it knows where to submit the data):
<%= form_tag your_path_here do %>
This is for the form_tag helper, however, as you're editing an object, you'll probably be better using the form_for helper - which takes an actual object (value):
<%= form_for #user do |f| %>
<%= f.text_field :full_name %>
<%= f.submit "Submit" %>
<% end %>
This type of form has to be used with the resourceful structure of Rails, which is my second point...
--
Resources
Rails is built around the idea of a resourceful infrastructure (where every data object is a resource). If you're updating / editing an object, the typical explanation is that you'll be editing a resource, and consequently will want to employ rails' resourceful structure to handle it:
#app/controllers/users_controller.rb
class UsersController < ApplicationController
before_action :set_user, only: [:edit, :update, :show]
def index
#users = User.all
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
#user.save
end
def edit
end
def update
#user.update(user_params)
end
private
def user_params
params.require(:user).permit(:fullname, :etc)
end
def set_user
#user = User.find params[:id]
end
end
This will allow you to define the resourceful routes for this:
#config/routes.rb
resources :users
Using this setup with form_for will work for you
Didn't notice in the beginning. Your form is incorrect. You did not specify a URL for action, and you put your submit tag within a link so basically your link is getting called not your form submitted.
<%= form_tag '/userEdit' do %>
<%= text_field_tag :fullname, params[:fullname], placeholder: 'fullname' %>
.
.
.
<%= submit_tag "save" %>
<% end %>
Make sure that you have specified a route for userEdit post method.

How to get a Rails form that is submitted with invalid data to repopulate the user entered fields

I'm working to build a Rails 3 + devise, user registration page. This will be an additional page that does not replace the existing devise registration page. This page will include user info and billing info.
I'm trying to get the form to submit and if the form fields do not save, have the reloaded page include the user's previously inputted data. Here's a snippet:
<%= form_for(User.new, :url => '/pricing/sign_up') do |f| %>
<%= f.label :email %>
<%= f.email_field :email %>
<% end %>
When the form submits with invalid data. When the view re-renders, the existing email entered is not persisted. How can I make the existing user's input persist to help the user quickly correct mistakes and submit a valid form?
The key is to have the form_for use the right object. So, instead of
<%= form_for(User.new, :url => '/pricing/sign_up') do |f| %>
you should be using an instance variable to contain the object, like this
<%= form_for(#user, :url => '/pricing/sign_up') do |f| %>
The controller actions would look like this:
# Note: this may need to be an `edit` method instead?
def new
#user = User.new
end
# Note: this may need to be an `update` method instead?
def create
#user = User.new(params[:user])
if #user.save
# Do something... Usually a redirect with success message.
else
render :new
end
end
What this create method is doing is it's filling the #user object with params from the form. And then the call to #user.save will, behind the scenes, call #user.valid? and, if no errors are returned, then the record is saved to the database. But this part is key. If #user.valid? does result in errors, then the errors collection on #user will be populated. Then, after the render :new completes, and re-renders your user form, the form will be able to spit out errors messages by accessing the #user.errors collection. Otherwise, the way you had it before, you always had a User.new object in the form which would never have had any errors because it was never used to attempt record validation before.
How to display the errors in your form is a matter of preference and a little beyond the scope of this question. Here's a guide: http://guides.rubyonrails.org/active_record_validations.html#displaying-validation-errors-in-views
I think it's because of your form_for declaration where you're creating new instance of User on every call.
If you move the User.new to your controller and render the new action upon failure in create action then you should see the user entered values in the form fields.
Something like the following should work:
# app/controllers/users_controller.rb
def new
#user = User.new
end
def create
#user = User.new(params[:user])
respond_to do |format|
if #user.save
...
else
format.html { render :new }
end
end
end
Then in your view:
<%= form_for(#user, :url => '/pricing/sign_up') do |f| %>
<%= f.label :email %>
<%= f.email_field :email %>
<% end %>
The values of the form fields are driven by the model you pass to form_for, so in your case they will always be empty because you are passing a brand new user object.
You should be using an instance variable which is set in the controller; in the new action this will be a new User model but in the create action it will be a model which has attributes set via the form:
# app/controllers/users_controller.rb
def new
#user = User.new
end
def create
#user = user.create(user_params)
if #user.save
redirect_to user_path(#user)
else
render :new
end
end
Then in the form:
<%= form_for(#user, :url => '/pricing/sign_up') do |f| %>
<%= f.label :email %>
<%= f.email_field :email %>
<% end %>

Redirect user depending on user type at login

I've got two controllers: admin and customers, plus one more called sessions for handling login and authentication. I'm trying to use one login form so that when an admin logs in, they are redirected to their part of the site, and if a customer logs in, they are taken to their part.
Edit: changed the params to login by email, but now getting the error No route matches {:action=>"show", :controller=>"customers"} when I try to log in as a customer :S!!
Code:
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by_email(params[:email])
customer = Customer.find_by_email(params[:email])
if user and user.authenticate(params[:password])
session[:user_id] = user.id
redirect_to admin_url
elsif customer and customer.authenticate(params[:password])
session[:customer_id] = customer.id
redirect to customer_url
else
redirect_to login_url, alert: "Invalid user/password combination"
end
end
def destroy
session[:user_id] = nil
session[:customer_id] = nil
redirect_to store_url, notice: "Logged out"
end
end
Code for the login page (stored in app/sessions/new.html.erb):
<div class="depot_form">
<% if flash[:alert] %>
<p id="notice"><%= flash[:alert] %></p>
<% end %>
<%= form_tag do %>
<fieldset>
<legend>Please Log In</legend>
<div>
<%= label_tag :email, 'Email:' %>
<%= text_field_tag :email, params[:email] %>
</div>
<div>
<%= label_tag :password, 'Password:' %>
<%= password_field_tag :password, params[:password] %>
</div>
<div>
<%= submit_tag "Login" %>
</div>
</fieldset>
<% end %>
</div>
Also, if relevant, I've got this in the config/routes file:
controller :sessions do
get 'login' => :new
post 'login' => :create
delete 'logout' => :destroy
end
Edit: changed the params to login by email, but now getting the error No route matches {:action=>"show", :controller=>"customers"} when I try to log in as a customer :S!!
The problem is that your params[:email] does not exist at all! Try using the params[:name], and hope that your customer knows that he has to write his email into the name field.
user = User.find_by_name(params[:name])
customer = Customer.find_by_email(params[:name])
If you only have one login form, then the input for the username/email will either end up in the params hash as either
params[:email]
or
params[:name]
But it looks like params[:email] doesn't exist since you're only using one form. Since you can successfully authenticate as an admin, I would guess that the input for the username is titled "name", so in your params hash, when you authenticate as a customer, your authenticating against
params[:name] #this is what I think the input on the form is named
instead of
params[:email] #which is what it looks like your code is looking for.
Can you post the view code?

Resources