• Stars
    star
    346
  • Rank 122,430 (Top 3 %)
  • Language
    Ruby
  • License
    MIT License
  • Created over 7 years ago
  • Updated 2 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Adds Favorite, Follow, Vote, etc. functionality to ActiveRecord models

acts_as_favoritor

acts_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.

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.

This Medium article gives a good introduction to this gem.

Installation

You can add acts_as_favoritor to your Gemfile with:

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

Usage

Setup

Add acts_as_favoritable to the models you want to be able to get favorited:

class User < ActiveRecord::Base
  acts_as_favoritable
end

class 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_favoritor methods

book = Book.find(1)
user = User.find(1)

# `user` favorites `book`.
user.favorite(book)

# `user` removes `book` from favorites.
user.unfavorite(book)

# Whether `user` has marked `book` as his favorite.
user.favorited?(book)

# Returns an Active Record relation of `user`'s `Favorite` 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 the `favoritable_type` is `Book`.
user.favorites_by_type('Book')

# Returns an Active Record relation of all favorited objects of `user` where `favoritable_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 by `book`.
user.blocked_by?(book)

# Returns an array of all favoritables that blocked `user`.
user.blocked_by

acts_as_favoritable methods

# Returns all favoritors of a model that `acts_as_favoritable`
book.favoritors

# Returns an Active Record relation of records with type `User` following `book`.
book.favoritors_by_type('User')

# Returns the exact same as `book.favoritors_by_type('User')`.
book.user_favoritors

# Whether `book` has been favorited by `user`.
book.favorited_by?(user)

# Block a favoritor
book.block(user)

# Unblock a favoritor
book.unblock(user)

# Whether `book` has blocked `user` as favoritor.
book.blocked?(user)

# Returns an array of all blocked favoritors.
book.blocked

Favorite model

# Returns an Active Record relation of all `Favorite` records where `blocked` is `false`.
Favorite.unblocked

# Returns an Active Record relation of all `Favorite` records where `blocked` is `true`.
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)

Scopes

Using 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

By default all of your favorites are scoped to 'favorite'.

You 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.

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_favoritor also provides some handy scopes for you to call on the Favorite model:

# Returns all `Favorite` records where `scope` is `my_scope`
Favorite.send("#{my_scope}_list")

## Examples
### Returns all `Favorite` records where `scope` is `favorites`
Favorite.favorite_list
### Returns all `Favorite` records where `scope` is `watching`
Favorite.watching_list

Caching

When 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.

For that you need to add some database columns:

acts_as_favoritor

add_column :users, :favoritor_score, :text
add_column :users, :favoritor_total, :text

acts_as_favoritable

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_favoritor makes 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.

The 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.

Configuration

You can configure acts_as_favoritor 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_scope Specify your default scope. Takes a string. Defaults to :favorite. Learn more about scopes here.

cache Whether acts_as_favoritor uses caching or not. Takes a boolean. Defaults to false. Learn more about caching here.

Development

To start development you first have to fork this repository and locally clone your fork.

Install the projects dependencies by running:

$ bundle install

Testing

Tests are written with RSpec and can be found in /spec.

To run tests:

$ bundle exec rspec

To run RuboCop:

$ bundle exec rubocop

Contributing

We warmly welcome everyone who is intersted in contributing. Please reference our contributing guidelines and our Code of Conduct.

Releases

Here you can find details on all past releases. Unreleased breaking changes that are on the current main can be found here.

acts_as_favoritor follows Semantic Versioning 2.0 as defined at http://semver.org. Reference our security policy.

Publishing

  1. Review breaking changes and deprecations in CHANGELOG.md.
  2. Change the gem version in lib/acts_as_favoritor/version.rb.
  3. Reset CHANGELOG.md.
  4. Create a pull request to merge the changes into main.
  5. After the pull request was merged, create a new release listing the breaking changes and commits on main since the last release.
  6. The release workflow will publish the gem to RubyGems.

More Repositories

1

notifications-rails

A flexible notification library supporting the delivery to external services, rendering in various environments, and user configuration by category.
Ruby
242
star
2

pwa

Progressive Web Apps for Rails
Ruby
144
star
3

turbolinks-animate

Rich & adaptive animations for apps using Turbolinks
JavaScript
134
star
4

pubtex

Publicly host LaTeX files on GitHub Pages
18
star
5

turbolinks-animate-rails

Rich & adaptive animations for apps using Rails with Turbolinks
Ruby
13
star
6

amp-html

Reliable high-⚡ apps across devices and distribution platforms with AMP & Rails.
Ruby
6
star
7

omniauth-paypal-oauth2

OAuth2 strategy for PayPal
Ruby
6
star
8

plaain

A serverless web app to organize and stream media from anywhere.
TypeScript
5
star
9

onsignal-rails

OneSignal API wrapper simplifying user targeted cross platform notifications
Ruby
5
star
10

onsignal

OneSignal API wrapper simplifying user targeted cross platform notifications
JavaScript
4
star
11

themer

Themes for Rails apps
Ruby
3
star
12

soco

Algorithms for Smoothed Online Convex Optimization
Jupyter Notebook
2
star
13

teaching-fpv-rev

Functional Programming and Verification revision course
TeX
2
star
14

modalist

A powerful & (really) lightweight asynchronous modal plugin
JavaScript
2
star
15

myg-rails

Myg on Rails
Ruby
2
star
16

ahoy-views

Track views of ActiveRecord objects in Rails
Ruby
2
star
17

pubtex-action

A GitHub action to publicly host LaTeX files on GitHub Pages
Shell
2
star
18

blurry.js

Blurry image loading with StackBlur
JavaScript
2
star
19

sandboxy

Virtual data-oriented environments for Rails
Ruby
2
star
20

tip-the-world-cup

Tip the 2018 World Cup with friends & family
Ruby
2
star
21

graph-algorithms-and-optimization

TeX
1
star
22

transductive-active-learning

Transductive Active Learning with Application to Safe Bayesian Optimization
Python
1
star
23

teaching-dwt-rev

Discrete Probability Theory revision course
TeX
1
star
24

learning-halfspaces-with-massart-noise

Implementation of "Distribution-Independent PAC Learning of Halfspaces with Massart Noise"
Jupyter Notebook
1
star
25

mozaic

An opinionated layout engine for Rails
Ruby
1
star
26

myg

An opinionated & modular state-of-the-web framework
CSS
1
star
27

r404

Error handler & renderer for Rails
Ruby
1
star
28

vscode-cyp

VS Code syntax highlighting for cyp
1
star
29

material-components-web

Provides a set of helpers simplifying the use of the Material Components Web library
Ruby
1
star
30

lovasz-local-lemma-deterministic-algorithms

Talk on Deterministic Algorithms for the Lovász Local Lemma
TeX
1
star
31

algorithms-lab

Solutions for the problems posed during the Algorithms Lab of 2021 at ETH Zurich.
C++
1
star
32

cancancan-system

Conventions & helpers simplifying the use of CanCanCan in complex Rails applications
Ruby
1
star
33

teaching-theo

Theoretical Computer Science teaching materials
TeX
1
star