url validation in ruby on rails - ruby-on-rails

1)Url field should also accept url as “www.abc.com”. If user enters url like this, it should be automatically appended with “http://” resulting in value saved in database as “http://www.abc.com”. If user enters url as “http://www.xyz.com” system should not append “http://”. User should be able to save url with “https://”. what is the code for it in ruby on rails?

This code worked:
def validate
if !self.external_url.blank? && self.external_url != "external url"
if self.external_url.match(/^(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(([0-9]{1,5})?\/.*)?$/ix).nil?
if self.external_url.match(/^[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(([0-9]{1,5})?\/.*)?$/ix).nil?
self.errors.add :base, 'External url is invalid' #message is changed by Hemant
else
self.external_url = "http://"+self.external_url
end
end
end
end

Not a great way to ask for assistance which is why I suspect you've been down voted twice!
I would suggest you start by looking at before_validation, validates_format_of and regular expressions. See how you get on with those and perhaps post any updates if you get stuck.
Ps:
prepend = "To attach to the beginning of data"
append = "To attach to the end of the data"

Related

Rails validation is still firing despite unless option evaluating to true

I use devise_invitable in my app to allow users to send invitations. I realized a bad case in which a user has been invited but ignores the invitation and later returns to the app to sign up on their own. Because devise_invitable handles invitations by creating a new user using the provided email address for the invitation, my uniqueness validation on the email field will cause Rails to complain, telling the user that the email address is already taken.
I'm trying to write some logic to handle this case. I see two paths - either figure a way to detect this and destroy the previously created user and allow the new one to be created, or detect the user was invited and execute another flow. I've decided to implement the second option, as I'd like to still utilize the invitation if possible.
My limited experience has me questioning if what I've written will work, but I can't actually fully test it because the Rails validation on the email is triggered. I've made sure Devise's :validatable module is inactive. I created a method that (I think) will detect if a user was invited and in that case the uniqueness validation should be skipped.
#user.rb
...
validates :email, uniqueness: true, unless: :was_invited?
...
def was_invited?
if self.invitation_sent_at.present? && self.sign_in_count == 0
true
else
false
end
end
FWIW, I had originally written this in shorthand rather than breaking out the if/else, but I wanted to be very explicit in an effort to find the bug/failure.
The hope is that once the form passes validation, the create action will do some detection about a user's invitation status and, if they were invited, redirect them to the accept_user_invitation_path. Again, I haven't been able to actually test this yet because I can't get around the validations.
#registrations_controller.rb
def create
if User.find_by_email(params[:email])
#existing_user = User.find_by_email(params[:email])
#existing_user.save(validate: false)
if #existing_user.was_invited?
redirect_to accept_user_invitation_path(:invitation_token => #existing_user.invitation_token)
end
else
super
end
end
In a desperate effort, you'll see I've also added the .save(validate: false) to try to short circuit it there, but it's not even getting that far.
If I comment out the email validation entirely, simply to test the rest of the logic/flow, I get a PG error complaining on uniqueness because of an index on the email address - I don't want to tear all this apart simply to test this method.
I've tried to mess with this for hours and I'm at a loss - any help is appreciated. Let me know if there's any other code you want to see.
Looking at the redirect:
redirect_to accept_user_invitation_path(:invitation_token => #existing_user.invitation_token)
I can see that there is no return which should mean that if that redirect was being called you should be getting an AbstractController::DoubleRenderError error as the parent controller's create method should be trying to render the new view.
From this I would guess that the query you are using to find the existing user is not actually returning a result, possibly because you are using params[:email] whereas if you are using the default views or a properly formatted form it should be params[:user][:email].
Maybe you should give more responsibilities to your controller...
If you find the user, use that, else create a new one. Assuming your form appears with http://yourapp/users/new, change it in your routes to http://yourapp/users/new/:email, making the user input their email before advancing to the form.
def new
#existing_user = User.find_by_email("#{params[:email]}.#{params[:format]}") || User.new
if #existing_user.was_invited? # will only work for existing user
redirect_to accept_user_invitation_path(:invitation_token => #existing_user.invitation_token)
else
render 'new'
end
end
def create
# do maybe something before saving
if #existing_user.save(user_params)
# do your magic
else
render 'new', notice: "Oops, I didn't save"
end
end

Devise: Show error when the user is already confirmed

I would like to show a error message when a confirmed user tries to resend confirmation. Is this something that is already provided by devise or should i have to override some of their methods? If so which methods?
I got this working by overriding create action of confimations controller
def create
self.resource = resource_class.send_confirmation_instructions(resource_params)
if successfully_sent?(resource)
flash[:notice] = "Confirmed already, Please try signing in" if resource.confirmed?
respond_with({}, :location => after_resending_confirmation_instructions_path_for(resource_name))
else
respond_with(resource)
end
end
I am just overriding the flash notice in the case of confirmed user
The first thing you want to decide is if you really want to put a message for confirmed users. This might allow user enumeration (i.e have a robot try to find the users' email on your site... that's why there's the paranoid mode.
If you truly want to display a confirmation message, you do not need to override the controller. Your user will already have an error in the resource something like: "was already confirmed, please try signing in". You therefore don't need to modify the flash for this, you might simply want to use devise_error_messages! (or your own custom code to display error content).
Hope this helps.

Rails: How to enter value A in Model X only if value A exists in Model Y?

I'm trying to build a registration module where user can only register if their e-mail is already in an existing database.
Models:
User
OldUser
The condition on User will be
if OldUser.find_by_email(params[:UserName]) exists, allow user registration.
If not, then indicate error message.
This is really simple to do in PHP where I can just run a function to execute a mysql query. However, I couldn't figure out how to do it on Rails. It looks like I have to create a custom validator function but seems to be overkilled for a such simple condition.
It should be pretty simple to do. What have I missed?
Any pointer?
Edit 1:
This solution by dku.rajkumar works with a slight modification:
validate :check_email_existence
def check_email_existence
errors.add(:base, "Your email does not exist in our database") if OldUser.find_by_email(self.UserName).nil?
end
For cases like this, is it better to do validation in the model or at the controller?
you can do it as
if OldUser.find_by_email(params[:UserName])
User.create(params) // something like this i guess
else
flash[:error] = "Your email id does not exist in our database."
redirect_to appropriate_url
end
UPDATE: validation in model, so the validation will be done while calling User.create
class User < ActiveRecord::Base
validates :check_mail_id_presence
// other code
// other code
private
def check_mail_id_presence
errors.add("Your email id does not exist in our database.") if OldUser.find_by_email(self.UserName).nil?
end
end
I'd recommend starting with Devise.
See https://github.com/plataformatec/devise
Even if you have unusual needs like these, you can normally adapt it. Once you get to know it, it's extremely powerful, solid and debugged, and you can do all sorts of things with it.
Bellow is just an initial implementation .../app/controller/UsersController for User registration related actions.
def new
#user = User.new
end
def create
#user = User.new(params[:user])
#old_user = User.find_by_email(user.email)
if #old_user
if #user.save
# Handle successful save
else
render 'new' # and render some error message telling why registration was not succeed
end
else
# render some page with some sort of error message of 'new' new users
end
end
Update:
Check out the following resources for more info:
Ruby on Rails Tutorial
Rails: User/Password Authentication from Scratch, Part I/II

Soft db error handling for duplicate entries? -- Rails 3.1 newbie

If a user tries to enter a duplicate entry into a table, they get a full page nasty error
ActiveRecord::RecordNotUnique in Admin::MerchantsController#add_alias
Mysql2::Error: Duplicate entry '2-a' for key 'merchant_id': ..
Is there a way to give a flash message instead like "This record already exists"?
This thread from railsforum can help you
I wouldn't recommend checking for the uniqueness and specifically responding to this validation rule via a flash message, this conventionally should be handled by the form's error messages instead.
Nonetheless, in the controller actions for creating and updated you could wrap the conditional block which decides where to send the user, with a check for uniqueness first:
def create
#merchant = Merchant.new params[:merchant]
if Merchant.where(:merchant_id => #merchant.id).count > 0
flash[:error] = "The merchant id #{#merchant.id} already exists"
render :create # amend this to :update when applying to the update action
else
# your normal generated save and render block
end
end
This isn't the cleanest way of achieving your goal, but I think it'll be easiest to understand.
Would really recommend the model validations and form error messages instead, which if you are usung the generated scaffolding, all you need to do is add a model validation and the form throw out the error messages to the user for you:
# app/models/merchant.rb
class Merchant < ActiveRecord::Base
validates_uniqueness_of :merchant_id
end

Friendly Form Validations (Rails)

I checked out both of these previously-asked questions, and they're a help but not a full solution for my case.
Essentially I need to validate a user-submitted URL from a form. I've started by validating that it begins with http://, https://, or ftp:// :
class Link < ActiveRecord::Base
validates_format_of [:link1, :link2, :link3,
:link4, :link5], :with => /^(http|https|ftp):\/\/.*/
end
That works great for what it's doing, but I need to go these two steps further:
Users should be allowed to leave the form fields blank if needed, and
If the URL provided by the user does not already start with http:// (say they enter google.com, for example), it should pass the validation but add the http:// prefix while being processed.
I'm having a hard time determining how to make this work cleanly and efficiently.
FYI, you don't have to pass an array to validates_format_of. Ruby will do arrays automagically (Rails parses the output of *args).
So, for your question, I'd go for something like this:
class Link < ActiveRecord::Base
validate :proper_link_format
private
def proper_link_format
[:link1, :link2, :link3, :link4, :link5].each do |attribute|
case self[attribute]
when nil, "", /^(http|https|ftp):\/\//
# Allow nil/blank. If it starts with http/https/ftp, pass it through also.
break
else
# Append http
self[attribute] = "http://#{self[attribute]}"
end
end
end
end
Just to add to the above, I use the Ruby URI module to parse URLs for validity.
http://www.ruby-doc.org/stdlib/libdoc/uri/rdoc/classes/URI.html
It works really well and it helps me to avoid regexes.

Resources