RESTful Quiz Representation - ruby-on-rails

I'm building a quiz. A user can pick a subject and answer 5 questions. After each question they view the answer. I'm trying to stick to a strict RESTful representation of this workflow but cant really settle on a url scheme. For example:
User Joe picks the subject sport and is ready to see the first question. The url is
user/joe/subject/sport/question/1
When he submits his answer 'B' ( its a multiple choice quiz) , Joe is creating a new answer, we POST to
user/joe/subject/sport/question/1/answer/B
before viewing the correct answer at
user/joe/subject/sport/answer/1
we then view the next question at
user/joe/subject/sport/question/2
This is all obviously too complicated. How would you approach this problem in a RESTful manner?

Start with this presentation. It's is a great resource for RESTful API design. With that in mind, here are some starting suggestions:
RESTful URLs have an implicit hierarchy. Take the user information out of the URL. It belongs in the HTTP headers.
/subject/sport/question/1
/subject/sport/question/1/answer/B
/subject/sport/answer/1
/subject/sport/question/2
I don't see any useful information added by the subject part. The subject is identified by (in your example) sport.
/sport/question/1
/sport/question/1/answer/B
/sport/answer/1
/sport/question/2
Categories should be plural.
/sports/questions/1
/sports/questions/1/answers/B
/sports/answers/1
/sports/questions/2
When you POST to answer a question, you're not POSTing to add a new answer resource (that is, defining a new possible answer). Aren't you are POSTing to an existing resource?
You haven't mentioned anything about HATEOAS. If you're really going to implement REST, you should be doing things like providing "next" links in the hypermedia.

For me the basic idea in a REST service is the "resource". First you need to identify your resources.
So there is a user and she starts a new quiz.
POST /user/joe/quiz.
It returns: Location: /user/joe/quiz/1
Then the user selects a sports question, so you update your quiz to include a random (server selected) question.
POST /user/joe/quiz/1 -> Subject:sport
It returns: Location: /user/joe/quiz/1/question/1
The user answers:
PUT /user/joe/quiz/1/question/1 -> Answer B
Now rinse and repeat.
The resources we've got:
Users
Quiz for a user
Questions in a Quiz (The question is updated with an answer)

I would remove /user/joe from the routes entirely. You can get the current_user using Devise, Authlogic, or some other authentication framework.
Otherwise this looks okay to me, as it's only two nests which is readable enough. So you'd have:
GET subjects/sports/questions/1
POST subjects/sports/questions/1 # pass along params with {:answer => 'B'}
GET subjects/sports/answers/1

Related

URL Parameter Encoding and Rewriting in Rails

