ActiveScaffold, Acts_as_taggable_on_steroids

Update: also read Active Scaffold + Acts_as_taggable + Auto Completion.

This is kind of an advanced topic, but I think it may be useful to a lot of people.

ActiveScaffold is a great plugin to start building a user interface. The great thing about AS is, that is automatically recognizes associated models. When editing a model, you can easily add or select another model that you want to associate with is.

The best example of this is an Article, where you can select the author (the associated User model) with a drop down box.

There is only one point where I ran into trouble with ActiveScaffold: acts_as_taggable_on_steroids.

Acts_as_taggable_on_steroids allows you to easily attach tags to models and do all kinds of crazy stuff with them. But, if you want to integrate in into AcitveScaffold, you’re in for a tough ride.

ActiveScaffold supports has_many :through associations, but not in a way that is compatible with acts_as_taggable_on_steroids. Let me show you.

In your ArticlesController you specify which columns to show. “tag_list” is a stringified version of the tags associated with the Article, which is great for showing to a user.

However, if you want to edit it an article (or create one), I don’t want a text field where I have to enter tags manually, all I want are a bunch of check boxes, so I can check which tags apply to this article.

Showing the check boxes is easy with AS. By default I show ‘tags’, only in the list view do I use ‘tag_list’ instead. Also, make sure to set the ui_type for the tags column to :select. This will show you check boxes, instead of a sub form that allows you to create tags manually.

active_scaffold :article do |config|
    config.columns = [:title, :body, :tags, :author, :created_at]
    config.list.columns = [:title, :author, :tag_list, :created_at]
    config.columns[:tags].ui_type = :select
end

Well, very nice, right. You can now happily select the tags you want, and save your article. Not.

As you may have noticed, the tags are not saved. Why? Acts_as_taggable adds a ‘tags’ attribute to the model, however, when the Article model is saved, the tags attribute is overwritten by the tags specified in the “tags_list” attribute.

The only way to solve this is to convert the tags selected in AS and store them as the tags_list attribute for the Article.

First, let’s add a private method in the ArticleController class:

private
 
def new_tag_list(tag_ids)
    tag_ids.map {|k,h| h['id']}.collect {|i| Tag.find(i)}.map do |tag|
      tag.name.include?(Tag.delimiter) ? "\"#{tag.name}\"" : tag.name
    end.join(Tag.delimiter.ends_with?(" ") ? Tag.delimiter : "#{Tag.delimiter} ")
end

And add two protected methods that extend the functionality of ActiveScaffold:

protected
 
def before_create_save(record)
    record.tag_list = new_tag_list(params[:record][:tags])
end
 
def before_update_save(record)
    record.tag_list = new_tag_list(params[:record][:tags])
end

This will take the actual form values from AS and create a tags_list. This new tags_list is then assigned to the article (named ‘record’ here). The two protected methods process the tags every time an Article is created or updated.

With this in place, you can happily assign tags to your articles! Please let me know if it worked for you, or if you have made any improvements to this solution.

  • Twitter
  • Digg
  • del.icio.us
  • DZone
  • Reddit
  • email

