How to setup routes when the controller only has edit and update? - ruby-on-rails

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"

Related

Overriden route id field is being reset

In one of my models i have a field called token, that is created with before_create. It is never modified again.
In routes.rb i am using it instead of id, like: resources :model, param: :token
So for example the edit route is now model/:token/edit.
In my actions i am doing find_by(:token, params[:token]).
I have a partial form created with simple_form which is being loaded in the new and edit actions.
This works fine, the routes are generated properly, all showing :token: instead of :id.
The show action works fine. In the show page i have a link_to which links to the edit view. This works fine also.
Both are using :token in the route, the edit view loads the models fields, everything is normal.
However when you look at the source code for the edit view, it shows the action as /model/<id>, instead of /model/<token>. For example /model/5.
When you submit the edit form, it tries to go to /model/5/.
In addition, when i checked the params being sent, it shows token set to the value of id. So somehow, the token field has been reset.
So
1) The token field is somehow being reset to the value of id. I have no idea how this is happening.
2) simple_form seems to be generating the action based off of id instead of token. However i have realized that since token is being reset, it could be that simple_form isnt doing anything wrong and is using the value of token.
The only fix i could come up with, was setting the url field in simple_form_for manually, but if i do that, it then breaks the new action.
For simple_form I'm just doing:
<%= simple_form_for #model do |f| %>
In routes.rb I'm doing:
resources :model, param: :token
In the controller, the edit action is:
#model = Model.find_by(token: params[:token])
The update action is:
#model = Model.find_by(token: params[:token])
if #mode.update model_params
redirect_to model_path #model.token
else
render 'edit'
end
Nothing fancy in the least.
The simple_form uses the default behavior of Rails when generating the route string for you. Since the config/routes.rb just provide the named pattern matching for the Outside In URL, so it is not too much meaning for the application from inside. For ex, with a route like this
model/:token/edit
just means any string between model/ and /edit will be assigned to params[:token]. It doesn't mean that string has to be the value of YourModel#token. Of course, you can assign that pattern to params[:foobar] by the same way without breaking anything model/:foobar/edit
For fully replace id key by token key, you have to override the method YourModel#to_param
# app/models/your_model.rb
class YourModel < ActiveRecord::Base
def to_param
token
end
end
So that, the ActiveSupport will know to use token as the value when generating routing pattern from inside out.
You can read more about it at the Rails's Guides

Rails 3.2 - How to merge params without displaying everything in the URL?

In my app I have a /thanks page that users were originally redirected to when completing a certain action. Now I want to redirect them to this page after multiple kinds of events and render different partials based on what the event was. So I added this to the /thanks page:
- case #event
- when "reservation"
= render 'thanks_job_created'
- when "charge"
= render 'thanks_job_charged'
Then, in the the JobsController#create action, I changed redirect_to thanks_jobs_path to this:
redirect_to thanks_jobs_path(params.merge(event: "reservation"))
...and added #event = params[:event] to JobsController#thanks.
The behavior works as intended, but I've found that using params.merge this way now displays every paramter in the URL, including authenticity token of the #create form, all of the Job attributes, etc. Before the URL looked correct (/jobs/thanks) because the only params were action and controller which are already indicated in the URL.
Is there a way for me to use params.merge without displaying all of that info in the URL?
If you just want to pass the event as a parameter, you don't need to use params.merge at all.
redirect_to thanks_jobs_path(event: "reservation")
will give you the path /jobs/thanks?event=reservation.
You could simply use except:
params.merge.except(:auth_token)
In your situation:
redirect_to thanks_jobs_path(params.merge(event: "reservation").except(:auth_token))

Stay on the same page after edit

I'm trying to follow the RailsTutorial guide, but by doing my own application instead. I have trouble with section 7, with the forms.
My controller :
def update
d = Deck.find(params[:id])
d.title = params[:deck][:title]
d.slug = params[:deck][:slug]
d.category = params[:deck][:category]
if d.save
redirect_to deck_path(d), notice: "Deck saved successfully"
else
render :edit
end
end
I know it's very, very far from good code, but I will refactor it later (if you have a suggestion I'm all ears, but I use Rails 3, so I guess the strong parameters of Rails 4 are out).
The problem is when the d.save does not work (due to validation), with the render :edit.
Right now, when I enter invalid data, it tries to redirect to the show action, and crashes because it does not have any data to display.
If I add #deck = d above the render, it works, but the url still is the show action.
If my validation fail, how can I stay on the same URL and display my error messages ? Is the "change URL but render the same page" behavior accepted as valid ?
Thanks !
If you're interested in looking at the rest of the code, it's here : https://github.com/cosmo0/TeachMTG/tree/remodel-decks
Actually when it fails your url is not the 'show' it is the 'update' url.
Your code works.
When you submit your form, your browser sends a POST request to controller#update.
When the update fails, you tell your update action to "render :edit". What it does is it renders the :edit action inside the :update route.
The update route uses the same url as your show action :
you can check this when running 'rake routes', the only difference is that the method is POST for 'update' vs GET for 'show'
That's why you think it is the show url in the browser but actually everything works: you are on the update action that renders :edit.
(Telling your update action to 'render :edit' doesn't mean you are redirected to :edit back from :update)
Is that clear enough ?
I believe that you're looking for respond_with method.
I think you are misunderstanding what is happening in the restful world. When you do an update the URL is changing because your form is performing a HTTP POST.
If d.save works it redirects to your deck_path with d as the object. If it fails it does not change the URL in the browser, but renders the same page as the edit action.
I'm guessing in the call for the edit action you have something like:
#deck = Deck.find(id)
Your render is failing because you don't have a #deck variable assigned in your update. So you can either change all of your instances of d to #deck or use your solution of setting #deck = d.

Rails Controller Action Doesn't Respond to Changes

I have a subscriptions controller that at the end redirects to :root and a line on my routes file that points a url to that action. At first I was testing the action without any trouble. But yesterday I tried to change something and the server didn't respond to the changes, no matter what I put in the action it always ignores everything and redirects to :root.
This is how my action looks like:
def fail
test = Subscription.new
p test
binding.pry
redirect_to :account
end
This is my routes line:
match 'subscriptions/failed' => 'subscriptions#fail'
And whenever I try to go to subscriptions/failed it ignores the print instruction, the binding instruction and redirects to :root instead of :account. If I comment everything on the action it will still do the same, the only different reaction it is when I delete the action. Probably obvious but I can't see it.
From your problem description, I would go and check if there is a before_filter or around_filter for fail action that redirects to :root. They would exactly do what you describe if the action is called and wont do anything and wont do anything if the action is not called.
May be you can post more code for the related controllers, just to be sure.

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