Rails Many to Many Relationship with occasional Nested Routes - ruby-on-rails

As I am learning more about rails, and breaking my design thinking from ASP.Net days, I was considering a scenario this morning but did not know if it was possible to do.
Practitioners have many Treatments through services - and vice versa
In my control Panel I have an area for Practitioners to edit their details (names, contact info etc) and can also choose their treatments via check-boxes.
I would like to remove the check-boxes from the Practitioners _form. Having their own form which I could call on like this:
<%= link_to "Edit Treatments", edit_practitioner_treatments(#practitioner) %>
However, from an admin point of view I would still need to be able to manage treatments without a practitioner object in sight:
<%= link_to "Edit Treatments", edit_treatments(#treatment) %>
which also has authentication barriers.
Is there an easier solution to extract treatments I have overlooked?
Is it possible to have nested routes just some of the time?
Did I have too much coffe this morning and are therefore currently in a state of insanity?

Generally when considering admin functions, which often present an entirely different interface to the user with permission checking based more on "will this break something" than "should you be allowed", it is advantageous to create an admin area with separate controllers. For example:
map.namespace :admin do |admin|
# Admin::PracticionersController
map.resources :practicioners
# Admin::TreatmentsController
map.resources :treatments
end
map.resources :practicioners do |practicioner|
practicioner.resources :treatments
end
map.resources :treatments do |treatment|
treatment.resources :practicioners
end
All Admin::* controllers can inherit from something such as Admin::BaseController that performs sufficiently rigorous authentication checking before allowing any actions to be performed.
In most applications I've seen, the user-facing front-end has an element of design or style to it, where they layout is often hemmed in by navigational elements, advertising, or other editorial content. Creating a separate admin view that's uncluttered, shows far more information per page, and allows sorting or searching on dimensions not exposed to the user is very valuable when managing even medium-sized sets of data.
Although it might seem like a lot of work to make these extra admin controllers and their associated forms, if you are careful in your design, you can recycle a lot of the functionality between the two areas, especially page partials.

Related

How to handle cases in which the generated content depends on the current user authorization?

I am using Ruby on Rails 3.2.2 and, since my system implementation, I would like to generate different outputs (in views) and / or to retrieve different records (in controllers) depending on the "access-er" user authorization (for instance, the authorization could depend on if the "access-ed" user is or not is the current "access-er" user).
How can I handle the situation? That is, for example in order to handle if the user is or not the current user and so to display different content, should I implement two view files and / or controller actions for each case, or should I use if else statements directly in the view file and / or in the controller action?
A cleaner approach I would suggest is to create different roles for users and group them. So for that particular group you can have a separate view files and controllers. One advantage of this approach is it will be very easy to read the code and we can easily understand the code. We get more control on the page easily without having to worry about other users. We can even avoid the need for many filters. But if there is only two type of users then it could be managed easily with if else statement, so choosing the right method will depends on the problem too.
I usually use if else statements right in the views, and render partials if there is a lot of markup between the statements to keep things clean.
That is a style choice though so if you are finding you are making massive conditionals on every page, it could be that you need to rethink your organization in the controller or even make a new resource. For simple things like adding an 'edit' or 'delete' button if the user is an admin, I would use conditionals in the view.
For .html.erb markup, you can do normal if/else blocks like this:
<% if <condition> %>
<%= render 'partial_name' %>
<% else %>
<p>Some other content</p>
<% end %>
Where condition is your condition, like for example current_user? #user

Ruby on rails - what should a controller look like when dealing with collections?

I have a simple application in which the user can manage decks of cards.
In my model I have:
Card
DeckOfCards
In my view /DeckOfCards/:id/edit
I want to allow the user to create a new card and add it to the current deck.
I currently have a form_for helper that posts to to /Cards/new
Should my Cards controller be hard coded to redirect back to DeckOfCards? What if later I want to create cards independently of a deck?
Another possible approach I am considering is a custom action on my DeckOfCards controller to handle this case. If I do that is there a way to use form helpers or does that require I post back to the associated controller for the object I am creating?
I am liking the structure that rails brings but when dealing with multiple entities it is not clear in my mind what the architecture should look like. I fear if I misuse these paradigms I will end up in a world of pain!
Can you recommend a more flexible way to approach this?
Thanks for any help
The setup should be quite simple:
You said you have two models: Cards and DeckOfCards. Perfect!
Now in your routes:
resources :cards
resources :deckofcards do
resources :cards, :controller => "cardsindeck" # gives you, e.g. <root>/deckofcards/1/cards/5/show
end
Now you need two distinct controllers for cards:
CardsController: handles CRUD for cards independent from DeckOfCards (you can still have links there to the DeckOfCards a card belongs to)
CardsInDeckController: handles cards through DeckOfCard's
In the CardsInDeckController, you can access the DeckOfCards the current card belongs to by params[:deckofcards_id], e.g. in your new action:
#card = DeckOfCards.find(params[:deckofcards_id]).cards.build

Rails and REST confusion

I am confused about how to set up a RESTful rails app. I have some models- Courses, Students, Assignments, and StudentHandsInAssignment. There's a many to many relationship between Courses and Students, a one to many between Courses and Assignments, and two one to many's from Students and Assignments to StudentHandsInAssignment.
Now I am thinking about controllers and routing. From what I have read I want to be RESTful which means making resources. However, I can't quite figure out how to get all of the things I want in the resources supplied by rails.
In particular, I want to have an elaborate view of all the grades for the students in a course. Now, do I use the simple "GET /courses/:id" route and corresponding method in the controller or use something more specific like "GET /courses/:id/view_complete_grade_summary". I think I would like to do something like the latter because I might have multiple different ways of viewing a course. How does one handle this situation and set up that route/controller?
Also, I'll need to have a form to enter in grades in the app. This will create StudentHandsInAssignment objects and store them in the database. But, when I use "GET student_hands_in_assignments/new" to display a form (and eventually submit to call "create") how do I pass around the student id and the assignment id? Should it be something like this:
"GET student_hands_in_assignments/new/:student_id/:assignment_id"
or like this:
"GET student_hands_in_assignments/new/?student_id=1&assignment_id=2"
I guess I am confused about how to get the required info about the student and the assignment into a RESTful url. What is the best way to handle these situations?
There's no one "right" way to organize resources, IMO. My first thought for grading would be student-centric: student/:student_id/course/:course_id/grade, but it could just as easily be course-centric, or both.
I wouldn't have a separate page for entering grades, though, rather something hanging off a student page (list of enrolled courses with in-page, Ajax-centric grade entry) and off a course page (list of students with same grade entry scheme). Otherwise there are app transitions that just add cognitive overhead without any benefit.
For things like the course grade summary I'd just have /course/:course_id/grade_summary or whatever is necessary.
Using 'pretty' URLs is not necessary for a RESTful application, though I find that creating pretty URLs helps me organize my resources helps other maintain their sanity while maintaining my code.
I would consider using this URLs with the scenarios you describe:
# Renders the form to be submitted
/students/:student_id/assignments/:assignment_id
Add something like the following to your routes.rb:
resources :students do
resources :assignments
end
which will call:
{controller => 'assignments', :action => 'show'}
with the parameters:
{id => ':assignment_id', student_id => ':student_id'}
As a side note, consider changing the name of your model 'StudentHandsInAssignment' to something else. A student handing in an assignment is neither a Model nor a Controller, it's an action upon a resource. It may make more sense to have a AssignmentController with an action "hand_in" that updates the Assignment model (presumably with a status).
Hope this helps.

Rails Routes/Controller/Directory Structure design question

I have a design question where I would appreciate a thoughtful response.
Let's say you have a simple application (for example's sake) that has a User, Company and Theme model. A Company has_one Theme and has_many Users.
Administrators (a User) can fully manage Companies, Users and Themes - the whole REST stack, in addition to a few other actions too. Administrators are expected to do things to all 3 of these resources that other user roles cannot do.
We also have a Company role. This role can edit their own Company, as well as select a Theme from the ones the admin-user added as nice defaults, or they can just make their own theme.
Companies can also add/edite/delete users, but only for their company. These pages will have different views and they'll have different behaviour from admins - some overlaps, but some things will be restricted while others will be added.
Now, here we have some non-trivial design choices, and I would like to know what the best-practice is.
PART 1
In Rails, it makes sense to have resources :users, :companies, :themes for the administrators and probably resource :company, :theme, :users for the Company users.
But of course, we run into some naming conflicts here - both singular and plural - so we might want to try something like resource :my_company, :my_theme, :my_users to separate them? Or is there a better solution?
Furthermore, a theme is just a component of a company, so maybe we want to nest them?
:resource :my_company do
:resource :theme
:resources :users
end
This works okay, but it could be confusing as to which UsersController we are referring to... no? This is really sticky and I would love to know how to deal with this. Do you have 1 controller, or 2? What do you name them?
So this would be an example:
http://myapp.com/my_company/theme/edit
http://myapp.com/my_company/users/1/delete
Company users also might want the list of themes via ajax, so is it correct for them to call:
http://myapp.com/themes.json
?
Is this how to approach this situation, or is there a better way?
PART 2
Also, what should your directory structure look? Should you have controllers separated by user role?
/app/controllers/admin/companies_controller.rb
/app/controllers/admin/themes_controller.rb
/app/controllers/admin/users_controller.rb
/app/controllers/company/my_company_controller.rb
/app/controllers/company/theme_controller.rb
/app/controllers/company/users_controller.rb
Or is there better ways to handle this?
It seems weird that users_controller is duplicated 2x and that there is a minor difference between Theme and Themes.
I would really appreciate a thoughtful response on this. Thanks!
I appreciate your desire to organize your codebase as I constantly have to convince myself not to take my default impulse to nest a resource or namespace a model. As there is no right answer to this question, I will just offer the reasons I use to convince myself not to.
A resource lives in once place. User.find(1) should have a single locator (URL), which I like to call user_path. I like calling it user_path because of all the times that I have made myself call it admin_company_user_path([#company, #user]) which malaise makes in me each time I write it.
That resource may render itself in different ways for different situations, like if the requester was an XHR or indicated that they would prefer German to English. Why is the header indicating that the user is an administrator any different?
If I can make it look like the simplest examples in the rails/README, shouldn't I?
At this point I would concede and end up with:
/app/controllers/companies_controller.rb
/app/controllers/users_controller.rb
/app/controllers/themes_controller.rb
And my routes.rb would have:
resources :users
resources :companies
resources :themes
I should also address how I would handle the thing that makes you want to separate them in the first place–a different view for each of the roles. In my ideal scenario, my decision would result in a themes/_form.haml that looks like:
= form.input :title if user_can_change_title?
= form.input :theme if user_can_change_theme?
And the rest of the differences would handled in CSS, with perhaps a stylesheet for each role.
In the less ideal scenario, I might be using:
= render :partial => "#{current_user.role}_form"

How to organize actions that don't fit into the normal MVC

I am creating a survey application so I created a surveys controller that behaves very restfully creating, updating, etc a Survey. However now I'm adding other actions to it like 'take', for taking a survey, and 'share', for sharing a survey. There are more actions too. I'm starting to wonder if I should organize my code differently and move those new actions into their own controllers however I'm not so sure take or share or some of my other actions really fit into REST really well. They almost make more sense as actions if I wasn't a little worried about the survey controller size.
Either I could leave it the way it is or I was thinking of creating a survey namespace and creating like Survey::TakeController and the Survey::ShareController. Then I would then I guess use the new action or index?
I'm not exactly sure the proper way to do it. If I do create a survey namespace should I then move the orginal SurveyController in it? That would make some weird looking methods like survey_survey_path.
To think RESTfully, you should probably stop thinking of them as "controllers with actions" and start thinking of them as "objects that can be created/updated etc" - controllers are just proxies for the views that show the results of creating/updating an object.
A lot of the time, I've found that an extra action is really just a variation of "update" - just with its own special requirements (eg only certain people can update it or whatever). That sort of logic can often go inside the model itself (eg "MyModel#can_be_edited_by?(some_user)").
Sometimes you find that actually you have an extra "hidden" model that needs its own RESTful interface.
Eg with your "take" a survey - I'm guessing, but what you have is something like a "SurveyResult" and a person can "create "survey" but when they "take" a survey, they are actually creating a "SurveyResult" (the other commentor called this a "SurveyParticipation" - but it's the same thing).
The thing being that you will probably have multiple SurveyResults that each belong_to :survey and belong_to :some_user_model.
Then you can set up nice restful routes such as:
/surveys/123-my_favourite_colour/results
which will return a set of all the results for a single survey
This is actually the RESTful way to view this part of your object-space.
As to sharing a survey - that's a more intersting question. It depends on how you've got your authorisation setup for "sharing". It also depends on what you mean by "share".
Are you sharing the results of a survey, or sharing the survey-object itself (so another user can edit the questions) or are you (as a person that has just taken part in a survey) sharing a link to the survey so that your friends can also take the survey?
For the first two above - I'd consider a "SurveyPermission" class that belongs_to :survey and belongs_to :some_user_model.
The you can create a SurveyPermission for another user - and Surveys can be edited by the creator- or anybody that has a permission to edit it.
Thus the share action is to create a SurveyPermission.
Though to be honest - your SurveyPermission is likely only to be used for create and delete, so it may be simpler to stick those two actions in the Survey controller.
For the latter - well, that's just sending a "create_survey_result(#survey)" link to somebody...
Update:
I don't generally bother with namespaces unless there are two resources named the same thing (but in different contexts). You only need namespaces to disambiguate between them and that doesn't seem to be the case here.
In this case - the only namespacing that occurs is in the routing:
map.resources :surveys do |s|
s.resources :results
s.resources :shares # ???
end
gives such routes as:
new_survey_path
surveys_path
new_survey_result_path(#survey)
survey_results_path(#survey)
If you want to stay with the RESTful approach then you could have a SurveyParticipation resource, with the new/create actions being invoked when somebody takes a survey. And a, ahem, SurveyShare resource for sharing a survey. That name's pretty awkward though!
Personally I don't think it's the end of the world if you have to augment your existing controllers with a small number of additional actions, as long as it doesn't get out of hand. RESTful Rails saved us from the bad old days when controllers had tens of actions.

Resources