Skip to content

Commit

Permalink
Create AnonymousUser model.
Browse files Browse the repository at this point in the history
We want to start recording anonymous users so we can store
relevant data such as a fake name, fake avatar, and when
their chatroom was last viewed by an admin.

We generate the relevant migrations and add the fields we
need for an `anonymous_user`. Since the frontend generates
a UUID for every anonymous user, the UUID would be perfect as
`id` for our AnonymousUser records. We use the `uuid` type
for the `id` column of our AnonymousUser and set it to
not autogenerate an id. That way, we use the UUID passed to
us from the frontend as `id` every time we create a new
AnonymousUser record.

Now that we have an AnonymousUser, we can associate it with
Message so we can easily get all the messages sent by a user.
Note that there are a few extra steps for this because we
are using a `:uuid` type as `id` instead of the default
`:integer`.
  • Loading branch information
gjaldon committed Sep 15, 2016
1 parent a8237db commit 06c2f83
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 4 deletions.
2 changes: 2 additions & 0 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ defmodule PhoenixChat.Mixfile do
applications: [
:comeonin,
:cowboy,
:faker,
:gettext,
:logger,
:phoenix,
Expand All @@ -45,6 +46,7 @@ defmodule PhoenixChat.Mixfile do
{:comeonin, "~> 2.3"},
{:corsica, "~> 0.4"},
{:cowboy, "~> 1.0"},
{:faker, "~> 0.7"},
{:gettext, "~> 0.11"},
{:guardian, "~> 0.10"},
{:phoenix, "~> 1.2.0"},
Expand Down
1 change: 1 addition & 0 deletions mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"db_connection": {:hex, :db_connection, "1.0.0-rc.5", "1d9ab6e01387bdf2de7a16c56866971f7c2f75aea7c69cae2a0346e4b537ae0d", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, optional: true]}, {:sbroker, "~> 1.0.0-beta.3", [hex: :sbroker, optional: true]}]},
"decimal": {:hex, :decimal, "1.1.2", "79a769d4657b2d537b51ef3c02d29ab7141d2b486b516c109642d453ee08e00c", [:mix], []},
"ecto": {:hex, :ecto, "2.0.5", "7f4c79ac41ffba1a4c032b69d7045489f0069c256de606523c65d9f8188e502d", [:mix], [{:db_connection, "~> 1.0-rc.4", [hex: :db_connection, optional: true]}, {:decimal, "~> 1.1.2 or ~> 1.2", [hex: :decimal, optional: false]}, {:mariaex, "~> 0.7.7", [hex: :mariaex, optional: true]}, {:poison, "~> 1.5 or ~> 2.0", [hex: :poison, optional: true]}, {:poolboy, "~> 1.5", [hex: :poolboy, optional: false]}, {:postgrex, "~> 0.12.0", [hex: :postgrex, optional: true]}, {:sbroker, "~> 1.0-beta", [hex: :sbroker, optional: true]}]},
"faker": {:hex, :faker, "0.7.0", "2c42deeac7be717173c78c77fb3edc749fb5d5e460e33d01fe592ae99acc2f0d", [:mix], []},
"fs": {:hex, :fs, "0.9.2", "ed17036c26c3f70ac49781ed9220a50c36775c6ca2cf8182d123b6566e49ec59", [:rebar], []},
"gettext": {:hex, :gettext, "0.11.0", "80c1dd42d270482418fa158ec5ba073d2980e3718bacad86f3d4ad71d5667679", [:mix], []},
"guardian": {:hex, :guardian, "0.12.0", "ab1f0a1ab0cd8f4f9c8cca6e28d61136ca682684cf0f82e55a50e8061be7575a", [:mix], [{:jose, "~> 1.6", [hex: :jose, optional: false]}, {:plug, "~> 1.0", [hex: :plug, optional: false]}, {:poison, ">= 1.3.0", [hex: :poison, optional: false]}, {:uuid, ">=1.1.1", [hex: :uuid, optional: false]}]},
Expand Down
18 changes: 18 additions & 0 deletions priv/repo/migrations/20160915111446_create_anonymous_users.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
defmodule PhoenixChat.Repo.Migrations.CreateAnonymousUsers do
use Ecto.Migration

