【Rails】Railsチュートリアル第６章　復習用

<h2>はじめに

今日もチュートリアルに取り組んでいく。
今回はモデルを主に扱っているようだ

<h2>Userモデル

・Railsでは、データモデルとして扱うデフォルトのデータ構造のことを **モデル（Model)** と呼びます
・データベースを使うことで長時間データを保存することができる
・データベースをやりとりするライブラリ(テンプレのようなもの)を **Active Record** という
・ActiveRecordにはデータの作成、保存、検索のメソッドが用意されている
・これらをつかうためにSQLを意識する必要がない！！
・**マイグレーション** = Rubyでデータのはいったテーブル(表)を操作する仕組み

<h4>Railsにおける属性

・第４章では`attr_accesor`を用いてUserクラスにnameとemail属性を定義した。
・しかしこれはRubyだから必要なのであって、実はRailsでは属性を定義する必要がない(デフォルトでその仕組が提供されている)

<h4>モデル作成

・ここでこれらのユーザー情報を保存するモデルを作成する
・Controllerを作るときと要領は同じ

```bash
rails generate controller Users new
```

さらにこのモデルにはnameとemail属性を持たせたいので

```bash
rails generate model User name:string email:string
```

このように記述する（モデル名は必ず単数形に！）

・モデル作成のときに属性も一緒に渡す
・この時これらの属性の型情報も一緒に渡します(今回は両方とも文字列なのでstringを渡す)

<h4>データベースのマイグレーション

・`generate`コマンドを使うと、同時にマイグレーションと呼ばれるファイルも作成される
・マイグレーションは、データベースの構造を段階的に変更する手段を提供します
・マイグレーションファイルの冒頭には、作成した時間がタイムスタンプとして名付けられる
```Ruby:20240730095908_create_users.rb
class CreateUsers < ActiveRecord::Migration[7.1]
  def change
    create_table :users do |t|
      t.string :name
      t.string :email

      t.timestamps
    end
  end
end

```
・コードを見て分かる通り、マイグレーションファイルはデータの変更を行うchangeメソッドの集まりである
・`create_table`メソッドは、ユーザーを保存するためのテーブル(表)をデータベースに作成するメソッド
・create_tableメソッドはブロック変数を1つ持つブロックを受け取ります(今回はt)

・このコードは、`:users`テーブルに`:name`と`:email`要素をブロック変数を使ってそれぞれusersテーブルに作成します
・テーブル内には複数のユーザーを追加するので、単数形ではなく複数形を使っている。
・モデル自体は**一人のユーザーを表す!**
・最後の`t.timestamps`はテーブルにcreated_atとupdated_atという要素を作成する
・created_atとupdated_atは「マジックカラム（Magic Columns）」と呼ばれる
・以上でマイグレーションを用いてデータモデルを作成することができた。
・**つまりマイグレーションファイルでデータベースを操作する！！**

<h4>マイグレーションの実行

しかしまだこれではマイグレーションは実行されていない。そこで

```bash
rails db:migrate
```
・このコマンドを実行することで、マイグレーションが実行される
・実行すると、development.sqlite3というファイルが実行される
・ここから考えるに、マイグレーションでSQLiteを操作している、？
・エディタではこのファイルは見ることができないが、<a href="https://sqlitebrowser.org/"DB> Browser for SQLite</a>というツールを使うとデータベースの中身を見ることができる。

<h4>ユーザーオブジェクトの作成

・User.newを引数なしで読んだ場合はすべての属性がnilになる
・しかしUser.newを実行しただけではデータベースに保存されていない！（ただオブジェクトを作成しただけ）
・保存するためには、saveメソッドを呼び出す必要がある
・saveメソッドは保存が成功したらtrue 失敗したらfalseを返す

```bash
> user = User.new(name: "Michael Hartl", email: "michael@example.com")
> user
=> #<User id: nil, name: "Michael Hartl", email: "michael@example.com",
created_at: nil, updated_at: nil>
```
コンソールでUser.newを起動するとこのようになる
User.newでuserオブジェクトを作成した時点ではまだ`id` `created_at` `updated_at`がnilになっている。
しかしsaveメソッドを取り出すと、
```bash
> user.save #saveを実行
> user　 #その後userを呼び出すと,,,
=> #<User id: 1, name: "Michael Hartl", email: "michael@example.com",
created_at: "2022-03-11 01:51:03", updated_at: "2022-03-11 01:51:03">
```

このように`id` `created_at` `updated_at`がそれぞれ作成された
もちろん今後userの内容が更新されるとupdated_atのタイムスタンプは変更される。

