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
Related
I am trying to get a customized dropdown menu on my app so that each of my list instances have their own link on the navigation. So far here's my application.html.erb:
<li class="dropdown">
My Lists<b class="caret"></b>
<ul class="dropdown-menu">
<li><%= link_to "All Lists", lists_path %></li>
<% #lists.each do |list|%>
<li><%= link_to list_path(list) do %>
<%= list.name %>
<% end %></li>
<% end %>
</ul>
</li>
However, I now get an error that says undefined method 'each' for nil:NilClass on the line with #lists.each do |list|. I know this (or a similar) structure works on another page, so I'm thinking it must have something to do with the controllers. My lists_controller performs this action correctly on my lists#index page with these definitions:
class ListsController < ApplicationController
def index
#lists = List.all
#list = List.new
end
...
And I have this in my application_controller currently to mirror that:
def application
#lists = List.all
#list = List.new
end
But I'm pretty sure that's not right. What should I have in my application_controller to get it to recognize this in my menu on all the pages?
First, be wary of performing the List query each time the a page is rendered on your app and either cache the query or use manual links if at all possible to avoid any performance problems.
That being said...
You have a few options one of which being a before_action in your application_controller that sets any instance_variables you would like present.
ApplicationController < ActionController::Base
before_action :set_lists
def set_lists
#lists = List.all
end
end
You could also add a dropdown_lists method to your ApplicationHelper file similar to:
def dropdown_lists
List.all
end
which you would use as a replacement to #lists in your view similar to: dropdown_lists.each.....
Also (as an aside) you probably want to move this header information to a partial to keep your layout file clean, but thats not really part of the answer :)
Hope this helps!
I think I know what is wrong here. I believe that you are using application.html.erb incorrectly.
application.html.erb is for generally for placing html that will be displayed on all of your web pages. Think of a header menu as an example.
For your list, it looks to me like you want to display your links on your index action which would be `app/views/list/index.html.erb'.
So I would do this:
Remove the def application method from application_controller.
Create an index.html.erb file for app/views/list` directory.
Now transfer your code in application.html.erb to app/views/list/index.html.erb
That should do it.
I'm building a rails app that has a lot of things that change based on whether or not the user has completed certain quizzes. Everything works fine when a user is logged in, but I just tried logging out and I got a NoMethodErroron this line of code:
<% if current_user.per_quiz.nil? %>
Basically, if the user has completed per_quiz they go to one page (the edit view), if they haven't they go to a different page (the new view). The only thing that changed was the fact that I logged out, so I can only assume that is what caused the error. Do I need to add some kind of if statement to account for a state in which no user is logged in? How should I fix this error in accordance with Ruby best practices?
You just need to check to see if current_user is set before checking per_quiz. You can handle checking for the login state easily in a view by querying current_user.nil?:
<% if !current_user %>
<p> You must be logged in to do anything useful. Please go login</p>
<% elsif !current_user.per_quiz %>
<p>Cool quiz stuff goes here...</p>
<% else %>
What you probably really want is to have a logged out user go elsewhere, such as the home page or signin page. To do that, you need to do a couple of simple things to your controller. I'm going to assume that the controller is called 'QuizzesController' since you hadn't included your controller code in the question.
Here's how to do it:
class QuizzesController < ApplicationController
# Other devise authentication logic goes here...
before_action :authorize_user!
# Your actions go here...
private
def authorize_user!
if !current_user
redirect_to '/', notice: "You must be logged in to access to this page"
end
end
end
What this does is install a "before_action" handler that will check that the user is logged in before letting them do anything in this controller. If they are not logged in, you can redirect them wherever you wish.
Note that sometimes, only certain actions need this kind of treatment. In that case, you can use an :only or :except option to specify which action(s) are or are not handled. It looks like this:
before_action :authorize_user!, only: [ :new, :edit, :create ]
or like this:
before_action :authorize_user!, except: :list
This will give you greater flexibility in managing the authorization part of the equation, where devise handles the authentication part.
That's because current_user is nil when no user logged in. And if you will cal some method on nil it will throw NoMethodError.
I assume that have code you provided in the view. So, you can check if user is logged in.
<% if user_signed_in? %>
# do smth with user quizzes
<% else %>
# do something else
<% end %>
But the best way is to use before filter in the controller and not allow to anonymous to see that page.
Here is the example application with Rails and devise. https://github.com/RailsApps/rails-devise
Hope that helps.
I'm not sure if this is the "correct" Ruby way to do this, but the way I eventually found was to change it to an if/elsif/else statement:
<% if current_user.nil? %>
...
<% elsif current_user.bal_quiz.nil? %>
...
<% else %>
...
<% end %>
Probably should have figured this out before I posted the question, but I was (and still am) interested to see if there's a better or "more Ruby" way of doing this.
I'm working on an application with a namespacing (admin section). Is there a DRY solution for not creating 2 controllers? Because I will need to create an public user controller and a admin user controller to manage the users.
How about inheriting the user controller? I use it myself (for images) and it suits me nicely:
# file: apps/controllers/images_controller.rb
class ImagesController < ApplicationController
# image code (to show the image for example)
end
# file: apps/controllers/admin/images_controller.rb
class Admin::ImagesCOntroller < ImagesController
# additional admin code (to delete the image for example)
end
You might consider rendering the page with optional "edit" buttons. For example, something like this:
Name: <%= #user.name %>
<% if #user.admin? %>
<% form_for #user do |f| %>
Editing stuff
<% end %>
<% end %>
That way, a user only sees it as a page, but an admin sees additional controls that allows them to edit the field. P.S. Make sure in your controller that you are checking to make sure it's an admin that is calling the update call.
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.
I have a Project Index View that shows all the projects in an app
I want that view to show if the user signed in is a member or not....
In the Project Index View I have:
<% if teammember? %>
<td>Request to Join</td>
<% else %>
<td>Already Joined</td>
<% end %>
Then in the project's controller I have
def teammember(projectid)
do some stuff.....
end
But this gives me a "undefined method `teammember?"
You don't include the teammember method in the controller, you put that in the helper file (app/helpers/project_helper.rb)
module ProjectHelper
def team_member?(project_id)
# include other logic here
true
end
end
Then in any view that your Project controller renders, you can do:
<% if team_member?(project.id) %>
This is a team member.
<% else %>
This isn't a team member.
<% end %>
If this is a controller method that you need to access in the view, you can make it available like this:
class ProjectsController < ActionController::Base
helper_method :team_member?
end
This is essentially the same as if you had defined the method in helpers/projects_helper.rb
Just make sure you call the methods the same: your example shows one with a question mark, and one without.