class ActiveRecord::Base #:nodoc: # These extensions make models taggable. This file is automatically generated and required by your app if you run the tagging generator included with has_many_polymorphs. module TaggingExtensions # Add tags to self. Accepts a string of tagnames, an array of tagnames, an array of ids, or an array of Tags. # # We need to avoid name conflicts with the built-in ActiveRecord association methods, thus the underscores. def _add_tags incoming taggable?(true) tag_cast_to_string(incoming).each do |tag_name| begin tag = Tag.find_or_create_by_name(tag_name) raise Tag::Error, "tag could not be saved: #{tag_name}" if tag.new_record? tag.taggables << self rescue ActiveRecord::StatementInvalid => e raise unless e.to_s =~ /duplicate/i end end end # Removes tags from self. Accepts a string of tagnames, an array of tagnames, an array of ids, or an array of Tags. def _remove_tags outgoing taggable?(true) outgoing = tag_cast_to_string(outgoing) <% if options[:self_referential] %> # because of http://dev.rubyonrails.org/ticket/6466 taggings.destroy(*(taggings.find(:all, :include => :<%= parent_association_name -%>).select do |tagging| outgoing.include? tagging.<%= parent_association_name -%>.name end)) <% else -%> <%= parent_association_name -%>s.delete(*(<%= parent_association_name -%>s.select do |tag| outgoing.include? tag.name end)) <% end -%> end # Returns the tags on self as a string. def tag_list # Redefined later to avoid an RDoc parse error. end # Replace the existing tags on self. Accepts a string of tagnames, an array of tagnames, an array of ids, or an array of Tags. def tag_with list #:stopdoc: taggable?(true) list = tag_cast_to_string(list) # Transactions may not be ideal for you here; be aware. Tag.transaction do current = <%= parent_association_name -%>s.map(&:name) _add_tags(list - current) _remove_tags(current - list) end self #:startdoc: end # Returns the tags on self as a string. def tag_list #:nodoc: #:stopdoc: taggable?(true) <%= parent_association_name -%>s.reload <%= parent_association_name -%>s.to_s #:startdoc: end private def tag_cast_to_string obj #:nodoc: case obj when Array obj.map! do |item| case item when /^\d+$/, Fixnum then Tag.find(item).name # This will be slow if you use ids a lot. when Tag then item.name when String then item else raise "Invalid type" end end when String obj = obj.split(Tag::DELIMITER).map do |tag_name| tag_name.strip.squeeze(" ") end else raise "Invalid object of class #{obj.class} as tagging method parameter" end.flatten.compact.map(&:downcase).uniq end # Check if a model is in the :taggables target list. The alternative to this check is to explicitly include a TaggingMethods module (which you would create) in each target model. def taggable?(should_raise = false) #:nodoc: unless flag = respond_to?(:<%= parent_association_name -%>s) raise "#{self.class} is not a taggable model" if should_raise end flag end end include TaggingExtensions end