email 规则

邮箱的限制有如下三个:

因为我们在前几章的 username 里涉及过这三个规则,所以这里不再啰嗦分出章节。

email 必填

首先,添加测试规则,验证 email 为空时的错误提示:

diff --git a/test/tv_recipe/users_test.exs b/test/tv_recipe/users_test.exs
index f70d4a1..bae1e57 100644
--- a/test/tv_recipe/users_test.exs
+++ b/test/tv_recipe/users_test.exs
@@ -70,4 +70,9 @@ defmodule TvRecipe.UserTest do
     assert %{username: ["系统保留,无法注册,请更换"]} = errors_on(%User{}, %{@valid_attrs | username: "admin"})
     assert %{username: ["系统保留,无法注册,请更换"]} = errors_on(%User{}, %{@valid_attrs | username: "administrator"})
   end
+
+  test "email should not be blank" do
+    attrs = %{@valid_attrs | email: ""}
+    assert %{email: ["请填写"]} = errors_on(%User{}, attrs)
+  end
 end

因为我们前面在处理 username 必填时,也一起处理过 email,所以这个测试是会通过的。

email 格式

因为邮箱地址的格式花样太多,所以这里只简单验证用户填写的邮箱地址中是否包含 @ 字符。一般情况下,在用户注册成功后,系统会发送一封确认邮件到用户邮箱,但因为邮件系统涉及第三方服务,所以本教程不做展开。

我们先添加一个测试:

diff --git a/test/tv_recipe/users_test.exs b/test/tv_recipe/users_test.exs
index bae1e57..67aab23 100644
--- a/test/tv_recipe/users_test.exs
+++ b/test/tv_recipe/users_test.exs
@@ -75,4 +75,9 @@ defmodule TvRecipe.UserTest do
     attrs = %{@valid_attrs | email: ""}
     assert %{email: ["请填写"]} = errors_on(%User{}, attrs)
   end
+
+  test "email should contain @" do
+    attrs = %{@valid_attrs | email: "ab"}
+    assert %{email: ["邮箱格式错误"]} = errors_on(%User{}, attrs)
+  end
 end

因为我们的规则还没写,所以测试不会通过。

下面在 user.ex 文件中添加 validate_format 验证规则:

diff --git a/lib/tv_recipe/users/user.ex b/lib/tv_recipe/users/user.ex
index 35e4d0b..fef942b 100644
--- a/lib/tv_recipe/users/user.ex
+++ b/lib/tv_recipe/users/user.ex
@@ -21,6 +21,7 @@ defmodule TvRecipe.User do
     |> validate_length(:username, max: 15, message: "用户名最长 15 位")
     |> validate_exclusion(:username, ~w(admin administrator), message: "系统保留,无法注册,请更换")
     |> unique_constraint(:username, name: :users_lower_username_index, message: "用户名已被人占用")
+    |> validate_format(:email, ~r/@/, message: "邮箱格式错误")
     |> unique_constraint(:email)
   end
 end

再运行测试,悉数通过。

email 不允许重复

仍是先写测试:

diff --git a/test/tv_recipe/users_test.exs b/test/tv_recipe/users_test.exs
index 67aab23..f6c99e5 100644
--- a/test/tv_recipe/users_test.exs
+++ b/test/tv_recipe/users_test.exs
@@ -80,4 +80,16 @@ defmodule TvRecipe.UserTest do
     attrs = %{@valid_attrs | email: "ab"}
     assert %{email: ["邮箱格式错误"]} = errors_on(%User{}, attrs)
   end
+
+  test "email should be unique" do
+    # 在测试数据库中插入新用户
+    user_changeset = User.changeset(%User{}, @valid_attrs)
+    TvRecipe.Repo.insert! user_changeset
+
+    # 尝试插入同邮箱地址的用户,应报告错误
+    assert {:error, changeset} = TvRecipe.Repo.insert(User.changeset(%User{}, %{@valid_attrs | username: "samchen"}))
+
+    # 错误信息为“邮箱已被人占用”
+    assert %{email: ["邮箱已被人占用"]} = errors_on(changeset)
+  end
 end

然后给 unique_constraint 添加自定义消息。

打开 user.ex 文件,添加 message 如下:

diff --git a/lib/tv_recipe/users/user.ex b/lib/tv_recipe/users/user.ex
index fef942b..54e7e4c 100644
--- a/lib/tv_recipe/users/user.ex
+++ b/lib/tv_recipe/users/user.ex
@@ -22,6 +22,6 @@ defmodule TvRecipe.User do
     |> validate_exclusion(:username, ~w(admin administrator), message: "系统保留,无法注册,请更换")
     |> unique_constraint(:username, name: :users_lower_username_index, message: "用户名已被人占用")
     |> validate_format(:email, ~r/@/, message: "邮箱格式错误")
