Handle non-REST logic in Rails API - ruby-on-rails

For example, let's say I have a Post resource.
With GET /posts I retrieve all posts (index action). What if I want to retrieve trending posts (posts with a lot of likes in the last 24 hours)? Should I implement the logic in the index action (through a parameter), or should I implement a new action trending that responds to a new endpoint GET /trending, making the API non-REST?
Note: I read this article and I understand the parameter way, but I can't figure out how adding an extra endpoint works...

you can do both. I would opt for the new action. But you can pass an extra param as well in a link:
link_to posts_path(trending: true)
In your controller you can then check if params[:trending].present? and then only pass to the posts variable #posts the trending posts.
You can also assign the trending indicator to a variable that will be passed to the index view so that you can adapt the layout (change header from "posts" to "trending posts") with if params[:trending].present? then #trending = true end
Creating a new action makes controllers and views less cluttered with conditions (if ...)
By the way, creating a new action is still a REST logic if you make it a GET query. If your new action was about updating a post it would need be a PATCHquery

Problably you have the resource :posts inside your routes.rb. This is the place you have to add the new endpoint. In rails the route to the new action.
You have
resources :posts
You should have
resources :posts do
get 'trending', on: :collection
end
Now you can see all routes that rails generate to you line, index, show, create, update... and more one trending
/posts/trending
You can have a look here: Rails guides add restful

Related

Route to /post/new instead of /posts/new in rails?

This is related to a question I asked here: undefined method `posts_path' for #<#<Class:0x007fe3547d97d8>:0x007fe3546d58f0>
I was told to switch my controllers, view etc from "post" to "posts" which fixed the issue, however if I did want to use the URL /post/new, how would I do that without receiving the "undefined method `posts_path'" error I was before?
I don't understand why it's looking for "posts_path" when my controller, model and view are all called "post".
Add this before resources :posts line/block in routes.rb file,
get '/post/new', to: 'posts#new'
When you define routes using resources :posts, by default the route to the new action is /posts/new, So to override the same you need to define custom route like I did above. Also, to search the routes, Rails scans the routes.rb file from top to bottom, whatever matches first is taken. Therefore, to override the default behaviour, I asked you to define this custom route before the default routes.
Hope that helps!
I would suggest that you take a look at Rails Routing Guide.
In short:
Because the model Post describes only one record, it makes sence to call the model Post and not Posts.
With resources :posts within your routes.rb you define that you will have multiple Post objects and you want to expose all CRUD actions with a restfull interface through your controller. Your controller is named PostsController that too makes sence, because your controller provides CRUD actions for all Post objects not only one.
Furthor more rails generate some helpers for every defined route:
posts_(path|url) returns /posts=> shows multiple posts => plural helper name
new_post_(path|url) returns /posts/new => show one post for edit => singular helper name
edit_post_(path|url)(:id) returns /posts/:id/edit => edit one post => singular helper name
photo_(path|url)(:id) => show one post => singular helper name
The route name is always plural because you are always changing the resources. For instance add a new post to the posts resources.
You can also define a singleton resource via resource :geocoder in this case you say you only have one of this thing. For singletons helpers and routes are slightly different. But I saw it until now only rarely.

Rails controller and routes configuration for website

I have an application in RAILS, it is composed of a set of API, a basic website, and an admin dashboard.
For the API routing I have no problems, as they belong to a model and a controller and are compliant with the RAILS RESTful pattern (a controller for each model, and a method for each HTTP method).
What I'm not comfortable with is writing routes and controllers for website.
The main website is at / so the default route is root :to => "home#index"and I have
Routes for the website pages which look like
get "home/index"
get "map/index"
get "api/index"
get "cgu/index"
get "legal/index"
Which I think it is not good, as I have a controller per view and I need to define a get for each views.
Now for the dashboard I tried a different approach.
It is at /dashboard, the default route is match "dashboard" => "dashboard#index" and here is few pages as an examples
get "dashboard/index"
get "dashboard/users"
get "dashboard/users_stats"
get "dashboard/routes"
get "dashboard/routes_stats"
get "dashboard/charts"
get "dashboard/financial"
So for the dashboard I have a massive dashboard_controller, which contains a def method for each dashboard pages. IE:
#dashboard/users
def users
#users = User.all
respond_to do |format|
format.html {render :layout => 'dashboard'}
end
end
the controller of the dashboard is at /controller but for views and assets I have put it in /folder/dashboard/
Here is 2 questions:
What is the best way to build the home website and dashboard ? Should I have a controller per page or a global controller where I have a method per pages ? (Which I find very convenient, less code).
How should I organize my routes to avoid to set a get "something/something" for each page ?
Or it is normal with RAILS that I have a route defined for each of my page ? I'm fairly new.
EDIT:
To clarify, the dashboard is built around an existing application with API that follow RESTFul Rails pattern:
resources :users
resources :routes
But the dashboard is not tied to any existing resources, it only do stats about those resources.
If you have custom controller action names, then yes, you'll need to define every route. If you use Restful routes, then you can define them easily as
resources :users
which will automatically create routes for actions: index, show, edit, update, create and destroy.
This might be helpful: http://guides.rubyonrails.org/routing.html
For your dashboard, which probably is bringing together a lot of resources, so they'll probably be custom methods. I'd suggest focusing on building your app by individual resource. Then, once you've defined them all, build your dashboard.
I agree with everything that other people have said here. You should definitely try to be more RESTful and create more routes like this:
resources :users
However, there is usually a controller that is not RESTful (usually called pages or static) that serves pages such as Privacy, About Us, etc, etc. For those routes, I usually do this:
['api', 'privacy', 'us'].each do |p|
get p, :controller => 'pages', :action => p
end
check this guide out if you haven't;
I think you haven't embraced the MVC concept of Rails.
For example, say you have "users". You should have users_controller.rb, users.rb (model), /views/users (view) under the app directory.
users_controller contains the index, show, create, etc default actions and your custom actions like stats
user.rb contains instance/static/helper methods
/views/users/ contains templates that corresponds to actions in the controller.

