I am using Devise for authentication in my rails app and I have a _header partial in my layout folder for navbar. I wanna put there a link for Create Profile (user model is created w/ devise, user has_one profile and profile belongs_to user) but only if the user profile doesn't exist yet.
I would like to create a method for this and put the if statement into the view but I can't figure it out where to create the method and how it would look like.
The basic devise method works fine when it comes to checking if user is signed in. I want a similar method that can check if user profile exists.
layout/_header.html.erb
<% if user_signed_in? %>
<% if user.profile(current_user) %>
<li><%= link_to "Create Profile", new_user_profile_path(current_user) %></li>
So my questions:
Where to put the method (helper/controller/model/appcontroller/etc.)?
How the method would look like?
You can define it in your Helper files (app/helpers/). You can use the application_helper but for a better consistency we will name this file users_helper:
# app/helpers/users_helper.rb
module UsersHelper
def user_has_profile?(user = current_user)
return false unless user.present?
Profile.where(user_id: user.try(:id) || user).exists?
end
end
and use it like this:
# any view
<% if user_signed_in? && !user_has_profile? %>
I would put this in the helpers directory(app/helpers/application_helper.rb) as a method called has_profile?
the method would look like
def has_profile?
current_user.profile.present?
end
then in your view:
<% if user_signed_in? && has_profile? %>
<li><%= link_to "Create Profile", new_user_profile_path(current_user) %></li>
Related
beginner in Rails and I am making a rails news page with devise authentication and when someone visits localhost:3000/news, it shows a list of news. I am trying to show "edit" link only if it was posted by current user or if the current user's username is "admin". Here is my code for it
<% #news.each do |element| %>
<p>Title: <%= element.title%></p>
<p><%= element.body%></p>
<li>Posted by: <%= element.user.username %></li>
<li><%= link_to "Comments", newsone_path(element) %></li>
<% if current_user.username == "admin" || current_user == element.user %>
<li><%= link_to "Edit this drink", edit_news_path(element) %></li>
<% end %>
<% end %>
It works and it shows the edit link only if username "admin" is signed in or if the user who posted it is signed in. But if I try to go to localhost:3000/news without signing in, it shows an error
"undefined method `username' for nil:NilClass" on line "<% if
current_user.username == "admin" || current_user == element.user %>"
I think this maybe because when I am signed out, there is no username maybe? How do I get around this please help?
Devise here is setting current_user to nil, which doesn't have a method .username.
To get around that, you can check for the existence of current_user before attempting to call that method.
An easy-to-read way is to do this instead:
<% if current_user && (current_user.username == "admin" || current_user == element.user) %>
In this line you're checking "does current_user exist"? Then, if so, are they an admin? Because of the way that Ruby evaluates boolean logic statements like this, the second part of that line after the && does not get evaluated, and so it doesn't raise an error.
An alternative would be to use the try method, which is a little more complex to understand, but it attempts to run the named method ('try') by referring to it as a symbol (:try):
<% if current_user.try(:username) == "admin" || current_user == element.user %>
As you develop this, you might want to look at the Rolify gem or similar, so that you can have various different named roles, and then one of the permissions gems, such as Pundit, which allows you to define what different types of users can and can't do.
I have a page with a configuration part where some users can edit element and variable of the site.
I want to cancan controle the access to the page but as it's not a model, and I'm not sure how to do.
My controller
class ConfigurationController < ApplicationController
def index
end
end
My menu
<% if can?(:read, Configuration) # not sure of that %>
<li><%= link_to t('texts.configuration.title'), configuration_index_path %></li>
<% end %>
I have a error with this code :
uninitialized constant ActionView::CompiledTemplates::Configuration
I'm actualy not sure of what mean this error.
What will be the correct way to do that?
Thanks.
This is possible follow this post on Github it describe your problem and how to solve it:
https://github.com/ryanb/cancan/issues/22
and also this:
CanCan and controllers without models
In the /views/layouts directory, how do I get the current user? I am using Devise, but current_user does not work here for some reason, it describes it as an unknown method. I want to do something like:
<% if User.role? == "gen_admin" %>
<li>
<%= link_to('Admin', users ) %>
</li>
<% end %>
I do have a role? method defined in my User model, but I still get this exception:
undefined method 'role?' for #<Class:0x3fcc1e0>
So how can I get the current user, and access its fields at this level of the source tree? Thanks!
Here is the roles? method:
# in User
ROLES = %w[gen_admin teacher_admin student]
def role?(base_role)
ROLES.index(base_role.to_s) <= ROLES.index(role)
end
Your code must account for when users are logged in, and when they are not logged in.
If no user is logged in, then current_user will return nil (as in your case, which you thought was an error on Devise's part).
Your view code must handle this - eg.
<% if current_user.present? && current_user.role?('gen_admin') %>
You have defined your role? method as an instance method. This means that in order to use it you should first create the instance from User class e.g.:
#user=User.new
Now you can call #user.role?
If you want role? to be available through the model User then you should define it as class method an pass in an object for verification
def self.role?(user)
...
end
You'll need to change the code in your view to use current_user rather than User, as well as call the role? method correctly (no ==). If this is used in a view that can be accessed by a non-logged in user, you'll want to confirm that the user is logged in first (so you don't try and call role? on nil, which would raise an exception):
<% if current_user && current_user.role?("gen_admin") %>
<li>
<%= link_to('Admin', users ) %>
</li>
<% end %>
Do you forget to add before_filter :authenticate_user! in your controller? This will ensure that current_user is available in your views
Is there a simple and straightforward way to provide a link in a view to either create a resource if it doesn't exist or edit the existing on if it does?
IE:
User has_one :profile
Currently I would be doing something like...
-if current_user.profile?
= link_to 'Edit Profile', edit_profile_path(current_user.profile)
-else
= link_to 'Create Profile', new_profile_path
This is ok if it's the only way, but I've been trying to see if there's a "Rails Way" to do something like:
= link_to 'Manage Profile', new_or_edit_path(current_user.profile)
Is there any nice clean way to do something like that? Something like the view equivalent of Model.find_or_create_by_attribute(....)
Write a helper to encapsulate the more complex part of the logic, then your views can be clean.
# profile_helper.rb
module ProfileHelper
def new_or_edit_profile_path(profile)
profile ? edit_profile_path(profile) : new_profile_path(profile)
end
end
Now in your views:
link_to 'Manage Profile', new_or_edit_profile_path(current_user.profile)
I came across this same problem, but had a lot of models I wanted to do it for. It seemed tedious to have to write a new helper for each one so I came up with this:
def new_or_edit_path(model_type)
if #parent.send(model_type)
send("edit_#{model_type.to_s}_path", #parent.send(model_type))
else
send("new_#{model_type.to_s}_path", :parent_id => #parent.id)
end
end
Then you can just call new_or_edit_path :child for any child of the parent model.
Another way!
<%=
link_to_if(current_user.profile?, "Edit Profile",edit_profile_path(current_user.profile)) do
link_to('Create Profile', new_profile_path)
end
%>
If you want a generic way:
def new_or_edit_path(model)
model.new_record? ? send("new_#{model.model_name.singular}_path", model) : send("edit_#{model.model_name.singular}_path", model)
end
Where model is your instance variable on your view. Example:
# new.html.erb from users
<%= link_to new_or_edit_path(#user) do %>Clear Form<% end %>
Try this:
module ProfilesHelper
def new_or_edit_profile_path(profile)
profile ? edit_profile_path(profile) : new_profile_path(profile)
end
end
and with your link like:
<%= link_to 'Manage Profile', new_or_edit_profile_path(#user.profile) %>
I have the following evaluation which works great:
In my listings/detail.html.erb view
<% if user_signed_in? && current_user.id == #listing.user_id %>
I use this several times in my views so I wanted to make this a helper method.
What code can I place in my listings_helper file so I can call something like this in my view instead:
<% if isListingOwner? %>
Any help would be appreciated. Thanks.
When I need to do something like that, I use Cancan. Then you can write stuff like:
<% if can?(:update, #listing) %>
Listing
<% end %>
In my view much cleaner.
Put it to /app/helpers folder.
Railscast has intro tutorial on this topic
http://railscasts.com/episodes/64-custom-helper-modules
All good suggestions, but the code you need to add to your listings_helper.rb is
def isListingOwner?
user_signed_in? && current_user.id == #listing.user_id
end
Personally, I'd rather put that check in the model:
class Listing
def owned_by?(user)
user.id == self.user_id
end
end
Then in your view, you would write:
<% if #listing.owned_by(current_user) %>
You might want to look into a role based authorization plugin if you're doing a lot of this type of thing.
If you have a belongs_to :user in your Listing model, why don't you simply do
if current_user == #listing.user
I'm assuming you're using Devise or something that returns nil from current_user when the user is not signed in.