Ruby on Rails / MVC - Splitting Views for different user levels - ruby-on-rails

I have a Rails application which handles hotel room reservations. I would like an admin to see all reservations and specific users to only see their reservations. Is the 'proper' way of doing this:
Create a new action for each user in the reservation controller
OR
Put an if statement in the index action, deciding whether to return all reservations or only those for a user id sent by parameter
I have it set up and working the second way but I'm not sure this is the correct way of doing things.

I would do this by having a single view, which is populated by the appropriate collection of reservations. For an admin this would be all reservations, but for a regular user the collection would only contain their bookings.
This then requires a single if statement in the controller to ascertain the appropriate reservations:
if user.is_admin?
#reservations = Reservation.all
else
#reservations = user.reservations
end
The above code assumes that you've setup an appropriate relationship between a user and their reservations, but you get the idea.

Related

How do associations affect the way we create objects in the controller?

I understand that associations are important because this gives us a way of linking two objects together so they have a 'relation' and we can query through both of them. For example,
A landing page belongs to a blog user & a blog user can have many landing pages
We obviously go into the models and apply the correct methods 'has_many' and 'belongs_to'. We also create a migration and add the foreign key to the 'belongs_to' model. This being the 'landing page'.
My problem:
When creating a landing page, it is possible to choose a blog user. This obviously passes the blog user ID into the params. I want to save this ID into the foreign key field in the landing page model.
Is this possible without doing:
def create
#landing_page = #blog_user.landing_pages.build(landing_pages_params)
end
Why do you have to go through a blog user? Another example:
def new
#landing_page = #blog_user.landing_pages.new
end
What is the purpose of doing it this way? Surely passing the ID into the field is enough without going through the blog_user?
Sure, you could do something like
#landing_page = LandingPages.new(blog_user_id: X)
Obviously, you need to know the ID of the BlogUser record. You'll then be able to access the newly associated BlogUser as you'd expect
#landing_page.blog_user #=> BlogUser(id: X)

Administratively putting users into groups

There are many users and one administrator. Users can create groups and send/receive requests to join those groups. That system works fine. I am now building separate "super groups" that the admin creates. These "super groups" don't rely on requests, they rely on appointment. The admin selects a user, selects a group, and adds the chosen user into the chosen group. How do I accomplish this?
How do I set it up so that the admin can simply pick a user(on their profile page), pick a group(preferably from a drop-down) and then make the association?
For example, as an admin, I would like to go to the user's profile page and on the profile page there is a drop-down(only admin-accessible) and add the user to the group of my choice. How would I accomplish this? Through a type of form? A boolean field? Could I just add an append method (<<) into my join model create action and have a select-tag for a group and then a create-submit button? I just need to be pointed in the general direction and I think that I can manage.
Of note: I am using a has_many :through association to capture the relationship. An admin has many "super groups" which are created/deleted fine. The "super group" has many members(users). I just need to know how to put users into a group administratively.
UPDATE - What is the best practice for adding a user to a has_many :through association via dropdown?
You are talking about wanting to add a user to a super group FROM the user's profile page. This means you want to pass a super_group_id to the Rails controller, find that super group, and then add the user to that group.
This is one option:
#user = User.find(params[:id]) # or user_id, depending on the controller
#super_group = SuperGroup.find(params[:super_group_id])
#super_group.users << #user
Original Answer:
How are you currently creating and deleting the Super Groups? I assume you have a SuperGroupsController. One way to accomplish this is to have a sub-controller SuperGroups::UsersController with your same RESTful actions (create, update, etc). Those restful actions handle the assigning/removing of the users to groups. This allows you to separate the checking that you need to do to make sure only an admin is taking these actions.
class SuperGroups::UsersController < ApplicationController
before_action :verify_admin # create this method to do the checking
def create
# create a user super group association here
end
def delete
# remove a user from the super group here
end
end
This is apparently a best practice according to DHH. I first used this method after reading this article and I've found this pattern very clean and useful.

Different update / edit methods available to different users