def change do
# We want to use a `uuid` as primary key so we need to set `primary_key: false`.
create table(:anonymous_users, primary_key: false) do
# We add the `:id` column manually with a type of `uuid` and set
# it as `primary_key`.
add :id, :uuid, primary_key: true
add :name, :string
add :avatar, :string
add :public_key, :string
add :last_viewed_by_admin_at, :datetime

timestamps
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
defmodule PhoenixChat.Repo.Migrations.MessageBelongsToAnonymousUser do
use Ecto.Migration

def up do
alter table(:messages) do
# We need to set `type` as `uuid` so it does not default to `integer`.
add :anonymous_user_id, references(:anonymous_users, on_delete: :nilify_all, type: :uuid)
remove :from
end
end

def down do
alter table(:messages) do
remove :anonymous_user_id
add :from, :string
end
end
end
65 changes: 65 additions & 0 deletions web/models/anonymous_user.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
defmodule PhoenixChat.AnonymousUser do
use PhoenixChat.Web, :model

alias PhoenixChat.Message

# Since we provide the `id` for our AnonymousUser record, we will need to set
# the primary key to not autogenerate it.
@primary_key {:id, :binary_id, autogenerate: false}
# We need to set `@foreign_key_type` below since it defaults to `:integer`.
# We are using a UUID as `id` so we need to set type as `:binary_id`.
@foreign_key_type :binary_id

schema "anonymous_users" do
field :name
field :avatar
field :public_key
field :last_viewed_by_admin_at, PhoenixChat.DateTime
has_many :messages, Message

timestamps
end

def changeset(model, params \\ :empty) do
model
|> cast(params, ~w(public_key id), ~w())
|> put_avatar
|> put_name
end

def last_viewed_changeset(model) do
params = %{last_viewed_by_admin_at: System.system_time(:milliseconds)}
model
|> cast(params, ~w(last_viewed_by_admin_at), [])
end

@doc """
This query returns all users and the respective last messages they
have sent.
Once the query is run, the return value is a tuple of two elements:
`{user, message}`
"""
def by_public_key(public_key, limit \\ 20) do
from u in __MODULE__,
join: m in Message, on: m.anonymous_user_id == u.id,
where: u.public_key == ^public_key,
limit: ^limit,
distinct: u.id,
order_by: [desc: m.inserted_at],
select: {u, m}
end

# Set a fake name for our anonymous user every time we create one
defp put_name(changeset) do
name = (Faker.Color.fancy_name <> " " <> Faker.Company.buzzword()) |> String.downcase
changeset
|> put_change(:name, name)
end

# Set a fake avatar for our anonymous user every time we create one
defp put_avatar(changeset) do
changeset
|> put_change(:avatar, Faker.Avatar.image_url(25, 25))
end
end
13 changes: 9 additions & 4 deletions web/models/message.ex
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
defmodule PhoenixChat.Message do
use PhoenixChat.Web, :model

alias PhoenixChat.{DateTime, User, AnonymousUser}

schema "messages" do
field :body, :string
field :timestamp, PhoenixChat.DateTime
field :timestamp, DateTime
field :room, :string
field :from, :string
belongs_to :user, PhoenixChat.User

belongs_to :user, User
# Note that we set `:type` below. This is so Ecto is aware the type of the
# foreign_key is not an `:integer` but a `:binary_id`.
belongs_to :user, AnonymousUser, type: :binary_id

timestamps
end

@required_fields ~w(body timestamp room)
@optional_fields ~w(user_id from)
@optional_fields ~w(anonymous_user_id user_id)

@doc """
Creates a changeset based on the `model` and `params`.
Expand Down

0 comments on commit 06c2f83

Please sign in to comment.