Tagalong
Tagalong is a Rails tagging plugin that is intended to be clean, efficient, and simple. I have tried hard to keep the API slim and intelligable in terms of Object Oriented Programming. I focused heavily on this as I feel most other Rails tagging plugins seriously neglect OO in their APIs.
The other key differentiation between Tagalong and other tagging libraries is the relational database structure behind the scenes. This database structure allows Tagalong to provide not only an Object Oriented API, but also a set of features that help differentiate it from other Rails tagging plugins.
Feature Overview
- clean Object Oriented API
- does NOT require saving of the model when tagging/untagging
- keeps history of tags Taggers have used
- allows defining multiple Taggers and Taggables
- tracks the number of times tags are used
- returns tag lists in alphabetical order (imporant for UI)
Installation
Add this line to your application's Gemfile:
gem 'tagalong'
And then execute:
$ bundle
Or manually install it:
$ gem install tagalong
API Concepts
There are three major concepts that one should understand before reading the documentation for the API, the Tag, Tagger, and Taggable.
Tag
A Tag in the Tagalong API is represented by a string. Conceptually it is simply a textual label that is associated with a Taggable and a Tagger.
Tagger
A Tagger is an object that will be performing the action of applying a tag to a Taggable. In most Rails apps this is the a User object.
Taggable
A Taggable is an object that will be having the tags applied to it. In a Rails app this could be a Task object, Contact object, or any type of object that you want to be able to tag.
Usage
Migration Setup
In order to use Tagalong, generate the proper migrations so that the tags can be stored in the database. This can be done with the following command:
rails generate tagalong:migration
The above will generate the migration and place it appropriately in the db/migrate/
project path.
Declaring Taggers and Taggables
It is necessary to declare at least one Tagger and Taggable in addition to generating and running the migrations. This is done as follows:
class Contact < ActiveRecord::Base
tagalong_taggable
end
class User < ActiveRecord::Base
tagalong_tagger
end
Once Taggers and Taggables are declared, they can be used in numerous ways as outlined below.
Tag Usage
Tagging
To tag a taggable, call the tag
method on a Tagger object and hand it a persisted Taggable object with the given label that you want to apply.
@user.tag(@contact, "sometag")
Untagging
To untag, call the untag
method on a Tagger object and hand it a persisted Taggable object with the given label that you want to untag.
@user.untag(@contact, "sometag")
List Tagger tags
To list Tagger tags, call the tags
method on a Tagger object. This will return an array of all tags that Tagger has ever used in ascending alphabetical order.
@user.tags
# => ['another_tag', 'some_tag', 'woot_tag']
List Tagger tags with more information
You can get a list of tags that belong to a Tagger along with some additional information by calling the tags_including
method. The result will be a hash that always contains the key :name which is the name of the tag. There can be other keys added to the hash if you choose to pass the option for them to the method. A list of the params you can pass and their explanations are below:
:number_of_references => true
This only takes true as a value. When passed, it will add :number_of_references to the result hash. The value of :number_of_references is the number of times the tag is used by the Tagger.
@user.tags_including(:number_of_references => true)
# => [
{ name: 'another_tag', :number_of_references => 42 },
{ name: 'some_tag', :number_of_references => 23 },
{ name: 'woot_tag', :number_of_references => 2 }
]
:has_been_tagged => @contact
Takes a Taggable as a value. When passed, it will add :has_been_tagged to the result hash for each tag returned. The value of :has_been_tagged will contain a boolean representing if the Taggable passed is tagged by the current tag in the iteration.
@user.tags_including(:has_been_tagged => @contact)
# => [
{ name: 'another_tag', :has_been_tagged => true },
{ name: 'some_tag', :has_been_tagged => false },
{ name: 'woot_tag', :has_been_tagged => true }
]
Search Tagger tags
A Tagger's tags can be searched for using the matching_tags
method and a given search phrase as follows:
@user.matching_tags("cats have")
# => [
{ name: 'cats have 9 lives', :number_of_references => 42 },
{ name: 'what do cats have', :number_of_references => 23 },
]
The above returns an array of hashes representing tags ordered in descending order of their number of references. Note: This function by default simply uses the ActiveRecord backend to search for matching tags. However, this method switches to using Sunspot to handle the searching if Sunspot has been enabled for this plugin. For more information please see the Sunspot section below.
List Taggable tags
To list Taggable tags, call the tags
method on a Taggable object. This will return an array of all tags that Taggable is currently tagged with in ascending alphabetical order.
@contact.tags
# => ['some_tag', 'woot_tag']
List Taggables that have a tag
To list Taggables that have a tag, call the taggables_with
method on a Tagger object as follows. This will return an array of Taggable objects that are currently tagged with the given tag.
@user.taggables_with('some_tag')
# => [Contact Object, Contact Object]
Check if Taggable is tagged with a tag
To check if a Taggable is tagged with a tag, call the tagged_with?
method on a Taggable object as follows. This will return true
in the case that the Taggable IS currently tagged with the given tag, and false
in the case where the Taggable is NOT currently tagged with the given tag.
@contact.tagged_with?('some_tag')
# => true
Check if Tagger has a tag
To check if a Tagger has a specific tag, call the has_tag?
method. This will return a boolean representing if the Tagger has used the passed tag (and not deleted it).
@user.has_tag?('sometag')
# => true
Tag Management
Creating
To create a tag on the Tagger object without applying the tag to a Taggable, call the create_tag
method.
@user.create_tag("sometag")
Renaming
To rename a tag that exists on a Tagger object, simply call the rename_tag
method with the existing tag and new desired name. If the name is already in use, it will raise an ActiveRecord::RecordInvalid error.
@user.rename_tag("sometag", "another_tag_name")
Deleting
To delete a tag, call the delete_tag
method on a Tagger object and hand it the label of the tag you want to delete. This will remove the tag from the Tagger, as well as all Taggables that might be using it.
@user.delete_tag("sometag")
Sunspot
Tagalong currently supports Sunspot for searching tags. In order to use Sunspot for searching tags a few things need to happen in the client Rails application.
- Add Sunspot to your Rails app as you normally would
- Create a Rails initializer file called
tagalong.rb
that containsTagalong.enable_sunspot
.
Credits
I just wanted to thank all of the other open source Rails tagging plugins out there. Especially, acts-as-taggable-on, is_taggable, and rocket_tag. I learned a lot from you all.
I also want to thank RealPractice, Inc. for donating some developer hours to the project as well as being our initial user.
Beyond that I want to specifically thank @cyoungberg and @russCloak for discussing the API decissions with me. It definitely helped having you guys as a sounding board.
Contributing
If you are interested in contributing code please follow the process below and include tests. Also, please fill out issues on our GitHub issues page if you have discovered a bug or simply want to request a feature.
- Fork it
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Added some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create new Pull Request