また、Userモデルのインスタンスはドットを用いて属性にアクセスできる
```bash
>> user.name
=> "Michael Hartl"
>> user.email
=> "michael@example.com"
>> user.updated_at
=> Fri, 11 Mar 2022 01:51:03 UTC +00:00
```

先程はUser.new(インスタンスの作成)をした後にUser.save(インスタンスをデータベースに保存)し、順序を踏んだがこれを同時に行うメソッドが存在する。

`create`メソッドを使うことにより、この２つを同時に実行することができる。
`save`の際はtrueとfalseを返していたが、その代わりに`create`ではユーザーオブジェクト自身を返す

```bash
>> User.create(name: "A Nother", email: "another@example.org")
#<User id: 2, name: "A Nother", email: "another@example.org", created_at:
"2022-03-11 01:53:22", updated_at: "2022-03-11 01:53:22">
>> foo = User.create(name: "Foo", email: "foo@bar.com")
#<User id: 3, name: "Foo", email: "foo@bar.com", created_at: "2022-03-11
01:54:03", updated_at: "2022-03-11 01:54:03">
```
また、上のfooのように変数を代入することもできる

`destroy`メソッドも存在するが、これは完全に`create`の逆であり、オブジェクトを削除する。

<h4>ユーザーオブジェクトの検索方法

早速ユーザーの作成ができたところで、作成したユーザーを検索していきたい

<h5>findメソッド

`find`メソッドでは引数にidを渡して適合するユーザーを返します
```bash
> User.find(1)
=> #<User id: 1, name: "Michael Hartl", email: "michael@example.com",
created_at: "2022-03-11 01:51:03", updated_at: "2022-03-11 01:51:03">
```

<h5>find_byメソッド

`find_by`メソッドではオブジェクトの属性を指定して適合するユーザーを返す
```bash
>> User.find_by(email: "michael@example.com")
=> #<User id: 1, name: "Michael Hartl", email: "michael@example.com",
created_at: "2022-03-11 01:51:03", updated_at: "2022-03-11 01:51:03">
```
→おそらくユーザー名検索機能はこれを使っているのではないか...?

<h5>first,allメソッド

 `first`は単にデータベースの最初のユーサーを返します
 `all`メソッドもその名の通りすべてのユーザーを返します

<h4>ユーザーの更新

基本的な更新の方法は2つある

1つは属性を個別に代入していく方法
```bash
>> user           # userオブジェクトが持つ情報のおさらい
=> #<User id: 1, name: "Michael Hartl", email: "michael@example.com",
created_at: "2022-03-11 01:51:03", updated_at: "2022-03-11 01:51:03">
>> user.email = "mhartl@example.net"
=> "mhartl@example.net"
>> user.save
=> true
```
最後にsaveを実行してデータベースに保存することをお忘れなく。
保存を行わずにreloadを実行すると、データベースの情報を元にオブジェクトを再読み込みするので、次のように変更が取り消されます。
```bash
>> user.email
=> "mhartl@example.net"
>> user.email = "foo@bar.com"
=> "foo@bar.com"
>> user.reload.email
=> "mhartl@example.net"
```

もう１つの方法は、`update`を使う方法

`update`メソッドは属性のハッシュを受取り、更新を行う
このとき更新と保存を同時に行うため、saveはいらない
```bash
>> user.update(name: "The Dude", email: "dude@abides.org")
=> true
>> user.name
=> "The Dude"
>> user.email
=> "dude@abides.org"
```
特定の属性のみを変更したいときは、`update_attribute`メソッドを使用する

<h2>ユーザーの検証 "Validation"

先ほど`name`と`email`属性を持ったユーザーデータを作成しました。
しかし現時点ではどんな値にすることもできてしまいます。

つまりemailをabcdという値にすることだってできてしまう。
それだと全然emailアドレスではないのでフォーマットに従わせる必要がある

また`name`は空の値ではいけないし、ユーザー名は重複しないようにする必要がある(今回はemailをユーザー名とする)

そこで使う要素が**Validation**だ

<h4>Validation(検証)とは？

→今回においてはそれぞれのデータの属性に対する値に何かしらの成約を与えることをいう

制約の内容は、先ほど取り上げたメールアドレスの**フォーマット**や重複しないという意味の**一意性**、空白であってはいけない**存在性*、文章の長さを制限する**長さ**など

このような制約をすることがValidationだ

最後に最終検証として**確認**という作業もある

<h4>早速検証してみよう

