validation triggered when click back button Rails - ruby-on-rails

A user submits a form and all of the information is valid.
I just discoverd that when they click the back button the following validation gets triggered.
validates_format_of :email,
:with => /^[A-Z0-9._%-]+#([A-Z0-9-]+\.)+[A-Z]{2,4}$/i,
:message => "should be something like youremail#something.com"
Why is this happening and how can I stop it from being triggered?

If the users re-submit the form - which the browser warns them about and which always happens when you go BACK to a POST-request in a browser - there is nothing you can do. It's how browsers work. Besides, just for clarity of language, it's not the "going back" that triggers a validation. It's the operation on the model you specified the validation for, probably before_save. So there is a model.save operation going on, and that's likely triggered (don't know YOUR code, you could write #somemodel.save anywhere in your controller, but it's standard) by that repeated POST operation. Which comes from the user, in the end. one should not go "back" to a form that was posted, it's the same all over the Internet regardless of backend technology.

Related

Rails/Capybara- Test that form does not submit

In my Rails app, I used the following jQuery to disable the ability to submit a form by pressing 'enter' in an input field:
$('input').on('keydown', function(e) {
if(e.which == '13') {
return false;
}
})
I wanted to test this with Capybara, but can't figure out how. For example:
# the form being tested creates a new Shift object:
test "enter does not submit form" do
shift_count = Shift.count
# fill in form, etc...
page.driver.browser.execute_script "var e = jQuery.Event('keydown'); e.which = 13; $('#shift_employee').trigger( e );"
assert Shift.count == shift_count
end
This test passes even when the jQuery function is commented out, presumably because it calculates Shift.count before ActiveRecord has had time to save the new the record to the database.
I also tried having the jQuery function do a custom $.get() request, to which the controller responds with simple text, thinking that this would build in enough time for all server-side work to be completed. But this still doesn't work, and I think it's because, no matter how I set up the jQuery function, the submission of the form and saving of the new record will always occur after any AJAX stuff I build in.
Any ideas on how I can reliably test that the form was not submitted?
So the form submission is done via AJAX? This is a common problem that testers run in to with Capybara. What you need to do is wait for the AJAX to complete.
There are two ways to do this. One is very specific and is the preferred method, the other is generic and should only be used if the first method can't be done.
The first way is to wait for content to change on the DOM. In your case nothing might change, but if, for example, you had a warning come up saying "You cannot press Enter, you must click the Save button!" then you could wait for that to come up by doing something like:
page.driver.browser ... # your code
page.should have_css(".info", :text => "You cannot ...etc...")
Of course you don't have to use the :text option though.
The other way is to make a helper method, like wait_for_ajax, that does a generic wait until AJAX is complete.
def wait_for_ajax
start = Time.now
while true
break if (page.evaluate_script('$.active') == 0)
if Time.now > start + Capybara.default_wait_time.seconds
fail "AJAX did not register as complete after #{Capybara.default_wait_time} seconds!"
end
sleep 0.1
end
end
This is based off the old way that people used to check for AJAX, but this new script is preferred for Capybara 2.0.
Then your step would do:
page.driver.etc
wait_for_ajax
That wait_for_ajax method can go in to any file Cucumber will load.
I got it to work by having Capybara revisit the page, and then checking Shift.count. The test now fails when the jQuery function is commented out, and passes when the function is uncommented. I'd still like to know if there's a better way, though.

How to ensure presence validation on a form before performing operations

I've got a form that creates a device. I want to ensure that the cabinet I'm passing to it exists. In the device model, I've got:
validates_presence_of :cabinet_id, :message => 'Please enter full cabinet name beginning with data center code'
The controller does an operation with the cabinet to determine if there is an empty spot for it. This makes the app crash if the cabinet does not exist, because it tries to use attributes of the cabinet.
How can I get the form to alert the user that the cabinet doesn't exist before it tries to perform the operation?
Without seeing the rest of the model code it's kinda hard, but I suggest wrapping the 'operation with the cabinet' in a conditional, eg. if cabinet_id.present? (do stuff) end
If the operation is a custom validation, make it conditional, eg. validate :space_in_cabinet, if: :cabinet_id.

Rails: Displaying state_machine validation errors

