No release in over 3 years
Low commit activity in last 3 years
A general purpose, typed tagging system for ActiveRecord
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

~> 2.0
~> 2, >= 2.13
~> 13.0
~> 3.0
~> 0.88
~> 1.4

Runtime

>= 5.1, < 7
 Project Readme

HasAttachedTags

Provides a general-purpose, typed tagging mechanism for ActiveRecord models.

HasAttachedTags does:

  • Use tag types (e.g. Allowing a "Jane" character tag to be separate from a "Jane" artist tag.)
  • Allow a model to have multiple, separate lists of tags
  • Allow a model to specify only a single tag, rather than a list
  • Allow a model to have multiple lists of the same kind of tag (e.g. A user preferences object with an allow-list and a block-list of tags.)
  • Use only run-of-the-mill ActiveRecord models and associations, without introducing any new infrastructural types.

HasAttachedTags does not:

  • Parse tags from comma-separate lists
  • Embed tag information directly onto a model
  • Add any specialized methods for interacting with tags
  • Monkey-patch ActiveRecord or any other library

In general, HasAttachedTags handles only the storage and association of tags, and leaves other concerns to the application.

Installation

Add this line to your application's Gemfile:

gem 'has_attached_tags'

And then execute:

$ bundle

Or install it yourself as:

$ gem install has_attached_tags

To generate configuration and locale files:

$ rails g has_attached_tags:install

To generate the database migration:

$ rails g has_attached_tags:migration

Namespacing

By default, HasAttachedTags with map its Tag and Tagging models into the local application's top-level scope for easy access. This simplifies operations like:

# Fully-qualified access:
tag = HasAttachedTags::Tag.of_type('place').find_or_create_by(name: 'Home')

# Simplified access:
tag = Tag.of_type('place').find_or_create_by(name: 'Home')

If this naming conflicts with existing types in your application, or you simply do not want this behaviour, remove these lines from config/initializers/has_attached_tags.rb:

Tag     = HasAttachedTags::Tag
Tagging = HasAttachedTags::Tagging

Usage

In most cases, it's simplest to install HasAttachedTags globally by adding it to the ApplicationRecord base class:

class ApplicationRecord < ActiveRecord::Base
  extend HasAttachedTags

  # ...
end

Alternatively, it's possible to install the tag DSL only for specific models:

class Image < ActiveRecord::Base
  extend HasAttachedTags

  # ...
end

Attaching Tags to Models

To define a model that accepts tags, the has_many_tags macro helper can be used:

class Image < ApplicationRecord
  has_many_tags :characters
end

We refer to this association between the model and its tags as an "attachment". Tags attachments can be used like any other has_many association, without any special methods or magic:

image = Image.new
image.characters # => #<ActiveRecord::Associations::CollectionProxy [...]>

image.characters << Tag.find_by(name: 'J', type: 'character')
image.save

Validations will prevent assigning unsupported tag types to an attachment:

image.characters << Tag.find_by(name: 'J', type: 'character')
image.valid? # => true

image.characters << Tag.find_by(name: 'safe', type: 'rating')
image.valid? # => false

Singular Tag Attachments

If only a single tag should be attached to a model, use has_one_tag macro helper:

class Image < ApplicationRecord
  has_one_tag :rating
end

Similarly, the has_one_tag attachment uses has_one associations under the hood.

image = Image.new
image.rating # => nil

image.rating = Tag.new(name: 'safe', type: 'rating')
image.rating # => #<Tag id: nil, name: "safe", type: "rating", ...>

Marking Tags as Required

Sometimes, it might be necessary to validate that as tag is attached to a model. To do this, a presence: true validation can be used, however, a required option is provided for convenience:

class Image < ApplicationRecord
  has_many_tags :artists, required: true
end

The Image model will now validate that at least 1 artist tag is attached.

image = Image.new
image.valid? # => false

image.artists << Tag.new(name: 'J', type: 'artist')
image.valid? # => true

This option is available for both has_one_tag and has_many_tags attachments.

Specifying a Tag Type

By default, the name of the tags attachment will be used to infer the tag type. (For example has_one_tag :rating will assume "rating" or has_many_tags :characters will assume "character".) This value can be customized using the type option:

class Image < ApplicationRecord
  has_one_tag :source, type: 'website'
end

This option is available for both has_one_tag and has_many_tags attachments.

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/mintyfresh/has_attached_tags. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.

License

The gem is available as open source under the terms of the MIT License.

Code of Conduct

Everyone interacting in the HasAttachedTags project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.