MakeTaggable
Tagging for Active Record models, across any number of named contexts.
One model can carry several independent sets of tags — genres and moods, skills and interests — each with its own list, counts and queries. Tags can belong to the user who applied them, keep the order they were added in, and be cached on the record for display.
Requirements
| Component | Supported |
|---|---|
| Ruby | 3.2 or newer |
| Active Record | 7.2 or newer |
| Databases | PostgreSQL, MySQL, SQLite |
Tested against Rails 7.2, 8.0 and 8.1 on Ruby 3.2 through 4.0.
Install
bundle add make_taggable
rails make_taggable_engine:install:migrations
rails db:migrate
Quick start
class Book < ApplicationRecord
make_taggable # the :tags context, for free-form tags
make_taggable :genres # a context of your own, for a curated set
end
Each context is a separate set of tags, with its own list:
book = Book.create!(title: "Dune", genre_list: "sci-fi, classic")
book.genre_list # => ["sci-fi", "classic"]
Add and remove individual tags. Nothing is written until you save:
book.genre_list.add("space opera")
book.genre_list.remove("classic")
book.save
book.genre_list # => ["sci-fi", "space opera"]
book.genres # => [#<MakeTaggable::Tag name: "sci-fi">, #<MakeTaggable::Tag name: "space opera">]
Assigning replaces the whole list, and the two contexts never touch each other:
book.tag_list = "desert, chosen-one, re-read"
book.save
book.tag_list # => ["desert", "chosen-one", "re-read"]
book.genre_list # => ["sci-fi", "space opera"]
Find them again:
Book.tagged_with("sci-fi") # carries this tag
Book.tagged_with(["sci-fi", "space opera"]) # carries both
Book.tagged_with(["sci-fi", "fantasy"], any: true) # carries either
Book.tagged_with(["fantasy"], exclude: true) # carries neither
Scope a query to one context, and tags in the others stop counting:
Book.tagged_with("sci-fi", on: :genres) # => [#<Book title: "Dune">]
Book.tagged_with("sci-fi", on: :tags) # => []
Counts, for tag clouds and "most used" lists:
Book.tag_counts_on(:genres) # tags carrying a `count`
Book.top_genres(10)
Tags are ordinary attributes as far as your controller is concerned:
params.expect(book: [:title, :tag_list]) # Rails 8
params.require(:book).permit(:title, :tag_list) # Rails 7.2
Documentation
| Guide | Covers |
|---|---|
| Getting started | Install, first tagged model, reading and writing |
| Tag contexts | Multiple contexts, ordered tags, contexts created at runtime |
| Querying | Every tagged_with option, counting, related records |
| Ownership | Taggers, owned tags, and why tag_list can look empty |
| Parsers and delimiters | Custom parsers, delimiters, escaping |
| Caching | cached_*_list columns, and what they cost |
| Tag clouds | Counts and the tag_cloud helper |
| Configuration | Every setting |
| Database | Schema, indexes, per-adapter notes |
| Migrating from acts-as-taggable-on | Method and constant mapping |
API documentation is generated with YARD:
bundle exec yard doc
Upgrading
See UPGRADING.md. Version 1.0 removes the acts_as_* method names and changes how
delimiters are escaped.
Install new migrations when upgrading:
rails make_taggable_engine:install:migrations
rails db:migrate
Development
The test suite runs against bare Active Record — there is no dummy application to generate.
bundle install
bundle exec rake
That uses in-memory SQLite. To run against another adapter, point it at a database:
DATABASE_ADAPTER=postgresql DATABASE_URL=postgres://localhost/make_taggable_test bundle exec rake
DATABASE_ADAPTER=mysql2 DATABASE_URL=mysql2://root@127.0.0.1/make_taggable_test bundle exec rake
Across every supported Rails version:
bundle exec appraisal install
bundle exec appraisal rake
Format with Standard before opening a pull request:
bundle exec standardrb --fix
Public API needs YARD documentation. bundle exec yard stats --list-undoc should report 100%.
See CONTRIBUTING.md for more.
Credits
MakeTaggable is a fork of acts-as-taggable-on by Michael Bleigh and Joost Baaij, with thanks to its contributors.
License
Available as open source under the terms of the MIT License.