I'm using state_machine for an online purchasing process in Ruby on Rails. One of the states consists of the user filling in a form with billing information. This form is required, so I'm validating it when transitioning to the next state like so:
state :confirm do
validates_presence_of :name, :email
end
Where the form is on state :info and the next one is :confirm.
This works fine: If any fields are missing state_machine will not transition to the next state.
However I can't find information on how to display an error notification when the validation returns false. I want to display a message when this validation fails, and redirects to the form state. Thanks in advance for your help!
(I assume you have solved this long ago, but...)
I think this answer has nothing to do with state_machine. This is the problem:
I want to display a message when this validation fails, and redirects to the form state.
If you redirect, the object is reloaded and all the unsaved attributes are lost, as are any validation errors.
Standard way to handle failed validation is to (re-)render the edit view, not redirect to the edit action. This retains the unsaved attributes and validation errors.

Rails - User Pressing 'Back' after object creation, Creating Duplicates

I'm having a problem where when a user fills out my evaluation form, click "Create", then click the browser's back button, make some edits, and click "Create" again, it's creating duplicate Evaluations.
What is the best way to prevent something like this happening.
Only ONE evaluation should exist for each survey_criterion on creation. I don't want the user to lose any data they enter after hitting the back button, filling out the form with new stuff, and clicking "Create" again.
UPDATE
routes.rb
resources :survey_criteria do
resources :groups do
resources :evaluations
end
end
survey_criterion.rb
has_many :evaluations
evaluation.rb
belongs_to :survey_criterion
belongs_to :group
There are more complicated associations, but the answer I'm looking for is more, "how does one handle it when users press the 'Back' button, modify the form, then click Create again".
I want it to update the one that was automatically created I think in this instance, and not throw an error to the user. I know I could add a validation that would error out, but I want this to be invisible to the user I think.
Thoughts?
The simplest solution, would be to change the create action, which should work like this pseudocode:
def create
# ...
if evaluation_exists?
update_evaluation(params[:evaluation])
else
create_evaluation(params[:evaluation])
end
# ...
end
As for Your question "how does one handle it when users press the 'Back' button, modify the form, then click Create again", then I use some random token (a short string) placed as a hidden field in the form.
When the create-request comes, I check whether this token is already stored in the session. If it is not, then I create the object, and add that token to the list of used ones. If the token is already present in the session, I know that user has just resubmitted the form, and I can act accordingly. Usually I ask him whether another object should be created. In the session I store usually not more that 3-5 tokens.
It looks like this (yes, that's just an illustration):
def create
token = params[:token]
session[:tokens] ||= []
if session[:tokens].include? token
render_the_form_again( "You have already created the object. Want another?" )
else
create_the_object
session[:tokens] << token
end
# ...
end
In your Evaluation model, add this line :
validates_uniqueness_of :survey_criterion_id
This is assuming that SurveyCriterion holds the foreign key that associates with your Evaluation.
You can also do 2 things :
Prevent the browser cache.
Disable the Create button with :disable_with => "Processing" option.
It is discussed here too: https://stackoverflow.com/a/12112007/553371
A less elegant way but more generic way to do this is to use history.pushState. On the page after create:
$(function(){
if(history.pushState){
window.onpopstate = function(event){
if(window.history.state && window.history.state.previousStep){
window.location = window.history.state.previousStep;
}
}
window.history.replaceState({ previousStep: '#{edit_resource_url(resource)}'}, document.title, window.location);
window.history.pushState({}, document.title, window.location);
}
})
This example uses HTML5's History API. A similar thing can be done with fallback using the history.js project

RAILS: I've got an action that calls itself, when the form of the action's view is being sent, now I want to rout out of that action

this time it's about Ruby On Rails.
I have a form in the view "progress.html.erb" and an action called "progress". When the form is sent, the action "progress" is called again.
This must be like that, because I'm asking the user several questions by an automatic system.
Then, when the questioning is finished and the user is done with one seminar of questions, I want to route out of "progress" to "finishing" (where session data is erased and a "happy wishes"-site is shown).
But this won't work because of that routing error. There must be a way, even just rendering won't work :(
The complete system is the following:
I have a box with different panels.
In these panels are cards with questions.
All card get asked to the user.
When all panels evaluate to empty, the user is done.
Please help me!
Yours,
Joern.
When you want to redirect, and then stop rendering, you need to not reach the end of control of the function. The way I do this (for redirecting when someone is somewhere they are not supposed to be, usually) is:
redirect_to(:finishing) and return
optionally, you can do this conditionally:
redirect_to(:finishing) and return if #survey.completed?
I'm not sure what routing error you're getting. Your question/problem isn't very clear. However, it sounds like you want to redirect_to(:finishing) at the end of #progress when the user is "done".

Resources