RESTful API best practices, update vs custom action [closed] - ruby-on-rails

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 6 years ago.
Improve this question
I'm implementing a RESTfull API to talk to AWS RDS, security_groups resource supports the typical CRUD verbs. When it comes to "authorize" and "revoke" i'm not sure what's the best practice, which one do you think is best?
Custom action, params in url
PUT agifog:3000/rds/security_groups/:security_group/authorize?ec2name='default'&ec2owner='0123456789'
Custom action, passing params
PUT agifog:3000/rds/security_groups/:security_group/authorize
{
"ec2name": "default"
"ec2owner": "0123456789"
}
Standard update
PUT agifog:3000/rds/security_groups/:security_group
{
"operation": "authorize"
"ec2name": "default"
"ec2owner": "0123456789"
}

PUT does not mean "update" any more than POST means "insert". PUT means "put this here".
RESTful practises revolve around treating your URLs as resources, entities which have some meaning in your domain, which you perform actions against (the verb of the HTTP request).
What you could do is consider the security group to be the resource on which you are acting and PUT users into the group or DELETE them from the group:
PUT agifog:3000/rds/security_groups/:security_group/default
{
"ec2owner": "0123456789"
}
DELETE agifog:3000/rds/security_groups/:security_group/default
These could then correspond to authorize and revoke actions, plus makes it easy to see how a GET on the group could produce a list of all the users currently in the group.

