How to validate unique url with Rails and validates_url? - ruby-on-rails

Use validates_url can validate url format.
Set validation in model:
validates :homepage, uniqueness: true, url: true
Can't validate these two cases as unique url:
https://stackoverflow.com
https://stackoverflow.com/
But they should be the same. If the first one is inserted into database, the second one should be validated and not allow to be inserted.
I didn't find a way to realize it using validates_url. Is it possible?

So the two validations, uniqueness and URL, happen separately, and there is nothing in the uniqueness check to handle the fact that those two URLs are essentially the same - instead, the string values are technically different, and thus it doesn't trip the uniqueness validation.
What you could do is look to tidy up your URL data before validation, with a before_validation callback in your model:
before_validation :process_url
def process_url
self.homepage = self.homepage.slice(0, self.homepage.length - 1) if self.homepage.present? && self.homepage.ends_with?("/")
end
This is called before the validations kick in, and will make sure that if the homepage attribute is present (even if you add a presence validation later if it becomes non-optional, remember this is running before validations), then any trailing / is removed.
Those two URL strings will then be the same after tidying up, and thus the second time around the validation will kick in and stop it from being saved.
Hope that helps!

I do use rubular.com to validate using regex. You can try this ^[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\.*)?$

Related

What is the robust way to check form input values in Rails

I have a form and want the form input values to be check. If the values pass the check, then values can be stored in DB. I know I can check them in view, controller, or even model. I think probably the best way is to check them in all the three parts, and only check them in view before submit could cause problem because others can manually modify and send the request to the controller.
So for example, I have user variable in view. I don't want date type attribute user.start_date to be modify to be earlier than user.apply_date, how should I make the protection robust?
What you're looking for are model validations: http://guides.rubyonrails.org/active_record_validations.html
Check out the date_validator gem (https://github.com/codegram/date_validator)
It allows for validations like this:
validates :start_date, date: { after: :apply_date }
Doing it in the model, as that's the usual way. Ruby on Rails supports model errors extensively, and if there's an error you can simply redisplay the record and the errors in that record.
There are standard validations you can do to ensure a value is present, is unique, etc.
You can also do custom validations in the model
validate :start_date_cannot_be_before_apply_date
def start_date_cannot_be_before_apply_date
if start_date < apply_date
errors.add(:start_date, "can't be before the apply date")
end
end

validate ActiveRecord field with especific class

I want to validate that my field is from an especific class.
Concrete example. I have a model that must by unique by day. So, I insert a validation
validates :my_date_field, uniqueness: {scope: [:scope_one, :scope_two]}
If I create my record with a Date, the validation works fine. If I create my record with a Time, however, (my tests use Time::now) it parses it to a Date, but does not trigger the validation.
How should I fix it?
Thanks in advance.
--Edit--
A related problem:
How I forbid someone to do
MyModel::create my_date_field: 4
? It is passing the validate_presence from active_record, passing the null: false from migration, and saving an ugly 4 in my date_field.
I'm completely lost here =\
ActiveRecord will type-cast your values to whatever type it thinks is appropriate for the underlying database field. You can still access the original type, though, via my_date_field_before_type_cast. The *_before_type_cast accessors exist for every attribute. There you can check the class or do any other conversions you might need. These are available in custom validation methods as well.

Cannot skip validation in Rails 3?

I'm working on a project in Rails 3 where I need to create an empty record, save it to the database without validation (because it's empty), and then allow the users to edit this record in order to complete it, and validate from then on out.
Now I've run into a pretty basic problem: I can't seem to save a model without validating it under any circumstances.
I've tried the following in the console:
model = Model.new
model.save(false) # Returns RuntimeError: Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id
model.save( :validate => false ) # Returns same error as above
model = Model.create
model.save(false) # Same runtime error
model.save( :validate => false ) # Same runtime error
I then tried changing all the validations in the model to :on => :update. Same error messages on any attempt to save.
So what am I missing here? How can I create an empty record and then let validation occur as the user edits it?
Thanks!
It is a bad practice to have invalid models saved by normal use cases. Use conditional validations instead:
validates_presence_of :title, :unless => :in_first_stage?
or if you have many:
with_options :unless => :in_first_stage? do
validates_presence_of :title
validates_presence_of :author
end
This way nothing stands in way to have nightly integrity tests, which checks all records for validity.
A valid use case for saving without validations would be for testing edge cases, e.g. to test that a database constraint is enforced.
*sigh...*
Found the problem... one of my after_validate method calls was adding information and resaving the model, hence the errors I was getting weren't from the console input, they were coming from the after_validate method which was saving again.
Thanks all.
For emergencies only
Assuming that you have considered this very carefully and are certain that this is a good idea, you can save without validation using:
my_model.save validate: false
There are almost no valid use cases for this, and it should be considered an emergency one off procedure. Your use case does not qualify.
Problems with invalid records
Having invalid records in the database leads to all manner of problems down the line. For example, you send an email to all users and update a 'last_contacted_at' field on your user model. Your invalid users will not be updated and will descend into an email spiral of death.
Conditional validation
As other posters have pointed out, conditional validation will solve most issues for which you might otherwise have used validate: false.
Instead of placing an invalid model in the database, store the partially completed model (created with Model.new) in a session. Only save it to the database when it is completely valid.