-    |> unique_constraint(:email)
+    |> unique_constraint(:email, message: "邮箱已被人占用")
   end
 end

再运行测试,就都通过了。

最后,还有一个测试,是关于 email 大小写的,即 a@bA@b 应当认为是一致的:

diff --git a/test/tv_recipe/users_test.exs b/test/tv_recipe/users_test.exs
index f6c99e5..82dcf6a 100644
--- a/test/tv_recipe/users_test.exs
+++ b/test/tv_recipe/users_test.exs
@@ -92,4 +92,14 @@ defmodule TvRecipe.UserTest do
     # 错误信息为“邮箱已被人占用”
     assert %{email: ["邮箱已被人占用"]} = errors_on(changeset)
   end
+
+  test "email should be case insensitive" do
+    user_changeset = User.changeset(%User{}, @valid_attrs)
+    TvRecipe.Repo.insert! user_changeset
+
+    # 尝试插入大小写不一致的邮箱,应报告错误
+    another_user_changeset = User.changeset(%User{}, %{@valid_attrs | username: "samchen", email: "chenXsan@gmail.com"})
+    assert {:error, changeset} = TvRecipe.Repo.insert(another_user_changeset)
+    assert %{email: ["邮箱已被人占用"]} = errors_on(changeset)
+  end
 end

现在,测试是失败的。

如果你忘了接下来要怎么处理,请先打开 username 已被人占用一章,回顾一下。

我们的步骤是这样的:

  1. 执行 mix ecto.gen.migration alter_email_index 命令新建一个 migration 文件:

    $ mix ecto.gen.migration alter_email_index
    Compiling 2 files (.ex)
    * creating priv/repo/migrations
    * creating priv/repo/migrations/20170124142809_alter_email_index.exs
  2. 打开新建的 20170124142809_alter_email_index.exs 文件,做如下修改:

    diff --git a/priv/repo/migrations/20170124142809_alter_email_index.exs b/priv/repo/migrations/20170124142809_alter_email_index.exs
    index 313bda6..746a00d 100644
    --- a/priv/repo/migrations/20170124142809_alter_email_index.exs
    +++ b/priv/repo/migrations/20170124142809_alter_email_index.exs
    @@ -2,6 +2,7 @@ defmodule TvRecipe.Repo.Migrations.AlterEmailIndex do
      use Ecto.Migration
    
      def change do
    -
    +    drop index(:users, [:email]) # 移除旧索引
    +    create unique_index(:users, ["lower(email)"]) # 增加新索引
      end
    end
  3. 接着在命令行下执行 mix ecto.migrate 命令:

    $ mix ecto.migrate
    
    22:30:46.531 [info]  == Running TvRecipe.Repo.Migrations.AlterEmailIndex.change/0 forward
    
    22:30:46.531 [info]  drop index users_email_index
    
    22:30:46.532 [info]  create index users_lower_email_index
    
    22:30:46.568 [info]  == Migrated in 0.0s
  4. 最后,将新索引的名称赋给 unique_constraintname 参数:

    diff --git a/lib/tv_recipe/users/user.ex b/lib/tv_recipe/users/user.ex
    index 54e7e4c..9307a3c 100644
    --- a/lib/tv_recipe/users/user.ex
    +++ b/lib/tv_recipe/users/user.ex
    @@ -22,6 +22,6 @@ defmodule TvRecipe.User do
        |> validate_exclusion(:username, ~w(admin administrator), message: "系统保留,无法注册,请更换")
        |> unique_constraint(:username, name: :users_lower_username_index, message: "用户名已被人占用")
        |> validate_format(:email, ~r/@/, message: "邮箱格式错误")
    -    |> unique_constraint(:email, message: "邮箱已被人占用")
    +    |> unique_constraint(:email, name: :users_lower_email_index, message: "邮箱已被人占用")
      end
    end

再跑一遍测试:

$ mix test test/tv_recipe/users_test.exs
..............

Finished in 0.2 seconds
14 tests, 0 failures

通过了。这样,我们就搞定了 email 所有规则。如果你心里不踏实,可以打开浏览器页面人肉测试一番 - 但建议你不要,要控制住这种无用的欲望。

下一章,我们开始编写 password 规则

上一章:禁止用户注册 admin 下一章:password 规则

最后更新于