The second seems the most RESTful. You've got a resource (security group) and a custom action (authorize) that will respond to your request's verb (PUT).
PUT agifog:3000/rds/security_groups/:security_group/authorize
{
"ec2name": "default"
"ec2owner": "0123456789"
}
and similarly:
PUT agifog:3000/rds/security_groups/:security_group/revoke
(NOTE: I'd probably prefer a POST for the above if it will be generating a session or some other authentication data/token.)
For comparison, if you were interested in updating the attributes of that resource, you'd want to do something like:
PUT agifog:3000/rds/security_groups/:security_group
{
"some_attr1": "some_val"
"some_other_attr": "some_val"
}
In which case the PUT implies that it is an UPDATE to this resource.

Related

Route perform wrong action on submit form_with [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 20 days ago.
Improve this question
i'm having trouble with my rails aplication.
i have a form who should submit some fields to be search to /cities/search/<params_here>.
but, when i submit form, the routing goes to wrong action, and perform a set_city funcion... (??? why this happens ??? )
if the route exists and be declared before the other routes generates from :resources, that should't work?
It seems that there are two problems with the above code.
1.The URL in the form_with:
The URL ideally should be url: "cities/search",.
Since you are using form_with, the values will be available in the form of query params.
This is where our 2nd change comes in.
2.The route that you've set:
It should be get '/cities/search', to: 'cities#search', and as mentioned above, the form fields and their values will be available in the query params.
In the cities_controller's search action, you'd get the params by using params[:query] and params[:search].
Please check form helpers once so that you get a clear idea of it's working.
I have not tested this, so let me know if this helps and if there are any other issues after the above changes.
Also, it is good practice to post the code in the questions in text format rather than images. Ref. this link to understand why.
You can use a collection route without needing to think about route priority:
resources :cities do
get 'search', on: :collection
end

Admin panel not working in ruby on rails [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
Currently i am learning rails so i'm sorry for my bad knowledge and probably for a silly question. :)
I created a simple index page in a controller called pages.
Inside index.html.erb i have a form where a user can sign up for a newsletter which is stored in #newsletter and i also have a variable called #title where title of the page is stored in database.
I want to create an administration panel available only for the owner of the website where he can access and display all newsletters, also i would like the owner to have the possibility to change the title.
Can i achieve that if i create another controller ?
What do you recommend, to keep the same controller for admin and pages or create a new controller for admin area ?
Also, one more question, when do i need to create a model? Everytime when i need a new table in database, isn't ?
I should have a single model for every controller or i can have more models and one controller?
Is there any connection between pages and the newsletter?
If newsletter is not in a relationship with pages, then you can place the newsletter form in a partial and render it where you want.
This a possible db schema for you:
'Page.rb' model with the fields that you need like: title:string, slug:string (i prefer friendly_id), body:text...
'NewsletterEmail.rb' model with the fields that you need: email_address:string, name:string (optional), subscription_status:boolean (to track un-subscription)...
Every model needs a controller for you to track in admin.
For the front-end, you must setup a route like 'match: "/:slug" => "public#page"', where you can catch your page like #page = Page.friendly.find(:slug) and then assign the #title variable #title = #page.title.
Hope this helps you.
For your admin controllers pages_controller.rb and newsletter_emails_controller.rb you can use a before_action :check_admin_rights and define in application_controller.rb
def check_admin_rights
authenticate_or_request_with_http_basic('Administration') do |username, password|
username == 'admin' && password == 'password'
end
end

In Rails, Can we auto submit data to the controller upon user visit and reload the view? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
Here's what I need to achieve :
A user visit the login page. Upon the first visit, the browser will record his IP and data from a cookie (cookies[:user]) and send it to the server. The server then check whether the IP or the user is in the block list and if the user is in the block list, it will automatically redirect the use to a blocked page!
Can we do this in Rails and how?
This could be done quite easily, take this for example
def new
if cookies[:userid].present?
if User.find(cookies[:userid]).userblocked == true
redirect_to "/block_page"
end
elsif #failedtries == max_tries
User.find(cookies[:userid]).userblocked = true
cookies[:userid] = #id_entered
end
end
Where 'userblocked' is a true or false value in your database. This method is full proof against people who can freely change their IP. However if your heart is set on using IP's then this is an example:
def new
if cookies[:blocked].present?
redirect_to "/block_page"
else
if BlockedIps.find_by_ip(request.remote_ip).blocked == true
redirect_to "/block_page"
elsif #failedtries == max_tries
BlockedIps.find_by_ip(request.remote_ip).blocked = true
cookies[:blocked] = true
end
end
end
Something like this could be what you are after. Give it a try, I haven't tested the code. This is just from my head at this moment in time, but it should do what you want, providing you have some sort of system to store the IP in a database when the max tries have reached.
Explanation:
The way it works is that it checks to see if the blocked cookie is present, if so it redirects to the block page, if not it checks to see if the users IP address is in the block list of IP's. If the IP is present and the block column is set to true then the user will be redirected to the block page, else the system checks how many tries they have made and if it is equal to the max tries it adds the users IP to the database and creates a cookie on their PC which can be checked on next visit.
You can make a method for checking the IP address and authenticating then use before_action :yourmethod on top of your controllers. before_action runs before any other action in your controller so it is a good way to authenticate before showing any views.
You can use Conner Stephen McCabe solution above or take a gem for this purpose.
User can easily clear the cookie, so it's not safe anyway.
Example: https://github.com/kickstarter/rack-attack

How rails component interact with each other in background? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
For example: How objects are called from controller on view?
Which methods, techniques are running in background?
Please help.
Thanks in advance.
you should check these out
http://railscasts.com/episodes/397-action-view-walkthrough
http://railscasts.com/episodes/395-action-controller-walkthrough
http://railscasts.com/episodes/319-rails-middleware-walkthrough
These screencasts will show you what happens in the background.
This question is like how components in airplane interact with each other so plane flies?
Anyway, when you call any action at any url, Rails will try to match it with routes.
get 'posts', to: "posts#index"
will match /posts routes and will look through your controllers and start PostsController.action(:index).
After that, while executing index action of your controller it will initialize instance variable #posts which is usually Post.all.
And the last thing, PostsController will call renderer, passing action, all instance variables, sessions and url-params. ActionView Renderer will initialize the instance of view with proper template app/views/posts/index.html.erb, put variables there and send page to user.

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

Resources