Store different kind of actions (Like ❤️, Follow 👁, Star ⭐, Block ...) in one table via ActiveRecord Polymorphic Association.
Ruby
Latest commit 037dc06 Feb 24, 2017 @huacnlee huacnlee Version 0.2.2

README.md

ActionStore

Gem Version Build Status Code Climate codecov.io

Store different kind of actions (Like, Follow, Star, Block ...) in one table via ActiveRecord Polymorphic Association.

  • Like Post/Comment/Reply ...
  • Watch/Subscribe Post
  • Follow User
  • Favorite Post
  • Read Notification/Message

And more and more.

中文介绍和使用说明

Basic table struct

Field Means
action_type The type of action [like, watch, follow, star, favorite]
action_option Secondly option for store you custom status, or you can let it null if you don't needs it.
target_type, target_id Polymorphic Association for difference Target models [User, Post, Comment]

Usage

gem 'action-store'

and run bundle install

Generate Migrations:

$ rails g action_store:install
create  config/initializers/action_store.rb
migration 20170208024704_create_actions.rb from action_store

and run rails db:migrate.

Define Actions

You can use action_store to define actions:

app/models/user.rb

class User < ActiveRecord::Base
  action_store <action_type>, <target>, opts
end

Convention Over Configuration:

action, target Target Model Target counter_cache_field User counter_cache_field Target has_many User has_many
action_store :like, :post Post has_many :like_by_user_actions, has_many :like_by_users has_many :like_post_actions, has_many :like_posts
action_store :like, :post, counter_cache: true Post likes_count has_many :like_by_user_actions, has_many :like_by_users has_many :like_post_actions, has_many :like_posts
action_store :star, :project, class_name: 'Repository' Repository stars_count star_projects_count has_many :star_by_user_actions, has_many :star_by_users
action_store :follow, :user User follows_count follow_users_count has_many :follow_by_user_actions, has_many :follow_by_users has_many :follow_user_actions, has_many :follow_users
action_store :follow, :user, counter_cache: 'followers_count', user_counter_cache: 'following_count' User followers_count following_count has_many :follow_by_user_actions, has_many :follow_by_users has_many :follow_user_actions, has_many :follow_users

for example:

# app/models/action.rb
class User < ActiveRecord::Base
  action_store :like, :post, counter_cache: true
  action_store :star, :post, counter_cache: true, user_counter_cache: true
  action_store :follow, :post
  action_store :like, :comment, counter_cache: true
  action_store :follow, :user, counter_cache: 'followers_count', user_counter_cache: 'following_count'
end

Counter Cache

And you need add counter_cache field to target, user table.

add_column :users, :star_posts_count, :integer, default: 0
add_column :users, :followers_count, :integer, default: 0
add_column :users, :following_count, :integer, default: 0

add_column :posts, :likes_count, :integer, default: 0
add_column :posts, :stars_count, :integer, default: 0

add_column :comments, :likes_count, :integer, default: 0

Now you can use like this:

@user -> like @post

irb> User.create_action(:like, target: @post, user: @user)
true
irb> @user.create_action(:like, target: @post)
true
irb> @post.reload.likes_count
1

@user1 -> unlike @user2

irb> User.destroy_action(:follow, target: @post, user: @user)
true
irb> @user.destroy_action(:like, target: @post)
true
irb> @post.reload.likes_count
0

Check @user1 is liked @post

irb> action = User.find_action(:follow, target: @post, user: @user)
irb> action = @user.find_action(:like, target: @post)
irb> action.present?
true

User follow cases:

# @user1 -> follow @user2
@user1.create_action(:follow, target: @user2)
@user1.reload.following_count => 1
@user2.reload.followers_count_ => 1
@user1.follow_user?(@user2) => true
# @user2 -> follow @user1
@user2.create_action(:follow, target: @user1)
@user2.follow_user?(@user1) => true
# @user1 -> follow @user3
@user1.create_action(:follow, target: @user3)
# @user1 -> unfollow @user3
 @user1.destroy_action(:follow, target: @user3)

Builtin relations and methods

When you called action_store, ActionStore will define Many-to-Many relations for User and Target model.

for example:

class User < ActiveRecord::Base
  action_store :like, :post
  action_store :block, :user
end

It will defines Many-to-Many relations:

  • For User model will defined: <action>_<target>s (like_posts)
  • For Target model will defined: <action>_by_users (like_by_users)
# for User model
has_many :like_post_actions
has_many :like_posts, through: :like_post_actions
## as user
has_many :block_user_actions
has_many :block_users, through: :block_user_actions
## as target
has_many :block_by_user_actions
has_many :block_by_users, through: :block_by_user_actions

# for Target model
has_many :like_by_user_actions
has_many :like_by_users, through: :like_user_actions

And User model will have methods:

  • @user.create_action(:like, target: @post)
  • @user.destroy_action(:like, target: @post)
  • @user.find_action(:like, target: @post)
  • @user.like_post(@post)
  • @user.like_post?(@post)
  • @user.unlike_post(@post)
  • @user.block_user(@user1)
  • @user.unblock_user(@user1)
  • @user.like_post_ids
  • @user.block_user_ids
  • @user.block_by_user_ids