{"id":13879194,"url":"https://github.com/jonhue/acts_as_favoritor","last_synced_at":"2025-07-16T15:31:41.384Z","repository":{"id":28030526,"uuid":"101085577","full_name":"jonhue/acts_as_favoritor","owner":"jonhue","description":"Adds Favorite, Follow, Vote, etc. functionality to ActiveRecord models","archived":false,"fork":false,"pushed_at":"2024-12-11T09:05:53.000Z","size":1293,"stargazers_count":346,"open_issues_count":7,"forks_count":19,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-05-20T15:12:59.224Z","etag":null,"topics":["acts-as","favorites","rubygem"],"latest_commit_sha":null,"homepage":"","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/jonhue.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-08-22T16:45:11.000Z","updated_at":"2025-05-12T06:52:04.000Z","dependencies_parsed_at":"2024-02-18T09:24:56.518Z","dependency_job_id":"91a51aa3-740a-4d61-baec-a56cf30c0d86","html_url":"https://github.com/jonhue/acts_as_favoritor","commit_stats":{"total_commits":392,"total_committers":10,"mean_commits":39.2,"dds":0.5408163265306123,"last_synced_commit":"6cdda0071ebe5bab64342a116726fe6f0429d95f"},"previous_names":[],"tags_count":28,"template":false,"template_full_name":null,"purl":"pkg:github/jonhue/acts_as_favoritor","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonhue%2Facts_as_favoritor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonhue%2Facts_as_favoritor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonhue%2Facts_as_favoritor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonhue%2Facts_as_favoritor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jonhue","download_url":"https://codeload.github.com/jonhue/acts_as_favoritor/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonhue%2Facts_as_favoritor/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265521431,"owners_count":23781501,"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":["acts-as","favorites","rubygem"],"created_at":"2024-08-06T08:02:13.043Z","updated_at":"2025-07-16T15:31:40.399Z","avatar_url":"https://github.com/jonhue.png","language":"Ruby","funding_links":[],"categories":["Ruby"],"sub_categories":[],"readme":"# acts_as_favoritor\r\n\r\nacts_as_favoritor is a Rubygem to allow any ActiveRecord model to associate any other model including the option for multiple relationships per association with scopes.\r\n\r\nYou are able to differentiate followers, favorites, watchers, votes and whatever else you can imagine through a single relationship. This is accomplished by a double polymorphic relationship on the Favorite model. There is also built in support for blocking/un-blocking favorite records as well as caching.\r\n\r\n[This Medium article](https://medium.com/swlh/add-dynamic-like-dislike-buttons-to-your-rails-6-application-ccce8a234c43) gives a good introduction to this gem.\r\n\r\n## Installation\r\n\r\nYou can add acts_as_favoritor to your `Gemfile` with:\r\n\r\n```ruby\r\ngem 'acts_as_favoritor'\r\n```\r\n\r\nAnd then run:\r\n\r\n    $ bundle install\r\n\r\nOr install it yourself as:\r\n\r\n    $ gem install acts_as_favoritor\r\n\r\nIf you always want to be up to date fetch the latest from GitHub in your `Gemfile`:\r\n\r\n```ruby\r\ngem 'acts_as_favoritor', github: 'jonhue/acts_as_favoritor'\r\n```\r\n\r\nNow run the generator:\r\n\r\n    $ rails g acts_as_favoritor\r\n\r\nTo wrap things up, migrate the changes into your database:\r\n\r\n    $ rails db:migrate\r\n\r\n## Usage\r\n\r\n### Setup\r\n\r\nAdd `acts_as_favoritable` to the models you want to be able to get favorited:\r\n\r\n```ruby\r\nclass User \u003c ActiveRecord::Base\r\n  acts_as_favoritable\r\nend\r\n\r\nclass Book \u003c ActiveRecord::Base\r\n  acts_as_favoritable\r\nend\r\n```\r\n\r\nSpecify which models can favorite other models by adding `acts_as_favoritor`:\r\n\r\n```ruby\r\nclass User \u003c ActiveRecord::Base\r\n  acts_as_favoritor\r\nend\r\n```\r\n\r\n### `acts_as_favoritor` methods\r\n\r\n```ruby\r\nbook = Book.find(1)\r\nuser = User.find(1)\r\n\r\n# `user` favorites `book`.\r\nuser.favorite(book)\r\n\r\n# `user` removes `book` from favorites.\r\nuser.unfavorite(book)\r\n\r\n# Whether `user` has marked `book` as his favorite.\r\nuser.favorited?(book)\r\n\r\n# Returns an Active Record relation of `user`'s `Favorite` records that have not been blocked.\r\nuser.all_favorites\r\n\r\n# Returns an array of all unblocked favorited objects of `user`. This can be a collection of different object types, e.g.: `User`, `Book`.\r\nuser.all_favorited\r\n\r\n# Returns an Active Record relation of `Favorite` records where the `favoritable_type` is `Book`.\r\nuser.favorites_by_type('Book')\r\n\r\n# Returns an Active Record relation of all favorited objects of `user` where `favoritable_type` is 'Book'.\r\nuser.favorited_by_type('Book')\r\n\r\n# Returns the exact same as `user.favorited_by_type('User')`.\r\nuser.favorited_users\r\n\r\n# Whether `user` has been blocked by `book`.\r\nuser.blocked_by?(book)\r\n\r\n# Returns an array of all favoritables that blocked `user`.\r\nuser.blocked_by\r\n```\r\n\r\n### `acts_as_favoritable` methods\r\n\r\n```ruby\r\n# Returns all favoritors of a model that `acts_as_favoritable`\r\nbook.favoritors\r\n\r\n# Returns an Active Record relation of records with type `User` following `book`.\r\nbook.favoritors_by_type('User')\r\n\r\n# Returns the exact same as `book.favoritors_by_type('User')`.\r\nbook.user_favoritors\r\n\r\n# Whether `book` has been favorited by `user`.\r\nbook.favorited_by?(user)\r\n\r\n# Block a favoritor\r\nbook.block(user)\r\n\r\n# Unblock a favoritor\r\nbook.unblock(user)\r\n\r\n# Whether `book` has blocked `user` as favoritor.\r\nbook.blocked?(user)\r\n\r\n# Returns an array of all blocked favoritors.\r\nbook.blocked\r\n```\r\n\r\n### `Favorite` model\r\n\r\n```ruby\r\n# Returns an Active Record relation of all `Favorite` records where `blocked` is `false`.\r\nFavorite.unblocked\r\n\r\n# Returns an Active Record relation of all `Favorite` records where `blocked` is `true`.\r\nFavorite.blocked\r\n\r\n# Returns an Active Record relation of all favorites of `user`, including those who were blocked.\r\nFavorite.for_favoritor(user)\r\n\r\n# Returns an Active Record relation of all favoritors of `book`, including those who were blocked.\r\nFavorite.for_favoritable(book)\r\n```\r\n\r\n### Scopes\r\n\r\nUsing scopes with `acts_as_favoritor` enables you to Follow, Watch, Favorite, [...] between any of your models. This way you can separate distinct functionalities in your app between user states. For example: A user sees all his favorited books in a dashboard (`'favorite'`), but he only receives notifications for those, he is watching (`'watch'`). Just like YouTube or GitHub do it. Options are endless. You could also integrate a voting / star system similar to YouTube or GitHub\r\n\r\nBy default all of your favorites are scoped to `'favorite'`.\r\n\r\nYou can create new scopes on the fly. Every single method takes `scope`/`scopes` as an option which expects a symbol or an array of symbols containing your scopes.\r\n\r\nSo lets see how this works:\r\n\r\n```ruby\r\nuser.favorite(book, scopes: [:favorite, :watching])\r\nuser.unfavorite(book, scope: :watching)\r\nsecond_user = User.find(2)\r\nuser.favorite(second_user, scope: :follow)\r\n```\r\n\r\nThat's simple!\r\n\r\nWhen you call a method which returns something while specifying multiple scopes, the method returns the results in a hash with the scopes as keys when scopes are given as an array:\r\n\r\n```ruby\r\nuser.favorited?(book, scopes: [:favorite, :watching]) # =\u003e { favorite: true, watching: false }\r\nuser.favorited?(book, scopes: [:favorite]) # =\u003e { favorite: true }\r\nuser.favorited?(book, scope: :favorite) # =\u003e true\r\n```\r\n\r\n`acts_as_favoritor` also provides some handy scopes for you to call on the `Favorite` model:\r\n\r\n```ruby\r\n# Returns all `Favorite` records where `scope` is `my_scope`\r\nFavorite.send(\"#{my_scope}_list\")\r\n\r\n## Examples\r\n### Returns all `Favorite` records where `scope` is `favorites`\r\nFavorite.favorite_list\r\n### Returns all `Favorite` records where `scope` is `watching`\r\nFavorite.watching_list\r\n```\r\n\r\n### Caching\r\n\r\nWhen you set the option `cache` in `config/initializers/acts_as_favoritor.rb` to true, you are able to cache the amount of favorites/favoritables an instance has regarding a scope.\r\n\r\nFor that you need to add some database columns:\r\n\r\n*acts_as_favoritor*\r\n\r\n```ruby\r\nadd_column :users, :favoritor_score, :text\r\nadd_column :users, :favoritor_total, :text\r\n```\r\n\r\n*acts_as_favoritable*\r\n\r\n```ruby\r\nadd_column :users, :favoritable_score, :text\r\nadd_column :users, :favoritable_total, :text\r\nadd_column :books, :favoritable_score, :text\r\nadd_column :books, :favoritable_total, :text\r\n```\r\n\r\nCaches are stored as hashes with scopes as keys:\r\n\r\n```ruby\r\nuser.favoritor_score # =\u003e { favorite: 1 }\r\nuser.favoritor_total # =\u003e { favorite: 1, watching: 1 }\r\nsecond_user.favoritable_score # =\u003e { follow: 1 }\r\nbook.favoritable_score # =\u003e { favorite: 1 }\r\n```\r\n\r\n**Note:** Only scopes who have favorites are included.\r\n\r\n`acts_as_favoritor` makes it even simpler to access cached values:\r\n\r\n```ruby\r\nuser.favoritor_favorite_cache # =\u003e 1\r\nsecond_user.favoritable_follow_cache # =\u003e 1\r\nbook.favoritable_favorite_cache # =\u003e 1\r\n```\r\n\r\n**Note:** These methods are available for every scope you are using.\r\n\r\nThe total counts all favorites that were recorded, while the score factors in favorites that were removed. In most use cases the score is the most useful.\r\n\r\n## Configuration\r\n\r\nYou can configure acts_as_favoritor by passing a block to `configure`. This can be done in `config/initializers/acts_as_favoritor.rb`:\r\n\r\n```ruby\r\nActsAsFavoritor.configure do |config|\r\n  config.default_scope = :follow\r\nend\r\n```\r\n\r\n**`default_scope`** Specify your default scope. Takes a string. Defaults to `:favorite`. Learn more about scopes [here](#scopes).\r\n\r\n**`cache`** Whether `acts_as_favoritor` uses caching or not. Takes a boolean. Defaults to `false`. Learn more about caching [here](#caching).\r\n\r\n## Development\r\n\r\nTo start development you first have to fork this repository and locally clone your fork.\r\n\r\nInstall the projects dependencies by running:\r\n\r\n    $ bundle install\r\n\r\n### Testing\r\n\r\nTests are written with RSpec and can be found in `/spec`.\r\n\r\nTo run tests:\r\n\r\n    $ bundle exec rspec\r\n\r\nTo run RuboCop:\r\n\r\n    $ bundle exec rubocop\r\n\r\n## Contributing\r\n\r\nWe warmly welcome everyone who is intersted in contributing. Please reference our [contributing guidelines](CONTRIBUTING.md) and our [Code of Conduct](CODE_OF_CONDUCT.md).\r\n\r\n## Releases\r\n\r\n[Here](https://github.com/jonhue/acts_as_favoritor/releases) you can find details on all past releases. Unreleased breaking changes that are on the current `main` can be found [here](CHANGELOG.md).\r\n\r\nacts_as_favoritor follows Semantic Versioning 2.0 as defined at http://semver.org. Reference our [security policy](SECURITY.md).\r\n\r\n### Publishing\r\n\r\n1. Review breaking changes and deprecations in `CHANGELOG.md`.\r\n1. Change the gem version in `lib/acts_as_favoritor/version.rb`.\r\n1. Reset `CHANGELOG.md`.\r\n1. Create a pull request to merge the changes into `main`.\r\n1. After the pull request was merged, create a new release listing the breaking changes and commits on `main` since the last release.\r\n2. The release workflow will publish the gem to RubyGems.\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjonhue%2Facts_as_favoritor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjonhue%2Facts_as_favoritor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjonhue%2Facts_as_favoritor/lists"}