Rails: remember user input in devise registration form after error - ruby-on-rails

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.

Related

custom validation on django-allauth

I'm using dj-allauth on my django app. When people sign up to my django project/app, I want to check that the domain on their email matches mydomain.com. Is this possible? I think I can do it by changing the signup form. I've tried the following which doesn't seem to do anything when I submit the signup form. There aren't any errors. It isn't checking for the domain, it's prompting for the user's nickname and then appending the domain automatically.
forms.py
class VSBSignupForm(SignupForm):
def save(self, request):
# Ensure you call the parent classes save.
# .save() returns a User object.
user = super(VSBSignupForm, self).save(request)
user.email_address = user.username + "#mydomain.com"
user.save()
# You must return the original result.
return user
settings.py
ACCOUNT_FORMS = {'signup': 'myapp.forms.VSBSignupForm'}
I changed the template for the signup.html such that it asks for the nickname and password, but not the email address.
edit I tried putting a print statement in the form save method and it didn't print anything in the shell, which makes me think the form isn't being used.

Rails devise reset password fails if user record contains invalid data

I have an existing application in production where we have added new mandatory fields to the user record. As a result, the forgot password function is failing. I believe the failure occurs in the following method in recoverable.rb
# Update password saving the record and clearing token. Returns true if
# the passwords are valid and the record was saved, false otherwise.
def reset_password(new_password, new_password_confirmation)
if new_password.present?
self.password = new_password
self.password_confirmation = new_password_confirmation
save
else
errors.add(:password, :blank)
false
end
end
The new attributes cannot be generated mechanically. Only the user himself will know the correct values. Does anyone know of a way to get around this problem?
The only idea that I've come up with so far is as follows:
use a rake script to populate the new fields with values that I know would never occur in real life but would be accepted by the model validation rule
when the user logs in, detect if the user record contains invalid data and force them to do an update. In the user edit form, I would use some javascript to change the bogus values to blank.
You could use a custom validation context to make sure the validations on these added fields are only run when a user edits their profile, but skipped when (for example) Devise saves the user record.
In the User model, add an on:-clause to these validations
validates_presence_of :recently_added_mandatory_field, on: :user_updates_profile
And in the appropriate controller action, add this validation context when calling #valid? or #save:
# Existing code uses `#user.valid?` -> change to:
if #user.valid?(:user_updates_profile)
…
# Existing code uses `#user.save` -> change to:
if #user.save(context: :user_updates_profile)
…
See here for a full description of validation contexts: https://api.rubyonrails.org/classes/ActiveModel/Validations/ClassMethods.html#method-i-validates

Rails Form Helper - Always have the beginning value for the user

I'm creating a form with placeholders for the user in my rails app.
For one of the input fields, it's currently set up where the user needs to add "http://" every time and I don't trust users always doing this.
I'm trying to create a permanent "http://" and then they could fill out the rest of their url.
I've noticed when I click on the placeholder locally it will disappear but on heroku it needs to be actually deleted as well.
= f.form_group do
.input-group
input#scan-url.form-control type="text" placeholder="http://"
A better way will be to handle it on the back-end (either in the controller or model). I would do this:
#app/models/your_model.rb
Class Model < ActiveRecord::Base
before_save :set_url
private
def set_url
if (self.attribute[0,7] != "http://") && (!self.attribute.blank?)
self.attribute.prepend("http://")
end
end
This will prepend http:// to your submitted string before you save it into the db, allowing you to add http:// to every time you submit the attribute
It may not be a good practice but one way you could do what you want is to put a clause just above this point in your form...
if f.object.form_group.blank?
f.object.form_group = "http://"
end
This way if the url hasn't been set yet then the input will contain http:// already (as if the user had already entered it in)
p.s. your syntax might differ slightly as to how to reference the object, based on what f is, but I'm assuming f is a form_builder...

Can I stop Rails from storing invalid attributes on the model after update is called?

