So I, a rails newbie, am currently trying to get the User ID from the current Devise session, and I am having a bit of trouble.
I have this in my controller now:
def index
#currentUser = current_user.id
end
And I want to make a simple if statement to show/hide fields in my form.
<% if #currentUser === #product.user_id %>
<%= link_to "Back", products_path %>
<%= link_to "Edit", edit_product_path(#product) %>
<%= link_to "Delete", product_path(#product), method: :delete, data: { confirm: "Are you sure?"} %>
<% else %>
<span></span>
<% end %>
I have a feeling my syntax in defining my currentUser variable is bad, but I have no idea how to fix this. There were a few similar questions on Stack, but none of them really applied to me or helped me.
Thanks in advance!
I see a few problems here
def index
#currentUser = current_user.id
end
Other than what #HolyMoly already commented, you should use underscore and not camelcase, if current_user is nil here, .id will fail on it, resulting in an exception.
Second, you are checking "ability" by comparing values of ids, I would change your code to do this
<% if allow_product_delete?(#product) %>
In a helper
def allow_product_delete?(product)
return false unless current_user
#product.user_id == current_user.id
end
if you are using devise current_user exists in controller and views, you don't need to define it as an instance variable, it's already defined as a controller method on all actions (by default). so by calling current_user in your views you are done.
If a user is not logged in, current_user will be nil, you always have to prepare for this and protect against it.
Last, I would look into ability gems (cancan or cancancan), those provide a very nice DSL for dealing with what you were trying here.
Have you set it up to actually be able to user current_user ?
in your code you have :
#currentUser = current_user.id
But where was current_user defined? I haven't used Devise but with sessions you could define the value of current_user with a helper method (you may be able to also do it right there in the sessions_controller #create method), like this:
def current_user
#current_user ||= User.find_by(id: session[:user_id])
end
then throughout your app you could use current_user.id or current_user.name or whatever you needed.
So while sessions is not Devise, I hope this helps .
First of all, ruby like undescored_rather_than camelCased style.
Secondly, you need two equal signs instead of three. #currentUser == #product.user_id should work
At third, you don't need to compare integer ids, you can compare models, something like that:
<% if current_user == #product.user %>
<%= link_to "Back", products_path %>
<%= link_to "Edit", edit_product_path(#product) %>
<%= link_to "Delete", product_path(#product), method: :delete, data: { confirm: "Are you sure?"} %>
<% end %>
Please note that I omitted the # in current_user method, because current_user is helper for devise, else I removed unnecessary <% else %> (in my opinion)
You should use current_user for this. You can do it everywhere in project. No needs to define variable #currentUser or etc.
You can compare like models
if current_user == #product.user
that is the equivalent to
if current_user.id == #product.user.id
In some cases you can use
if current_user.id == #product.user_id
This prevent extra sql query to load user model
If you are using Devise for authorization and you need control access for actions you should see (maybe use) gem https://github.com/CanCanCommunity/cancancan
https://github.com/airbnb/ruby will help you to stay in tune ;)
Comparing IDs is not the best practice for Rails. If you have previously set up the associations correctly, then you don't need to make a comparison between IDs. For example:
User
has_many :group_events
GroupEvent
belongs_to :user
In this scenario, instead of comparing IDs, you can directly compare with association:
Bad
#group_event.user_id == current_user.id
Good
#group_event.user == current_user
Related
Lets start off with i am new to programming in rails and trying to learn by building a project. I am creating a project that has following and follower capability similar to twitter... I have implemented the option to delete a post. However, it seems that i can delete other people post as well that i am following etc. How can i implement the delete of my own post and have other user have the ability to edit modify and delete their own post.
post.rb
class Post < ActiveRecord::Base
belongs_to :user
validates :user_id, presence: true
validates :content, presence: true, length: { maximum: 140 } #
default_scope -> { order(created_at: :desc) } # newest tweets / posts first
end
post controller
def destroy
#status_update = Post.find(params[:id])
if #status_update.present?
#status_update.destroy
end
redirect_to root_url
end
home
<%= link_to('Delete', post_path(#p), :method => :delete,data: { confirm: "Are you sure?" } ) %>
i was also looking at something like this:
def owned_post
unless current_user == #post.user
flash[:alert] = "That post doesn't belong to you!"
redirect_to root_path
end
end
Lets say you have a Post model and views all set up:
In your views/posts/show you can set up something like this:
<% if #post.user.id == current_user.id %>
<%= link_to "Edit", edit_post_path(#post), class:"btn btn-success btn-sm" %>
<% end %>
You will still have a small issue, users can still access the form to edit, so now in your views/posts/edit it renders a form so put a condition on it:
<% if user_signed_in? %>
<% if #post.user.id == current_user.id %>
<%= render 'form', tutorial: #post %>
<% end %>
<% else %>
<h1>stop trying to edit others post</h1>
<% end %>
Good question, though there isn't one single answer I can give you. The question of "authorization" of actions in your app is a big one, and there are gems like Pundit that you could look into for a full-fledged solution.
However, it's always good to start with the basics on your own and build up to a bigger solution. What you have already isn't wrong -- just add before_action :owned_post, only: [:delete] (perhaps rename to ensure_post_owner or such) to your controller to enforce your rule.
Another option is to scope your ActiveRecord queries to the set of objects your current user is allowed to operate on. So instead of #post = Post.find(params[:id]), you could do, #post = current_user.posts.find(params[:id]). If the user tries to modify a post they don't own, they'll get back a 404 as if that post simply doesn't exist. This can be a good strategy to avoid letting attackers enumerate which posts are and aren't in your database.
On view specify something like this:
<% if user_signed_in? && current_user.id == #post.user_id %>
# something like edit. links... delete links..
<% end %>
or you can also use gem like: cancancan
I rather clumsily replaced my own auth system (based on Michael Hartl's tutorial) with the Devise gem today.
I've got most things working again but have a lot of errors relating to use of current_user.
This, for instance, doesn't work any more:
<% if current_user.admin? && !current_user?(user) %>
| <%= link_to "delete", user, method: :delete,
data: { confirm: "You sure?" } %>
<% end %>
I previously had current_user defined in a sessions helper as follows:
module SessionsHelper
def sign_in(user)
remember_token = User.new_remember_token
cookies.permanent[:remember_token] = remember_token
user.update_attribute(:remember_token, User.encrypt(remember_token))
self.current_user = user
end
end
def current_user=(user)
#current_user = user
end
def current_user
remember_token = User.encrypt(cookies[:remember_token])
#current_user ||= User.find_by(remember_token: remember_token)
end
def current_user?(user)
user == current_user
end
I've done away with that, thinking that devise provided the same functionality but that doesn't seem to be the case. In almost all of the situations where I was previously using current_user I now get undefined methodcurrent_user?'`. I'm hoping there is something global I can do to make the old usages work?
Any pointers much appreciated. I've had by far my worst day of rails in the six months I've been using it.
EDIT: The comments explain that I no longer have current_user? defined. I've tried adding the following to my users_controller but it doesn't seem to have worked:
def current_user?(user)
user == current_user
end
You deleted your current_user?(user) method with sessions_helper. Now Devise do all the necessary for you, but Devise has only current_user method, no current_user?(user).
You can define it by yourself in any helper, methods from all of them works in any view and any controller.
In fact, if you have many conditions, where you need to check if user is admin, and user isn't a current_user. You can make a separate helper for it. But, as I remember from Michael Hartl's tutorial, there is not many such blocks (:
Something like:
def not_admin?(user)
current_user.admin? && !current_user?(user)
end
So, you can refactor you view:
<% if not_admin?(user) %>
| <%= link_to "delete", user, method: :delete,
data: { confirm: "You sure?" } %>
<% end %>
Also you can make it more clear with:
| <%= link_to "delete", user, method: :delete,
data: { confirm: "You sure?" } if not_admin?(user) %>
Currently, I have in my 'show' view for a question model
<% if current_user.id == #question.user_id %>
<%= link_to 'Edit', edit_question_path(#question) %>
<% else %>
<% end %>
To allow the user that created the question to edit it. This works fine when a user is logged in.
However, if a guest is not logged in. I get this error:
NoMethodError in Questions#show
undefined method `id' for nil:NilClass
It doesn't seem to like this line
<% if current_user.id == #question.user_id %>
Can anyone advise a rewrite to get this to work with guest users too?
Thanks
Why not do something like <% if current_user == #question.user %>? Take out the IDs.
Or if you really want the IDs. something like <% if current_user.present? && current_user.id == #question.user_id %>
In a helper
def current_user?(user)
current_user && current_user == user
end
Then in the view
<% if current_user?(#question.user) %>
You can't have an user.id if the user is not in the database. There is a good screencast about this: http://railscasts.com/episodes/393-guest-user-record?view=asciicast
#BillyChan - for that requirement, i would allow upvoting if someone isn't signed in. No need to create an unsaved user record. I'd probably want to use cookies to stop people multiple-upvoting, but to be honest i'd probably argue with the client that we shouldn't let people upvote without being logged in.
Use this
<% if current_user.present? && current_user.id == #question.user_id %>
Basically I have a follow button and when click the page refreshes and I show an unfollow button in place. Below is the code I use to render the particular form needed:
follow_forms partial:
<% unless current_user?(#user) %>
<% if current_user.following?(#user) %>
<%= render 'relationships/partials/unfollow' %>
<% else %>
<%= render 'relationships/partials/follow' %>
<% end %>
<% end %>
Any I changed the form to an ajax form because I don't want the page refresh and on success of the form submission I'd like to replace the follow button/form with an unfollow button/form. This isn't straight forward because only 1 form shows at a time so I can't use my jquery selector to find this form anyway.
What I decided to do was create a new action that renders the follow_form partial this way the appropriate form will be available for me to manipulate with my jquery selector.
The new action:
class RelationshipsController < ApplicationController
def get_follow_form
respond_to do |format|
format.html { render :partial => 'relationships/partials/follow_form_ajax' }
end
end
end
The problem now is that I don't have access to the #user instance variable. That doesn't matter to much because I can get the user who was just followed via the jquery success data then pass that as data in the new ajax call to get_follow_form_url and then pass that info into the partial as a local variable.
I still have an issue with the #user instance variable not being available. Which brings me to my question.
How can I make another value be used if the instance variable isn't nil/doesn't exist?
The form for following:
<%= form_for current_user.relationships.build(:followed_id => #user.id), :remote => true do |f| %>
<%= f.hidden_field :followed_id %>
<%= f.submit "Follow", :class => 'followButton' %>
<% end %>
Can I do something like this
:followed_id => #user.id <-if this doesn't exist use this-> user.id
There are other ways around this like creating new partials that are only used for this whole situation or creating some messy if statements but I feel like creating duplicate forms should be my very very very last option.
I look forward to you solutions thanks
Kind regards
There's a very simple way to do this, assuming you have your 'fallback' ID:
:followed_id => #user.present? ? #user.id : fallback_id
Use something like the andand gem or just try and a logic expression:
:followed_id => #user.andand.id || user.id
Even without that you can use identical logic, and certainly don't need multiple partials:
:followed_id => (#user && #user.id) || user.id
But as Frederick says, if you have a replacement value for the object already, couldn't you just set it?
I'm just starting out with Ruby and Rails, trying out Devise with Rails 3. I've got a loop around a list of Posts, each of which has an associated user. I only want to display editing controls for those posts which are associated with the current user.
<% #posts.each do |post| %>
<%= link_to "show" %>
<% if current_user = post.user %>
<%= link_to "edit" %>
<% end %>
<% end %>
(The above is simplified, and from memory, so I'm sure the syntax isn't entirely right - but you get the gist.)
If no user is logged in, the posts show as intended - there's a Show link, but no Edit link. However, if I am logged in at all, all of the Edit links show up, even fir posts created by a different user.
I've verified in the console that User.find(1) != User.find(2), but for some reason the current_user = post.user evaluates to true no matter who is currently logged in. Is this to do with current_user being a helper as opposed to a "real" user object? How can I use current_user to get at the ACTUAL current user to make my comparison?
Thanks,
Dan
You're assigning rather than testing - use == - i.e.
<% if current_user == post.user %>