I am developing an app in Rails 3 and upon signup I need the user to enter their email address and I need it to be unique and case sensitive. I.e. no one should be able to sign up with myEmail#yahoo.com when MyEmail#yahoo.com already exists in the database.
This is my code and it crashes the app:
validates :email, :presence => true, :uniqueness => true, :case_sensitive => true,
:format => {:with => /^([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})$/i}
What is wrong with it?
Please dont use case sensitive there!!!. It will fetch all the users! So if you have 100.000 users. first it will fetch them all with LOWER(email). This can be VERY slow and it wont use your index on email.
Here an article that i found just now about this topic: http://techblog.floorplanner.com/post/20528527222/case-insensitive-validates-uniqueness-of-slowness
My suggesting is: Run a query to make all the emails downcased and make a before validation filter to downcase the email attribute so you dont have any uppercased characters in that column.
User.update_all('email = LOWER(email)')
before filter:
before_validation :downcase_email
private
def downcase_email
self.email = email.downcase if email.present?
end
For the Rails 3 type of validation you need to nest the casse insensitive block like so
validates :email, :uniqueness => { :case_sensitive => false }
I don't have the reputation to comment on the accepted answer, but #medBo asked about how this behaves in Rails 4. For reference, when using Rails 4.2 + MySQL, if I specify
validates :username, uniqueness: { case_sensitive: true }
ActiveRecord performs this query:
SELECT 1 AS one FROM `users` WHERE `users`.`username` = 'TEST_USER' LIMIT 1
In this case the search is not case sensitive. But when I set:
validates :username, uniqueness: { case_sensitive: false }
it performs:
SELECT 1 AS one FROM `users` WHERE `users`.`username` = BINARY 'TEST_USER'
The BINARY operator ensures the search is case sensitive without fetching all users, meaning for my setup at least, the case_sensitive flag doesn't suffer from the performance issue that #Michael Koper notes for earlier versions of Rails. I can't comment on how ActiveRecord performs for other database setups.
I'm not sure if it is possible to do case insensitive validations using that syntax (at least, I haven't found any documentation for it).
You should be able to validate case insensitive uniqueness like this though:
validates_uniqueness_of :email, :case_sensitive => false
Without more detail on the crash you get, I can't help more than that.
You can use a callback in your model like "before_validation" on email attribute to make it lowercased like this:
before_validation { self.email = email.downcase }
This will make the email input lowercased, after that try uniqueness validation without case sensitive:
validates :email, uniqueness: true
For more info about callbacks: here is ruby guide
https://guides.rubyonrails.org/active_record_callbacks.html
Related
Hopefully this isn't an invalid question, but could someone please explain to me what happens when rails creates and saves an object to the database or updates one? I've put several validations in place, but I'm not sure if I've missed something as there doesn't seem to be too much information on how rails secures its models under the hood.
In this code the user is supplying some data(supposed to be a url), I check with a REGEX to see if it is a url. I'm wondering if I need to do any additional SQL protection techniques for something as complicated as a url?
class ListLink < ActiveRecord::Base
belongs_to :list
default_scope -> {order('created_at DESC')}
#the REGEX urls are matched against
VALID_URL_REGEX = /\A(http:\/\/|https:\/\/|www|)[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(([0-9]{1,5})?\/.*)?\z/i
validates :link_url, presence: true,
format:{with: VALID_URL_REGEX, message: "Please enter a valid url."}
validates :list_id, presence: true
#if is a valid url, ping embedly for more information on it
before_save :embedly #:set_link_info
#not sure what checks rails does on attributes created after validation
#any suggestions on how I can make these safer would be appreciated!
before_create :title, presence: true, length:{minimum: 4, maximum: 200}
before_create :image_url, presence: true
private
def embedly
embedly_api = Embedly::API.new :key => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
:user_agent => 'Mozilla/5.0 (compatible; mytestapp/1.0; my#email.com)'
url = link_url.dup
obj = embedly_api.extract :url => url
#extract and save a title and image element to the database
#are these validated by Rails too?
self.title = obj[0].title
self.image_url = obj[0]["images"][0]["url"]
end
end
Thanks for any help!
No extra care required; validates is enough.
Using ActiveRecord you can be sure the input data is properly escaped.
User.last.update_attributes(first_name: 'DROP TABLE users;')
# => true
User.last.first_name
# => "DROP TABLE users;"
You seems to misunderstand the purpose of before_create. It is a callback that's executed right before the record is added into the database. Its purpose is not to validate the object, but to execute custom code, as in the case with :embedly. You want to change before_create to validates here. The latter is called every time before the object is saved on both create and update actions. If you have
validates :title, presence: true, length:{minimum: 4, maximum: 200}
and your object has a too short title, ActiveRecord won't allow you to save it.
As addition to #shock_one answer:
ActiveRecord will call ActiveRecord::Base.connection.connection.quoute on every property, which will properly escape the values against SQL Injections. Same happens if you use hash queries or question mark placeholders in you queries.
e.g
User.where(name: params[:name])
User.where("name=?", params[:name]).
However if you write your own concatenated sql queries you have to use qoute on user input
c = ActiveRecord::Base.connection
User.where("name=#{c.quote params[:name]}")
I have a model that has
attr_accessible :name, :activity
validates :name, uniqueness: { scope: :activity }
It works and it doesn't allow the creation of duplicate entries. But with simple_form it only shows the error on the :name field. I'd like it to have errors on both fields saying that this 'name' and 'activity' combination has already been taken.
I'm thinking I need to create a custom validation method, but I'm hoping there's a more elegant solution that I've overlooked so far.
Is there a way to show errors on both these fields?
You can add another validation on :activity so that it will be marked as duplicate as well:
validates :activity, uniqueness: { scope: :name }
I'm not sure that's the most elegant solution but it will spare you the custom validation method.
I ended up doing
validates :name, uniqueness: { scope: :activity, message: 'This name and activity combination has already been taken.' }
I haven't decided if I'm going to have it validate both and put the message on both fields yet, but that opposite for the :activity field would be the same.
I have a following code:
validates :name, :presence => true
Error message produced is "Name can't be blank"
Instead of using the actual attribute name (in this case "name") I want to
display message as "Registration name can't be blank".
How do I overwrite the default message on the validations? I tried appending :message
but it didn't work...
Thanks!
In en.yml file define custom keys as:
activerecord:
attributes:
model_name:
attribute_name1: key1
attribute_name2: key2
......
This key will be used automatically when errors are generated.
Reference: http://edgeguides.rubyonrails.org/i18n.html#translations-for-active-record-models
(5.1 Translations for Active Record Models)
This will do the trick:
validates :name, presence: { message: "Registration name can't be blank" }
or the old hash rocket syntax version:
validates :name, :presence => { :message => "Registration name can't be blank" }
Its a little late now (after about 35 days) to answer this. So, sorry for this. But just wanted to share that I had used a gem, more than a few months back, for custom error messages.
This plugin allows you to omit the attribute name for specific messages. All you have to do is begin the message with a ‘^’ character.
I just checked it at
https://github.com/nwise/custom_error_message
& it has not been updated since march. So, i probably used it at the right time.
ps : Your answer for defining the custom keys in the yml file is more appropriate though.
I've got the client_side_validations gem working really great on my forms. I'm using Devise for my users model and when I go to the edit_user_registration form, validations are thrown up for everything except the :current_password, :password, and :password_confirmation fields.
Fort instance is I leave the email blank, the validation pops up right when I tab out of the field. However, if I leave the current_password field blank and tab out of it nothing happen, then when I submit the form I get the error "1 error prohibited this user from being saved: password can't be blank"
Thanks for any help
http://pastie.org/1907464
Currently ClientSideValidations will filter out any conditional validators. Devise sets some of the validators as conditional: https://github.com/plataformatec/devise/blob/master/lib/devise/models/validatable.rb#L24-32
The reason I did this is because there is no good way for the client to determine the true value of that conditional. I could do it at the time the form is generated but what if that conditional relied upon a value that could be changed on the form? So I opted to filter them and let things fall back to the server.
That was the idea but clearly it has imposed unfair limitations on some things. This being the most obvious (and popular).
So I plan on releasing a new version very soon that will allow you to explicitly override the conditional filters. It will work like this:
<%= f.text_field :password, :validate => { :presence => true, :confirmation => true } %>
or
<%= f.text_field :password, :validate => true %>
In the first case you can choose which validators to turn the filter off. In the 2nd case it will turn the filter off for all validators on that attribute. The conditional will be evaluated at the time the form is generated and if it passes it will add the validator to the input element for use on the client.
The master branch now supports this format. Point your Gemfile to it and you should be good
It’s simple! The gem extends the Rails default form builder, and all you have to do is set a :validate => true option on any form_for (or simple_form_for for simple form users) tag that you want the inline validations for. The form builder uses some rails reflections on your model validations to generate some json that gets included in a script tag after your form. The json is then used by the gem’s Javascript framework to perform the validations that need to be performed.
<%= form_for(#user, :url => registration_path(resource_name), :validate => true) do |f| %>
I have a Rails 3.0 web app that allow user to create own path to the application.
example : www.my_app.com/user_company_name
So I store a custom path in user DB field. User can changing path throught a input.
I have added this validation in model
validates_presence_of :custom_page
validates_format_of :custom_page, :with => /^([a-z]|[0-9]|\-|_)+$/, :message => "Only letter (small caps), number, underscore and - are authorized"
validates_length_of :custom_page, :minimum => 3
validates_uniqueness_of :custom_page, :case_sensitive => false
But I don't know how I can validate url to check it isn't in conflict with another route in my routing.
For example in my route.rb I have
resources :user
Validation need to don't allow using www.my_app.com/user, how I can do that?
Thanks, vincent
In your routes, you match the company name to a variable
match 'some_path/:company_name.format'
you can then do the lookup using company_name which rails will populate for you.
Validating the uniqueness of the custom_page variable should be enough to ensure there's no overlap. (note that validate uniqueness of doesn't scale -- if this will be big, you need a db constraint as well) as long as users can only specify one field.
If you're letting users specify
'some_path/:custom_path_1/:custom_path_2.format'
then you have to validate across both fields, and now it's getting messy. Hope you're not doing that.
You can try a custom validation to weed out "user"
validate :custom_page_cant_be_user
def custom_page_cant_be_user
errors.add(:custom_page, "can't be `user`") if self.custom_page =~ /^user$/i
end
assuming :custom_page comes in as a basic [a-z], if :custom_page has /user you need to update the regex a bit.