How to stick with REST? - ruby-on-rails

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 :)

Related

How does ruby on rails map http request to handler function?

I am new to RoR, so please forgive me if this is a stupid thing to ask.
I was looking into routes.rb file and found these two lines:
get "question/question"
get "question/answer"
But there was no mention of the functions they are mapped to.
I tried to look how they are mapped to the functions and in all the tutorials or reference docs I found on net, requests were hashed to function names.
So I was not able to understand the routing in this case. Can someone give names of some files to look into or some documents suitable for beginners which can explain routing clearly, removing the magical part?
Look at the QuestionsController and the question and answer methods.
See these routing docs for details on how routing works for these types of paths.
Allow defaults for values when possible.
These lines
get question/question
and
get question/answer
means respond to get requests that use a url with question/question or question/answer to be processed by the:
question controller and the question method
question controller and the answer method
You may be more used to working with constructs like:
get 'users/change_district/:district_id' => "users#set_district", :as => 'change_district'
which allow you to specify which controller (users) and which action (set_district)
If, however, you omit some parts then the router will use what you give and use defaults for anything not specified.

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 ?

RESTful Quiz Representation

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

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.

Non-CRUD Controller Actions

This might seem like a n00b question, but I am trying to break some of my bad practice that I may have adopted using MVC, so I hope you can help me out
So, imagine I want to do something like "Upload CSV And Parse It", it doesn't seem obvious to me to fit it into the CRUD pattern... I am not interacting with the DB, so i don't need add or update or delete, but I still want to be able to use the action in a meaningful way from different views. Thus, it is "ok" to just an action called "UploadCSV" and have it be accessible via a URL such as "/data/uploadcsv"
Your thoughts are much appreciated!
Tom
It sounds like you are talking about RESTful ideas (having actions called index, create, new, edit, update, destroy, show).
In MVC you can call an action largely whatever you want (so yes, you can call it uploadcsv if you want). If you want it fit RESTful principles you might want to think about what the action is doing (for example is a data upload essentially a create or an update function) and name it using one of the RESTful action names.
I believe I have the same point of view as you.
In my projects I try to be as restful as possible whenever I can. However as you said sometimes a special case just does not 'fit'
After all it is also a question of 'feeling'
If you provide a csv import function, I see it as perfectly correct to not create a full REST implementation for CSV.
Let's imagine in your application you have clients. And you wnat to give the option for clients to import data using csv. You can add a route for this action using:
map.resources :clients, :member => { :uploadcsv => :get }
The route is properly declared, Your 'clients' resource is completely restful and you have an additional action properly declared to manage data importation.
The only warning I have is: don't use a route like this one "/data/uploadcsv". From my point of view It lacks clarity. I like to be able to understand what my application is going to do just be looking at the url. And '/data' is too vague for me :)
The persistence of the resource is not crucial here. I suppose that what you are doing here is this - creating some kind of resource (although not persistent) out of the csv provided. The thing here is to think about what this csv file represents. What's inside? Is it something that will become a collection of resources in your system, or is it a representation of only one object in your system? If you think about it it has to be something concrete. Can you be more specific about your problem domain?

Resources