19 Responses to “ActiveScaffold, Acts_as_taggable_on_steroids”

  1. Guillaume says:

    Hi,

    i found your article very interesting, and i tried to reproduce what you did with AS…
    But unfortunatly, it doesn t work for me…

    I did the exact samething as you, but AS tell me “no option” in the Tags field….
    i ll appreciate your help

    Guillaume.

  2. Ariejan says:

    Guillaume: Did you create the proper tables for the tags?

    class AddTagSupport < ActiveRecord::Migration
      def self.up
        #Table for your Tags
        create_table :tags do |t|
          t.column :name, :string
        end
     
        create_table :taggings do |t|
          t.column :tag_id, :integer
          #id of tagged object
          t.column :taggable_id, :integer
          #type of object tagged
          t.column :taggable_type, :string
        end
     
        # Index your tags/taggings
        add_index :tags, :name
        add_index :taggings, [:tag_id, :taggable_id, :taggable_type]
      end
     
      def self.down
        drop_table :taggings
        drop_table :tags
      end
    end

    Make sure you have the acts_as_taggable call in your model as well. Maybe restart your development server, that helps too sometimes. Let me know how it goes.

    – You probably have the correct tables, but no tags are in it yet. You cannot currently add a new tag, hence the “no option” message. I’ll post a new article with more info on that later.

  3. Guillaume says:

    Ok, so actually i added manually in the DB one tag, and now it works, but something still weird in you article,

    is’nt it possible to add manually tag ?
    i mean having checkbox to choose the tags for your article is very cool, but if the right tag is’nt present, is there a way to show a text field where you can add tags to the artcile and by the way to the tag DB ?

    Guillaume.

  4. Ariejan says:

    Yes, I know. I’m working on a solution for that. I’ll post it in a new article later this week, probably.

  5. Guillaume says:

    Ok, so far i m waiting for a genius solution from you….

    thkx…

  6. Guillaume says:

    Any progress ?

    thanks :)

  7. Ariejan says:

    Nope, not yet. I’m currently having exams and next tuesday I have to deliver a presentation to get my CS degree.

    I’m trying different approaches now, but I think I have found something. Take a look at the _form_association views. That could be very useful.

  8. Guillaume says:

    I will… but i guess you ve got more knowledge that me on AS ans RoR…

    Good luck for your exams

  9. Guillaume says:

    Hi,

    Did you have any success of resolving the tag problem ?
    (the problem that you can not add tags)

    Thanks
    Guillaume

  10. [...] digg_url=’http://ariejan.net/2007/07/01/activescaffold-acts_as_taggable-auto-complete/’; digg_skin = ‘button’; digg_bgcolor = ‘#FFFFFF’; digg_title = ‘ActiveScaffold + acts_as_taggable + Auto Complete’; digg_bodytext = ”; digg_topic = ”; I’ve talked before on how to use ActiveScaffold with acts_as_taggable_on_steroids. [...]

  11. Ariejan says:

    I’ve just posted a new article describing a (quite different) solution to this problem.

    Read it here: http://ariejan.net/2007/07/01/activescaffold-acts_as_taggable-auto-complete/

  12. suci says:

    i have a question,how to short label in design with activescaffold????

  13. Kristofer says:

    so is there any possibility that this routine from above -

    def new_tag_list(tag_ids)
    tag_ids.map {|k,h| h['id']}.collect {|i| Tag.find(i)}.map do |tag|
    tag.name.include?(Tag.delimiter) ? “\”#{tag.name}\”" : tag.name
    end.join(Tag.delimiter.ends_with?(” “) ? Tag.delimiter : “#{Tag.delimiter} “)
    end

    - wouldn’t work in ruby 1.86 and rails 2.0.2? Or with acts_as_taggable_on_steroids (as of 30 March 2008)?

    It throws a

    NoMethodError (You have a nil object when you didn’t expect it!
    You might have expected an instance of Array.
    The error occurred while evaluating nil.map):
    /app/controllers/recipes_controller.rb:46:in `new_new_tag_list’
    /app/controllers/recipes_controller.rb:29:in `before_create_save’
    /vendor/plugins/active_scaffold/lib/actions/create.rb:75:in `do_create’

    when trying to create a new “recipe”. the tag table is not empty, I put a couple in by hand…

    any ideas?

  14. @Kristofer: I haven’t used this for a while. This probably has to do with acts_as_taggable_on_steroids. I’m not sure you need these helper methods anymore. Did you try using the ‘tag_list’ property directly? I think that should work now.

  15. Kristofer says:

    Yep. I took out the before_*_save routines and it works like a champe, even your autocomplete stuff works. Thanks!

  16. Kristofer says:

    I do have to say, even after working with Ruby for 9 months, your new_tag_list method made my head hurt trying to figure it out. I wasn’t able to – that’s why I finally wrote to ask you about it all.

  17. vitaly says:

    How i can edit tags (rename, delete, add new) with activescaffold?

  18. vintyara says:

    I try debug private methods:

    protected

    def before_create_save(record)
    record.tag_list = new_tag_list(params[:record][:tags])
    end

    def before_update_save(record)
    record.tag_list = new_tag_list(params[:record][:tags])
    end

    And i can`t =) Becouse this methods nt used when i try update tags.

    Maybe its not work with rails 2.3.2 ?

  19. sommer says:

    I got this to work in rails 2.2.2 with “acts_as_taggable_on’ (the preferred plugin nowadays) by changing the added code to the controler to the code below – maybe not a nice code, but hope it helps someone:privatedef new_tag_list(tag_ids)
    tag_ids.map {|k,h| h['id']}.collect {|i| Tag.find(i)}.map do |tag|
    tag.name + ‘,’
    end
    end
    protecteddef before_create_save(record)
    if params[:record][:tags] != nil
    record.tag_list = new_tag_list(params[:record][:tags])
    else
    record.tag_list = new_tag_list(”)
    end
    enddef before_update_save(record)
    if params[:record][:tags] != nil
    record.tag_list = new_tag_list(params[:record][:tags])
    else
    record.tag_list = new_tag_list(”)
    end
    end

Leave a Reply