1

In my devise sign-up page, I have implemented an IP tracking feature that, when a user signs up, sends the country the user comes from, in order to populate the newly created account's attribute user_country.

I'd like to know if it's possible to implement a sort of validation on -user_country the same way I do for other User attributes (email, password...) when the user is created, to implement a sort of validation on user_country to be sure it's not empty nor nil, i.e that a user must always have a country identified.

I can't use the classic way with validates :user_country, presence: true, because when the user is created the user_country is still not filled but WAITS that RegistrationController makes a call to geocoder this way => see the method on the bottom called 'after_sign_up_path_for'

/app/controllers/registrations_controller.rb

class RegistrationsController < Devise::RegistrationsController

  layout 'lightbox'

  def update     
    account_update_params = devise_parameter_sanitizer.sanitize(:account_update)

    # required for settings form to submit when password is left blank
    if account_update_params[:password].blank?
      account_update_params.delete("password")
      account_update_params.delete("password_confirmation")
    end    
    @user = User.find(current_user.id)
    if @user.update(account_update_params) # Rails 4 .update introduced with same effect as .update_attributes
      set_flash_message :notice, :updated
      # Sign in the user bypassing validation in case his password changed
      sign_in @user, :bypass => true
      redirect_to after_update_path_for(@user)
    else
      render "edit"
    end
  end

  # for Rails 4 Strong Parameters
  def resource_params
    params.require(:user).permit(:email, :password, :password_confirmation, :current_password, :user_country)
  end
  private :resource_params

  protected
    def after_sign_up_path_for(resource)
      resource.update(user_country: set_location_by_ip_lookup.country) #use concerns/CountrySetter loaded by ApplicationController
      root_path
    end  

end

Is there a way to implement a sort of 'validate' in this kind of situation?

user229044
  • 232,980
  • 40
  • 330
  • 338
Mathieu
  • 4,587
  • 11
  • 57
  • 112
  • as the user_country is an attribute depends on the existence of the ip attribute, so make a validation on the ip attribute and add a before_save method inside the User model may do the stuff. – nickcen Jul 27 '14 at 15:43

1 Answers1

2

My understanding is you want a User model to validate user_country field but only during steps after the initial creation?

class User
   validates_presence_of :user_country, on: :update
end

will only validate on update and not creation. See http://apidock.com/rails/ActiveRecord/Validations/ClassMethods/validates_presence_of for other options that you can add.

But for your use case, it seems odd that you need to do this in the controller after signup. I think it makes more sense to always validate user_country then inject the attribute during User creation in the controller.

rickypai
  • 4,016
  • 6
  • 26
  • 30
  • 1
    I concur. using this pattern would open up the possibility of a confusing validation failure later on - say, if an update was attempted by the user but the user_country field had not been set. The error would be completely incongruous to the context. What's stopping you from adding the field in the sign-up action and using a normal validator? – A Fader Darkly Jul 27 '14 at 16:28
  • @AFaderDarkly i don't want people to themselves say which country they're from. They only give emails/passwords. i induce myself with geocoder which countyr they're form (dfaster from them). I use this guy's advice: http://stackoverflow.com/questions/24418553/undefined-method-model-name-for-trueclassclass-for-deviseconfirmations-rails – Mathieu Jul 27 '14 at 17:49
  • @rickypai thanks I'll try this. I used this guy's advice http://stackoverflow.com/questions/24294169/devise-sign-up-how-to-save-an-attribute-without-having-a-form-field-for-it-ra and use update but i might be able to do it diffrently. any suggestions on how to inject it directly during creation? – Mathieu Jul 27 '14 at 17:57
  • Note: users will be able to change themselves (explicitly) their country if they wish within their MyAccount page – Mathieu Jul 27 '14 at 17:59
  • Use a slightly different form for create and update - only showing the country on update. (the MyAccount page). Then override the Devise controller method for create: http://stackoverflow.com/questions/10117045/overriding-devise-registration-create-method – A Fader Darkly Jul 27 '14 at 18:16
  • 1
    You can insert the country into the resource just before the save, then use a standard validator to check for presence. – A Fader Darkly Jul 27 '14 at 18:17
  • (Ab?)using validators as above is exactly what you asked for (and I have upvoted), but feels like a hack on top of a hack. – A Fader Darkly Jul 27 '14 at 18:19
  • I finally decided to let it in the RegistrationsController using update but i used validates_presence_of :user_country, on: :update – Mathieu Jul 28 '14 at 17:13