I am trying to implement signup & login features on ROR. on Index page i have created 2 links saying 'new user' and 'login' and in userscontroller i have signup & login methods(updated routes accordingly).
prob: upon clicking new user or login i am getting an error saying
"The action 'show' could not be found for UsersController"
routes.rb:
Rails.application.routes.draw do
resources :users
get 'users/register', :to=>'users#register'
get 'users/signup', :to=>'users#signup'
post 'users/signup', :to=>'users#signup'
post 'users/login', :to=>'users#login'
get 'users/login', :to=>'users#login'
post "users/change_password" => "users#change_password"
get "users/change_password" => "users#change_password"
index.html.erb
<%= link_to "New User", users_register_path %><br>
<%= link_to "Login", users_login_path %><br>
<%= link_to "Change Password", users_change_password_path %><br>
userscontroller:
def index
#user_details = User.all
end
def register
puts "**********************"
puts params
#new_user = User.new
end
def signup
#new_user = User.new(user_register)
if #new_user.save
session[:user] = User.authenticate(#user.name,#user.password)
redirect_to :action=>"welcome"
else
redirect_to :action=>"login"
end
end
def login
puts params
if request.post?
session[:user] = User.authenticate(params[:user][:name], params[:user][:password])
redirect_to :action=>"welcome"
else
# redirect_to :action=>"signup"
end
end
def change_password
puts "**********************"
puts params
puts "**********************"
if request.post?
#pass_change = User.new_password(params[:user][:name], params[:user][:password], params[:user][:new_password])
end
end
def welcome
end
def user_register
params.require(:user).permit(:name,:email,:password,:password_confirmation)
end
end
usermodel.rb:
require 'digest/sha1'
class User < ActiveRecord::Base
attr_accessor :password, :password_confirmation
def password=(pass)
#password = pass
self.salt = User.random_met(10)
self.hashedpassword = User.encrypt(#password, self.salt)
end
def self.encrypt(pass,salt)
Digest::SHA1.hexdigest(pass+salt)
end
def self.authenticate(login, pass)
user_auth = User.find(:first, :conditions=>["login = ?", login])
return nil if user_auth.nil?
return user_auth if User.encrypt(pass,user_auth.salt)==user_auth.hashedpassword
end
def self.new_password(name,old,new)
user = where(:name=>name)
#puts user.email
# how to call setter(password) method from here. because My idea is
#if User.encrypt(old, user.salt)==user.hashedpassword
# run def password=(pass), thereby storing new password.
end
def self.random_met(len)
char = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
salted = ""
1.upto(len) { |i| salted << char[rand(char.size-1)] }
return salted
end
end
As written in the Guides,
Rails routes are matched in the order they are specified, so if you
have a resources :photos above a get 'photos/poll' the show action's
route for the resources line will be matched before the get line. To
fix this, move the get line above the resources line so that it is
matched first.
You have defined resources :users above the get routes, so the rails will look for a show route, so is the error.
Related
I have this newbie error when i want to upvote a "hack" :
ActionController::ParameterMissing at /hacks/6/upvote
param is missing or the value is empty: vote
With Request parameters exemple :
{"_method"=>"post", "authenticity_token"=>"r+fYieTQDsD6fuonr3oe0YEzkzBXH1S8k6bDENS0wCVr3LEpxGA4mps5saM4RQLvBNDVzsm2zXpGm9TKe3ZIYA==",
"controller"=>"hacks", "action"=>"upvote", "id"=>"6"}
I don't understand why my #vote do not appear in parameters...
Controller hacks_controller.rb
class HacksController < ApplicationController
skip_before_action :authenticate_user!, only: [:upvote]
def upvote
#vote = Vote.new(vote_params)
#hack = Hack.find(params[:id])
# raise
#vote.hack = #hack
if #vote.save
redirect_to root_path
else
p 'Problème de #vote.save !'
end
end
private
def vote_params
params.require(:vote).permit(:hack_id, :user_id)
end
end
Model Vote.rb
class Vote < ApplicationRecord
belongs_to :user
belongs_to :hack
validates :hack, presence: true
end
Thanks !
The Rails strong parameters are meant as mass assignment protection and are not suited to this case.
To create an additional CRUD method properly you can just add the additional route to resources:
resources :hacks do
post :upvote
delete :downvote
end
Note that we are using POST not GET as this is a non-idempotent operation.
You also don't need to pass any parameters. :hacks_id will be present in the path and you should fetch the current user id from the session and not the request parameters.
Passing a user id via the parameters is a really bad practice as its very trivial to spoof by using just the web inspector.
class HacksController < ApplicationController
before_action :set_hack!, except: [:new, :index, :create]
# POST /hacks/:hack_id/upvote
def upvote
#vote = #hack.votes.new(user: current_user)
if #vote.save
redirect_to #hack, success: 'Vote created'
else
redirect_to #hack, error: 'Vote could not be created'
end
end
# DELETE /hacks/:hack_id/downvote
def downvote
#vote = #hack.votes.where(user: current_user).first!
#vote.destroy
redirect_to #vote, success: 'Vote deleted'
end
private
# this will raise ActiveRecord::RecordNotFound if
# the id or hack_id param is not valid. This triggers a 404 response
def set_hack!
if params[:id].present?
Hack.find(params[:id])
else
Hack.find(params[:hack_id])
end
end
end
Then in your view you can create the links / buttons like so:
<% if current_user && #hack.votes.where(user: current_user) %>
<%= button_to 'Downvote', hack_downvote_path(#hack), method: :delete %>
<% else %>
<%= button_to 'Upvote', hack_upvote_path(#hack), method: :post %>
<% end %>
While implementing what I thought was a simple signup/login system for a Ruby on Rails app, results haven't matched what tutorials have shown.
I'm trying to use bcrypt for authentication and PostgreSQL for the database.
I continually get 'ActionController::ParameterMissing (param is missing or the value is empty: name): ', even though it will show name as being input. '"users"=>{"name"=>"asdf", "password"=>"Qq!1asdfasdf", "password_confirmation"=>"Qq!1asdfasdf"}, "commit"=>"Submit"} (0.1ms)
output from the console when attempting to sign in
users controller
class UsersController < ApplicationController
def new
end
def create
user = User.new(
name: params[:name],
password: params[:password],
password_confirmation: params[:password_confirmation])
if user.save
session[:user_id] = user.id
redirect_to '/'
else
redirect_to '/signup'
end
end
private
end
The table
class UsersController < ApplicationController
def new
end
def create
user = User.new(
name: params[:name],
password: params[:password],
password_confirmation: params[:password_confirmation])
if user.save
session[:user_id] = user.id
redirect_to '/'
else
redirect_to '/signup'
end
end
private
end
and the signup form
class UsersController < ApplicationController
def new
end
def create
user = User.new(
name: params[:name],
password: params[:password],
password_confirmation: params[:password_confirmation])
if user.save
session[:user_id] = user.id
redirect_to '/'
else
redirect_to '/signup'
end
end
private
end
the user model
class User < ActiveRecord::Base
PASSWORD_FORMAT = /\A
(?=.{10,}) # Must contain 10 or more characters
(?=.*\d) # Must contain a digit
(?=.*[a-z]) # Must contain a lower case character
(?=.*[A-Z]) # Must contain an upper case character
(?=.*[[:^alnum:]]) # Must contain a symbol
/x
#formatting for password
USERNAME_FORMAT = /\A[a-z0-9A-Z\-_]{2,15}\z/ #Can contain lowercase and upercase letters, numbers, - and _, must be between 2 and 15 length
#username formatting
validates :name,
:presence => true,
:uniqueness => true,
:format => USERNAME_FORMAT
validates :password,
:presence => true,
:format => PASSWORD_FORMAT,
:confirmation => true,
:on => create
has_secure_password
end
I've tried troubleshooting, all similar questions haven't yielded an answer or fix.
EDIT: More clarity on issue
You need to use rails Strong Parameter like the following
class UsersController < ApplicationController
def new
end
def create
user = User.new(user_params)
if user.save
session[:user_id] = user.id
redirect_to root_path
else
redirect_to new_user_path
end
end
private
def user_params
params.require(:user).permit(:name, :password, :password_confirmation)
end
end
I think it's a problem of passing data between your form and your controller.
In your logs your parameters for user looks like: "users"=>{"name"=> ...} but it should be "user"
To pass data between your controller and your view, you need to use instance variable such as #user to make the new instance of User available in the view. (source)
In that way your controller should be:
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
session[:user_id] = #user.id
redirect_to '/'
else
redirect_to '/signup'
end
end
private
def user_params
params.require(:user).permit(:name, :password, :password_confirmation)
end
(with strong parameters like #fool-dev explained)
Then in your view, use this #user to pass the parameters to the controller:
<%= form_for #user do |f| %>
//...the form
<% end %>
I have 2 user-like models in my app: 'Participant' and 'Member'.
I'm trying to allow them to include a custom message when they invite other members/participants through Devise Invitable. However, I can't make it work.
I'm following this official tutorial so I've made the following changes to override Devise Invitable Controller but when using pry it seems that this custom controller goes untouched when sending an invite. What am I doing wrong:
controllers/participants/invitations_controller.rb
class Participants::InvitationsController < Devise::InvitationsController
before_action :update_sanitized_params, only: :update
def create
binding.pry
#from = params[:from]
#subject = params[:invite_subject]
#content = params[:invite_content]
#participant = Participant.invite!(params[:user], current_member) do |u| #XXX Check if :user should be changed
u.skip_invitation = true
end
ParticipantInvitationNotificationMailer.invite_message(#participant, #from, #subject, #content).deliver if #participant.errors.empty?
#participant.invitation_sent_at = Time.now.utc # mark invitation as delivered
if #participant.errors.empty?
flash[:notice] = "successfully sent invite to #{#participant.email}"
respond_with #participant, :location => root_path
else
render :new
end
end
def update
respond_to do |format|
format.js do
invitation_token = Devise.token_generator.digest(resource_class, :invitation_token, update_resource_params[:invitation_token])
self.resource = resource_class.where(invitation_token: invitation_token).first
resource.skip_password = true
resource.update_attributes update_resource_params.except(:invitation_token)
end
format.html do
super
end
end
end
protected
def update_sanitized_params
devise_parameter_sanitizer.permit(:accept_invitation, keys: [:password, :password_confirmation, :invitation_token, :username])
end
end
config/routes.rb
Rails.application.routes.draw do
devise_for :members, controllers: { invitations: "members/invitations" }
devise_for :participants, controllers: { invitations: "participants/invitations" }
end
models/participant.rb
class Participant < ApplicationRecord
attr_reader :raw_invitation_token
end
mailers/notification_mailer.rb
class NotificationMailer < ApplicationMailer
def invite_message(user, from, subject, content)
#user = user
#token = user.raw_invitation_token
invitation_link = accept_user_invitation_url(:invitation_token => #token)
mail(:from => from, :bcc => from, :to => #user.email, :subject => subject) do |format|
content = content.gsub '{{first_name}}', user.first_name
content = content.gsub '{{last_name}}', user.last_name
content = content.gsub '{{full_name}}', user.full_name
content = content.gsub('{{invitation_link}}', invitation_link)
format.text do
render :text => content
end
end
end
end
If I send an invitation:with Participant.invite!({:email => 'example#email.com'}, Member.first) the invitation is sent through the default mailer as shown in the console but not through my new mailer. why?
Rendering /Users/andres/.rvm/gems/ruby-2.4.0#pixiebob/gems/devise_invitable-1.7.1/app/views/devise/mailer/invitation_instructions.html.erb
Rendered /Users/andres/.rvm/gems/ruby-2.4.0#pixiebob/gems/devise_invitable-1.7.1/app/views/devise/mailer/invitation_instructions.html.erb (0.6ms)
Rendering /Users/andres/.rvm/gems/ruby-2.4.0#pixiebob/gems/devise_invitable-1.7.1/app/views/devise/mailer/invitation_instructions.text.erb
Rendered /Users/andres/.rvm/gems/ruby-2.4.0#pixiebob/gems/devise_invitable-1.7.1/app/views/devise/mailer/invitation_instructions.text.erb (0.8ms)
Finally, I could solve this issue.
It ended up being a rookie mistake I was thinking that calling the invite! method would have anything to do with the custom create method in the custom invitations controller.
I had of course to reach the create method through the specified route and within that method prevent the invite! method to send the email through the default mailer using code below (as established clearly in the Devise Invitable Documentation):
#participant = Participant.invite!({:email => #invitation_draft.email}, current_member) do |u|
u.skip_invitation = true
end
After this we can call any custom mailer in the create method.
I'm trying to insert the input from a select_tag to my controller method.
I've looked and cannot seem to resolve the issue.
This is the code I have below, nothing for the rank's selection comes up in the params at all.
<h1>hello please set <%= #user.username %>'s rank'</h1>
<%= select_tag 'rank', options_for_select(#ranks.collect{ |r| [r.rank_name] }) %>
<%= button_to "Update", :action => "set_user_rank_update", value: "#{#user.id}", method: :post %>
Update below with the controller and routes
Controller:
class Admin::RankController < ApplicationController
before_action :admin?
def new
#rank = Rank.new
end
def create
#rank = Rank.new(rank_params)
if params["rank"]["admin"].to_i == 1
#rank.toggle! :admin?
end
if #rank.save
flash[:success] = "Rank created"
redirect_to root_path
else
flash[:danger] = "Failed to create rank"
render 'new'
end
end
def set_user_rank_new
#user = User.find_by_id(params["format"])
#ranks = Rank.all
end
def set_user_rank_update
#user = User.find_by_id(params["value"])
#rank = Rank.find_by_id(params["rank"])
#rank_backup = #user.rank.first
debugger
#user.rank - #user.rank.first
#user.rank << #rank
if #user.rank.first == #rank
flash[:success] = "Set user's rank"
redirect_to root_path
else
flash[:danger] = "Failed to set user's rank"
#user.rank - #user.rank.first
#user.rank << #rank_backup
render 'set_user_rank_new'
end
end
private
def rank_params
params.require(:rank).permit(:rank_name, :rank_color)
end
end
Routes
Rails.application.routes.draw do
devise_for :users,
:controllers => { :registrations => "member/registrations" , :sessions => "member/sessions"}
scope module: 'public' do
root 'welcome#index'
end
scope module: 'member' do
get 'members/:id' => 'member#show'
end
scope module: 'admin' do
get 'rank/new' => 'rank#new'
post 'rank/create' => 'rank#create'
get 'rank/set_user_rank/new' => 'rank#set_user_rank_new'
post 'rank/set_user_rank/update' => 'rank#set_user_rank_update'
end
end
Try passing 2 element arrays to options_for_select. The way you have it looks like it would get you option text but no values, which could explain why it doesn't show up in the params.
So for example:
<%= select_tag 'rank', options_for_select(#ranks.collect{ |r|[r.rank_name, r.id] }) %>
The button_to helper creates an inline form with just the parameters included in the helper statement (in this case the user id).
You can check this by examining the resulting HTML on your page - which I think will show an input field for rank sitting outside the form tag.
To include the rank parameter, you should set up the form using a form helper and make sure the rank input is included inside the form.
For what purpose do you need it in your controller action?
With the link_to you can forwards params as well.
It would be very helpful to see your controller and also your routes.
I have a small game in which after the game is over the user is linked to a view that displays a leader board of all users and their scores. To do this I have treated the link as an update action so that the users score can be updated after the game is over, however, upon clicking the link I get an error saying "param is missing or the value is empty: user". I am also wondering if this is being caused because there is no form to be filled simply a variable being updated.
Controllers:
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
session[:user_id] = #user.id
redirect_to '/play'
else
render '/'
end
end
def update
#user = User.find(current_user)
if #user.update(user_params)
redirect_to '/leaderboard'
else
render '/play'
end
end
private
def user_params
params.require(:user).permit(:nick_name, :score)
end
end
class ScoresController < ApplicationController
before_action :require_user, only: [:index]
def index
#user = User.find(current_user)
#score = #user.score
#score = 0
end
def leaderboard
#users = User.all
end
end
View-link:
<div class="game-over"><%= link_to 'Game Over', "/update", :style => 'text-decoration:none; color:white;' %></div>
Routes:
Rails.application.routes.draw do
root 'users#new', as: :users
post '/' => 'users#create'
get '/logout' => 'sessions#destroy'
get '/play' => 'scores#index', as: :user
get '/update' => 'users#update'
get '/leaderboard' => 'scores#leaderboard'
user has to be present in the request params because you required it in user_params. You can change link_to to use query parameters as follows:
link_to "Refresh", {controller: 'users', action: 'update', nick_name: "#{user.nick_name}", score: "#{get_score}"}, style: '...'
Or change update route to contain those parameters in the url.
# routes.rb
get '/update/:nick_name/:score' => 'users#update'
Tip: You probably should change it to PUT and use form instead, since update action alters state in the server.
Looks like whatever you are using to pass through your params to make the request is not properly nested under a user key.