Reputation: 620
So I understand that i can write
validates :column_name, uniqueness: :true
and I can test to see if a field is unique. But if it isn't the above application throws an error. Instead of throwing an error I want to be able to make it unique with the addition of an integer.
The attribute is called 'legacy_code'. The code is generated by the first letters of the title wording and is called to action via
after_create :generate_article_legacy_code
So the test I have written is like this
@article = FactoryGirl.create :article, title: 'Big Cheesey Pudding Yum Yum'
@article_one = FactoryGirl.create :article, title: 'Blue Chaffinches Purchased Yoko Yamamoto'
it "should be unique" do
expect(@article.legacy_code).to eq('BCPYY')
expect(@article_one.legacy_code).to eq('BCPYY1')
end
So how do I implement this kind of functionality in the article class?
Thanks
Upvotes: 1
Views: 130
Reputation: 815
Well what if there are multiple copies of an article's title's acronym? For instance say there are 3 copies, the title_acronym wouldn't be unique on the second or third find and you would end up with ABC, ABC1, ABC2. That's what you want. But you find an issue if the title ends in a number. You could have (ABC1), (ABC1)1, (ABC1)2 ... and so forth but when you want to increment the acronym you can't tell if it's 1 or 11 without doing some work before hand. This is going to get messy.
I would make the title and a column called number unique together. So in a Rails 4 model you would have something like this
validates :title_acronym, uniqueness: {scope: :number}
Whenever an article with the same title_acronym pops up, that article will just need to set it's own number counter appropriately. And you can create an attribute on the model called legacy_code that returns the union of these two which is what you want. The full code if below.
validates :title, presence: :true
validates :title_acronym, presence: true
validates :number, presence: true
validates :legacy_code, presence: true
validates :title_acronym, uniqueness: {scope: :number}
before_validate :setupArticle
def setupArticle
generate_title_acronym
generate_number
generate_legacy_code
end
def generate_title_acronym
if self.title.nil?
return nil
end
#Code to set the title_acronym from the title
end
def generate_number
if self.title_acronym.nil?
return nil
end
self.number = Article.where(title_acronym, title_acronym).maximum(:number) || 0
end
def generate_legacy_code
if self.title_acronym.nil? || self.number.nil?
return nil
end
if self.number == 0
self.legacy_code = "#{self.title_acronym}"
else
self.legacy_code = "#{self.title_acronym}#{self.number}"
end
end
Uniqueness can also be enforced at the DB level but that may not be necessary.
Now there is a fundamental problem with this if you want to find a single record by its legacy_code. Say there is title_acronym ABC and another title_acronym ABC1. Since those are the first of their title_acronym, their title_acronym and legacy_code are the same. If there is another article with the title_acronym ABC, its legacy_code would have to become ABC1 but that isn't unique because that legacy_code already exists.
What this says is, if you can't guarantee titles don't end in numbers, you can't reliably search for a unique record using legacy_code because by it's nature legacy_code won't produce unique values. You need to search for a unique record using both title_acronym and number. I'm working on the assumption that titles can end in numbers since you are working with articles after all.
If you're willing to change how your legacy_codes look to gain uniqueness over them. You can add a delimiter in between title_acronym and number. I used the underscore in this example.
if self.number == 0
self.legacy_code = "#{self.title_acronym}"
else
self.legacy_code = "#{self.title_acronym}_#{self.number}"
end
legacy_codes are now guaranteed to be unique since it's made up of parts that are guaranteed to be unique and a delimiter but you can add this in for good measure.
validates :legacy_code, uniqueness: :true
Upvotes: 2
Reputation: 3626
You could try:
before_validate :generate_article_legacy_code
Ensure that if the code exists, the generate method does nothing.
Upvotes: 2
Reputation: 489
You can check whether the record is valid or not. If it is not vaild then add the integer and try again. Something like this:
if(@article.valid?)
@article.save!
# do whatever
else
# add integer
end
Upvotes: 0