Rails Tutorial 拡張機能の返信機能を作ってみた(その4)：ユーザーをユニークにする機能

Rails Tutorialの第14章にある、返信機能を作る件の続きです。
前回まででreplyの入力と表示までできました。次にユーザーをユニークにするところを作ります。

###ユーザーをユニークにする機能
####modelの変更 設計
tutorialにあるヒントの
>ユーザー登録の項目に一意のユーザー名を追加し、@replyで使えるようにすることです。

を踏まえ、modelに列を追加することにします。

同じように列を追加するやり方を、tutorialで探します。

userモデルに列を追加したのは9章にありました。
念のため、ソースと比べて最新なのか確認します。sample_app/db/schema.rb
を見たところ、adminやreset_sent_atなど大分違います。migrate以下のフォルダにもいくつかありました。
テキストの後ろから「migra」で検索してみます。すると、12章で引っかかりました。リスト12.6で列を追加していました。図12.5が最新でした。

列を追加するイメージを、tutorialと同様に図にします。

|列名|　　属性　　　|
|:-:|:-:|
|id   | integer  |
|name   | string  |
| email  |  string  |
|..  |..   |
|reset_sent_at  | datetime  |
|unique_name   | string  |


図．ユニークな名前のカラムを追加したUserモデル

ユニークなindexを作ったところをtutorialで探します。「index」で検索してみます。すると6章で引っかかりました。リスト6.29でメールアドレスにindexを追加していました。

####modelの変更 コーディング
modelに列を追加します。

```
ubuntu:~/environment/sample_app (reply-micropost) $ rails generate migration add_unique_name_to_users unique_name:string
Running via Spring preloader in process 2911
      invoke  active_record
      create    db/migrate/20201018232033_add_unique_name_to_users.rb
```

indexを作る行を、リスト6.29を参考にして手で入力します。

```ruby:db/migrate/20201018232033_add_unique_name_to_users.rb
class AddUniqueNameToUsers < ActiveRecord::Migration[5.1]
  def change
    add_column :users, :unique_name, :string
    add_index :users, :unique_name, unique: true
  end
end
```

データベースに変更を加えます。

```
ubuntu:~/environment/sample_app (reply-micropost) $ rails db:migrate
```

####modelの変更 テスト
ユニークな名前のテストを作ります。先に作っておいたほうがよかったのかもしれません。
user_test.rbにテストを追加します。

```ruby:test/models/user_test.rb　green
  def setup
    @user = User.new(name: "Example User", email: "user@example.com",
                     password: "foobar", password_confirmation: "foobar",
                     unique_name: "Example")
  end
```

```ruby:test/models/user_test.rb　red
   test "unique_name should be present" do
    @user.unique_name = "    "
    assert_not @user.valid?
  end
```
emailと同様なテストを作ります。
テストを作ってみて、ユニークだがブランクを許していることが分かりました。

ブランクを許さないようにする機能は、email列で同様に作っていたので、tutorialで探します。リスト 6.9にありました。同様に追加します。

```ruby:app/models/user.rb
  validates :password, presence: true, length: {minimum: 6}, allow_nil: true
  validates :unique_name, presence: true, length: {maximum: 50}, uniqueness: true
```

ユニークであること、ブランクを許さないことをテストできました。

```ruby:test/models/user_test.rb　green
  test "unique_name shuould be unique" do
    duplicate_user = @user.dup
    duplicate_user.email = @user.email.upcase + "aa"
    @user.save
    assert_not duplicate_user.valid? 
  end
```

コンソールでテストデータを確認してみます。

```
>> user = User.first
>> user.valid?
=> false
>> user.errors.full_messages
=> ["Unique name can't be blank", "Unique name has already been taken"]
```

2人目も同じなのでテストデータを修正します。unique_nameを入れるように変更します。
nameから姓名の名の部分、英語だとfirst nameを取りだしてunique_nameに入れることにします。

```ruby:db/seeds.rb
99.times do |n|
  name  = Faker::Name.name
  email = "example-#{n+1}@railstutorial.org"
  password = "password"
  unique_name = name.split(' ')[0]
  User.create!( name: name,
                email: email,
                password:              password,
                password_confirmation: password,
                activated: true,
                activated_at: Time.zone.now,
                unique_name: unique_name)
end
```

データをデータベースに入れ直します。

```
$ rails db:migrate:reset
ubuntu:~/environment/sample_app (reply-micropost) $ rails db:seed
rails aborted!
ActiveRecord::RecordInvalid: Validation failed: Unique name has already been taken
/home/ubuntu/.rvm/gems/ruby-2.6.3/gems/activerecord-5.1.6/lib/active_record/validations.rb:78:in `raise_validation_error'
```

エラーが出ました。何件データが入ったのかコンソールで見てみます。

```
> User.count
   (0.2ms)  SELECT COUNT(*) FROM "users"
=> 14
```

100件入るはずが、14件しか入っていません。エラー・メッセージに「Unique name has already been taken」とあるので、unique_nameが重複しているのではと考えます。
なぜ重複するのかコンソールで試してみます。

```
ubuntu:~/environment/sample_app (reply-micropost) $ rails console
>> 99.times do |n|
?> name = Faker::Name.name
>> puts name.split(' ')[0].split(' ')[0]
>> end
Mr.
Forest
Araceli
Mrs.
Cody
Mr.
```

first_nameを取り出せると踏んでいたのですが、nameの先頭の単語がMR.になっていました。MR.はMrsなどいろいろバリエーションがありそうで、単純なロジックでユニークにするのは難しいと考え、取り出すのは諦めることにします。
nameとunique_nameは関連がない値でよしとして、変更します。

```ruby:db/seeds.rb
99.times do |n|
  name  = Faker::Name.name
  email = "example-#{n+1}@railstutorial.org"
  password = "password"
  unique_name = Faker::Name.first_name
  User.create!( name: name,
                email: email,
                password:              password,
                password_confirmation: password,
                activated: true,
                activated_at: Time.zone.now,
                unique_name: unique_name)
end
```

改めてデータをデータベースに入れ直します。その際に、既にデータが入っているので、いったん初期化します。

```
ubuntu:~/environment/sample_app (reply-micropost) $ rails db:migrate:reset
ubuntu:~/environment/sample_app (reply-micropost) $ rails db:seed
```

エラーは起きませんでした。データが入ったかコンソールでデータを見てみます。

```
>> User.last
  User Load (0.2ms)  SELECT  "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT ?  [["LIMIT", 1]]
=> #<User id: 100, name: "Cassandre Cummerata", email: "example-99@railstutorial.org", created_at: "2020-10-21 23:25:17", updated_at: "2020-10-21 23:25:17", password_digest: "$2a$10$5/ddAbowTuZim/atIYzia.jYf5omGvECsfm0AX78v3i...", remember_digest: nil, admin: false, activation_digest: "$2a$10$wMZu8WO6BCWrvYY.oPjMReGiXs0nqJ0TuyA3OZ4QWhu...", activated: true, activated_at: "2020-10-21 23:25:17", reset_digest: nil, reset_sent_at: nil, unique_name: "Julia">
```

unique_nameが設定されました。

##所要時間

10/18から10/22までの3.5時間です。