I had wanted to create the equivalent of scaffold for my user model which is handled by devise and since I was not allowed to create the User scaffold I created an Execs controller that will just handle the user models 7 actions.
I have no references to an exec Model but on my show and edit views I keep getting this error Couldn't find Exec with id=2 I thought it might be something rails is doing under the hood with resources :execs so I changed it to:
get "execs/index"
get "execs/new"
get "execs/edit"
post "execs/create"
get "execs/show"
post "execs/update"
delete "execs/destroy"
but even with that I still get the same error. Here is my execs_controller.rb.
class ExecsController < ApplicationController
before_action :set_user, only: [:show, :edit, :destroy]
before_filter :authenticate_user!
load_and_authorize_resource
def index
#users = User.where("client_id = ?", current_user.client_id)
end
def show
end
def new
#user = User.new
end
def edit
end
def create
#user = User.new(user_params)
if #user.save
redirect_to action: 'index'
else
render 'new'
end
end
def update
#user = User.find(params[:id])
if #user.update(user_params)
redirect_to action: 'index'
else
render 'edit'
end
end
def destroy
#user.destroy
redirect_to action: 'index'
end
private
# Use callbacks to share common setup or constraints between actions.
def set_user
#user = User.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def user_params
params.require(:user).permit(:first_name, :last_name, :email, :phone, :position, :client_id, :password, :password_confirmation, :role_id)
end
end
Here are the links from the view I am clicking:
<td><%= link_to 'Show', execs_show_path(id: user.id) %></td>
<td><%= link_to 'Edit', execs_edit_path(id: user.id) %></td>
<td><%= link_to 'Delete', execs_destroy_path(id: user.id) , data: { confirm: 'Are you sure?' } %></td>
The load_and_authorize_resource is trying to load a model of Exec on all instances. It sounds like the ExecsController deals with objects that are User not Exec. If this is the case, you could either a) change load_and_authorize_resource to look up User objects or b) exclude show and edit from the actions that load_and_authorize_resource will run on.
For a, change the load_and_authorize_resource line to:
load_and_authorize_resource :class_name => 'User'
and for b, change the load_and_authorize_resource line to:
load_and_authorize_resource :except => [:show, :edit]
I think you want option "a" above. If you do "a", this will allow you to get rid of the #user = User.find(...) and #user = User.new(...) lines in other controller actions, as the resource will either be found or initialized by load_and_authorize_resource.
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 %>
I've been going round in circles all day with this. I have a large multi-step form using the Wicked gem and Ruby on Rails. It works perfectly but I can't figure out how to to get back into the form to edit individual entries.
Iim trying to make the ability to go into the client show page, click an individual client and then from there go back into the quote to edit and update it. As the Wicked gem only seems to work with show and update actions, if I try to build a standard edit action Wicked expects to be on a step therefore doesn't work.
I read the I would have to factor the edit action into my show/update actions but I'm having difficulties. Any help would be great thanks!
Clients Controller:
class ClientsController < ApplicationController
before_action :authenticate_user!, only: [:index, :show, :edit]
before_action :set_client, only: [:edit, :show, :update]
def index
#clients = Client.order('created_at DESC').paginate(page: params[:page], per_page: 10)
end
def show; end
def new
#client = Client.new
end
def edit; end
def update
if #client.update_attributes(client_params)
redirect_to client_quotes_path
flash[:success] = 'Client successfully updated'
else
render 'edit'
end
render_wizard #client
end
# After client is completed:
def create
#client = Client.new(client_params)
if #client.valid?
#client.save
session[:current_user_id] = #client.id
ClientMailer.new_client(#client).deliver
redirect_to quotes_path
else
flash[:alert] = 'Sorry, there was a problem with your message. Please contact us directly at ...'
render :new
end
end
private
def set_client
#client = Client.find(params[:id])
end
def client_params
params.require(:client).permit(:first_name, :last_name, :title, :email, :email_confirmation,
:phone, :time, :reminder, :ref_number, :day, :note, :logs_reminder)
end
end
Quotes Controller:
class QuotesController < ApplicationController
include Wicked::Wizard
before_action :set_client, only: [:show, :update, :quote_success]
steps :profile, :employment, :general_questions, :indemnity_details, :declarations
def show
#client.build_doctor unless #client.doctor.present?
#client.build_dentist unless #client.dentist.present?
#client.old_insurers.build
#client.practice_addresses.build
render_wizard
end
def update
#client.update(client_params)
render_wizard #client
end
def quote_success; end
private
def set_client
current_user = Client.find_by_id(session[:current_user_id])
#client = current_user
end
# After full quote form is completed:
def finish_wizard_path
if #client.valid?
ClientMailer.new_quote(#client).deliver
ClientMailer.new_quote_user_message(#client).deliver
end
quote_success_path
end
end
def client_params
params.require(:client).permit(:name, :email, :email_confirmation, :phone, :date_required,
:title, :first_name, :last_name, :date_of_birth, :nationality, :reg_body, :reg_date, :reg_type, :reg_number,
:qual_place, :qual_year, :post_grad, :membership ...
Routes:
Rails.application.routes.draw do
devise_for :users
root 'clients#new'
get 'client', to: 'clients#new', as: 'client'
post 'client', to: 'clients#create'
get '/client_quotes', to: 'clients#index', as: 'client_quotes'
get '/client_quotes/:id', to: 'clients#show', as: 'client_quote'
get '/client_quotes/:id/edit', to: 'clients#edit', as: 'edit_client_quote'
patch '/client_quotes/:id', to: 'clients#update'
put '/client_quotes/:id', to: 'clients#update'
resources :quotes, only: [:index, :show, :update, :quote_success]
get 'quote-success' => 'quotes#quote_success'
devise_scope :user do
get '/login' => 'devise/sessions#new'
end
end
My solution to this in the end was rather than have the edit form as a multi step wizard, I've joined the form data together in a separate view page and got a traditional route to it as you mention. Not perfect but does the job!
When you are updating it is like you are "editing" the element, so you need to redirect to the wizard when you want to edit and when you call the update method you really would be editing that entry. So call the wicked path.
I have A comments Controller Where a User can go tip the comment (right now clicking it will flash a sentence, will add functions later). The problem I'm having is that the page is loading but when I click the link nothing is happening. I'm getting this error when I go to the console:
AbstractController::ActionNotFound - The action 'tip' could not be found for CommentsController
But I have the action in our controller here:
def tip
redirect_to :back, :notice => "Thank you for the Tip. The User will Love it"
end
Here's the route for the tip:
get "/:id/tip" => "comments#tip", :as => "tip"
Here's the Link_to also"
= link_to(tip_path(comment), :class => "story-likes-link", :remote => true, :title => "Tip comment" ) do
%i.fa.fa-money.fa-lg
Tip
Thank you so much for the help : )
edit: whole Controller
class CommentsController < ApplicationController
before_action :authenticate_user!
before_action :set_user
before_action :set_resource, :except => [:destroy]
before_action :set_parent, :except => [:destroy]
before_action :set_comment, :only => [:update, :destroy]
respond_to :js
# Create the comments/replies for the books/comics
def create
#comment = current_user.comments.new(comment_params)
if #comment.save
#comment.move_to_child_of(#parent) unless #parent.nil?
end
respond_with #comment, #resource
end
# Update the comments for the user
def update
#comment.update_attributes(comment_params)
respond_with #comment, #resource
end
# Delete the comments for the books/comics
def destroy
#resource = #comment.commentable
#comment.destroy
respond_with #resource
end
private
# Permitted parameters
def comment_params
params.require(:comment).permit(:body, :commentable_id, :commentable_type, :parent_id)
end
# Set the parent resource for the comments and replies
def set_resource
if params[:comment][:commentable_type].eql?("Book")
#resource = Book.find(params[:comment][:commentable_id])
else
#resource = Comic.find(params[:comment][:commentable_id])
end
end
# Set the parent for the comments to make then as the child of the parent
def set_parent
#parent = params[:comment].has_key?(:parent_id) ? Comment.find(params[:comment][:parent_id]) : nil
end
# Set the comment for the source
def set_comment
#comment = Comment.find(params[:id])
end
def tip
redirect_to :back, :notice => "Thank you for the Tip. The User will Love it"
end
def set_user
#user = User.find(params[:user_id])
end
end
The problem is the action #tip method is hidden in private section, so the router sees not the method at all.
Well, then move the method's code above the private keyword:
def tip
redirect_to :back, :notice => "Thank you for the Tip. The User will Love it"
end
private
NOTE: that action method should not be place to private or protected sections, only to public, which is default section for ruby class definition flow.
So I am in the process of setting up a forum and everything is setup/working well except for my replies are not appearing on the thread "show" page. After checking the rails console, I see they are saving but the user_id and discussion_id are no. The user_id is always nil and the discussion_id is always 0. The discussion threads were easier to setup but with having these replies, I obviously seem to be having an issue. Here are my snippets of code:
class PostsController
# ...
before_filter :authenticate_user!
before_filter :set_discussion, only: [:new, :create, :destroy]
def create
#post = #discussion.post.new(create_params) do |post|
post.user = current_user
end
if #post.save
redirect_to #discussion, notice: "It has been posted!"
else
render :new
end
end
def destroy
#post = #discussion.posts.find(params[:id])
#post.destroy
flash.notice = "Deleted"
redirect_to discussion_path(#discussion)
end
private
def create_params
params.require(:post).permit(:reply)
end
def set_discussion
#discussion = Discussion.friendly.find(params[:id])
end
end
class DiscussionsController
def show
#discussion = Discussion.friendly.find(params[:id])
#post = Post.new
render :layout => 'discussion'
end
end
Partial rendered to reply:
<h2>Reply</h2>
<%= form_for [ #discussion, #post ] do |f| %>
<p>
<%= f.label :reply, "Reply" %><br/>
<%= f.text_field :reply %>
</p>
<p>
<%= f.submit 'Submit' %>
</p>
<% end %>
Partial rendered to show replies in on discussion page:
<h3><%= post.user.first_name %></h3>
<%= post.reply %>
Posted: <%= post.created_at.strftime("%b. %d %Y") %></p>
<p><%= link_to "Delete Comment", [post.discussion, post], data: {confirm: "Are you sure you wish to delete?"}, method: :delete, :class => "post_choices" %></p>
Just want to mention that I also have the correct associations between the three models (User, Discussion, Post). If there is more code needed, please let me know. I appreciate it very much for any information that may be helpful =)
Joe
EDIT
class User < ActiveRecord::Base
has_many :articles
has_many :discussions
has_many :posts
# ...
end
class Discussion
belongs_to :user
has_many :posts
extend FriendlyId
friendly_id :subject, use: :slugged
end
class Post
belongs_to :discussion
belongs_to :user
end
I could post the entire user model if needed but its all validations/devise aspects =P The other two I listed all of the contents in the models.
Edit 2
Thanks to Max, the user_id returns correctly in the console but still not the discussions. Going go dig around a bit more with the recent changes to see what else =)
There are a few issue you need to deal with.
First you should ensure that Devise is actually authorizing your controller action.
class PostsController < ApplicationController
before_filter :authenticate_user!
end
Otherwise current_user will return nil if there is no signed in user. And I'm
guessing that you do not want un-authenticated users to be able to create posts.
Also if you have a nested route you most likely want to check that the discussion actually
exists before trying to add posts.
class PostsController
before_filter :authenticate_user!
before_filter :set_discussion, only: [:new, :create, :destroy]
private
# Will raise an ActiveRecord::NotFoundError
# if the Discussion does not exist
def set_discussion
#discussion = Discussion.friendly.find(params[:id])
end
end
When you are creating resources be careful not to query the database needlessly.
This especially applies to CREATE and UPDATE queries which are expensive.
def create
#post = Post.create(post_params) # INSERT INTO 'users'
#post.discussion_id = params[:discussion_id]
#post.user = current_user
#post.save # UPDATE 'users'
flash.notice = "It has been posted!"
redirect_to discussions_path(#post.discussion)
end
Also you are not even checking if the record was created successfully.
So lets put it all together:
class PostsController
before_filter :authenticate_user!
before_filter :set_discussion, only: [:new, :create, :destroy]
def new
#post = #discussion.post.new
end
def create
# new does not insert the record into the database
#post = #discussion.post.new(create_params) do |post|
post.user = current_user
end
if #post.save
redirect_to #discussion, notice: "It has been posted!"
else
render :new # or redirect back
end
end
def destroy
#post = #discussion.posts.find(params[:id])
#post.destroy
flash.notice = "Deleted"
redirect_to discussion_path(#discussion)
end
private
def create_params
# Only permit the params which the user should actually send!
params.require(:post).permit(:reply)
end
# Will raise an ActiveRecord::NotFoundError
# if the Discussion does not exist
def set_discussion
#discussion = Discussion.friendly.find(params[:id])
end
end
it might be a silly question but I am stuck with this for some time as I am new to rails.
I am basically using a custom registration controller to overwrite devise
class RegistrationsController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy]
# GET /Users
def index
#Users = User.all
end
# GET /Users/1
def show
end
# GET /Users/new
def new
#User = User.new
#course = Course.find_by id: params["course_id"]
end
# POST /Users
def create
#User = User.new(user_params)
if #User.save
redirect_to #User.paypal_url(registration_path(#User))
else
render :new
end
end
protect_from_forgery except: [:hook]
def hook
params.permit! # Permit all Paypal input params
status = params[:payment_status]
if status == "Completed"
#User = User.find params[:invoice]
#User.update_attributes notification_params: params, status: status, transaction_id: params[:txn_id], purchased_at: Time.now
end
render nothing: true
end
private
# Use callbacks to share common setup or constraints between actions.
def set_user
#User = User.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def user_params
params.require(:user).permit(:course_id, :name, :email, :password,:password_confirmation)
end
end
In my routes I have
devise_for :users ,:controllers => { :registrations => "registrations" }
So now I have
edit_user_registration_path GET /users/edit(.:format) registrations#edit
My question is how can I route only edit back to devise/registrations/edit or what can i add to my registrations controller so that I get something similar?
If you want to delegate the create action of the registrations controller to Devise, I recommend you to create a controller that inherits from the Devise one:
class RegistrationsController < Devise::RegistrationsController
def create
super #We call super because we don't want to override this action
end
def edit
#Custom code to override this action
end
end
Your route's configuration stays as it is, you just have to change your controller, you may also want to know that it's possible to ADD functionality to what devise already does, instead of override it:
def edit
super do |resource|
#Here you add what you'll do AFTER devise works
end
end