Sorcery redirects wrong - ruby-on-rails

User wants to sign up at domain.com/users/new, however the user accidentally presses "enter" and all fields are filled in blank.
Now comes the problem. I have two options. If I remove...
...else
redirect_to new_user_path
end
the user will automatically be redirected to domain.com/users (after filling the fields in blank) and where the sign up field errors will nicely be displayed above the form.
However if I add that code above instead of removing it, the user will be redirected to the same link - that means /users/new and unfortunately the user will not have his form errors displayed nicely, they will actually not be displayed at all.
How come that the errors show up at domain.com/users and not at domain.com/users/new?

Related

Capybara: Session lost when adding a setTimeout on form.submit()

Rails v4.2.7
I have a rails form (not remote) with a submit button with this event attached for both preventing a double submission, and showing a better feedback for the user (basically what data-disable-with would do for a remote form):
$('#my-submit-button').on "click", ->
this.disabled = "disabled"
this.value = "Loading..."
$(this).removeClass("button-active-class")
$(this).addClass("button-busy-class")
myForm = this.form
setTimeout ->
myForm.submit()
, 500
The reason for the setTimeout is that if I instantly submit the form, for some reason in Safari the style changes are not applied.
This works great.
however, I have a Capybara test (which uses headless_chrome) like this:
sign_in user
visit my_page_path
# fill in form
find("#my-submit-button").click
expect(page).to have_content "some content"
For some reason I noticed the current_user was being set to nil after the form was submitted, so I added a log for cookies as a before_filter in my ApplicationController:
def my_before_filter
p cookies.first
end
And the output was effectively showing me two different cookies between the visit, and the request triggered by the form submission.
So I removed the setTimeout in order to submit the form instantly:
$('#my-submit-button').on "click", ->
# [...]
# myForm = this.form
# setTimeout ->
# myForm.submit()
# , 500
this.form.submit()
And now, even though the styling is not correctly applied after clicking on the button, the test passes and cookies are the same.
Any idea what's going on and how to fix it?
Note: This is a guess based on the the assumption your sign_in method actually visits the page, fills in the login info and submits the form. It your sign_in method is actually a shortcut via something like the devise warden test mode this won't be correct.
It sounds like your $('#my-submit-button') is also being applied to the submit button on the login form. By delaying that half a second (seems like a long time to delay) the driver no longer detects that the actions done in sign_in are triggering a page transition and therefore doesn't wait for it. This means the visit my_page_path gets executed before login has completed and the correct cookies have been returned. Your sign_in method should have a positive expectation at the end of it for something on the page that indicates logging in has completed along the lines of
expect(page).to have_text('You are logged in!')

Rails: remember user input in devise registration form after error

I'm using Rails 3.2.16 and devise 3.2.2.
I've added a (hidden) field account_level to my devise registration form. This is populated by a query string in the URL depending on what plan the user chooses.
If the user has an error with the registration form, such as the password do not match or a required field isn't filled in, the registration page then reloads with the errors present. However, the account_level field is now blank and the url does not have the query string on it for me to pull the data from it.
How can I have rails "remember" what was in that field after a page reload if a error occurs?
Bonus sanity check question: Is there a better way to pass which plan the user has choose when registering?
----------- Update -----------
I used a combination of the answers from Shamsul and aelor. I ended up grabbing the value of the query string and saving it in a cookie using javascript. If the page then reloads from a validation error, and the query string on the URL is no longer there, I look for the cookie that was set and grab the value from there to populate the hidden field.
I want to keep as much on the front end, which is why I opted for the JS solution. I also felt like cookies are a more tried and tested vs local storage.
use localStorage.
you can save your plan , when the user clicks it.
localStorage.setItem('plan','premium');
then you can check for the the item and fill up the hidden field.
if(localStorage.getItem('plan') != null){
$('#hiddeninput').val(localStorage.getItem('plan'));
}
and you can delete that plan whenever you like by using
localStorage.removeItem('plan');
In registrations_controller.rb of devise
Store account_level field in a cookies as
def new
if params[:account_level]
cookies[:account_level] = params[:account_level]
end
#other code
end
def create
if resource.save
unless cookies[:account_level].nil?
cookies.delete(:account_level)
#do other coding
end
end
end
In this way account_level field will not blank after reloaing the page.