As a learning experience for Ruby and Rails, I am creating a website for taking polls, stores the results, etc. As part of the polling process, a user has to go through a number of questions and provide answers to those questions. When they are done, they receive a list of recommendations based upon the answers they provided (of type Answer).
I have two parts to my question. One, I think I am heading down the right path. The other, I'm not even sure where to begin, and don't know if it is a good idea.
Here is my Answer model:
class Answer
attr_accessor :question_number, :description, :answer
end
Question 1
I am looking for a way that, when the user submits all the answers (I'm storing their responses in session storage), it goes to my search function - but it is encoded nicely.
Instead of:
http://localhost:3000/results/search?[biglongstringofdifferentanswers]
I would like something like:
http://localhost:3000/results/search/1-answer_2-answer_3-answer
After doing some searching, it seems that what I want to accomplish has to be done with the #parameterize method, but I'm not sure I understand how to do that exactly.
Question 2
The second part to my question is - can I encode my answers so that they aren't directly human readable. I want to do this to prevent people from browsing to each other's answers. For example, the first answer is always the person's unique ID and I don't want to someone to be able to just browse to any old set of results by switching around parameters.
So, I am hoping to get something along the lines of:
http://localhost:3000/results/search/798dh832rhhbe89rbfb289f9234972bdbdbbws3
For this second question, I'm not even sure if this is a good idea, so I'm open to suggestions for this one.
Appreciate any help and guidance on these questions as I continue to explore/learn Ruby and RoR.
If I get it right, there is not any login system and you want submitted answers that you store in your DB to be accessable via url for the user. You said you don't want users to navigate to other users' answers but the user getting the url can still share it.
What I would do is to submit answers via POST method, so you don't have to worry about encoding your params etc. It gets then real easy with Rails.
You can add a public_id column to your answer object that would be a generated big int. After the post methoded submit, once you save the answer in your DB, you could return a redirect to the answer public id url.
something like
def create
answer = Answer.new(params[:answer])
if answer.save
answer.generate_public_id # <= would be nice to add if in the answer model 'after_create' filter probably
return redirect_to public_id_answer_path
end
render :partial => 'error'
end
What do you think ?

Prevent modification ("hacking") of hidden fields in form in rails3?

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.

How to stick with REST?

I'm basically putting together an app that does a super simple question => answer test in an attempt to learn more about rails.
ie:
Question: "What's your dog's name?"
Answer: "Doggington"
I have a Question model with two simple attributes:
question:string
correct_answer:string
My struggle here is how do i apply REST principals to this -- specifically, when i am checking a user's input(answer) to see if he got the question right or not.
I'm not sure if i should do something like modify the "show" method (or any other action) to accept values for answer posted to it... and it SEEMS like I should create a new method in my questions_controller called "verify_answer" or something a long those lines.
This breaks REST.
What do you think?
thanks!
AnswersController#create should accept the answers. Whether or not this controller actually has a related Answer model is irrelevant. One action should never perform two actions. For instance, if your QuestionsController#show both displays the question, and accepts a :put or :post with the answer to the question then you are breaking basic rails design principals.
Note that your routes file might very well look like this:
resources :questions do
resource :answer
end
Which will expose the /questions/8/answer route that you can :post to, which will go to AnswersController#create.
Off the top of my head I'm forgetting the exact name of the helper url method you can use to generate the url. Something like question_answer_path(#my_question).
This routing file is for rails3, which I assume is what you're using since there's no reason to use anything else if you're starting a new app in my opinion :p
If you do have an Answer model (maybe you want to store users' answers and look at them later or aggregate them and come up with statistics and such) then you should change the router to use resources :answer instead of the singular version.
For more information about routing and some RESTful tips you should visit the Ruby on Rails guide for routing found here: http://guides.rubyonrails.org/routing.html
I'm on an editing spree! There are times when you might need to add an additional method to your Questions controller that isn't strictly REST. This isn't necessarily considered bad practice but just make sure you look at your decisions and find out if you aren't actually just hiding the existence of another resource. I don't consider this to be one of those times as explained above :)

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 routing: how to mix "GET" and "PUT"

Not sure how to frame this question (I'm still wrapping my head around Rails).
Let's try this:
Say I wanted to implement the user side of Ryan Bates' excellent railscast on nested models. (He shows how to implement a survey where you can add and remove questions and answers dynamically). I want the user's side of this: to be able to answer questions and, not in the tutorial, be able to add comments.
It seems to me that you have to implement a view that shows the questions and answers, allow selection of the answers, and the input of comments. So there would need to be a way to show the information, but also update the model on input, right?
I know I'm not explaining this very well. I hope you understand what I'm getting at.
Is it just a question of setting up the right routes? Or is there some controller mojo that needs to happen?
The typical way to do this in Rails uses "resourceful" routing, which more or less naturally maps the standard CRUD actions to methods in your controller, using the appropriate HTTP verbs.
In the routes file (config/routes.rb), you set up the desired resources and actions. For example:
map.resources :questions, :has_many => :answers
Would set up a routing scheme for a question with multiple answers, mapping to the actions according to Rails' conventions:
index: GET /questions/1/answers # list of answers for question id=1
show: GET /questions/1/answers/2 # display answer 2
new: GET /questions/1/answers/new # render form for new answer for question id=1
create: POST /questions/1/answers # create a new answer for question id=1
edit: GET /questions/1/answers/2/edit # render form for answer for question id=1
update: PUT /questions/1/answers/2 # update answer 2
destroy: DELETE /questions/1/answers/2 # delete answer 2
In the controller you create methods mapping to these standard actions. You can also create your own methods and actions for things that don't fall into the CRUD paradigm (like a search for an AJAXified autocomplete field, for example)
Hope that answers some of your question.
You need a "question" resource, "answer" resource and "comment" resource. You also need to implement:
POST for "answer (which is "create" method in controller) to answer the question
POST for "comment" (which is "create" method in controller) to create comments
PUT for the "question" (which is "update" in controller) to "pick" answers, which is effectively changing the state of the "question" resource
In ASP.NET MVC there are two controller methods with the same name but different parameter signatures. One method is decorated with an attribute that tells it to service GETs, the other is decorated with an attribute that tells it to service POSTs. The GET method displays the view, the POST method updates the model.
I assume that it works in a similar fashion in Rails.

Resources