Url missing action after model failure - ruby-on-rails-3

I have a profile controller action UPDATE which updates a user's account information. If the model is deemed invalid, I want to render my EDIT action like this:
if #user.valid?
#update
else
render 'edit'
end
The URL for the EDIT action is /my_profile/edit. However, when the render 'edit' code is processed upon failure, even though the correct view is displayed with appropriate errors, the URL that is loaded is /my_profile.
When I'm editing a profile on /my_profile/edit, how can I get the URL /my_profile/edit to be loaded when I call the EDIT action upon model failure?
Here are the current, applicable routes:
get 'my_profile' => 'my_profile#show'
get 'my_profile/edit' => 'my_profile#edit', as: 'edit_my_profile'
put 'my_profile' => 'my_profile#update'

Info: I'm also a beginner!
I thought "render" means, that only the view 'edit' will be rendered.
If you wanted a new request (which changes your also your url and goes in your edit action in the controller) you need an
redirect_to 'edit'
or am I on the wrong way?

Related

Can I use Rails' :notice to a non-rails page (without using AJAX)?

I have a form that I give to my customers to put on THEIR website. The form allows one of THEIR customers to request an appointment through my scheduling software.
My scheduling software has a controller action that creates the customer record and saves the work request to MY customer's environment (inside my software).
When the record is saved, I would like to pop and alert or do a flash[:success]-esque notice to THEIR customer that the requested was successfully submitted.
I DO NOT WANT TO USE AJAX FOR THIS.
Is there a way to put a on MY customer's page and have the Rails redirect_to :back, :notice => "Request submitted successfully" function properly?
I can't give my customer a form.html.erb file or snippet. It needs to be dumb, pure, basic HTML that will work under any webpage. Lot's of restrictions for this.
I understand how I might do this with AJAX - I want to know if there is a way to do it without AJAX.
I did NOT find any solution to using :notice the way that I want, but one work-around I did find was to put a hidden field in my form, manually set the callback URL, and pass that as the redirect like so:
<input type="hidden" name="redirect" value="CALLBACK URL HERE">
In the controller after I submit my data I do:
redirect_to params[:redirect]
Which works a treat because you can put a nice landing page instead of just having a JavaScript alert saying "Success" or whatever

rails render form after error in other controller

I have two models in the following releationship: Client has_many products, and Product belong_to client. In the client show view I present a form to create new products, that automatically belong to the current client. The show method in the client controller
def show
#client = Client.find(params[:id])
#products = #client.products.paginate(page: params[:page])
#product = #client.products.new
#product.client_id = #client.id
end
and the show view renders a partial
<h1>New Product:</h1>
<%= render 'shared/product_form' %>
That works, products are correctly created.
When an validation error occurs I set a flash in the product create method and redirect_to the client show page. There I loose the data that has been filled in correctly. I tried save the #product instance variable, which has all the data (including the wrong fields) doing
render client_path(client)
from the product controller, but that produces an error
Missing template /clients/17
with the address being
http://localhost:3000/products
Am I calling this wrong? I know that render ususally renders action of the same controller. Can I somehow render Client::show from the product controller? Is there another way to save the data the user has typed in?
If a validation error occurs you should redirect back to the page that generated the validation error. Ie: if the user is at products/new when they submit the form, then your products#create action should end with render :new to present the products form again.
If your products#create action is receiving the form from clients#show, then you do want to render clients#show with validation errors. In that case, all the information that was completed in the form will be available at params[:product], just like it is coming in to products#create.
You might want to take a look at another answer I wrote recently to understand the flow between controllers.
Specifically, the misunderstanding in your case is as follows:
When you have a validation error the record will not save, so you cannot "go back to that data" because your app has not kept it anywhere. The only copy of the data that was submitted is in the request.
If you REDIRECT you are not forwarding a request, you are responding to the initial POST request (which includes all the form information as params[:product]) by making a new request to a different url. This is why you want to use RENDER.
However, if you try to render clients_path(client), what happens is Ruby will first evaluate clients_path(client) to the string clients/(client.id), or in the example you gave, clients/17.
Then render tries to call render 'clients/17', which you don't have a template for. It's looking for a file called clients/17.html.erb. That's why you get the error you're getting.
So, again, to sum up - your products#create action receives the information that was sent from the form as params[:products]. That information is not available outside of this controller action. So, if there is a validation error, instead of creating the product, this controller action should render the same page that the user came from originally (normally products/new) so that they can see the form they just had (with the information filled back into it if you're using a form builder) and also see the error that prevented saving.
I hope that makes sense, feel free to ask follow-up questions.
Yes, you were calling it wrong.
There are three problems:
render should render a template name, say client/new, shared/form etc. The argument could not be a path or variable. The variable is passed by controller to view, nothing to do with render.
You should not use render for a saving fail. Even if you use a template name as #1 mentioned, you'll end up with a wrong URL like products/create with the client page. That's not acceptable.
My suggestion is to always use redirect for saving fail.
Minor problem. In controller, if you've used #product = #client.products.new, the #product object will have every attributes empty but with a valid client id. So you don't need to assign client id again by #product.client_id = #client.id. But this doesn't hurt the result.
Andrew's great answer plus this:
Where to render comments controller in Rails on model validations failure?
makes the solution more clear.
Your specific example: Just watch out that your create action in the ProductsController has all instance variables it needs to render 'clients/show' from it.