Simple Pagination in json reponse using Rails Rest routes

I am building a simple service(API) that will return json reponse of users objects using Ruby on Rails. Since it returns an array of objects i want to add pagination functionality (:next_page , :prev) as follows.
"nextpage":www.domain.com/users?page=2&per_page=5
Currently i have a restful route that return url www.domain.com/users.How do i reconstruct the url in my index action and how do i make use of limit and offset in this case to make the pagination to work? I want to return 5 items in every page.Sorry i know it is a newbie question, it is my first time creating a service, if somebody could show me how to do the nextpage i would appreciate and i will implement prev by myself. I have tried looking for tutorials to do this but all of them they don't show the logic in the action/method.Thank you
Note: I dont want to use will_paginate, i just need to build simple paginate
Just append your page and page_per to your links to whatever action. Assume you have standard restful resource based routing, i.e.
config/routes.rb:
resources :users
so you'd have the following route generated
GET '/users' => 'users#index'
if you send this url to your rails app:
'/users?page=2&per_page=5'
You will still get the index action, but you'll have two extra parameters , you can do this:
controllers/users_controller.rb
def index
if params[:page]
# do your custom pagination
else
#users = User.all
end
end

Rails 3: Using a POST form on the index page?

In my Rails 3.0 app I have a series of very large search forms on my resource:index page, requiring the use of POST instead of GET.
Currently, the app is routing the POST request to resource#create, when I want it to route to resource#index. I realize this is the RESTful route, but need to override it. How can I do that, while also preserving the ability to create a new record of that resource?
Thanks much.
You're better off having a "search" action that is post-only - and then renders the index template eg:
class MyController < ...
def search
#my_things = MyThing.find_with_search_params(params[:search])
render :action => :index
end
end
You can use index, just add this in Rails 3 routes:
resources :my_things do
post :index
end
So you want your "create" action end-point in the controller to do 2 things - Respond to search and do the create also? Bad idea, but the solution might be as simple as using an "if" condition in the create action to do one or the other. If its not a satisfactory answer, feel free to clarify your question a bit more.

How to pass a param from one view to another in Ruby on Rails, using POST

I feel like this should be an easy thing to figure out, but I'm stumped.
I have a value in a Project's instance variable called ID. I want to pass that value to a new Photos page to associate each photo that is created with that specific project, but I don't want the Project's ID to show up in the visible query string.
I've tried using link_to and button_to, but (I suspect) since I'm using "resources :photos" in my routes, all of the requests that come to photo#new are being interpreted as GET instead of POST.
Helllllllllllllllp!
Thanks to anyone that can give me some insight, I'v been killing myself over this for the past hour or two already.
--Mark
The usual way to do this in Rails is to create a route that matches urls like this: /projects/4/photos/new. Doing something else is up to you, but Rails makes it really easy to do stuff like this. See more on routes in Rails 3.
Your entry in routes.rb should look something like this:
resources :projects do
resources :photos
end
Then in app/controllers/photos_controller.rb you'd have this for the "New Photo" form page:
def new
#project = Project.find_by_id(params[:project_id])
end
and this for the action that the form in app/views/photos/new.html.erb submits to:
def create
#project = Project.find_by_id(params[:project_id])
#photo = #project.photos.create(params[:photo])
end
Of course you'll want to have error handling and validation in here, but this is the gist of it. And remember, use GET for idempotent (non state-changing) actions (e.g. GET /projects/4/photos), POST for creating a new thing (e.g. POST /projects/4/photos), and PUT for updating an existing thing (e.g. PUT /projects/4/photos/8).

Resources