Search code examples
ruby-on-railsacts-as-taggable-on

Not sure where this error is being thrown (acts-as-taggable-on tagged field)


So I'm making a blog and am trying to use acts_as_taggable_oneach entryfor a :category, :subcategory and :topic. The entry model belongs_to :user and acts_as_ordered_taggable/acts_as_ordered_taggable_on :category, :subcategory,.

In entries_controller.rb update is:

@entry = Entry.find_by_id(params[:id])
if @entry.update_attributes(params[:entry])
  redirect_to entries_path
else
  render "edit"
end

When I submit an edit to the entry I get this error:

> NoMethodError Exception: undefined method `each' for "test_category":String

In this situation the String 'test_category' is the value of what I edited :category to be.

Is this something to do with acts_as_taggable_on?



entry.rb


class Entry < ActiveRecord::Base
  belongs_to :user

  # Paperclip
  has_many :attached_assets, :dependent => :destroy
  accepts_nested_attributes_for :attached_assets, :allow_destroy => true 

  # Acts_As_Taggable
  acts_as_ordered_taggable
  acts_as_ordered_taggable_on :category, :subcategory, :topic

  validates_presence_of :title, :subtitle, :category, :post, :subcategory, :topic
  attr_accessible :title, :subtitle, :category_list, :post, :subcategory_list, :topic_list, :asset, :asset_file_name, :asset_type, :entry_id

  delegate :title?, :subtitle?, :category?, :post?, :subcategory?, :topic?, :to => :user

  before_save :to_l

  private

    def to_l
      self.category.downcase
    end
end


entries_controller.rb


class EntriesController < ApplicationController
  before_filter :authenticate_action

  def index
    @entries = Entry.order("created_at desc")
  end

  def new
    @entry = Entry.new
  end

  def show
    @entry = Entry.find_by_id(params[:id])
  end

  def create
    @entry = Entry.new(params[:entry])

    if @entry.save
      redirect_to entries_path
    else
      render "new"
    end
  end

  def edit
    @entry = Entry.find_by_id(params[:id])
  end

  def update
    @entry = Entry.find_by_id(params[:id])
    debugger
    if @entry.update_attributes(params[:entry])
      redirect_to entries_path
    else
      puts @entry.errors.messages.inspect
      render "edit"
    end
  end

  def destroy
    @entry = Entry.find_by_id(params[:id])
    @entry.destroy
    redirect_to entries_path, :notice => "#{@entry.title} has been deleted"
  end
end


WEBrick output


> /Users/kyle/Projects/blog/app/controllers/entries_controller.rb:33
> if @entry.update_attributes(params[:entry])
> (rdb:135) 
> {:category=>["can't be blank"], :subcategory=>["can't be blank"], :topic=>["can't be blank"]}


> Started PUT "/entries/4" for 127.0.0.1 at 2012-07-19 11:29:43 -0500
> Processing by EntriesController#update as HTML
  > Parameters: {"utf8"=>"✓", "authenticity_token"=>"47zbADuuFS3xC5RFc6nLR7qUnE2bn1MZoNm0IwESCcI=", "entry"=>{"title"=>"Test 4", "subtitle"=>"Just Kidding!", "category_list"=>"test_category new", "subcategory_list"=>"test_subcategory new", "topic_list"=>"test_topic new", "post"=>"I pulled a George Lucas, sploops! \r\n\r\njfk;dajgk;dasjkgl;dasczcvzcVzcxv"}, "commit"=>"Update Entry", "id"=>"4"}
  > User Load (0.6ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 2]]
  > Entry Load (1.1ms)  SELECT "entries".* FROM "entries" WHERE "entries"."id" = 4 LIMIT 1
   > (0.2ms)  BEGIN
  > ActsAsTaggableOn::Tag Load (0.7ms)  SELECT "tags".* FROM "tags" INNER JOIN "taggings" ON "tags"."id" = "taggings"."tag_id" WHERE "taggings"."taggable_id" = 4 AND "taggings"."taggable_type" = 'Entry' AND (taggings.context = 'category' AND taggings.tagger_id IS NULL) ORDER BY taggings.id
  > ActsAsTaggableOn::Tag Load (0.9ms)  SELECT "tags".* FROM "tags" INNER JOIN "taggings" ON "tags"."id" = "taggings"."tag_id" WHERE "taggings"."taggable_id" = 4 AND "taggings"."taggable_type" = 'Entry' AND (taggings.context = 'subcategory' AND taggings.tagger_id IS NULL) ORDER BY taggings.id
  > ActsAsTaggableOn::Tag Load (1.1ms)  SELECT "tags".* FROM "tags" INNER JOIN "taggings" ON "tags"."id" = "taggings"."tag_id" WHERE "taggings"."taggable_id" = 4 AND "taggings"."taggable_type" = 'Entry' AND (taggings.context = 'topic' AND taggings.tagger_id IS NULL) ORDER BY taggings.id
  > ActsAsTaggableOn::Tag Load (0.7ms)  SELECT "tags".* FROM "tags" INNER JOIN "taggings" ON "tags"."id" = "taggings"."tag_id" WHERE "taggings"."taggable_id" = 4 AND "taggings"."taggable_type" = 'Entry' AND (taggings.context = 'category') ORDER BY taggings.id
  > ActsAsTaggableOn::Tag Load (0.8ms)  SELECT "tags".* FROM "tags" INNER JOIN "taggings" ON "tags"."id" = "taggings"."tag_id" WHERE "taggings"."taggable_id" = 4 AND "taggings"."taggable_type" = 'Entry' AND (taggings.context = 'subcategory') ORDER BY taggings.id
  > ActsAsTaggableOn::Tag Load (0.7ms)  SELECT "tags".* FROM "tags" INNER JOIN "taggings" ON "tags"."id" = "taggings"."tag_id" WHERE "taggings"."taggable_id" = 4 AND "taggings"."taggable_type" = 'Entry' AND (taggings.context = 'topic') ORDER BY taggings.id
   > (0.2ms)  ROLLBACK
  > Rendered entries/_form.html.haml (49.3ms)
  > Rendered entries/edit.html.haml within layouts/application (53.0ms)
> Completed 200 OK in 9380ms (Views: 91.8ms | ActiveRecord: 6.9ms)



Solution

  • Okay, I got things working.

    First, I replaced the occurrences of :category, :subcategory and :topic with :category_list, :subcategory_list and :topic_list in validates_presence_of and attr_accessible.


    ** WARNING: acts-as-taggable-on still needs to have acts-as-[ordered-]taggable and acts-as-[ordered-]taggable-on :attr to be the original columns (i.e. :category, :subcategory and :topic) to function properly!


    Did you catch WARNING: above? Good.

    Then I went to my _form.html.haml and replaced the attributes :category, :subcategory and :topic for my f.text_field's with :category_list, :subcategory_list, and :topic_list.

    That's it! Things are working smoooothly now!


    Also, just for reference, I configured acts-as-taggable-on in application.rb with:

    ActsAsTaggableOn.delimiter = ' ' # use space as delimiter
    ActsAsTaggableOn.remove_unused_tags = true
    ActsAsTaggableOn.force_lowercase = true
    

    Update


    I decided that the much less complicated way to do things would be to have a single attribute called :tags for my ...tags, rather than having three separate fields to have 'act as taggable'. So, here's what I've done since answering this post:


    Drop Un-needed Tables


    kyle-/blog: rails g migration RemoveCategoryFromEntry category:string
          invoke  active_record
          create    db/migrate/20120719214435_remove_category_from_entry.rb
    
    kyle-/blog: rake db:migrate
    ==  RemoveCategoryFromEntry: migrating ========================================
    -- remove_column(:entries, :category)
       -> 0.0141s
    ==  RemoveCategoryFromEntry: migrated (0.0143s) ===============================
    

    kyle-/blog: rails g migration RemoveSubcategoryFromEntry subcategory:string
          invoke  active_record
          create    db/migrate/20120719214845_remove_subcategory_from_entry.rb
    kyle-/blog: rake db:migrate
    ==  RemoveSubcategoryFromEntry: migrating =====================================
    -- remove_column(:entries, :subcategory)
       -> 0.0024s
    ==  RemoveSubcategoryFromEntry: migrated (0.0025s) ============================
    

    kyle-/blog: rails g migration RemoveTopicFromEntry topic:string
          invoke  active_record
          create    db/migrate/20120719214922_remove_topic_from_entry.rb
    kyle-/blog: rake db:migrate
    ==  RemoveTopicFromEntry: migrating ===========================================
    -- remove_column(:entries, :topic)
       -> 0.0020s
    ==  RemoveTopicFromEntry: migrated (0.0021s) ==================================
    

    kyle-/blog: rails g migration AddTagsToEntry tags:string
          invoke  active_record
          create    db/migrate/20120719215013_add_tags_to_entry.rb
    kyle-/blog: rake db:migrate
    ==  AddTagsToEntry: migrating =================================================
    -- add_column(:entries, :tags, :string)
       -> 0.0039s
    ==  AddTagsToEntry: migrated (0.0040s) ========================================
    

    entry.rb


    class Entry < ActiveRecord::Base
      belongs_to :user
    
      # Paperclip
      has_many :attached_assets, :dependent => :destroy
      accepts_nested_attributes_for :attached_assets, :allow_destroy => true 
    
      # Acts_As_Taggable
      acts_as_ordered_taggable
      acts_as_ordered_taggable_on :tags
    
      validates_presence_of :title, :subtitle, :post, :tag_list
      attr_accessible :title, :subtitle, :tag_list, :post, :asset, :asset_file_name, :asset_type, :entry_id
    
      delegate :title?, :subtitle?, :tags?, :post?, :to => :user
    end
    

    entries/_form.html.haml


    - if @entry.errors
      - @entry.errors.messages.each do |message|
        = message
    
    = form_for @entry, :url => entry_path, :html => {:multipart => true} do |f|
      = f.label :title, "Title"
      = f.text_field :title
      %br
      = f.label :subtitle, "Subtitle"
      = f.text_field :subtitle
      %br
      = f.label :tag_list, "Tags"
      = f.text_field :tag_list
      %br
      = f.label :asset
      = f.file_field :asset, :html => {:multiple => true, :name => "entry[attached_assets_attributes][][assset]"}
      %br
      = f.label :post, "Post"
      = f.text_area :post
      %br
      = f.submit
    

    entries/show.html.haml


    %h1= @entry.title
    %h2= @entry.subtitle
    %h3= @entry.tag_list
    %h4= @entry.updated_at
    %entry
        = @entry.post
    
    = link_to "Back", entries_path
    
    = render "disqus"
    

    entries#create


    def create
      @entry = Entry.new(params[:entry])
    
      if @entry.save
        redirect_to entry_path(@entry), :notice => '#{@entry.title} has been created'
      else
        puts @entry.errors.messages.inspect
        render 'new'
      end
    end
    

    application.rb changes


    I've removed ActsAsTaggableOn.delimiter = ' ' and ActsAsTaggableOn.remove_unused_tags = true as I decided I wanted to use commas as delimiters after all and because acts-as-taggable-on was removing tags that I was testing with.

    # acts-as-taggable-on
    ActsAsTaggableOn.force_lowercase = true