I have a model Post, which is submitted and graded by different Users. The submitter and grader are identified by submitter_id and grader_id in Post model. Note that an user is both a submitter himself and a grader to others.
I want to make sure that the submitter can only edit the content of the Post but not the grade. Likewise, the grader can only edit the grade but not the content.
Is multiple edit methods the way to go? How should I accomplish this otherwise?
You can have a role column in your users table, and the role can be either submitter or grader. Not sure what you are using for authentication, but in case you are using devise, you can access the currently logged in user with current_user helper (in case you are using something else, figure this part out, or add a new helper).
Now in your update method, you can do something like this:
# Controller
# scope post to current user, so that a user cannot edit someone else's post. A crude way to achieve this is post = Post.find(params[:id])
post = current_user.posts.find(params[:id])
post.content = params[:content] if post.submitter?(current_user.id)
post.grade = params[:grade] if post.grader?(current_user.id)
post.save!
# Model - Post.rb
def submitter?(user_id)
self.submitter_id == user_id
end
def grader?(user_id)
self.grader_id == user_id
end
The advantage of keeping those methods in the model is that in case you permission logic changes (who is submitter, or a grader), you need to change it at a single location. DRY.
You can modify the above approach to show error messages, and do other similar stuff. In case you are looking for more granular authorization control, you can look into cancan gem:
https://github.com/ryanb/cancan
Your post model should only be concerned with persisting data. Better to use plain old ruby objects to encapsulate the higher order behavior of grading and submitting. Consider using service objects or form objects.
Each service or form object can then include ActiveModel::Model(rails > v4) to get its own validations.
See more about service and form objects here: http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/
If you only have one submit action and one grade action, its probably ok to keep in one controller. But if you start having multiple actions that are related to submitted, and multiple actions that are related to grading, this sounds like they would make great resources controllers on their own.

Create new page in Rails to add roles to existing users

I want to create a new page in my rails app that you can access once you're logged in. All you would see is a dropdown with the existing users and a dropdown with the role you want to assign to that user with a submit button that would add the role to the user_role column for that user. Do I do this with a
rails g controller add_roles new create
or
rails g scaffold add_roles
How do I get it to submit the correct info to the user table?
From my understanding, a rails scaffold is a full set of controller, model, and migration. In your case, I don't think you want a add_roles_controller, and an add_roles model, you just want to update a column of your existing Users DB correct?
If so, ask yourself if you really need a controller to do this, this type of functionality can be done in an existing user_controller (or something of the like). If you are going the CRUD route, you can consider this an update upon a user.
You can make an active record call from any controller, lets say you're in a user_controller and you have a users model, you could do something like:
#users = Users.all
That would return an object of all the user's stored in the db from which can can loop through them, picking out each individuals role attribute.
If you need help on creating a form, you're going to need to elaborate, this will require changing your routes to respond to a POST to a certain controller action. That controller action can then take the parameters of the post, say a user's role, and update the Users database accordingly
if you haven't yet, check out the gem devise - it's a very easy way to login/logout and it includes some pretty awesome session management
Devise
And if you want more functionality, I'd look into rolify. I haven't used it but it seems like a great way to add roles to users. Rolify

Which rails controller should I use?

In my app Users can Like Programs. Each of those is a model, Like is polymorphic.
At some point I will want to see all the Users that Like a Program or all Programs a User Likes.
Is it better to have a likes and likers controller action in the users and programs controller? Or should I have the likes controller as a nested resource with both a users and programs action (or an Index which checks which nested resource is being used)?
I realize all of these can work, but wasn't sure what was Rails best practices.
I would structure your app to have a UsersController with a likes member action which returns the Programs that user likes. And then have a ProgramsController with a likers member action, which gives the Users which like that program.
To simplify things further, you could also just include the user's likes in the show action, (and similarly, show the users who like a program in the program's show action), although you may end up fetching more information than is necessary in the show actions by doing it that way.
Restfully, you would have a LikesController and a 'create' action within it would take a user_id and a program_id. It's likely the user will be logged in (and won't be passed in the URL), and it will make sense to create a Like, passing a program_id to a url that looks something like this:
POST /likes, :params => { :program_id => ___ }
You may want to show a list of Likes (index page), perhaps allowing users to edit and delete. If this is the case, all your actions would be on the likes_controller. Usually, it depends on your situation, but a restful design is usually the right place to start.

Resources