Show a Dialog When Users Login for the First Time

I'm currently showing users a dialog if they're not following anyone within my app, but I only want to show this whenever it's the users first time logging in? Whenever a new users logs in for the first time, they're not following anyone, so their feed doesn't have any posts, so it shows the dialog. I'm achieving that using the following code:
<% if #posts.any? %>
I'm wanting to show a dialog on the users first login, and then it never appear again. I know Deivse has a sign_in_count option, but the dialog would stay there until a users logs out and logs back in.
If you add the :trackable module to your Devise setup, you'll get a last_sign_in_at and sign_in_count. Either of these should provide you with more than enough information to know when/if a user previously signed in. And posts.any? should probably go away.
For instance:
display_dialog unless current_user.last_sign_in_at.present?
or
display_dialog unless current_user.sign_in_count > 0
in controller
if #posts.blank? && user.sign_in_count == 1 # no posts and 1st time login
session[:display_dialog] = true
ens
in view
<%= display_dialog if session[:display_dialog] %>

Devise: how to store a particular location, not necessarily the last location?

I'm wondering if I'm asking too much of Devise. I'd like to allow a visitor to fill out a form and then but then be required to sign up when they press the "submit" button. That's the easy part. My problem is that once the user is redirected to the sign up form and they fill it out, I can't get them redirected back to the original form so that that form can be submitted (but now with the user ID).
I've played around with the "after_sign_in_path_for(resource)" method in the application controller, but my problem is that after signup, the request.referer isn't the original form, it's the signup form. I thought about using a before_filter :store_location callback, but then I realized that it'll store the most recent location, which is the url for the signup form.
How do I set up devise that original form as the correct location to render after sign up?
P.S. This question comes from a related one, located here.
Try this make 2 method in ApplicationController which saves
the current url to the session, and another that redirects to the stored
url (which should have a default in case it can't find it). Then, in
pages which i want to bookmark for returning to (you might not want
someone to get sent back to some pages), then save the url.
EG in application.rb
def store_location
session['saved_location'] = request.request_uri
end
def redirect_to_back_or_default(default)
if session['saved_location'].nil?
redirect_to default
else
redirect_to session['return-to']
session['saved_location'] = nil
end
end
Then, in a page that you want the user to be able to go back to:
Then, just call
redirect_to_back_or_default('enter your chosen default url here, depending on context')
in after_sign_in_path_for(resource_or_scope) to send someone back.

Create first, login or signup later

Using Rails 3.
In usual app, a form only appears if the user is logged in. Then in the controller, we will run another check on authentication before we save the new record.
However, I want to show the form to everyone regardless if he's logged in. When the visitor submits the form with attachment, etc., it will then check if the user is logged in. If he isn't, then redirect him to login/signup page, and when he's completed that, only then the new record is saved.
I understand this can be achieved with the create and save, but can someone elaborate more on how to achieve this in a clearer explanation?
User submits form
Controller sees user is not logged in
Controller persists submitted form to the database
Controller sets cookie with id of new object
Controller redirects to login
User logs in
Controller retrieves persisted form information from database
Controller assigns object to user
Run a periodic job to clean old unclaimed form data.
Well, in the view you'd show the form always without any kind of login check. The tricky part here is to save the data the user has sent before he's redirected to the login.
One solution would be to save the post data in a cookie and then redirect the user to the same page after the login, triggering the create again
On the controller you'd do exactly as you described:
class ThingController
def create
if user_logged_in?
if cookie[:stuff]
# create the stuff from cookie and remove it
else
# create the stuff
end
else
# redirect him to login with a callback to this same place
end
end
end

Resources