what is the right way to model status values as an association in Rails?

I have a model called Contacts.
Contacts can have different status "bad, positive, wrong..."
These status may need to be changed over time, but across all contacts, they are the same options.
Should I model it this way:
Contacts.rb
belongs_to :status_contact
StatusContacts.rb
has_many :contacts
Then I manually populate the types of status in the table?
I then want to use Ajax to click a button corresponding to a value to update the value for Contacts.
It looks like you're trying to ensure that the values for your status are always going to restricted to a set of possible answers of your choosing. If that's all you're trying to do, there's no special need for a separate table. You can use the magic of ActiveRecord validations instead here.
First, create a string database column for Contact called :status.
Then you can use a validation to ensure that the values are limited to the ones you want. In Rails 3, you can do it like this:
validate :status, :inclusion => { :in => %w( bad positive wrong ) }
(If you're using Rails 2, use #validates_inclusion_of instead.)
In ActiveRecord, validations check that the object's values are valid before saving; it refuses to persist the object into the database until all validations pass.
Your naming strikes me as a little weird—ContactStatus sounds a little nicer to me—but I see this as being the general idea to achieve what you want.
No clear answer yet --- I think I need to use the table because it would allow the users to add and modify the types of status used across their application.

Rails Complex Form Validations?

I have a complex form for my Work model. It accepts nested attributes for Credits, Images and Videos. (it is a modified version of Eloy Duran's complex form example)
I want to validate presence of at least one Credit
I want to validate presence of at least one Image or one Video
When I do (in work.rb):
validates_presence_of :credits
it validates properly when I try to submit the form without any credits but it does not validate if I remove the last credit (by checking a check_box that adds "_delete" to credit's attributes). It submits the work deleting the one and only credit leaving the work without any credits.
Do you have any idea on how I can validate these properly?
What you need is something along the lines of:
validate :credits_present
private
def credits_present
unless credits.any?{|c| !c.marked_for_destruction? }
errors.add_to_base "You must provide at least one credit"
end
end
The conditional in credits_present may be a bit messy so let me break it down just in case you don't follow. The any? method returns true if any of the items in the Enumerable respond true to the block provided. In this case, we check that the item is not going to be deleted. So, if any of the credits are not going to be deleted we get true. If true, we're in good shape, we have at least one credit that won't be deleted. If, on the other hand, we get false we either know there aren't any credits or that any that are present will be deleted. In this case validation fails and we add the error.
Try using before_save in work.rb
before_save :check_credits
private
def check_credits
self.credits > 0
end
If credits are <= zero, the save won't go through.

Resources