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

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.

Related

Handle non-REST logic in Rails API

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

Rails controller action duplication

I have a controller show action which does some stuff and renders a view but due to some custom routing, I need a totally different action in a totally different controller to perform the same stuff and render the same view.
I don't really wish to duplicate the code. Is there somewhere I can put it and call it from both locations?
Edit:
I basically need to run the following from Collection#Show AND also from Gallery#SplitUrl:
#collection = Collection.find_by_id(params[:id])
if #collection.has_children?
#collections = #collection.children
else
redirect_to [#collection, :albums]
end
I cannot just redirect_to Collection#Show at the end of Gallery#SplitUrl as the redirect causes my custom URL's to be lost as it's a new request.
You could put the view content into a partial (/app/views/home/_example.html.erb) and then use the render "example" command in the HomeController.
The "some stuff" code you talk about could be put into a helper file /app/helpers/... to save you from duplicating code in the two separate controllers although it could depend on what the code is trying to do in the first place.
http://guides.rubyonrails.org/layouts_and_rendering.html
This might provide more information on the subject in general.
I think the simplest approach would be to add a route definition for your new url and map that to your existing controller's action.
Something like follows:
# config/routes.rb
# Existing resource
resources :foos
# The resource in question
resources :bars do
get :show, controller: 'foos', action: 'show', as: bar_foo_common_show
end
Note that this will give you /bar/:id, where id represents a bar resource. So in your foo#show action, your finder needs to be executed on appropriate class. This is when a few lines of hacky codes get added, for e.g. checking for request referer.
I think moving the common code to a function in possibly application_controller and calling that function from both show action would be cleaner approach, but based on my understanding you already have a similar scenario except for the common code in application_controller, and would like to try out a different approach!

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

How to setup routes when the controller only has edit and update?

I can't seem to figure out how to get my routes setup properly.
In my app, I have a view that lets site owners update their address information. The new and create actions are part of the signup process and are located in the signups_controller. The edit and update actions are in the settings_controller.
When the user goes into the settings area, he/she sees only the edit form. When filled out, the user is then returned to the same form with a flash message, or error message. Here is what the controller looks like:
class SettingsController < ApplicationController
def edit
#account = current_account
#account.companies.first
#account.companies.first.addresses.first
#account.companies.first.phones.first
end
def update
#account = current_account
if #account.update_attributes(params[:account])
redirect_to edit_setting_path
flash[:notice] = "Success!"
else
render :edit
end
end
end
In my routes, I simply have:
resources :settings
The link to this area of the site is a basic RESTful named linke, with the parameter options:
edit_setting_path(:id => current_account.id)
When the user arrives to this page, they see the following URL:
http://domainname.com/settings/1/edit
When they submit the form and get errors, the URL changes to:
http://domainname.com/settings/1
Why is the URL changing -- I'd rather it not? Is there a way to make it stay the same as the initial edit view? I've tried doing a redirect on a failed update, but then I don't get the error messages.
Any ideas?
To answer your "why" question: The URL is changing because it's reflecting the URL of the failed request - which in this case is a PUT request to that URL (/settings/1). You've submitted the form and the submission of that form (correctly) points to that URL. This is a result of the RESTful routes that the helper gives you. Since the logic in your action, falls through to the render :action, there is no redirect and the form simply re-renders on the page using the same data available in this action (which is why you can see the errors).
If you want to redirect back to the edit page, yes, you will lose the errors that have been set in the #account instance variable since the redirect will reset (re-query for) the account.
You could add a route that matches a PUT to /settings/1/edit and point it to your update action and change your form etc. In short, I wouldn't recommend this, but it should work.
completely untested but attemptable:
routes.rb
put "/settings/:id/edit", :to=>"settings#update", :as=>"update_setting"
resources :settings, :except=>:update
your form would also have to submit to the update_setting_path (which also means it's not reusable for a new object... ew)
First you should read up on The Rails Guides for Routing. They will help a lot to understand why its working like that.
Secondly, to accomplish what you are trying to do, you will need to add manual routes via the match call. You'll need something like this.
match '/settings/:id/edit' => "settings#edit"

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