I've been developing on Rails for awhile and can't believe I haven't ran into this problem - maybe I am missing something simple?
My edit page displays information about the model being viewed. The model's to_s method returns the name attribute in this case, which is displayed in breadcrumbs and the page header.
I have a validation that the name cannot be blank and a simple update method:
def update
#model.update(permitted_params)
respond_with #model
end
The default ActionController responder will render my edit page automatically when #model is invalid, which it does. But #model still retains the invalid attributes so my breadcrumbs and page header are blank as they both display model.name which is "".
I could solve this by
def to_s
name.presence || name_was
end
But this application will be fairly large and most of my models will follow this same view pattern with the header containing a model attribute that could be invalid. I feel like using this pattern in to_s on all of my models will be frustrating to keep up with.
My current solution is to define this method in my custom responder, which reloads the #model if it is invalid:
class ApplicationResponder < ActionController::Responder
def initialize(*)
super
#resource.reload if has_errors?
end
end
This works but now any invalid request has an extra call to the database when the model is reloaded. Probably not a big deal, but still a code smell in my opinion.
Is there something I can do to stop Rails from keeping invalid attributes on #model after update? I am using Rails 4.1.0beta1 and Ruby 2.1 and have tried on Rails 4.0.0 as well.
Why not also provide some client-side validations to prevent users from submitting invalid details in the first place?
If you provided validations that give the user feedback before the page is re-rendered, you won't have the problem of ever having to work out how to display that invalid data (since it sounds like you are displaying data on the edit page itself).
I would recommend you check out Parsley.js or other similar JS based client-side validation.
The kind of behavior you are seeing, where they are still "stored" in the model object is a good thing in general because it allows you to automatically repopulate the input fields in a form with those incorrect values, so that a user can see what they entered wrong and change it.
If you are wanting to maintain certain 'correct' values statically on the page, like in a breadcrumb, I would copy those values off into their own variables and track them separately. For the breadcrumb on the application I work on, we actually store the link history in the browsers IndexedDB, and don't rely on what the current value of an object in the database is.
From my experience, most of the time the identifying attribute of an object, like it's name, isn't editable. If it is, I tend to make the name of my page something a little more general, like 'Edit Profile,' instead of 'Edit ', or 'Edit Organization' instead of 'Edit Name_of_Organization.'
If you really want to keep rendering the original name, I would just save that and track it through hidden HTML inputs. What you're doing with the to_s method isn't foolproof: if a user enters an invalid name that's not blank, you're going to render that invalid name.

How to store where a new user was referred? Using Rails + Devise

I have a rails app that uses devise. I'm curious to know, is it possible in the User table to somehow track where a new user came from, the HTTP referrer?
I'd like to know which came from Facebook, Twitter, LinkedIn, Google+ in order to track a viral loop.
Any ideas? Seen anyone do this? Possible? Where should this live in the rails app? Still very new. Thanks
It could be done like this. May require some tweaking and fixing but You'll get an idea
Make before filter for Application controller, you will call it for any action
def landing_filter
if from_other_site(request.referrer) and !session[:referer].blank?
session[:referer] = request.referrer #you don't want to delete first entrance
end
end
from_other_site should be the method which will check domain name in referrer url, if it match your then return false, otherwise true
in devise/registration/new.erb.html view add in form hidden field
<%= f.hidden_field :referrer, session[:referrer] %>
and don't forget to add migration with new database field for user
Save referer somewhere and after creating a user copy information to user table. Using session to save referer works but permanent cookies are better. Cookies can persist the information even when user closes browser and comes again in the next day.
# so basically in ApplicationContreller using before_filter
def referer_before_filter
if cookies[:referer].blank?
cookies.permanent[:referer] = request.env["HTTP_REFERER"] || 'none'
end
end
# and in signup action somewhere else saving that information
#user.referer = cookies[:referer] # or maybe to some other table
Instead of modifying every action you can also use rails sweepers/observers to handle automatic saving every time an object is created.
A good gem to automatically save referer and other needed information is
https://github.com/holli/referer_tracking . You can choose do you want to save information manually or use sweepers to do saving automatically.

Resources