After a lot of investigating I found a way to run validations on tags using something very close to Ryan's code.
I'll just put the code that I changed:
ruby
classPost < ActiveRecord::Base
attr_accessible :content, :summary, :title, :tag_list, :tags# association code here...
validates_associated :tag_list
before_save :save_tags# validation code here...# other methods shown in original code...deftag_list
caller[0][/`([^']*)'/, 1] == 'block in validate' ? @tag_list : tags.map(&:name).join(", ")
enddeftag_list=(names)
@tag_list = names.split(",").map do |n|
#self.tags.find_or_initialize_by_name(name: n.strip) #uncomment this if you want invalid tags to show in tag listTag.find_or_initialize_by_name(name: n.strip)
endend
private
defsave_tagsself.tags = Tag.transaction do@tag_list.each(&:save)
endendend
A little explanation: I found that adding Tag objects to @post.tags automatically validates, and I believe attempts to save, the Tag object. So if any Tag object is invalid, it's validation will run, and fail, when the array of Tag objects is set to the value of self.tags in the tag_list= method.
(The method used by the update method in the PostController to update the model, update_attributes, automatically saves the entry if it's valid by the way.)
So, we need a way then to validate the Tag objects when the post object is being validated. So what what we can do is store the array of Tag objects generated in the tag_list= method to an instance variable called, wait for it, '@tag_list'. By adding :tag_list to our list of attr_accessible items, we can then use validates_associated to run validations on @tag_list when the post object is validated.
Because we named the array @tag_list, errors will be returned as errors on the tag_list attribute that we access for our post form. Which is good.
If the post object successfully saves -- meaning everything, including the Tag objects, validated -- we use a before_save hook to save the array of Tag objects stored in the tag_list and update self.tags.
The only thing left to do was to update the tag_list method so that it returns the string of tags except when the method is being called by a validation method, in which case it returns the @tag_list array.
Here are the necessary model methods for any DataMapper users:
ruby
has n, :taggings
has n, :tags, :through => :taggingsdefself.tagged_with(name)
Tag.all(name: name).articles
enddeftag_list
tags.map(&:name).join(", ")
enddefself.tag_countsDataMapper.repository.adapter.select('select tags.*, t.count from tags inner join (select taggings.tag_id, count(taggings.tag_id) as count from taggings group by taggings.tag_id) t where tags.id = t.tag_id')
enddeftag_list=(names)
self.tags = names.split(',').map do |n|
Tag.first_or_create(name: n.strip)
endend
After a lot of investigating I found a way to run validations on tags using something very close to Ryan's code.
I'll just put the code that I changed:
A little explanation: I found that adding Tag objects to @post.tags automatically validates, and I believe attempts to save, the Tag object. So if any Tag object is invalid, it's validation will run, and fail, when the array of Tag objects is set to the value of self.tags in the tag_list= method.
(The method used by the update method in the PostController to update the model, update_attributes, automatically saves the entry if it's valid by the way.)
So, we need a way then to validate the Tag objects when the post object is being validated. So what what we can do is store the array of Tag objects generated in the tag_list= method to an instance variable called, wait for it, '@tag_list'. By adding :tag_list to our list of attr_accessible items, we can then use validates_associated to run validations on @tag_list when the post object is validated.
Because we named the array @tag_list, errors will be returned as errors on the tag_list attribute that we access for our post form. Which is good.
If the post object successfully saves -- meaning everything, including the Tag objects, validated -- we use a before_save hook to save the array of Tag objects stored in the tag_list and update self.tags.
The only thing left to do was to update the tag_list method so that it returns the string of tags except when the method is being called by a validation method, in which case it returns the @tag_list array.
Here are the necessary model methods for any DataMapper users:
Hope that helps.