Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/pat/gutentag
A good, simple, solid tagging extension for ActiveRecord.
https://github.com/pat/gutentag
Last synced: 2 days ago
JSON representation
A good, simple, solid tagging extension for ActiveRecord.
- Host: GitHub
- URL: https://github.com/pat/gutentag
- Owner: pat
- License: mit
- Created: 2013-07-11T08:25:51.000Z (over 11 years ago)
- Default Branch: main
- Last Pushed: 2024-11-23T10:27:23.000Z (about 1 month ago)
- Last Synced: 2024-12-14T04:00:24.272Z (9 days ago)
- Language: Ruby
- Size: 282 KB
- Stars: 479
- Watchers: 9
- Forks: 36
- Open Issues: 5
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
Awesome Lists containing this project
README
# Gutentag
[![Gem Version](https://badge.fury.io/rb/gutentag.svg)](http://badge.fury.io/rb/gutentag)
[![Build Status](https://github.com/pat/gutentag/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/pat/gutentag/actions/)
[![Code Climate](https://codeclimate.com/github/pat/gutentag.svg)](https://codeclimate.com/github/pat/gutentag)A good, simple, solid tagging extension for ActiveRecord.
This was initially built partly as a proof-of-concept, partly to see how a tagging gem could work when it's not all stuffed within models, and partly just because I wanted a simpler tagging library. It's now a solid little tagging Rails engine.
If you want to know more, read [this blog post](http://freelancing-gods.com/posts/gutentag_simple_rails_tagging), or have a look at [the Examples page](https://github.com/pat/gutentag/wiki/Examples) in the wiki (which includes a starting point for accepting tag values in a form).
## Contents
* [Usage](#usage)
* [Installation](#installation)
* [Upgrading](#upgrading)
* [Configuration](#configuration)
* [Extending](#extending)
* [Contribution](#contribution)
* [Licence](#licence)Usage
The first step is easy: add the tag associations to whichever models should have tags (in these examples, the Article model):
```Ruby
class Article < ActiveRecord::Base
# ...
Gutentag::ActiveRecord.call self
# ...
end
```That's all it takes to get a tags association on each article. Of course, populating tags can be a little frustrating, unless you want to manage Gutentag::Tag instances yourself? As an alternative, just use the tag_names accessor to get/set tags via string representations.
```Ruby
article.tag_names #=> ['pancakes', 'melbourne', 'ruby']
article.tag_names << 'portland'
article.tag_names #=> ['pancakes', 'melbourne', 'ruby', 'portland']
article.tag_names -= ['ruby']
article.tag_names #=> ['pancakes', 'melbourne', 'portland']
```Changes to tag_names are not persisted immediately - you must save your taggable object to have the tag changes reflected in your database:
```Ruby
article.tag_names << 'ruby'
article.save
```You can also query for instances with specified tags. The default `:match` mode is `:any`, and so provides OR logic, not AND - it'll match any instances that have _any_ of the tags or tag names:
```Ruby
Article.tagged_with(:names => ['tag1', 'tag2'], :match => :any)
Article.tagged_with(
:tags => Gutentag::Tag.where(name: ['tag1', 'tag2']),
:match => :any
)
Article.tagged_with(:ids => [tag_id], :match => :any)
```To return records that have _all_ specified tags, use `:match => :all`:
```ruby
# Returns all articles that have *both* tag_a and tag_b.
Article.tagged_with(:ids => [tag_a.id, tag_b.id], :match => :all)
```To return records that have _none_ of the specified tags, use `:match => :none`:
```ruby
# Returns all articles that have *neither* tag_a nor tag_b.
Article.tagged_with(:ids => [tag_a.id, tag_b.id], :match => :none)
```To return all tag names used by an instance of a model or relation
```ruby
# Returns array of tag names
Gutentag::Tag.names_for_scope(Article)
# => ['tag1', 'tag2', 'tag3']Gutentag::Tag.names_for_scope(Article.where(:created_at => 1.week.ago..1.second.ago))
# => ['tag3']# Return array of the tag names used from the two most recent articles
Gutentag::Tag.names_for_scope(Article.order(created_at: :desc).limit(2))
# => []
```Installation
### Dependencies
These are the versions the test suite runs against. It's possible it may work on older versions of Ruby, but it definitely won't work on older versions of Rails.
* Ruby: MRI v2.3-v3.1, JRuby v9.2.5
* Rails/ActiveRecord: v4.0-v7.0If you're using MRI v2.2 and/or ActiveRecord v3.2, the last version of Gutentag that fully supported those versions is v2.4.1.
### Installing
Get it into your Gemfile - and don't forget the version constraint!
```Ruby
gem 'gutentag', '~> 2.6'
```Next: your tags get persisted to your database, so let's import the migrations, update them to your current version of Rails, and then migrate:
```Bash
bundle exec rake gutentag:install:migrations
bundle exec rails generate gutentag:migration_versions
bundle exec rake db:migrate
```If you're using UUID primary keys, make sure you alter the migration files before running `db:migrate` to use UUIDs for the `taggable_id` foreign key column (as noted in [issue 57](https://github.com/pat/gutentag/issues/57).)
### Without Rails
If you want to use Gutentag outside of Rails, you can. However, there is one caveat: You'll want to set up your database with the same schema (as importing in the migrations isn't possible without Rails). The schema from 0.7.0 onwards is below:
```ruby
create_table :gutentag_tags do |t|
t.string :name, null: false, index: {unique: true}
t.bigint :taggings_count, null: false, index: true, default: 0
t.timestamps null: false
endcreate_table :gutentag_taggings do |t|
t.references :tag, null: false, index: true, foreign_key: {to_table: :gutentag_tags}
t.references :taggable, null: false, index: true, polymorphic: true
t.timestamps null: false
end
add_index :gutentag_taggings, [:taggable_type, :taggable_id, :tag_id], unique: true, name: "gutentag_taggings_uniqueness"
```Upgrading
Please refer to the [CHANGELOG](CHANGELOG.md), which covers significant and breaking changes between versions.
Configuration
Gutentag tries to take a convention-over-configuration approach, while also striving to be modular enough to allow changes to behaviour in certain cases.
### Tag validations
The default validations on `Gutentag::Tag` are:
* presence of the tag name.
* case-insensitive uniqueness of the tag name.
* maximum length of the tag name (if the column has a limit).You can view the logic for this in [`Gutentag::TagValidations`](lib/gutentag/tag_validations.rb), and you can set an alternative if you wish:
```ruby
Gutentag.tag_validations = CustomTagValidations
```The supplied value must respond to `call`, and the argument supplied is the model.
### Tag normalisation
Tag normalisation is used to convert supplied tag values consistently into string tag names. [The default](lib/gutentag.rb#L15) is to convert the value into a string, and then to lower-case.
If you want to do something different, provide an object that responds to call and accepts a single value to `Gutentag.normaliser`:
```ruby
Gutentag.normaliser = lambda { |value| value.to_s.upcase }
```### Case-sensitive tags
Gutentag ignores case by default, but can be customised to be case-sensitive by supplying your own validations and normaliser, as outlined by [Robin Mehner](https://github.com/rmehner) in [issue 42](https://github.com/pat/gutentag/issues/42). Further changes may be required for your schema though, depending on your database.
Extending
If you need to extend Gutentag's models, you will need to wrap the `include` inside a `to_prepare` hook to ensure it's loaded consistently in all Rails environments:
```ruby
# config/initializers/gutentag.rb or equivalent
Rails.application.config.to_prepare do
Gutentag::Tag.include TagExtensions
end
```Further discussion and examples of this can be found in [issue 65](https://github.com/pat/gutentag/issues/65).
Contribution
Please note that this project now has a [Contributor Code of Conduct](http://contributor-covenant.org/version/1/0/0/). By participating in this project you agree to abide by its terms.
Licence
Copyright (c) 2013-2022, Gutentag is developed and maintained by Pat Allan, and is released under the open MIT Licence.