Rails Routes/Controller/Directory Structure design question - ruby-on-rails

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"

Related

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.

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.

Rails: resources for unorthodox urls?

I'd like to have a URL like this:
/payroll/region/1
and I'd like it to map to the Tasks Controller's payroll_list function. I'd also like to use REST. What's the best way to do this?
Many thanks!
Well I'd suggest you better go with the convention how Rails handles this. If you still insist on using such "strange" URLs and want to ignore the problems/headaches this can create during further development, then try to use Refraction.
I don't want to be rude but currently it seems to me that you did not understand why restful URLs are the way they are. Please do understand the design behind this first, then rethink your application/controller and routing design. I bet you will be enlighted.
In this example, your URL should probably be /regions/1/payrolls with map.resources :regions, :has_many => :payrolls. Then your payroll list would be rendered by the PayrollsController having a params[:region_id] - and that actually makes sense (and probably what you tried to achieve with your URL layout). Code snippet:
def index
if params[:region_id]
#region = Region.find(params[:region_id])
#payrolls = #region.payrolls
else
#payrolls = Payroll.all
end
end
If you still want to have a resource under a different named URL, use the following:
map.resources :regions do |regions|
regions.resources :tasks, :as => :payrolls
end
This will map the nested resources to the tasks controller using the named URL part "payrolls." But this probably does not work as you might expect because restful logic means you should handle the payroll model in the PayrollsController. Otherwise you might run into strange looking code. Maybe your design of the TasksController is just wrong? Rails will probably expect tasks to be handled over to your tasks controller although you name it payrolls. This can be confusing at least (however, it does not actually expect these being task models, so it will probably work).
BTW - Keep in mind: "restful" also means your application should answer to standard verbs on a resource, not just using "resourceful" routes. It's also about the GET, PUT, DELETE and POST http verbs, and of course the "edit", "new" etc default actions. Do not try to make your controllers big and complicated. Follow the motto "skinny controllers - fat models".
OK, so a better question, then might be this:
How can I get it so that I use your suggestion:
/regions/1/payroll
and have that map RESTfully to:
Tasks controller with index, new, etc that are prefixed by "payroll_"?
Like this: TasksController#payroll_index or TasksController#payroll_new

Ruby on Rails - differentiating plural vs singular resource in a REST API

I'm working on building the URLs for my REST API before I begin writing any code. Rails REST magic is fantastic, but I'm slightly bothered the formatting of a URL such as:
http://myproject/projects/5
where Project is my resource and 5 is the project_id. I think if a user is looking to retrieve all of their projects, then a respective HTTP GET http://myproject/projects makes sense. However if they're looking to retrieve information on a singular resource, such as a project, then it makes sense to have http://myproject/project/5 vs http://myproject/projects/5. Is it best to avoid this headache, or do some of you share a similar concern and even better - have a working solution?
Rails (3) has a lot of conventions when it comes to singular vs plural. For example, model classes are always singular (Person), while the corresponding tables are always plural (people). (For example, Person.all maps to select * from people.)
For routes, there's a concept of a singular resource as well as a plural resource. So if you did resource :account then you would get paths like /account for the default path or /account/edit for a path to a form to edit the account. (Note that Rails uses /account with a PUT method to actually update the account. /account/edit is a form to edit the account, which is a separate resource from the account itself.) If you did resources :people, however, then you would get paths like /people, /people/1, and /people/1/edit. The paths themselves indicate whether there can only be one instance of a given type of resource, or whether there can be multiple instances distinguished by some type of identifier.
I agree, go with the flow. Consider how the URL forms a hierarchy.
The root of your website is where you start to access anything.
/projects/ narrows it down to only projects, not anything else. From projects you can do lots of things, /list, /index/, /export, etc... the /id limits things even further.
At each / the scope of what do becomes narrower, and I think it makes sense.
Further programming is all about arbitrary rules. Indexs starting at 1 vs 0, and so on. Anyone working with your urls will sort things out in short order.
There are cases where a singular path to a resource is helpful. If your resource ids are non-numeric user defined names then routing clashes are possible. Example:
/applications/new --> create a new application or show user's application named new?
In this situation you can choose to limit the user input to avoid the clash, or, this can be worked around by overwriting the default Rails 3 behavior:
class ActionDispatch::Routing::Mapper
module Resources
RESOURCE_OPTIONS << :singular_resource
class Resource
def member_scope
#options[:singular_resource] ? "#{singular}/:id" : "#{path}/:id"
end
def nested_scope
#options[:singular_resource] ? "#{singular}/:#{singular}_id" : "#{path}/:#{singular}_id"
end
end
end
end
Then when specifying a new resource route:
resources :applications, :singular_resource => true
Which will generate the routes:
GET /applications
GET /applications/new
POST /applications
GET /application/:id
GET /application/:id/edit
PUT /application/:id
DELETE /application/:id

Rails Many to Many Relationship with occasional Nested Routes

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.

Resources