Implements 👍 vote / ❤️ like / follow functionality for Ecto models in Elixir
Inspired from Acts as Votable ⭐ in Ruby on Rails
- Any model can be voted
- Any model can vote
- Supports self referential voting
- Easy to understand syntax
Add Votex to your project dependencies mix.exs
defp deps do
[{:votex, "~> 0.3.0"}]
end
Specify your root project's repo in config
config :votex, Votex.DB,
repo: MyApp.Repo
Votex needs a table in DB to store votes information. Install votex and generate votex schema migration
mix deps.get
mix votex.gen.migration
mix ecto.migrate
defmodule User do
use Ecto.Schema
use Votex.Voter
end
defmodule Post do
use Ecto.Schema
use Votex.Votable
end
post |> Post.vote_by user
user |> User.voted_for? post
# true
post |> Post.votes_for |> length
# 1
post |> Post.votes_for
[
%{
id: 1,
votable_id: 3,
votable_type: "posts",
voter: %Sample.User{
id: 5,
name: "John"
},
voter_id: 5,
voter_type: "users"
}
]
post |> Post.unvote_by user
defmodule User do
use Ecto.Schema
use Votex.Voter
use Votex.Votable
end
user2 |> User.vote_by user1
# {:ok, _}
Since polymorphic associations are not supported in Ecto and callbacks are deprecated, orphan votes need to be cleared when a parent entity is destroyed. Therefore, you need to call cleanup_votes when you delete a Voter or a Votable record.
# Delete user
Repo.delete(user) |> User.cleanup_votes
# Delete post
Repo.delete(post) |> Post.cleanup_votes
Some fields like total number of votes on a post can be cached in posts table to avoid extra calls to DB. Votex will update the field if present in schema.
defmodule Post do
use Ecto.Schema
use Votex.Votable
schema "posts" do
field(:cached_votes_for_total, :integer)
end
@fields ~w(cached_votes_for_total)a
# Publicly accessible changeset for Votex to update field
def changeset(post, attrs) do
post
|> cast(attrs, @fields)
|> validate_required(@fields)
end
end
The posts table will track total votes from now on
post |> Post.vote_by user
post = Repo.get(Post, 1)
post.cached_votes_for_total
# 5