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.
Related
Like rails routes resources, I am looking to make a sinatra application share the same code between different routes.
For example, in a company, we have divisions and divisions have departments. An employee can belong to a department or division.
So, I would like GET /divisons/:division_id/employees and GET /divisons/:division_id/departments/department_id/employees share the same code since the business logic is nearly the same.
My idea is to init #division and #department before action and use a helper function in order to fetch employees. Is it ok like that or is there a wiser way?
I finally used sinatra multiple_route which allowed me to have the same controller code for many routes.
What is the standard practice for a rails app when a standard 'show.html.erb' for one model essentially just lists its has_many of another model? There is no index for the 'child' model as without the context of its 'parent' model, a list of them would be useless.
Creating /parents/1/children/1 feels like the right thing to do (especially in terms of the API) and yet on the /parents/1 page there would be nothing but a bit button saying "carry on" ... again, useless.
Do I creat separate routes, like the one above, purely for the API or am I missing a cleaner more 'Railsy' way.
EDIT
I'm not sure the above is totally clear. If I have an Exam that was completely made up of Questions, would the Rails way to be to link to /exam/1/questions or purely to /exam/1 with a list of questions. Obviously the API would go to /exam/1/questions in most cases but what about the standard HTML page? It would seem like it wants to be /exam/1 but with nested resources the create route (by default) goes to the /exam/1/questions route. If there is a validation error on the /exam/1/questions/new route the form is rendered again on the create (/exam/1/questions) route; if the page is then refreshed you then get a 404 error as there is no index for the children.
All this leads me to believe that Rails expects be an index action for the majority of models; but in the above example this feels odd.
If I understand you right you dont whant the route to /parents/1 to be on your path. For this reason you should specify it in your routes.rb
resource :parents, except: :show do
resource :children
end
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
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"
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.