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 |
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を参考にして手で入力します。
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にテストを追加します。
def setup
@user = User.new(name: "Example User", email: "user@example.com",
password: "foobar", password_confirmation: "foobar",
unique_name: "Example")
end
test "unique_name should be present" do
@user.unique_name = " "
assert_not @user.valid?
end
emailと同様なテストを作ります。
テストを作ってみて、ユニークだがブランクを許していることが分かりました。
ブランクを許さないようにする機能は、email列で同様に作っていたので、tutorialで探します。リスト 6.9にありました。同様に追加します。
validates :password, presence: true, length: {minimum: 6}, allow_nil: true
validates :unique_name, presence: true, length: {maximum: 50}, uniqueness: true
ユニークであること、ブランクを許さないことをテストできました。
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に入れることにします。
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は関連がない値でよしとして、変更します。
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時間です。