{"id":13685560,"url":"https://github.com/pat/gutentag","last_synced_at":"2025-05-14T09:06:29.740Z","repository":{"id":9456269,"uuid":"11336901","full_name":"pat/gutentag","owner":"pat","description":"A good, simple, solid tagging extension for ActiveRecord.","archived":false,"fork":false,"pushed_at":"2024-11-23T10:27:23.000Z","size":289,"stargazers_count":480,"open_issues_count":5,"forks_count":36,"subscribers_count":8,"default_branch":"main","last_synced_at":"2025-05-14T09:05:08.524Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pat.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2013-07-11T08:25:51.000Z","updated_at":"2025-05-07T12:16:17.000Z","dependencies_parsed_at":"2024-12-07T03:05:04.185Z","dependency_job_id":"d52265df-53fc-4da4-8ce2-4578f8590e32","html_url":"https://github.com/pat/gutentag","commit_stats":{"total_commits":297,"total_committers":19,"mean_commits":"15.631578947368421","dds":"0.12457912457912457","last_synced_commit":"a2c13e983065343201dd59d2ebc80b9309f930de"},"previous_names":[],"tags_count":31,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pat%2Fgutentag","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pat%2Fgutentag/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pat%2Fgutentag/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pat%2Fgutentag/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pat","download_url":"https://codeload.github.com/pat/gutentag/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254110374,"owners_count":22016391,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-02T14:00:53.663Z","updated_at":"2025-05-14T09:06:29.709Z","avatar_url":"https://github.com/pat.png","language":"Ruby","funding_links":[],"categories":["Ruby"],"sub_categories":[],"readme":"# Gutentag\n\n[![Gem Version](https://badge.fury.io/rb/gutentag.svg)](http://badge.fury.io/rb/gutentag)\n[![Build Status](https://github.com/pat/gutentag/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/pat/gutentag/actions/)\n[![Code Climate](https://codeclimate.com/github/pat/gutentag.svg)](https://codeclimate.com/github/pat/gutentag)\n\nA good, simple, solid tagging extension for ActiveRecord.\n\nThis 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.\n\nIf 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).\n\n## Contents\n\n* [Usage](#usage)\n* [Installation](#installation)\n* [Upgrading](#upgrading)\n* [Configuration](#configuration)\n* [Extending](#extending)\n* [Contribution](#contribution)\n* [Licence](#licence)\n\n\u003ch2 id=\"usage\"\u003eUsage\u003c/h2\u003e\n\nThe first step is easy: add the tag associations to whichever models should have tags (in these examples, the Article model):\n\n```Ruby\nclass Article \u003c ActiveRecord::Base\n  # ...\n  Gutentag::ActiveRecord.call self\n  # ...\nend\n```\n\nThat'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.\n\n```Ruby\narticle.tag_names #=\u003e ['pancakes', 'melbourne', 'ruby']\narticle.tag_names \u003c\u003c 'portland'\narticle.tag_names #=\u003e ['pancakes', 'melbourne', 'ruby', 'portland']\narticle.tag_names -= ['ruby']\narticle.tag_names #=\u003e ['pancakes', 'melbourne', 'portland']\n```\n\nChanges to tag_names are not persisted immediately - you must save your taggable object to have the tag changes reflected in your database:\n\n```Ruby\narticle.tag_names \u003c\u003c 'ruby'\narticle.save\n```\n\nYou 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:\n\n```Ruby\nArticle.tagged_with(:names =\u003e ['tag1', 'tag2'], :match =\u003e :any)\nArticle.tagged_with(\n  :tags  =\u003e Gutentag::Tag.where(name: ['tag1', 'tag2']),\n  :match =\u003e :any\n)\nArticle.tagged_with(:ids =\u003e [tag_id], :match =\u003e :any)\n```\n\nTo return records that have _all_ specified tags, use `:match =\u003e :all`:\n\n```ruby\n# Returns all articles that have *both* tag_a and tag_b.\nArticle.tagged_with(:ids =\u003e [tag_a.id, tag_b.id], :match =\u003e :all)\n```\n\nTo return records that have _none_ of the specified tags, use `:match =\u003e :none`:\n\n```ruby\n# Returns all articles that have *neither* tag_a nor tag_b.\nArticle.tagged_with(:ids =\u003e [tag_a.id, tag_b.id], :match =\u003e :none)\n```\n\nTo return all tag names used by an instance of a model or relation\n\n```ruby\n# Returns array of tag names\nGutentag::Tag.names_for_scope(Article)\n# =\u003e ['tag1', 'tag2', 'tag3']\n\nGutentag::Tag.names_for_scope(Article.where(:created_at =\u003e 1.week.ago..1.second.ago))\n# =\u003e ['tag3']\n\n# Return array of the tag names used from the two most recent articles\nGutentag::Tag.names_for_scope(Article.order(created_at: :desc).limit(2))\n# =\u003e []\n```\n\n\u003ch2 id=\"installation\"\u003eInstallation\u003c/h2\u003e\n\n### Dependencies\n\nThese 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.\n\n* Ruby: MRI v2.3-v3.1, JRuby v9.2.5\n* Rails/ActiveRecord: v4.0-v7.0\n\nIf 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.\n\n### Installing\n\nGet it into your Gemfile - and don't forget the version constraint!\n\n```Ruby\ngem 'gutentag', '~\u003e 2.6'\n```\n\nNext: your tags get persisted to your database, so let's import the migrations, update them to your current version of Rails, and then migrate:\n\n```Bash\nbundle exec rake gutentag:install:migrations\nbundle exec rails generate gutentag:migration_versions\nbundle exec rake db:migrate\n```\n\nIf 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).)\n\n### Without Rails\n\nIf 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:\n\n```ruby\ncreate_table :gutentag_tags do |t|\n  t.string :name,           null: false, index: {unique: true}\n  t.bigint :taggings_count, null: false, index: true, default: 0\n  t.timestamps              null: false\nend\n\ncreate_table :gutentag_taggings do |t|\n  t.references :tag,      null: false, index: true, foreign_key: {to_table: :gutentag_tags}\n  t.references :taggable, null: false, index: true, polymorphic: true\n  t.timestamps            null: false\nend\nadd_index :gutentag_taggings, [:taggable_type, :taggable_id, :tag_id], unique: true, name: \"gutentag_taggings_uniqueness\"\n```\n\n\u003ch2 id=\"upgrading\"\u003eUpgrading\u003c/h2\u003e\n\nPlease refer to the [CHANGELOG](CHANGELOG.md), which covers significant and breaking changes between versions.\n\n\u003ch2 id=\"configuration\"\u003eConfiguration\u003c/h2\u003e\n\nGutentag tries to take a convention-over-configuration approach, while also striving to be modular enough to allow changes to behaviour in certain cases.\n\n### Tag validations\n\nThe default validations on `Gutentag::Tag` are:\n\n* presence of the tag name.\n* case-insensitive uniqueness of the tag name.\n* maximum length of the tag name (if the column has a limit).\n\nYou can view the logic for this in [`Gutentag::TagValidations`](lib/gutentag/tag_validations.rb), and you can set an alternative if you wish:\n\n```ruby\nGutentag.tag_validations = CustomTagValidations\n```\n\nThe supplied value must respond to `call`, and the argument supplied is the model.\n\n### Tag normalisation\n\nTag 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.\n\nIf you want to do something different, provide an object that responds to call and accepts a single value to `Gutentag.normaliser`:\n\n```ruby\nGutentag.normaliser = lambda { |value| value.to_s.upcase }\n```\n\n### Case-sensitive tags\n\nGutentag 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.\n\n\u003ch2 id=\"extending\"\u003eExtending\u003c/h2\u003e\n\nIf 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:\n\n```ruby\n# config/initializers/gutentag.rb or equivalent\nRails.application.config.to_prepare do\n  Gutentag::Tag.include TagExtensions\nend\n```\n\nFurther discussion and examples of this can be found in [issue 65](https://github.com/pat/gutentag/issues/65).\n\n\u003ch2 id=\"contribution\"\u003eContribution\u003c/h2\u003e\n\nPlease 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.\n\n\u003ch2 id=\"licence\"\u003eLicence\u003c/h2\u003e\n\nCopyright (c) 2013-2022, Gutentag is developed and maintained by Pat Allan, and is released under the open MIT Licence.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpat%2Fgutentag","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpat%2Fgutentag","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpat%2Fgutentag/lists"}