views:

1505

answers:

2

First Item
I Want to validate a field to make sure it is unique (in the last 6 months) before saving it to the database.

I am thinking I should use validates_uniqueness_of :field, case_sensitive => false, Scope => ...

For my application it only has to be unique if, it was used <6 months ago.

Thinking to compare it to created_at, but don't really know how to go about it.

Second Item
I think I should somehow use .strip to remove any spaces before or after the text that the use may have put in accidentally (I know that these extra spaces are used by default in rails and if they are there can make a filed unique.)

If anyone has any hints on how this should be done correctly I really would appreciate it.

A: 

You can probably do something like this:

def validate
    errors.add(:field, 'blah blah') if is_used_recently && !has_unique_field? 
end

def has_unique_field?
    Model.exists?(['field = ? and created_at > ?', self.field, 6.months.ago])
end 

def is_used_recently
    self.created_at < 6.months.ago || self.new? # i don't know if created_at would be set by this point
end

Alternatively you might want to create a new validation handler, or extend the existing one to pass in a :within option if that's something you're going to be doing often.

To get rid of leading and trailing white space the method you want is 'strip'. You can run this on all your fields by doing something like:

before_validation :clean_up_whitespace

def clean_up_whitespace
    self.some_field.strip!    # this does the strip in place
end

I hope this helps, let me know if I've made any mistakes!

jonnii
I don't quite think your is_used_recently will work for the given field, because when creating a new record, self.created_at < 6.months.ago == true, but you could have a second record created more than 6 months ago with the same value for :field
Daniel Vandersluis
Ah I see. I read the question wrong... I'll update my answer.
jonnii
Thanks for your info on the clean_up_whitespace,since I am using this for many fields I tried to make this more DRY by making Some_field an array of fields and just looping through them, however if I keep self.variable_name It doesn't know what variable_name is, if I remove the self. from the front it does know but doesn't apply it to the field. I know I am missing something simple. but don't know how to solve it :(
Datatec
You can access the attributes through the attributes hash:fields.each { |f| self.attributes[f].strip! }
jonnii
Sorry still stuck \n class Newsletter < ActiveRecord::Base \n before_validation :clean_up_whitespace \n end \n def clean_up_whitespace \n fields_to_strip = [title,notes] \n fields_to_strip.each { |f| \n #unless self.attributes[f].nil? \n self.attributes[f].strip! \n #end \n } \n end \n controller error given: \n You have a nil object when you didn't expect it! \n The error occurred while evaluating nil.strip
Datatec
try fields_to_strip = ['title', 'notes'] or fields_to_strip = [:title, :notes]
jonnii
yep,try fields_to_strip = ['title', 'notes'] worked, had tried the fields_to_strip = [:title, :notes] on my own before, and for some stupid reason, never though to try using ' ', thanks again ever so much :)
Datatec
+2  A: 

validates_uniqueness_of works by checking if a record already exists with the same value of the given field within the given scope. :scope lets you define the scope (obviously) of the uniqueness; for instance, if I was creating blog software and wanted to only allow a post title to be used once per blog, I could say validates_uniqueness_of :title, :scope => :blog_id -- without the scope, I'd only be allowing each title to be used once across the entire system. :scope won't let you do a complex check, like that which you desire.

What you're probably need to do is create your own validation function to check the uniqueness of the field in question within the given timeframe (code goes within the model):

validate :field_must_be_unique_within_six_months

def field_must_be_unique_within_six_months
  return if field.blank?
  num_duplicates = self.class.count(:conditions => ["field = ? AND created_at < ?", self.field, 6.months.ago])
  if num_duplicates > 0
    errors.add(:field, :taken)
  end
end

The field_must_be_unique_within_six_months method will work similarly to validates_uniqueness_of, in that it will add an error message if there is already a record with the same given field, but with the added condition that it will also check the date. The validate :field_must_be_unique_within_six_months will add the method to the validation process when a record is saved.

To validate multiple fields at the same time without violating DRY, you could use validates_each to do something like the following:

validates_each :field1, :field2 do |record, attr, value|
  if record.class.exists?(["#{attr.to_s} = ? AND created_at < ?", value, 6.months.ago])
    errors.add(attr, :taken)
  end
end

In the above block, record is the record being validated, attr is the attribute (so field1, field2, etc.) and value is the value of that attribute.

Daniel Vandersluis
Sorry I am really new at this. I placed this code in the model and wrapped it withclass Name < ActiveRecord::Baseendbut get the error "undefined method `field' "
Datatec
field was just used as an example, you want to replace field with whatever the actual field you want to be unique is called.
Daniel Vandersluis
ok, so I would need to repeat this code for each field I want to validate? Would their be any DRYish method to do this for multiple fields?
Datatec
Are you trying to do the same sort of validation (unique within 6 months) for each field?
Daniel Vandersluis
yes for almost all fields (for 50 out of 60 on the form)
Datatec
I have updated my answer to show how you could use a validation method on multiple fields at once.
Daniel Vandersluis