I have 3 type of users, 3 roles. With devise, I can get role with current_user.role method.
What is the cleanest way to make 3 different navigations (render some partial or whole layout) based on user's role?
If you want to redirect to different controllers and/or actions based on role, you may be interested in this question and answer: Rails 3 routing based on context.
I do this on this way:
<% if current_user.role? :admin %>
<li class="apanel"><%= link_to_unless_current("Administracioni panel", :controller => 'admin', :action => 'index') %></li>
<% end %>
It is a little stupid way, but works :)
Related
I have a (relatively) large rails app which uses declarative authorization for role based permissions. Admin users currently have the following permissions:
role :administrator do
has_omnipotence
end
I need to add a higher role (root) which can exclusively have permissions on certain actions.
The obvious thing to do is to get rid of has_omnipotence from the administrator role block and manually add all permissions on all controllers but root_accounts, but this is painful. Is there a way that I could do something like the following?
role :root do
has_permission_on [:root_accounts], :to => [:new, :create ... ]
end
role :administrator do
has_omnipotence {except [:root_accounts], :to => [:new, :create ...]}
end
There's no such syntax. But you can check for a specific role in your view/controller:
<% unless has_role?(:administrator) %>
<%= link_to 'New root account', new_root_account_path %>
<% end %>
or
def create
permission_denied if has_role?(:administrator)
...
end
It's not very scalable tho.
I have a user, who wants to be able to take vacation days off from work. My view looks like this:
<h2>Request Days Off</h2>
<%= form_for(#user, :as => :user, :url => vacation_days_path) do |f| %>
<div><%= f.label "How many vacation days would you like to take?" %>
<%= f.number_field :vacation_days %></div>
<div><%= f.submit "Submit" %></div>
<% end %>
In my controller, I have new and create methods. In all examples of the 'create' method I see on the internet, there is a line of code similar to
#person = User.new(user_params) or whatever
My issue is that I don't have a vacation_days model. Only a controller. I want to edit the User database, but creating a new user cannot be the answer (right?).
How do I create a working create method?
This is not really RESTful... However, if you want to update an existing user, you can do so like this
#user = User.find(params[:id])
#user.update_attributes(params[:user])
where params[:id] would hold the id of the user you want to update and params[:user] would hold the attributes you want to update.
Since you are using form_for(#user) with its form builder, it should be fine.
It doesn't seem to me that you need your separate controller for vacation days. Simply have a vacation_days/edit view, which contains your form, and have it submit to users/update.
For clarity, your action should be editing and updating your user, rather than 'creating' one. So, your controller action to update your user should have the line:
#user.update_attributes(params[:user])
Im just looking for some clarification on the following piece of code, well part of it.To give some background i have an app where you can upload recipes, search recipes and save them as favourites, this piece of code is in a controller "recipes", action is "my_recipes"
<%= link_to "Add to favorites", {:controller => 'favourites', :action => 'create', :recipe_id => recipe.id}, {:method => :post } %>
My understanding is that this creates a link_to (anchor tag if you will) that makes a post request through the create method within the favourites controller. This part I think i underdstand (corrections welcome), the part i am unsure of is
:recipe_id => recipe.id}
I know this is passing the recipe_id for example but I would like to know why we do this? and what relevance of the : before the first recipe_id.May seem obvious to some but you dont know until you learn.
Any help appreciated
Is this code in a partial? Is recipe being passed along? You should rewrite as so:
link_to "Add to favorites", new_favourite_path(recipe), method: :post
Do rake routes in your console and find out what the path is for creating favourites, then replace 'new_favourite' with that above. Note, the route might be identified with something more explicit like new_favourite_recipe.
To answer you question, you must pass recipe, or recipe.id because otherwise the controller wouldn't know which recipe to add to the favourites. You don't need to specify the user as that should be accessed directly from within the controller action using something like current_user.
I am in the process of trying to use the update_attribute command, but struggling to get it working (at all) and hoped someone could point me in the right direction?
I have previously posted a question about this issue, it was very useful in terms of giving a feel for the mechanics of what is going on, but unfortunately it didn't actually get it working.
I have a database of items (Items), which among other things contains ':item_name', ':click_count' and ':external_url'.
Currently I have a view (Showselecteditems) in which there is a list of all the items, when a user clicks on an item name, they are directed to the appropriate external url. This works using the code:
<%= link_to selecteditem.item_name.to_s, selecteditem.external_url %>
I would now like to add the ability to count the number of times a particular item name has been clicked on (i.e. in total for all users, not individual users) and therefore the number of times each external url has been visited in order to work out which is most popular.
Reading around, I believe i need to modify the code above to something of the form:
<%= link_to selecteditem.item_name.to_s, selecteditem.external_url, {:controller => params[:controller], :action => clickcountplusone, :identifier => selecteditem.item_name} %>
And need to define this function somewhere - it seems to only be found if located in 'application_helper'?
def clickcountplusone
clickeditem = Items.find(params[:identifier])
clickeditem.update_attribute(:click_count, clickeditem.click_count + 1)
rescue ActiveRecord::RecordNotFound # to avoid error if no identifier value
end
Needless to say, I cannot get this to work... My question is therefore, how can I set things up correctly so that when the link is clicked on the count is incremented? The other common problem people seem to report is that the number will be incremented each time the page is refreshed, which I would like to avod if possible.
Previously people have suggested adding to the 'show' section of the 'Items' controller, however, i don't know how this would work as the links are being clicked on the Showselecteditems view page, not the database itself where you get the show, edit, destroy commands. Any advice greatly appreciated.
This
<%= link_to selecteditem.item_name.to_s, selecteditem.external_url, {:controller => params[:controller], :action => clickcountplusone, :identifier => selecteditem.item_name} %>
will not point user to the some_controller#clickcountplusone, because you already specified an external link.
The easiest way to do this job is to modify your link_to like:
<%= link_to selecteditem.item_name.to_s, {:controller => params[:controller], :action => clickcountplusone, :identifier => selecteditem.item_name} %>
And then to modify your actions source:
def clickcountplusone
clickeditem = Items.find(params[:identifier])
redirect_to clickeditem.external_url if clickeditem.update_attribute(:click_count, clickeditem.click_count + 1)
rescue ActiveRecord::RecordNotFound # to avoid error if no identifier value
end
Hey all! Just joined up on stack overflow, as it has been a helpful resource while starting to learn about Ruby on Rails 3.
I can't seem to find one particular answer though, and maybe I'm barking up the wrong tree for all I know, but hopefully you folks can sort me out here. First some info on what I'm working with:
- In my web app I have 2 models: Projects and Tasks in a one-to-many relationship.
- Projects has many Tasks, and Tasks belong to Project
- Tasks IS NOT a nested resource, as users need to be able to see all current tasks, regardless of which project they are for.
- routes.rb therefore looks like this right now:
resources :projects
resources :tasks
In the project show view I display a list of tasks associated with that project. below that there is a link_to for creating a new task that looks like <%= link_to 'New Task', new_task_path, :class => "new-btn" %>. The link_to takes user to the new view for creating a new task. The rendered _form view starts with: <%= form_for(#task) do |f| %>.
Now, I think I need to pass the project id from the project show view, to the new task view: but, this is where I am getting lost and possibly, a bit mixed up.
Could someone please point me in the right direction: maybe to a resource outlining all steps involved in doing this, or maybe even provide an outline of the steps involved in the process here.
Many thanks!
You may create nested routes and still be able to show all tasks regardless of the project. Just make sure you have task resource defined later, like
resources :projects do |p|
resources :tasks
end
resources :tasks
Now in projects index or show view you can create link like
link_to 'New Task', new_project_task_path(project)
The task index view may be a little bit tricky. All depends on how you sort these tasks. If, for example, you show them sorted by project then you can create a link like
link_to 'New Task', new_project_task_path(task.project)
As for forms. In new action you have to get the id from params and put it into task object
if (params[:project_id])
#task.project_id = params[:project_id]
In form view you may create hidden field that will save this value
f.hidden :project_id
This will make it work, but you will not be able to go to the new task without providing project. Better solution it would be to create a select field with all projects. To accomplish that you should do the following:
in models/project.rb
def getProjectsList
projects = Project.all
projects.map do |p|
[project.name, project_id]
end
end
in controllers/application_controller.rb
def find_projects
#projects = Project.new.getProjectsList
end
in controllers/tasks_controller.rb at the begining
before_filter :find_projects, :only => [:new, :edit, :update, :create]
in views/tasks/_form
f.select :project_id, #projects
This way you can always select project and in case there is one given in params it will be already selected
If current_user returns User object then you should be able to call
current_user.projects
to get all user's projects.
Defining a relation between user and task may be working (though I am not sure this one).
#models/user.rb
has_many :projects
has_many :tasks, :through => :projects
In this case simply
current_user.tasks
should return user's tasks
There are many ways to do it. An easy one may be to add a parameter to you link and use it in the controller:
In your view:
<%= link_to 'New Task', new_task_path(:project_id => #project.id), :class => "new-btn" %>
In your tasks controller:
def new
#task = Task.new(:project_id => params[:project_id])
end
One way to pass the project ID from the project page to the New Task page is to add it to the query string on your url. Example HTML would look like this:
New Task
To get Rails to generate that HTML, you can do this in your ERB:
<%= link_to 'New Task', new_task_path(:project_id=>#project.id), :class => "new-btn" %>
Next, you need to pass the project ID from New Task page to the action that actually creates the Task. One way to do that would be to make a hidden input inside your form that contains the project ID so that it will be passed along with the other parameters when the form is submitted. The HTML would look like:
<input type="hidden" name="project_id" value="<%= params[:project_id] %>" />
To do this the Rails way, you can set the project ID in the new task action in the TasksController:
#task.project_id = params[:project_id]
and then do something like this in your view inside the form_for block (I'm not 100% sure on the syntax):
<%= f.hidden_field(:project_id) %>
Wow! Thanks for all the great info guys! I definitely learned a few neat tricks going through this.
Here is what I have working now:
routes.rb
resources :projects do |p|
resources :tasks
end
resources :tasks
I'm really happy to learn that that part is possible. Now I can enjoy the benefits of nested resource, but use the original non-nested routes for tasks, as well.
the link_to in show project view
<%= link_to 'New Task', new_project_task_path(#project), :class => "new-btn" %>
tasks_controller.rb new action
if (params[:project_id])
#task.project_id = params[:project_id]
end
new task form hidden field
<%= f.hidden_field :project_id %>
That all works great for adding new tasks to projects: but, showing a list of all tasks, related to all projects, that are related to the current user was a little bit trickier, and I wonder if there might be a better way than what I came up with:
tasks_controller.rb in the index action
#projects = Project.find_all_by_user_id(current_user)
#tasks = Array.new
#projects.each do |p|
p.tasks.each do |t|
#tasks << t
end
end
I'm using the "devise" and "cancan" gems for user management(both have been great!). The "current_user" above is simply what you would expect: the currently logged in user. Is this a reasonable solution, or is there a better way of getting all tasks for a user?
Just in case:
User has_many Projects, and Project has_many Tasks