I have 2 models - User and Activity - which are related by a has_many :through using the UserActivity model. A User can either "want" or "done" an activity, which creates a UserActivity record and sets the appropriate boolean.
What would you recommend for handling these actions when creating routes and controller actions? Would something like activity/:id/want, and activity/:id/done make the most sense, and thus have 2 member routes under the activity resource? Or would it make more sense to just use the update/create actions in the user_activity controller, with posting to /user_activity for a create, and putting to /user_activity/:id to update?
I would go with the latter approach, i.e. by allowing POST/PUT access to "user_activity". In the REST world this is considered as a new resource even though it just forms a relationship between "user" and "activity" resources.
One thought which comes to mind is to separate "want" or "done" activity types as a new resource as well(e.g. "status"). The idea is clean as it is even without it, but if there is a chance that you will have to extend the list to something on top of "want" or "done" (e.g. "someday/maybe"), it may be easier to define it as a new resource rather now than later.
So you would have:
user resource: /user and /user/id
activity resource: /activity and /activity/id
status resource: /status and /status/id
resource which forms the relationship between user, activity and resource: /user-activity and /user-activity/id
Depending on your URI design you can then work in accessable URIs which would allow you to get all activities for a user which are in a specific state, e.g.: /user-activity/user/{userId}/status/{statusId}
Please note: I cannot advise on Ruby-on-Rails specifics (as I'm from the PHP world), but I think that the (REST) principles should be very similar.
Related
The logic of the application that I currently work on demands a Payment mustn't be editable if its status is open. I see two ways of implementing this:
1 A routing constraint like:
constraint: lambda { |req| Payment.find(req.id).status != 'open' }
2 A simple condition in PaymentsController#edit:
if #payment.status == 'open'
redirect_to payments_path
end
What option should I go for? Which is more suitable and clean, Rails-ish? Is there any other option? If I go with the first option and have a resources :payments, how can I add the constraint only for the edit route?
As to the Rails way of solving this it is actually none of your suggestions.
Routing - You routes should just simply declaratively state the RESTful interface of your application. They should not be aware of the current request unless absolutely necessary.
Controller - Adding business logic in the controller will bloat your controllers and violates DRY.
In MVC the model is in charge of enforcing the business logic. You could handle this through a custom validation for example:
class Payment
validates :cannot_be_edited_when_open, on: :update
def cannot_be_edited_when_open
errors.add(:status, 'is open. Cannot edit this record.') if self.open?
end
end
This will cause any call to .update to fail - which means that you probably will not need to change anything in your controller.
Edited
Another place to handle this would be on the authorization layer - the key difference here is how the feedback should be handled.
A validation failing will just re-render the form (422 Unprocessable Entity for an API) while an authorization error should clearly tell the user "no you don't have permission to do that - and changing the input won't change that" (403 Forbidden).
To setup the rule in CanCan you would do:
can [:edit, :update], Payment do |payment|
payment.status !== 'open'
end
You could also possibly set this up with hash condition instead of a block if your business logic allows it:
can [:edit, :update], Payment, status: 'not-open'
If the rule you specified is a business logic, i.e. no Payment object should be updated with the status open, then, the correct way would be to add that logic to your model. You can use before_validation or before_update callbacks. Also, don't display any means to edit (link, button, etc.) your Payments that have status open. If a user somehow gets to your form, display it, but then validation in your model will not let them save it. I think, in this case, everyone is responsible for their own responsibilities.
But, if you have to choose from the 2 options you provided, I would go with the second one. Your first option, having the business logic in routes, doesn't seem not right. It does not belong there, IMO. The second option is better, but the drawback is that you and your team members will still have to remember that they have to check the Payment object's status attribute does not have some value before touching it. Obviously, someone will forget to do that at some point. So, in the long run, your system (models in your system) will have a corrupted state.
Strict REST proponents might say that if you ever find yourself defining an action on a controller that isn't CRUD, then you should strongly consider creating a new resource and defining your action as a CRUD operation on the new resource.
An example might be operations that change state on a model - say Purchase. In this example, instead of defining a 'complete' action on PurchaseController you might create a CompletePurchasesController and use the create action to update the purchase's state to completed.
Assuming the above, you obviously don't persist PurchaseState directly to the database.
My question is when do you couple the Controllers to Models? When do you define a PurchaseState model (which isn't persisted) and when do you simply work directly with Purchase.
Is it a question of complexity and the number of loosely associated models you are interacting with in the Controller actions?
'Complete' is a state transition event on (an existing) purchase. I find it counter-intuitive to conceptualize this action as a create on a virtual resource rather than an update action on a controller coupled to the Purchase model, in fact PurchaseController itself.
I would define individual update-like actions for such state transitions. I think this way you can leverage rails structure in the most economical way, including model-initialisation, view dispatching, routing, access control. Let's assume you use inherited_resources, cancan by simply adding
# routes.rb
resource :purchase do
put :complete, :on => :member
end
# purchase_controller.rb
def complete
#purchase.complete!
end
# cancan ability (entry already there for basic crud)
can :manage, Purchase, :user_id => user.id
you are already done implementing the entire UI (view/model logic aside).
How utterly cool is that in rails.
If your typical usecase is that purchase is only updated by state transitions, especially all having the same access rights and redirect views, then I would even use the update action of PurchaseController with state_event attributes. See
Can somebody give an active record example for pluginaweek - statemachine?
Strict REST-ists, bite me! :)
For updating the purchase state, you probably only need a PurchasesController update action, which you would define in your routes file as a 'put' or 'patch' method.
If all that happens on update is changing the state field on your purchase object, you can probably just do that right in the update action.
If there's some business logic around some of the state transitions, but ultimately you're only changing that purchase object, you probably want to put that in your Purchase model.
If other tables are also updated, or you're also doing things like queuing up an email to your user congratulating them on their new purchase, I think that's when you might add a separate PurchaseComplete or PurchaseAbort models / service objects. These seem to come into play most naturally when the logic for the action is more complex, and/or you have changes to more than one model, or are doing something else.
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.
So lets say I have a form for submitting a new post.
The form has a hidden field which specify's the category_id. We are also on the show view for that very category.
What I'm worried about, is that someone using something like firebug, might just edit the category id in the code, and then submit the form - creating a post for a different category.
Obviously my form is more complicated and a different scenario - but the idea is the same. I also cannot define the category in the post's create controller, as the category will be different on each show view...
Any solutions?
EDIT:
Here is a better question - is it possible to grab the Category id in the create controller for the post, if its not in a hidden field?
Does your site have the concept of permissions / access control lists on the categories themselves? If the user would have access to the other category, then I'd say there's no worry here since there's nothing stopping them from going to that other category and doing the same.
If your categories are restricted in some manner, then I'd suggest nesting your Post under a category (nested resource routes) and do a before_filter to ensure you're granted access to the appropriate category.
config/routes.rb
resources :categories do
resources :posts
end
app/controllers/posts_controller
before_filter :ensure_category_access
def create
#post = #category.posts.new(params[:post])
...
end
private
def ensure_category_access
#category = Category.find(params[:category_id])
# do whatever you need to do. if you don't have to validate access, then I'm not sure I'd worry about this.
# If the user wants to change their category in their post instead of
# going to the other category and posting there, I don't think I see a concern?
end
URL would look like
GET
/categories/1/posts/new
POST
/categories/1/posts
pst is right- never trust the user. Double-check the value sent via the view in your controller and, if it does't match something valid, kick the user out (auto-logout) and send the admin an email. You may also want to lock the user's account if it keeps happening.
Never, ever trust the user, of course ;-)
Now, that being said, it is possible to with a very high degree of confidence rely on hidden fields for temporal storage/staging (although this can generally also be handled entirely on the server with the session as well): ASP.NET follows this model and it has proven to be very secure against tampering if used correctly -- so what's the secret?
Hash validation aka MAC (Message Authentication Code). The ASP.NET MAC and usage is discussed briefly this article. In short the MAC is a hash of the form data (built using a server -- and perhaps session -- secret key) which is embedded in the form as a hidden field. When the form submission occurs this MAC is re-calculated from the data and then compared with the original MAC. Because the secrets are known only to the server it is not (realistically) possible for a client to generate a valid MAC from the data itself.
However, I do not use RoR or know what modules, if any, may implement security like this. I do hope that someone can provide more insight (in their own answer ;-) if such solutions exist, because it is a very powerful construct and easily allows safe per-form data association and validation.
Happy coding.
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.