Sign in through GitHub

RailsCasts Pro episodes are now free!

Learn more or hide this

Bradley Griffith's Profile

GitHub User: bradley


Comments by Bradley Griffith


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:

class Post < 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...

  def tag_list
    caller[0][/`([^']*)'/, 1] == 'block in validate' ? @tag_list :", ")

  def tag_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 list
      Tag.find_or_initialize_by_name(name: n.strip)


    def save_tags
      self.tags = Tag.transaction do

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:

has n, :taggings
has n, :tags, :through => :taggings

def self.tagged_with(name)
  Tag.all(name: name).articles

def tag_list", ")

def self.tag_counts'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 = t.tag_id')

def tag_list=(names)
  self.tags = names.split(',').map do |n|
    Tag.first_or_create(name: n.strip)

Hope that helps.