Yii CAPTCHA url is broken

I want to create an AJAX-registration form on my Yii-project. So on every page I have a login-button, which shows a popup if the user isn't authorized. In that popup he can see registration form with email field, password field, and CAPTCHA (default Yii captcha).
So, my model for user's registration is User. There is a validation rule:
array('code', 'captcha', 'allowEmpty'=>!Yii::app()->user->isGuest),
My controller, where all user's actions are, is User (url: site.url/user/) and I added there captcha action:
'captcha'=>array(
'class'=>'CCaptchaAction',
'backColor'=>0xFFFFFF,
'foreColor'=>0x000000,
),
In my next code:
$this->widget('CCaptcha', array(
'clickableImage' => true,
'showRefreshButton' => false,
))
And here is a problem:( Being inside a popup, which can be shown on every page (index page, for example, which belongs Main controller) captcha wants to load an image from that controller (from /main/captcha, not /user/captcha).
Adding captcha in every controller is bad idea, so I added
'captchaAction' => '/user/captcha',
But after that captcha wants to load from an url
http://site.loc/user/captcha/v/4fe99aca1bbed
User-friendly url's crashed captcha and I can't avoid it (as I think, not being a yii-expert, it's because of common path-config).
Is there an easy solution to solve this? Should I repair URL's or re-configure CAPTCHA?
So, while I was waiting for help, I solved the problem by myself :)
In my project i separated my controllers into the next hierarchy:
/frontend
/backend
All controllers for common users are (of course) in Frontend section, including UserController and MainController.
Yes, I wrote some configs to hide /frontend/ in URL's like this:
'<c:\w+>/<a:\w+>' => 'frontend/<c>/<a>',
And i was sure, that string in my CAPTCHA config ('captchaAction' => '/user/captcha') knows where to go (and it was almost so!), but NO!
I should write FULL PATH to my controller and action like below:
'captchaAction' => '/frontend/main/captcha',
As I said in question, I placed my CAPTCHA action in UserController, but now you can see it is located in MainController (i deleted it from UserController)
And I got an error:
CCaptchaValidator.action "captcha" is invalid. Unable to find such an action in the current controller.
So, to solve this, I just had to use a property captchaAction of CCaptchaValidator in my User MODEL! Code:
array('code', 'captcha', 'captchaAction'=>'/frontend/main/captcha', 'allowEmpty'=>!Yii::app()->user->isGuest)
So, now my AJAX Captcha validation works correctly where I want.
Good luck with Yii's default CAPTCHA, dear community!

Looking for a better way to redirect

I've got a user page, where user can change his settings (users#edit). When user changes his settings and submits. The page is redirected back to users#edit.
I've also got an events page with a scroll down div (triggered by JS, click event) that lets users edit their settings directly (specifically location).
On the event page. When user edits his settings and submits. It gets redirected to the user settings page (users#edit) instead of back to the events page.
How should I handle this?
I've got two approaches:
Have a "def update_for_user" in the events controller, which the user form in the events page points to.
Keep it DRY and pass a params :redirect into the form. Then in the users#update, check if I need to redirect depending on param :redirect?
Any other approach I can take? Your thoughts?
Your code in the update method is probably doing this after a successful edit:
redirect_to :action => 'edit'
But you could change it to this:
redirect_to :back
Which will redirect to the referring page, as passed by the browser in the HTTP_REFERER page, whether it's the event page or the users edit page.

Rails: Render actions with a specific path

In my Rails3 application I add a specific match for edit my Post model as:
match '/edit' => 'posts#edit', :as => 'post_edit'
So, I overwrite the default path of the edit action. - Now if any errors occurs in the update action, it will render the edit action with settting the URL path with the default edit path /posts/1.
How can I overwrite that to render the edit action with setting the URL path as /edit instead of posts/1.
You are actually seeing the URL for "update", the default path for edit would be /posts/1/edit. I don't think you will be able to change what the URL displays using render :action. An alternate, although somewhat sloppy, method would be to redirect and save the #post object in the session or flash. If you do not save the #post object, you will lose the error messages from the update.
if #post.update_attributes(params[:post])
#business as usual
else
session[:post] = #post
redirect_to post_edit(#post)
end
Note that it is bad practice to save the whole object in the session (especially large objects), so you may instead want to only send the error message string with the flash and render that in the view. These are both rather hackish methods, but I don't really see an elegant way to do this.
Out of curiosity, why do you want to change the default URL? Is it necessary?