Skip to content

iiwo/easy_tags

Repository files navigation

Maintainability CI Status Test Coverage

Table of Contents generated with DocToc

EasyTags

Easy contextual tagging for Rails

Migrating from ActsAsTaggableOn

This gem was inspired by ActsAsTaggableOn. It was built on similar model, but rewritten from scratch and simplified with better maintainability in mind. Considerable amount of features has been removed and we've made some breaking changes. Please refer to ActsAsTaggableOn migration notes if you want to migrate

Installation

Add this line to your application's Gemfile:

gem 'easy_tags'

And then execute:

$ bundle

Post Installation

generate migration with:

rails g easy_tags:migration

Usage

Setup

in your ActiveRecord model add:

include EasyTags::Taggable

easy_tags_on :highlights

with multiple tag contexts:

include EasyTags::Taggable

easy_tags_on :highlights, :tags, :notes

Interface

upon the easy_tags_on declaration:

easy_tags_on :highlights

the following methods will be auto-generated and made available for your model instance (highlights being an example context name):

description method
set tags using string of tag names highlights_list=(value)
get string of tag names highlights_list
get string of persisted tag names highlights_list_persisted
set tags with array of tag names highlights=(value)
get array of tag names highlights
  • the String accessor is helpful when working with client side tagging UI solutions such as ie. select2
  • the Array accessor gives you convenient array item manipulation

Examples

Adding/Removing tags

Using Array accessor

# assign/replace tags (comma separated using the default parser)
model.highlights = ['My Tag', 'Second Tag', 'Some Other tag']
# => [
#      [0] "My Tag",
#      [1] "Second Tag",
#      [2] "Some Other tag"
#  ]

# add tags
model.highlights.add('One More', 'And Another')
model.highlights
# => [
#     [0] "My Tag",
#     [1] "Second Tag",
#     [2] "Some Other tag",
#     [3] "One More",
#     [4] "And Another"
#  ]

# remove a single tag
model.highlights.remove('One More')
model.highlights
# => [
#     [0] "My Tag",
#     [1] "Second Tag",
#     [2] "Some Other tag",
#     [3] "And Another"
#  ]

# remember to persist changes
model.save

Using String accessor

# add multiple tags (comma separated using the default parser)
model.highlights_list = 'My Tag, Second Tag, Some Other tag'
# > "My Tag, Second Tag, Some Other tag"

# remove a single tag
model.highlights_list = 'My Tag, Second Tag'
# > "My Tag, Second Tag"

# remove all tags
model.highlights_list = ''

# remember to persist changes
model.save

Querying

EasyTags does not offer any query helper scopes, but it's fairly easy to query without them.

Context relations

Upon the easy_tags_on declaration:

easy_tags_on :highlights

the following relations will be auto-generated and made available for your model instance (highlights being an example context name):

description relation
has_many to EasyTags::Tag relation highlights_tags
has_many to EasyTags::Taggings relation highlights_taggings
  • EasyTags::Tag model represents a single tag name
  • EasyTags::Taggings represents a join table between your model and the EasyTags::Tag model, it also holds the tag context value

Find all model instances with a specific tag name:

MyModel.joins(:highlights_tags).where(tags: { name: 'My Tag' })

Eager loading:

MyModel.includes(:highlights_tags)
Global relations

Upon the EasyTags::Taggable inclusion

include EasyTags::Taggable

the fallowing context independent relations will be auto-generated and made available for your model instance:

description relation
has_many to EasyTags::Taggings relation has_many :taggings, as: :taggable
has_many to EasyTags::Tag relation has_many :base_tags, through: :taggings

You can use them for querying multiple contexts

MyModel.joins(:base_tags).where(taggings: { context: %w[highlights billing_highlights] })

Callbacks

Instance scope callbacks

include EasyTags::Taggable

easy_tags_on(
  highlights: { after_add: :add_tag_callback, after_remove: -> (tagging) { puts "removed #{tagging.tag.name}" } }
)

def add_tag_callback(tagging)
  puts "added #{tagging.tag.name}"
end

Active Support Instrumentation custom notifications

Tag notifications
ActiveSupport::Notifications.subscribe 'easy_tag.tag_added' do |tag|
  puts "added #{tag.name}"
end
ActiveSupport::Notifications.subscribe 'easy_tag.tag_removed' do |tag|
  puts "removed #{tag.name}"
end
Tagging notifications
ActiveSupport::Notifications.subscribe 'easy_tag.tagging_added.YOUR_MODEL_TABLEIZED.CONTEXT_NAME' do |tagging|
  puts "added #{tagging.tag.name}"
end
ActiveSupport::Notifications.subscribe 'easy_tag.tagging_added.YOUR_MODEL_TABLEIZED.CONTEXT_NAME' do |tagging|
  puts "removed #{tagging.tag.name}"
end

Dirty objects

EasyTags implements ActiveModel::Dirty attribute changes tracking fot all the context String accessors (the CONTEXT_NAME_list attributes)

model.highlights
# []

model.highlights = ['Tag One', 'Tag Two']
# [
#     [0] "Tag One",
#     [1] "Tag Two"
# ]

model.changed?
# true

model.highlights_list_changed?
# true

model.highlights_list_was
# ""

model.highlights_list_change
# [
#     [0] "",
#     [1] "Tag One,Tag Two"
# ]

model.save
# true

model.changed?
# false

model.highlights_list_changed?
# false

model.previous_changes
# {
#     "highlights_list" => [
#         [0] nil,
#         [1] "Tag One,Tag Two"
#     ]
# }

model.highlights_list_previously_changed?
# true

model.highlights_list_previous_change
# [
#     [0] nil,
#     [1] "Tag One,Tag Two"
# ]

Configuration

# config/initializers/easy_tags.rb

EasyTags.setup do |config|
  config.tags_table = :tags
  config.taggings_table = :taggings
  config.parser = EasyTags::Parsers::Default
  config.generator = EasyTags::Generators::Default
end

You can customize db table names with tags_table and taggings_table options.

You can customize the parser and the generator to use different separators, filtering or processing. The default parser uses comma as separator and is case sensitive.

Testing

bundle exec appraisal install
bundle exec appraisal rspec

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/[USERNAME]/easy_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.