Guide: Environments in Rails 1.1

0

This article covers what environments in Ruby on Rails are, how they are configured, and how you can create custom environments outside of the stock development, test and production.

read more | digg story

acts_as_taggable_tag (take two)

0

It's been a while since I wrote any update on that topic, but I'm glad that I will be reporting good progress this time.  The acts_as_taggable_tag (AATT from now on) plugin is shaping up nicely (along with my Ruby and Rails knowledge).

Currently the AATT is a real plugin that lives in /vendor/plugins in your rails app. The plugin enables you to do that to any of your models

class Person < ActiveRecord::Base
    acts_as_taggable_tag
end

This simple invocation adds the following to your model class

    #These methods are called to define the relations
    has_many :tag_joins, :class_name => "Tagging", :as => :tagged_one
    has_many :tagged_one_joins, :class_name => "Tagging", :as => :tag
       
    #These instance methods are defined for your model
        tags                   #returns a list of objects that tag yours
        tagged_ones            #
returns a list of objects that are tagged by you
        tag(tagged_one)        #tag this object by yourself
        remove_tag(tag)        #remove this tag from you
        clear_all_tags         #delete the relations between you and your tags
        clear_all_tagged_ones  #delete the relations between you and objects tagged by you


If you look at the implementation of the above methods you'll notice how inefficient they are (a select call for each tag on a certain object for example). Performance is not my primary concern at this point in time, I am just trying to get the concept right.

A class is created for the  dual polymorphic join model (name Tagging). Currently the name and the table mappings are not configurable (you have to use what I give you, period). The table structure is available in a migration format and can be invoked by:

    rake import_aatt_schema

and it can be dropped from the database using:

    rake drop_aatt_schema

I will be preparing a .zip file containing the plugin. To install it you only need to unzip it in the vendor/plugins directory. A great guide to using plugins can be found here

acts_as_taggable_tag

1

acts_as_taggable provides a very easy means for tagging various objects in your Rails application. By using this plugin you can now add a tag to every object and even look at those objects from the tag's point of view; thanks to :polymorphic => true.

I was entertaining the idea of using tagging in a system that I am intending to build. There would be a very generic framework that consists of a certain basic element that can be tagged by different types of tags. I then realized that this was actually the opposite of what acts_as_taggable does!. acts_as_taggable defines a single tag type that can be applied to any object. This lead me to thinking, why not join both ideas? And hence the acts_as_taggable_tag.

acts_as_taggable_tag is not yet a module, but I couldn't resist the name ;). What it does is that it simply enables any object to act as a tag for another object even if it was of the same class or even if it was tagging itself! Thus implementing dynamic many to many associations across all your persistent domain objects through tags

The caveat though is that has_many :through does not play nicely with polymorphic associations as explained here . I ended up using only the join table (taggings in my case) and adding methods for retrieving both the objects that act as tags for the current object and the objects that are tagged by the current object

This implementation is a bit lacking when it comes to performance. What would make it sweet though is to enable :polymorphic associations with a has_many :through, looks like the next thing to dig into :)

Now for the code:


Person Class
class Person < ActiveRecord::Base
has_many :tag_joins,
:class_name =>"Tagging",
:as => :tagged_one
has_many :tagged_one_joins,
:class_name => "Tagging",
:as => :tag

def tags
self.tag_joins.collect { |tj| tj.tag }
end

def tagged_ones
self.tagged_one_joins.collect { |tj| tj.tagged_one }
end
end

Message Class
class Message < ActiveRecord::Base
has_many :tag_joins,
:class_name =>"Tagging",
:as => :tagged_one
has_many :tagged_one_joins,
:class_name => "Tagging",
:as => :tag

def tags
self.tag_joins.collect { |tj| tj.tag }
end

def tagged_ones
self.tagged_one_joins.collect { |tj| tj.tagged_one }
end
end

Tagging Class
class Tagging < ActiveRecord::Base
belobgs_to :tag, :polymorphic => true
belongs_to :tagged_one, :polymorphic => true
end

The code in the Person and Message classes is identical, now each of them has a list of tags and a list of tagged_ones each containing whatever objects of whatever classes that happen to tag or be tagged by the current instance of Person or Message

The Tagging class represents the double polymorphic association between the tagging object and the tagged one regardless of the Class. This approach can be used to implement all sorts of hierarchies among your persistent objects through tagging. I am using it to build a multi process project management tool, which through tags can create different views of the same data like an Iteration/Story one for something like XPlanner or a TodoList one for the BaseCamp style.

What's next is to look at how to modify AR so it will accept polymorphic associations with a has_many :through. But that can wait, I'm already glad that I can tag with such a flexible structure.

Happy tagging :)