A Rubygem to add Favorite, Follow, Vote, etc. functionality to ActiveRecord models
actsasfavoritor is a Rubygem to allow any ActiveRecord model to associate any other model including the option for multiple relationships per association with scopes.
You 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.
You can add actsasfavoritor to your
Gemfilewith:
gem 'acts_as_favoritor'
And then run:
$ bundle install
Or install it yourself as:
$ gem install acts_as_favoritor
If you always want to be up to date fetch the latest from GitHub in your
Gemfile:
gem 'acts_as_favoritor', github: 'jonhue/acts_as_favoritor'
Now run the generator:
$ rails g acts_as_favoritor
To wrap things up, migrate the changes into your database:
$ rails db:migrate
Add
acts_as_favoritableto the models you want to be able to get favorited:
class User < ActiveRecord::Base acts_as_favoritable endclass Book < ActiveRecord::Base acts_as_favoritable end
Specify which models can favorite other models by adding
acts_as_favoritor:
class User < ActiveRecord::Base acts_as_favoritor end
acts_as_favoritormethods
book = Book.find(1) user = User.find(1)
user
favoritesbook
.user.favorite(book)
user
removesbook
from favorites.user.unfavorite(book)
Whether
user
has markedbook
as his favorite.user.favorited?(book)
Returns an Active Record relation of
user
'sFavorite
records that have not been blocked.user.all_favorites
Returns an array of all unblocked favorited objects of
user
. This can be a collection of different object types, e.g.:User
,Book
.user.all_favorited
Returns an Active Record relation of
Favorite
records where thefavoritable_type
isBook
.user.favorites_by_type('Book')
Returns an Active Record relation of all favorited objects of
user
wherefavoritable_type
is 'Book'.user.favorited_by_type('Book')
Returns the exact same as
user.favorited_by_type('User')
.user.favorited_users
Whether
user
has been blocked bybook
.user.blocked_by?(book)
Returns an array of all favoritables that blocked
user
.user.blocked_by
acts_as_favoritablemethods
# Returns all favoritors of a model that `acts_as_favoritable` book.favoritorsReturns an Active Record relation of records with type
User
followingbook
.book.favoritors_by_type('User')
Returns the exact same as
book.favoritors_by_type('User')
.book.user_favoritors
Whether
book
has been favorited byuser
.book.favorited_by?(user)
Block a favoritor
book.block(user)
Unblock a favoritor
book.unblock(user)
Whether
book
has blockeduser
as favoritor.book.blocked?(user)
Returns an array of all blocked favoritors.
book.blocked
Favoritemodel
# Returns an Active Record relation of all `Favorite` records where `blocked` is `false`. Favorite.unblockedReturns an Active Record relation of all
Favorite
records whereblocked
istrue
.Favorite.blocked
Returns an Active Record relation of all favorites of
user
, including those who were blocked.Favorite.for_favoritor(user)
Returns an Active Record relation of all favoritors of
book
, including those who were blocked.Favorite.for_favoritable(book)
Using scopes with
acts_as_favoritorenables 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
By default all of your favorites are scoped to
'favorite'.
You can create new scopes on the fly. Every single method takes
scope/
scopesas an option which expexts a symbol or an array of symbols containing your scopes.
So lets see how this works:
user.favorite(book, scopes: [:favorite, :watching]) user.unfavorite(book, scope: :watching) second_user = User.find(2) user.favorite(second_user, scope: :follow)
That's simple!
When 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:
user.favorited?(book, scopes: [:favorite, :watching]) # => { favorite: true, watching: false } user.favorited?(book, scopes: [:favorite]) # => { favorite: true } user.favorited?(book, scope: :favorite) # => true
acts_as_favoritoralso provides some handy scopes for you to call on the
Favoritemodel:
# Returns all `Favorite` records where `scope` is `my_scope` Favorite.send("#{my_scope}_list")Examples
Returns all
Favorite
records wherescope
isfavorites
Favorite.favorite_list
Returns all
Favorite
records wherescope
iswatching
Favorite.watching_list
When you set the option
cachein
config/initializers/acts_as_favoritor.rbto true, you are able to cache the amount of favorites/favoritables an instance has regarding a scope.
For that you need to add some database columns:
actsasfavoritor
add_column :users, :favoritor_score, :text add_column :users, :favoritor_total, :text
actsasfavoritable
add_column :users, :favoritable_score, :text add_column :users, :favoritable_total, :text add_column :books, :favoritable_score, :text add_column :books, :favoritable_total, :text
Caches are stored as hashes with scopes as keys:
user.favoritor_score # => { favorite: 1 } user.favoritor_total # => { favorite: 1, watching: 1 } second_user.favoritable_score # => { follow: 1 } book.favoritable_score # => { favorite: 1 }
Note: Only scopes who have favorites are included.
acts_as_favoritormakes it even simpler to access cached values:
user.favoritor_favorite_cache # => 1 second_user.favoritable_follow_cache # => 1 book.favoritable_favorite_cache # => 1
Note: These methods are available for every scope you are using.
You can configure actsasfavoritor by passing a block to
configure. This can be done in
config/initializers/acts_as_favoritor.rb:
ActsAsFavoritor.configure do |config| config.default_scope = :follow end
default_scopeSpecify your default scope. Takes a string. Defaults to
:favorite. Learn more about scopes here.
cacheWhether
acts_as_favoritoruses caching or not. Takes a boolean. Defaults to
false. Learn more about caching here.
To start development you first have to fork this repository and locally clone your fork.
Install the projects dependencies by running:
$ bundle install
Tests are written with RSpec and can be found in
/spec.
To run tests:
$ bundle exec rspec
To run RuboCop:
$ bundle exec rubocop
We warmly welcome everyone who is intersted in contributing. Please reference our contributing guidelines and our Code of Conduct.
Here you can find details on all past releases. Unreleased breaking changes that are on the current master can be found here.
actsasfavoritor follows Semantic Versioning 2.0 as defined at http://semver.org. Reference our security policy.
CHANGELOG.md.
lib/acts_as_favoritor/version.rb.
CHANGELOG.md.
master.
mastersince the last release.