【第6章】rails tutorial 備忘録

#ユーザのモデル

##MVCの　「M」
データモデルとして扱うデフォルトのデータ構造のこと。

[この説明がわかりやすかった。](https://udemy.benesse.co.jp/development/web/rails-model.html)
railsは、データベースのテーブルに対応するRubyのクラスである。
そのクラスから作られたインスタンスは
,
行を表すオブジェクトになって、テーブルのカラムに相当する属性を持つ。

つまり。
クラスでカラムの行を作り、オブジェクトはそのカラムの行以下の具体的なデータが入った行になる。

##モデルの作成

```
$ rails g model User name:string email:string

invoke  active_record
      create    db/migrate/20160523010738_create_users.rb
      create    app/models/user.rb
      invoke    test_unit
      create      test/models/user_test.rb
      create      test/fixtures/users.yml
```
上記のコマンドで
model/user.rbとmigrateファイルが作成されることに注目。

migrationファイルはデータベースに対する変更を定義したもので、

ファイルを見ると

```ruby:db/migrate/[timestamp]_create_users.rb
class CreateUsers < ActiveRecord::Migration[5.1]
  def change
    create_table :users do |t|
      t.string :name
      t.string :email

      t.timestamps
    end
  end
end
```

でデータベースにnameとemailカラムを追加する定義をしている。

timestampsでは
created_atとupdated_atの二つのカラムが追加される

migratinファイルで定義した変更を反映させるためには

```
$rails db:migrate
```
を実行する。

###重要
db/schema.rbで現在のデータベースの構造を見ることができる。

```ruby:db/schema.rb
ActiveRecord::Schema.define(version: 20190411013028) do

  create_table "users", force: :cascade do |t|
    t.string "name"
    t.string "email"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

end
```



###命名規約
controllerを命名する時は複数形
modelを命名する時は、単数系


##ユーザオブジェクトを作る

```
$rails console --sandbox
```

これでデータベースへの変更をコンソールが終了した時に取り消してくれるので,色々とデータベースをいじることができる。


```ruby:
irb(main):001:0> User.new
=> #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil>
```
何も値の入っていないオブジェクトが作成される。

引数を渡して、オブジェクトを作成すると

```ruby:
irb(main):002:0> User.new(name:"nametest",email:"test@mail")
=> #<User id: nil, name: "nametest", email: "test@mail", created_at: nil, updated_at: nil>
```

オブジェクトを作成しても、データベースに値が入っていないので、

```
$ user.save
```
を実行し、saveが成功すればtureを返し、失敗すればfalseを返す。


作成したuserとその情報を取り出して見る。

```ruby:
irb(main):008:0> user
=> #<User id: 1, name: "nametest", email: "test@mail", created_at: "2019-04-11 03:10:26", updated_at: "2019-04-11 03:10:26">
irb(main):009:0> user.name
=> "nametest"
irb(main):010:0> user.email
=> "test@mail"
```

createを使うと作成と保存を同時にしてくれる。
また、返ってくる値はtrue/falseではなく、
作成したオブジェクトになる。


```ruby:
irb(main):011:0> user2 = User.create(name:"nametest",email:"test@mail")
   (0.2ms)  SAVEPOINT active_record_1
  SQL (0.3ms)  INSERT INTO "users" ("name", "email", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["name", "nametest"], ["email", "test@mail"], ["created_at", "2019-04-11 03:14:26.801014"], ["updated_at", "2019-04-11 03:14:26.801014"]]
   (0.2ms)  RELEASE SAVEPOINT active_record_1
=> #<User id: 2, name: "nametest", email: "test@mail", created_at: "2019-04-11 03:14:26", updated_at: "2019-04-11 03:14:26">
```

destroyコマンドでデータベースの値を削除する。
オブジェクトを返すが、再度、destroyはできない。
しかし、メモリ上には残っている。
実際に削除されたかはDBから検索してあげる。

##演習
1 user.nameとuser.emailが、どちらもStringクラスのインスタンスであることを確認してみてください。

2 created_atとupdated_atは、どのクラスのインスタンスでしょうか?

```ruby:
irb(main):014:0> user.name.class
=> String
irb(main):015:0> user.email.class
=> String
irb(main):016:0> user.updated_at.class
=> ActiveSupport::TimeWithZone
irb(main):017:0> user.created_at.class
=> ActiveSupport::TimeWithZone
irb(main):018:0> 
```

## ユーザオブジェクトの検索

```
$User.find(1)

$User.find_by(name:"name test")

$User.all
```
##演習

１nameを使ってユーザーオブジェクトを検索してみてください。また、 find_by_nameメソッドが使えることも確認してみてください

```ruby:
irb(main):022:0> User.find_by_name("nametest")
  User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."name" = ? LIMIT ?  [["name", "nametest"], ["LIMIT", 1]]
=> #<User id: 1, name: "nametest", email: "test@mail", created_at: "2019-04-11 03:10:26", updated_at: "2019-04-11 03:10:26">
```

2 実用的な目的のため、User.allはまるで配列のように扱うことができますが、実際には配列ではありません。User.allで生成されるオブジェクトを調べ、ArrayクラスではなくUser::ActiveRecord_Relationクラスであることを確認してみてください。

```ruby:
irb(main):023:0> all = User.all
  User Load (0.3ms)  SELECT  "users".* FROM "users" LIMIT ?  [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<User id: 1, name: "nametest", email: "test@mail", created_at: "2019-04-11 03:10:26", updated_at: "2019-04-11 03:10:26">, #<User id: 2, name: "nametest", email: "test@mail", created_at: "2019-04-11 03:14:26", updated_at: "2019-04-11 03:14:26">]>
irb(main):024:0> all.class
=> User::ActiveRecord_Relation
```
3 User.allに対してlengthメソッドを呼び出すと、その長さを求められることを確認してみてください 

```
irb(main):027:0* all.length
  User Load (0.3ms)  SELECT "users".* FROM "users"
=> 2
```

##ユーザーの更新

update_attributesメソッドで更新と保存を同時に行う。

```ruby:
irb(main):030:0> user.update_attributes(name:"update",email:"update@new.email")
   (0.2ms)  SAVEPOINT active_record_1
  SQL (0.4ms)  UPDATE "users" SET "name" = ?, "email" = ?, "updated_at" = ? WHERE "users"."id" = ?  [["name", "update"], ["email", "update@new.email"], ["updated_at", "2019-04-11 04:40:04.233601"], ["id", 1]]
   (0.2ms)  RELEASE SAVEPOINT active_record_1
=> true
```

##有効性のテスト

```ruby:test/models/user_test.rb
class UserTest < ActiveSupport::TestCase
  
  def setup
    @user = User.new(name:"Example User",email: "user@example")
  end

  test "should be valid" do
    assert @user.valid?
  end
end
```

setupはテスト用のデータ

```
$ rails test:models
```
これは　modelsに関するテストだけを走らせる。
rails test:integrationは統合テストだけ走らせる。

## バリデーション


```ruby:app/models/user.rb
class User < ApplicationRecord
  validates :name, presence: true
end
```

## errors

```ruby:
irb(main):001:0> user = User.new(name:"",email:"")
=> #<User id: nil, name: "", email: "", created_at: nil, updated_at: nil>
irb(main):002:0> user.save
   (0.3ms)  SAVEPOINT active_record_1
   (0.1ms)  ROLLBACK TO SAVEPOINT active_record_1
=> false
irb(main):003:0> user.valid?
=> false
irb(main):004:0> user.errors.full_messages
=> ["Name can't be blank"]
irb(main):005:0> user.errors.full_messages[1]
=> nil
irb(main):006:0> user.errors.full_messages[0]
=> "Name can't be blank"
```

errors.full_messagesは配列


## 演習

１新しいユーザーuを作成し、作成した時点では有効ではない (invalid) ことを確認してください。なぜ有効ではないのでしょうか? エラーメッセージを確認してみましょう。


```ruby:
irb(main):003:0> u.errors.full_messages
=> ["Name can't be blank", "Email can't be blank"]
```
u.errors.messagesを実行すると、ハッシュ形式でエラーが取得できることを確認してください。emailに関するエラー情報だけを取得したい場合、どうやって取得すれば良いでしょうか?


```
irb(main):004:0> p u.errors.full_messages
["Name can't be blank", "Email can't be blank"]
=> ["Name can't be blank", "Email can't be blank"]
irb(main):005:0> u.errors.full_messages[1]
=> "Email can't be blank"
```

##演習

１　長すぎるnameとemail属性を持ったuserオブジェクトを生成し、有効でないことを確認してみましょう。

２　長さに関するバリデーションが失敗した時、どんなエラーメッセージが生成されるでしょうか? 確認してみてください。

```ruby:
irb(main):001:0> u = User.new(name:"a"*100,email:"s"*399)
=> #<User id: nil, name: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa...", email: "ssssssssssssssssssssssssssssssssssssssssssssssssss...", created_at: nil, updated_at: nil>
irb(main):002:0> u.valid?
=> false
irb(main):003:0> u.errors.full_messages
=> ["Name is too long (maximum is 50 characters)", "Email is too long (maximum is 255 characters)"]
```


##正規表現

指定したフォーマットでバリデーションをかける。
```
validetas :email, format:{with: /<regular expression>/}
```

[Rubular](http://www.rubular.com)



##一意性

@user.dupで同じデータを複製する。

```app.models/user.rb
..
..
validates :email,........
uniqueness: true
```

重複するデータを弾く。

メールアドレスは、大文字、小文字を区別しないので、
データ保存でも、それらを同じものとして捉えて、バリデーションに引っかかるようにしなくてはいけない。

```ruby:test/models/user_test.rb
..
..
duplicate_user = @user.dup
duplicate_user.email = @user.email.upcase
..
..
```

保存する前に全てのメールを大文字にして、保存すれば
大文字、小文字の違いがなくなって、重複をふせげる。

```
uniqueness: {case_sensitive: false}
```
で大文字、小文字を区別しない。


## データベースレベルで一意性を持たせる

連続で送信ボタンを押した時に、モデルのバリデーションを通り抜けて、重複したデータがメモリに作成されてしまい、データベースに重複したデータが作成される。

データ
↓
モデル
↓
メモリ
↓
データベース　保存される


モデルのバリデ  
case_sensitive: false
↓
メモリ
↓
データベースのバリデ　
add_index:   unique:true
+
email.downcase
↓
保存される

二重のバリデで重複データを防ぐ

user.rbでは

emialカラムにindexを持たせる

```
$rails g migration add_index_to_users_email
```

```ruby:db/migrate/[times_stamp]_add_index_to_users_email.rb
def change
  add_index :users, :email, unique: true
end
```
モデルの大文字小文字のバリデーションは、

```
uniqueness: [case_sensitive: false}
```
で実装して、次は、データベースの直前でも
before_saveメソッドで、emailを全て大文字にして
同一化し、migrationファイルで持たせたemailカラムへのunique: trueでバリデーションをする。


##演習

```ruby:test/models/user_test.rb
test "email addresses should be saved as lower-case" do
    mixed_case_email = "Foo@ExAMPle.CoM"
    @user.email = mixed_case_email
    @user.save
    assert_equal mixed_case_email.downcase, @user.reload.email
  end
```

reloadは
テストでdbに新たに保存されたデータに更新する

!をつけると、オブジェクトに対する一時的な変更ではなく、破壊的な変更（永続的）をする


##ハッシュ化されたパスワード

has_secure_passwordを生成する

```
$rails g migration add_password_digest_to_users password_digest:string
```
ハッシュ化するgemも必要
gem 'bcrypt", '3.1.12'


セキュアにハッシュ化したパスワードを、データベース内のpassword_digestという属性に保存できるようになる。


2つのペアの仮想的な属性 (passwordとpassword_confirmation) が使えるようになる
Userモデルのオブジェクトからは存在しているように見えるが、データベースには対応するカラムが存在しない



![スクリーンショット 2019-04-12 0.45.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322607/aa4a37ba-ae3a-ce18-d2ea-72d95469e83c.png)

![スクリーンショット 2019-04-12 0.48.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322607/04b3bb5b-399a-cda0-fa8a-9b8e0fbca97e.png)

新規作成の時に
モデルで
passwordとpassword_confirmationの値が必要
![スクリーンショット 2019-04-12 1.38.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322607/987f5f97-1bcd-378e-e38b-4a7b5e9ed1d5.png)

```ruby:schema.rb

create_table "users", force: :cascade do |t|
    t.string "name"
    t.string "email"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.string "password_digest"
  end
```
passwordとpassword_confirmationのカラムがないので、仮想だとわかる。

has_secure_passwordを設定したら、
オブジェクトを作る時に、passwordとpassword_confirmationカラムに対応する値を与えないと、存在性のバリデに引っかかる。

##パスワードの最小文字数

まずはテストを書く

```ruby:
test "password should be present (nonblank)" do
    @user.password = @user.password_confirmation = " " * 6
    assert_not @user.valid?
  end

  test "password should have a minimum length" do
    @user.password = @user.password_confirmation = "a" * 5
    assert_not @user.valid?
  end
```

上記は
passwordとpassword_confirmationに同時に代入している。

```ruby:user.rb
 validates :password, presence: true, length: { minimum: 6 }
```
バリデを作って、テストをパスさせる。

##演習

１　有効な名前とメールアドレスでも、パスワードが短すぎるとuserオブジェクトが有効にならないことを確認してみましょう。
２　上で失敗した時、どんなエラーメッセージになるでしょうか? 確認してみましょう。

```ruby:
irb(main):001:0> user = User.new(name:"aa",email:"aa@aa.com",password:"aa",password_confirmation:"aa")   
=> #<User id: nil, name: "aa", email: "aa@aa.com", created_at: nil, updated_at: nil, password_digest: "$2a$10$du/lPglIflLt38hAZws2PeV/0M2mymtCHPL64N4JZno...">
irb(main):002:0> user.save
   (0.2ms)  SAVEPOINT active_record_1
  User Exists (1.3ms)  SELECT  1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER(?) LIMIT ?  [["email", "aa@aa.com"], ["LIMIT", 1]]
   (0.1ms)  ROLLBACK TO SAVEPOINT active_record_1
=> false
irb(main):003:0> user.errors.full_messages
=> ["Password is too short (minimum is 6 characters)"]
```


##感想
ここまでは助走。しっかり理解できている。
本番は7章から。