私もこれを書いていて気付いたが、これこそテスト駆動開発と相性がいいじゃないか！！
例えばテストでユーザー名が空白でないか調べるテストを先に書いたら、後々楽になるじゃないか!!

もちろん同じことがチュートリアルにも書いてありました。(今度チュートリアルのテストの部分をまとめて復習できる記事を書きたい)

<h5>テスト方法　※重要

1.テストするバリデーションに適合するモデルのオブジェクトを作成
2.その属性のうちの１つを有効でない属性に意図的に変更する
3.バリデーションで失敗するか検証する

・バリデーションのコードが問題なのか、オブジェクトがバリデーションに適合してないだけなのかどちらの問題なのか切り離して考えられる
・わざとバリデーションが有効でない属性に変更したけど、成功する場合はバリデーションコードが間違っているということ(逆に適合させてもテストが失敗する場合も同じ)

```rb:user_test.rb
require "test_helper"

class UserTest < ActiveSupport::TestCase

  def setup
    @user = User.new(name: "Example User", email: "user@example.com")
  end

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

これはバリデーションに適合するオブジェクトをテストするテストコードである(つまり1の部分)
`@user`でバリデーションに適合するユーザーオブジェクトを作成しています。これはテスト内だけでテストのために仮に使われるオブジェクトです（つまりこのオブジェクトがバリデーションに適合する"答え"なのである）

また`setup`メソッドとは「テストが実行される前に実行されるメソッド」なので、テストが走る前に適合するユーザーオブジェクトを作成しているということ。

その後シンプルな`assert`メソッドを使い、`@user.valid?`で`@user`がバリデーションに適合していますか？ということをテストしている(`valid?`でバリデーションに合っているか調べている)

現在は何もバリデーションを設定していないので、無条件にテストは成功する

<h4>存在性のテスト

先ほども触れたが、そのオブジェクトの属性が存在するかどうかをテストしていく
つまり、空白ではエラーになってしまうようにしたい。

先にテストを書いていく。
```rb
  test "name should be present" do
    @user.name = "     "
    assert_not @user.valid?
  end
```
これは先程のテストファイルに追加する。
まず`@user`のname属性が空であることを示している

今回は`assert_not`なので、つまり **「バリデーションに適応しなかったらテストはクリアする」**  ということである。(失敗したら成功！みたいな感じ)

つまり「テストします！今回`@user`のname属性に何も入ってません！　そこで、バリデーションの条件に適合して**いなかったら**このテストは成功ですよ！」といっているようなもの

<h4>バリデーションの作成

バリデーションはuser.rbファイルの中に作成する。(app/models/user.rb)
バリデーションとは、単なる`validate`メソッドなのである。

```rb:user.rb
class User < ApplicationRecord
  validates :name, presence: true
end
```

今回の`presence:true`が存在性を示すvalidateのオプションハッシュなのである。
第４章でやったとおりカッコがすべて省略されている。

```rb
validates (:name, presence: true)
```
カッコを省略しないとこのようになる。
つまり今回はバリデーションを`:name`要素に適用し、その効果は`presence: true`(空であってはいけませんよ)という意味になる

この調子で`:email`属性にも存在性のテストとバリデーションを追加した(本書6.12)

<h4>長さの検証

文字の長さにも制約を与えます。
今回はユーザー名を50文字、メールアドレスを255文字まで設定可能ということにします。
つまりそれぞれ51,256文字だったらアウトにしたいのです


```rb:user_test.rb
  test "name should not be too long" do
    @user.name = "a" * 51
    assert_not @user.valid?
  end

  test "email should not be too long" do
    @user.email = "a" * 244 + "@example.com"
    assert_not @user.valid?
  end
```

先程のテストが読めたら、これも簡単に読めるのではないでしょうか
たとえばnameだったら、aを５１回かけることによって文字数がオーバーしていることを表しています
それぞれ最後にvalid?でバリデーションに適合しているかテストしている

```rb:user.rb
class User < ApplicationRecord
  validates :name,  presence: true, length: { maximum: 50 }
  validates :email, presence: true, length: { maximum: 255 }
end
```

気づいてるかもですが、基本的に`validates :name or :email`まで使いまわしていることがわかります。
validatesの引数にこのように渡してあげることで１行で書くことができるのです
```rb
class User < ApplicationRecord
  validates :name,  (presence: true, length: { maximum: 50 })
  validates :email, (presence: true, length: { maximum: 255 })
end
```
わかりやすくかっこをつけてあげると、このようになる(はず）
長さを表すためには、`length:{maximum:　文字数}`と書く
おそらくこの要領で最小文字数もいけるはず

残りのテストに関しては一旦寝て明日やる