Railsチュートリアル 第6章 ユーザーのモデルを作成する - Userモデル

# モデルに関する基礎知識

* モデル…データモデルとして扱うデフォルトのデータ構造
    * MVCアーキテクチャの「Model」のことである
* Active Record…データベースとやり取りするデフォルトのRailsライブラリ
    * ORMフレームワークとしての機能を含む
    * デザインパターンとしてのActive Recordに由来する名称
        * ただ、RailsライブラリのActive Recordは固有名詞である
    * LaravelのEloquentライブラリに相当する
* マイグレーション(Migration)
    * Acgtive Recordの機能の一つ
    * データの定義をRubyで記述することができる
    * LaravelにもDatabase:Migrationsという機能が存在する

# 永続化の必要性

4章で`User`というクラスを定義して使用してみました。`name`属性と`email`属性を持ち、`formatted_email`というメソッドが使用できるクラスでしたね。

```ruby:4章で使用したUserクラス
class User
  attr_accessor :name, :email

  def initialize(attributes = {})
    @name  = attributes[:name]
    @email = attributes[:email]
  end

  def formatted_email
    "#{@name} <#{@email}>"
  end
end
```

しかし、Ruby上でクラスを定義してオブジェクトを生成しただけだと、コンソールからexitした時点でクラスもオブジェクトも消えてしまいます。Webフレームワークで扱う動的データに重要な**永続性**がない状態ですね。この章では、「永続性を実現し、簡単に消えることのないユーザーのモデル」を構築していきます。

# データベースの移行

## そもそもRDBについて

RDBの主たる構成要素は、1個以上のテーブルです。テーブルの構成は、大雑把に言えば以下の2つです。

* データ行（レコード）
* カラム（列）
    * データの属性

例えば、nameとemailを持つユーザーオブジェクトを保存するのであれば、`name`カラムと`email`カラムを持つ`users`テーブルを生成するわけです。

Userのデータモデルのスケッチは、PlantUMLの`entity`を使って表現すると、以下のような感じになります。

## Userのデータモデルを仮に定義してみる

以下は`users`テーブルに含まれるデータのサンプル[^データのサンプルの画像生成方法]です。

[^データのサンプルの画像生成方法]: Markdownで表を記述→VSCodeのMarkdown PDF拡張でPNGにエクスポート→適当な大きさにトリミング、という方法をとりました。

![User_mockup.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/323778/b771fd16-307f-f57c-2a0d-ddf8a9ba33a3.png)

以下はUserのデータモデルのスケッチ[^データモデルのスケッチの画像生成方法]です。

![User_sketch.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/323778/4b6dd6c6-e92c-a3bc-d8e8-879450f263c8.png)

[^データモデルのスケッチの画像生成方法]: PlantUMLでデータモデルのスケッチを記述→VSCodeのPlantUML拡張でPNGにエクスポート、という方法をとりました。

## `rails generate model`コマンドによるモデルの生成

コントローラを生成する際に、`rails generate controller`というコマンドを使いました。とくれば、モデルを生成する際に使うコマンドは`rails generate model`というコマンドになります。

今回生成するモデルの条件は以下です。

* モデル名…User
    * コントローラ名に複数形を用いるのに対し、モデル名には単数形を用いる
* `string`型の`name`属性を持つ
* `string`型の`email`属性を持つ

上述前提に基づき、早速モデルを生成してみましょう。

```
# rails generate model User name:string email:string
Running via Spring preloader in process 2236
      invoke  active_record
      create    db/migrate/20190928080951_create_users.rb
      create    app/models/user.rb
      invoke    test_unit
      create      test/models/user_test.rb
      create      test/fixtures/users.yml
```

無事モデルを生成できたようです。

## マイグレーション - 生成したモデルを実際にRDBに反映する

`rails generate model`したのみの現状だと、「Railsフレームワーク側でデータモデルは定義されているものの、RDB上にデータモデルに対応するテーブルは存在しない」という状態です。データを永続化するためには、RDB上にもデータモデルに対応するテーブルを生成する必要があります。

Railsフレームワーク側をRDB側に反映させるための仕組みとして、Railsでは、マイグレーションと呼ばれる仕組みが存在します。マイグレーションの実体はRubyコードです。今回は`rails generate model`によって自動生成[^マイグレーションの自動生成]が済んでいます。

[^マイグレーションの自動生成]: 手でマイグレーションを定義する方法もあります。Railsチュートリアルを進めていく中で、そちらも学習していくことになっています。

### マイグレーションのコード解説

以下は自動生成されたマイグレーションです。

```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
```

このコードで重要な事柄は以下です。

* マイグレーションファイル名の先頭には、マイグレーション生成時点のタイムスタンプが追加されている
    * マイグレーションのコンフリクトはまず発生しない
* データベースに与える変更は、`change`メソッドによって定義される
* Railsでは`create_table`というイテレータメソッドが定義されている
    * RDB上にテーブルを作成するためのメソッドである
    * `t`というオブジェクトをブロック変数として用いる
    * 繰り返すのは「生成するカラムを定義する」という動作である
        * `string`というメソッドであれば、生成対象として`string`型のカラムを定義する
    * `t.timestamps`という特別なメソッドが存在する
        * `created_at`と`updated_at`という2つのマジックカラムを生成するメソッドである
            * `created_at`は、レコードの作成時刻を自動で記録するカラムである
            * `updated_at`は、レコードの最終更新時刻を自動で記録するカラムである
* テーブルに対するシンボル名が`:users`という複数形であることに注意
    * 「テーブルは複数のレコードから成る」という構造を反映した命名規則
    * 一方で、モデル名はUserという単数形である
        * 単一レコードを対象として処理を行うオブジェクトであるため

### コードの解説を踏まえた、Userの完全なデータモデル

マジックカラムを含めた、Userの完全なデータモデルは以下のようになります。

![User_full.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/323778/4a4fb302-be82-ccb1-aa1c-be114975ca1a.png)

### マイグレーションの適用

`rails db:migrate`コマンドを実行することにより、マイグレーションのコードを実行することができます。これを「マイグレーションの適用(migrating up)と呼びます。

```
# rails db:migrate 
== [timestamp] CreateUsers: migrating ======================================
-- create_table(:users)
   -> 0.0177s
== [timestamp] CreateUsers: migrated (0.0179s) =============================
```

### 実際に生成されたデータベース・テーブルの実体

マイグレーションの実行により、`db/development.sqlite3`という名前のファイルが生成されました。これはSQLiteデータベースの実体です。

<img width="301" alt="スクリーンショット 2019-09-29 11.03.35.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/323778/f762d0a6-fe45-9f84-8b13-4e5578570cbd.png">

上述スクリーンショットは、Visual Studio CodeのSQLite拡張(alexcvzz.vscode-sqlite
)の機能によって`users`テーブルの構成を確認したものです。

`id`というカラムは、Railsにより自動で生成されたカラムで、レコードを一意に識別するために使うものです。主キーであることを示す鍵アイコンが頭についていますね。

`id`が主キーであることを踏まえた、より厳密なUserの完全なデータモデルは以下のようになります。

![User_full.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/323778/e4f7262b-25f9-a4d3-1dec-dc577d5c8fa3.png)

# 演習 - データベースの移行

## 1. あなたの環境にあるdb/schema.rbの内容を調べ、その内容とマイグレーションファイル (リスト 6.2) の内容を比べてみてください。

> `db/schema.rb`は、データベースの構造 (スキーマ (schema) と呼びます) を追跡するために使われるRubyコードです。

まずは、`db/migrate/[timestamp]_create_users.rb`のコードを見てみましょう。

```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
```

次に、`db/schema.rb`のコードを見てみます。

```ruby:db/schema.rb
ActiveRecord::Schema.define(version: [timestamp]) 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
```

`db/migrate/[timestamp]_create_users.rb`に対し、`db/schema.rb`には以下のような違いがあるのが目に付きます。

* 同じActiveRecordのメソッドが使われているが、使われているメソッドはそれぞれ違う
    * `db/migrate/[timestamp]_create_users.rb`では`ActiveRecord::Migration[5.1]`
    * `db/schema.rb`では`ActiveRecord::Schema.define`
* `[timestamp]_create_users.rb`のファイル名にある`[timestamp]`が、`db/schema.rb`では、`ActiveRecord::Schema.define`の第1引数（ハッシュ）の`:version`キーに対する値として渡されている
* `t.datetime`を除けば、呼び出しているメソッドの順番・ブロックの構成に違いはない
    * ブロック変数の名前はいずれも`t`である
* 各メソッドの第1引数の型が異なる
    * `db/migrate/[timestamp]_create_users.rb`ではシンボル
    * `db/schema.rb`では文字列
* `t.timestamp`と`t.datetime`の違い
* `db/schema.rb`の`create_table`には、`force: :cascade`という第2引数が渡されている
* `db/schema.rb`で呼び出されている`t.datetime`には、`null: false`という第2引数が渡されている

## 2. `rails db:rollback`コマンドを実行後、`db/schema.rb`の内容を調べてみて、ロールバックが成功したかどうか確認してみてください ([コラム 3.1](https://railstutorial.jp/chapters/static_pages?version=5.1#aside-undoing_things)ではマイグレーションに関する他のテクニックもまとめているので、参考にしてみてください)。

演習1.が終了した時点で、`rails db:rollback`コマンドを実行します。

```
# rails db:rollback
== [timestamp] CreateUsers: reverting ======================================
-- drop_table(:users)
   -> 0.0176s
== [timestamp] CreateUsers: reverted (0.0283s) =============================
```

実行後の`db/schema.rb`の内容は以下のようになりました。

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

end
```

ポイントは以下でしょうか。

* `ActiveRecord::Schema.define`の第1引数のキー`:version`に対する値が`0`になっている
    * ロールバック前は、マイグレーションのタイムスタンプに対応する値であった
* `create_table`メソッド以下がきれいさっぱり削除された

### `rails db:migrate`と`rails db:rollback`の関係について追加の解説

* Railsの`change`メソッドには、`create_table`と`drop_table`がそれぞれ対応関係であるという情報が格納されている
    * `create_table`と`drop_table`に限らず、RDBに対する可逆的な操作に対する逆方向のマイグレーションは、Railsが持つ機能によって簡単に行うことができる
* 「あるカラムを削除する」等の不可逆的なマイグレーションの場合、`up`メソッドと`down`メソッドを別々に定義する必要がある
* 詳細は、Railsガイドの[Active Record マイグレーション](https://railsguides.jp/active_record_migrations.html)を参照のこと

## 3. もう一度`rails db:migrate`コマンドを実行し、`db/schema.rb`の内容が元に戻ったことを確認してください。

```
# rails db:migrate
== [timestamp] CreateUsers: migrating ======================================
-- create_table(:users)
   -> 0.0187s
== [timestamp] CreateUsers: migrated (0.0203s) =============================
```

実行後の`db/schema.rb`の内容は以下のようになりました。

```ruby:db/schema.rb
ActiveRecord::Schema.define(version: [timestamp]) 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
```

`db/schema.rb`の内容が元に戻りました。

ロールバック前の`[timestamp]`の値と、再度マイグレーションを実行した後の`[timestamp]`の値は**変わりません**。ここは大きなポイントではないかと思います。

# modelファイル

## マイグレーションふりかえり

1. `users`テーブルを作成し、`development.sqlite3`の内容を更新する
2. `users`テーブルに、以下5つのカラムを作成する
    * `id`: 主キー、integer型
    * `name`: varchar型
    * `email`: varchar型
    * `created_at`: マジックカラム、datetime型
    * `updated_at`: マジックカラム、datetime型

## modelファイルの内容

`rails generate model User`コマンドで生成された直後の`app/models/user.rb`の内容は、以下のようになっています。

```ruby:app/models/user.rb
class User < ApplicationRecord
end
```

* `User`というクラスが定義されている
    * `User`クラスは`ApplicationRecord`クラスを継承している
* 他には特に何も定義されていない

`ApplicationRecord`クラスのコードは、`app/models/application_record.rb`にて定義されています。

```ruby:app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true
end
```

ここでは、「`ApplicationRecord`クラスは、`ActiveRecord::Base`クラスを継承している」という事実が重要です。「Railsにおけるモデルという概念を知るにあたり、まず学習する必要があるのは、`ActiveRecord::Base`クラスについての知識である」ということですね。

# 演習 - modelファイル

## 1. Railsコンソールを開き、`User.new`で`User`クラスのオブジェクトが生成されること、そしてそのオブジェクトがApplicationRecordを継承していることを確認してみてください。

```
>> u = User.new
=> #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil>
>> u.class
=> User(id: integer, name: string, email: string, created_at: datetime, updated_at: datetime)
```

## 2. 同様にして、`ApplicationRecord`が`ActiveRecord::Base`を継承していることについて確認してみてください。

```
>> ApplicationRecord.superclass
=> ActiveRecord::Base
```

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

## Railsコンソールのサンドボックスモード

Railsコンソールには「サンドボックスモード」という特別なモードが存在します。「コンソール中でデータベースに対して加えた変更は、コンソールから抜ける際に自動で全てロールバックされる」というモードです。

Railsコンソールをサンドボックスモードで実行するには、`rails console`コマンドに`--sandbox`オプションを付けて実行します。

```
# rails console --sandbox
...略
Loading development environment in sandbox (Rails 5.1.6)
Any modifications you make will be rolled back on exit
>> 
```

確かに「Any modifications you make will be rolled back on exit（ここで行ったすべての変更は終了時にロールバックされます）」というメッセージが表示されています。

## モデルを使うと、`require`せずともユーザーオブジェクトにアクセスできる

[4章の学習](https://railstutorial.jp/chapters/rails_flavored_ruby?version=5.1#sec-a_user_class)において、「ユーザーオブジェクトを一から定義し、実際に使ってみる」という学習を行いました。このときは、「Railsコンソールにおいては、ユーザーオブジェクトを定義したコードを`require`しなければ、ユーザーオブジェクトにアクセスできない」という挙動でした。

しかしながら、モデルにてユーザーオブジェクトが定義されている場合、RailsコンソールがRailsの環境を自動で読み込む際にユーザーオブジェクトも読み込んでくれます。こうして、`require`せずともユーザーオブジェクトにアクセスできる、というわけなのです。

## 実際にユーザーオブジェクトを作成し、色々といじってみる

Railsコンソールで新しいユーザーオブジェクトを実際に作成してみます。

```
>> User.new
=> #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil>
```

上述のとおり、`User.new`を引数なしで呼ぶと、すべての属性が`nil`のユーザーオブジェクトが生成されます。

`User.new`は、引数としてハッシュを取ることができます。具体的には、「属性名をキーとし、属性値を値とするハッシュ」を取り、引数のハッシュの内容に基づき、ユーザーオブジェクトのインスタンス変数[^インスタンス変数？]を初期化するのです。

以下は、初期化ハッシュのname属性とemail属性を与えて`User.new`を呼んだ場合の例です。Userオブジェクトのname属性とemail属性が期待通り設定されていることがわかります。

```
>> user = User.new(name: "Michael Hartl", email: "mhartl@example.com")
=> #<User id: nil, name: "Michael Hartl", email: "mhartl@example.com", created_at: nil, updated_at: nil>
```

[^インスタンス変数？]: この点については疑問があります。確かにインスタンススコープの変数なのでしょうが、「変数名なくハッシュだけを返している」というのは、果たして本当にインスタンス変数なのでしょうか。

### `valid?`メソッド

Active Recordには、「有効性(Validity)」という重要な概念が存在します。後で詳しく学習しますが、現時点では「modelクラスオブジェクトの有効性は、`valid?`というメソッドによって確認することができる」というルールを覚えておきましょう。

```
>> user
=> #<User id: nil, name: "Michael Hartl", email: "mhartl@example.com", created_at: nil, updated_at: nil>

>> user.valid?
=> true
```

「modelクラスオブジェクトの有効性は、RDBにデータが格納されているかどうかには関係ない」ことも重要なので覚えておきましょう。

### RDBにUserオブジェクトを保存する

現時点では、まだRDBにデータは格納されていません。modelクラスオブジェクトにあるデータをRDBに格納するためには、modelクラスオブジェクトの`save`メソッドを実行する必要があります。例えば、現在言及しているUserオブジェクトをRDBに保存するためには、`user`オブジェクトの`save`というメソッドを呼び出す必要があります。

```
>> user
#<User id: nil, name: "Michael Hartl", email: "mhartl@example.com", created_at: nil, updated_at: nil>

>> user.save
   (5.7ms)  SAVEPOINT active_record_1
  SQL (25.6ms)  INSERT INTO "users" ("name", "email", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["name", "Michael Hartl"], ["email", "mhartl@example.com"], ["created_at", "2019-09-29 22:50:02.984378"], ["updated_at", "2019-09-29 22:50:02.984378"]]
   (0.1ms)  RELEASE SAVEPOINT active_record_1
=> true
```

`save`メソッドは、成功すれば`true`を、失敗すれば`false`を返します。

Railsコンソールにおける`save`メソッドの実行の流れを見ると、実際にRDBMS（開発環境ではSQLite3ですね）で実行されているSQL文の内容も見ることができます。Railsチュートリアルの範囲内で生のSQLが必要になる場面はほぼないですが、Active Recordに対応するSQL文を眺めたり推測したりすることには意味があると思います。

### `save`メソッド実行後のユーザーオブジェクト

```
>> user
=> #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2019-09-29 22:50:02", updated_at: "2019-09-29 22:50:02">
```

* `save`メソッド実行前
    * `id`: `nil`
    * `created_at`: `nil`
    * `updated_at`: `nil
* `save`メソッド実行後
    * `id`: `1`
    * `created_at`: 現在の日時
    * `updated_at`: 現在の日時

以上のように内容が変化しました。

### Userモデルのインスタンスの各属性に、ドット記法を用いてアクセスする

Userモデルのインスタンスの各属性には、Rubyのドット記法を用いてアクセスすることが可能です。以下のRailsコンソール出力は、Userモデルに`user`というインスタンスが存在する状態で、`user`の`name`・`email`・`updated_at`各属性にアクセスしてみた結果です。

```
>> user
=> #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2019-09-29 22:50:02", updated_at: "2019-09-29 22:50:02">
>> user.name
=> "Michael Hartl"
>> user.email
=> "mhartl@example.com"
>> user.updated_at
=> Sun, 29 Sep 2019 22:50:02 UTC +00:00
```

### `User.create`メソッド

Railsのモデルオブジェクトには、`create`というメソッドが定義されます。`create`メソッドは、モデルの生成とRDBへの保存を同時に行うメソッドです。引数はハッシュで、当該ユーザーオブジェクトのインスタンスに値を設定するために用います。

```
>> User.create(name: "A Nother", email: "another@example.org")
   (5.4ms)  SAVEPOINT active_record_1
  SQL (1.2ms)  INSERT INTO "users" ("name", "email", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["name", "A Nother"], ["email", "another@example.org"], ["created_at", "2019-10-01 22:40:17.812044"], ["updated_at", "2019-10-01 22:40:17.812044"]]
   (0.3ms)  RELEASE SAVEPOINT active_record_1
=> #<User id: 2, name: "A Nother", email: "another@example.org", created_at: "2019-10-01 22:40:17", updated_at: "2019-10-01 22:40:17">
>> foo = User.create(name: "Foo", email: "foo@bar.com")
   (0.2ms)  SAVEPOINT active_record_1
  SQL (0.3ms)  INSERT INTO "users" ("name", "email", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["name", "Foo"], ["email", "foo@bar.com"], ["created_at", "2019-10-01 22:40:47.991537"], ["updated_at", "2019-10-01 22:40:47.991537"]]
   (0.2ms)  RELEASE SAVEPOINT active_record_1
=> #<User id: 3, name: "Foo", email: "foo@bar.com", created_at: "2019-10-01 22:40:47", updated_at: "2019-10-01 22:40:47">
```

確かにSQLの`INSERT`文が実行過程で呼ばれていますね。

`create`オブジェクトの戻り値は、モデルオブジェクト自身です[^saveメソッドの戻り値との比較]。上述実行例の2番目のコマンドのように、`create`オブジェクトの戻り値を、何らかの変数に代入することもできます。

[^saveメソッドの戻り値との比較]: `save`メソッドの戻り値は、`true`または`false`でしたね。

### `User.destroy`メソッド

`destroy`は`create`の逆です。

```
>> foo.destroy
   (0.2ms)  SAVEPOINT active_record_1
  SQL (1.7ms)  DELETE FROM "users" WHERE "users"."id" = ?  [["id", 3]]
   (0.4ms)  RELEASE SAVEPOINT active_record_1
=> #<User id: 3, name: "Foo", email: "foo@bar.com", created_at: "2019-10-01 22:40:47", updated_at: "2019-10-01 22:40:47">
```

確かにSQLの`DELETE`文が実行過程で呼ばれていますね。

しかしながら、`destroy`メソッドが実行されてなお、当該モデルオブジェクトは（RDB上には残っていませんが）メモリ内に残っています。

```
>> foo
=> #<User id: 3, name: "Foo", email: "foo@bar.com", created_at: "2019-10-01 22:40:47", updated_at: "2019-10-01 22:40:47">
```

## この先学習すること

* RDB上からオブジェクトが削除されたことを知る方法
* 保存して削除されていないモデルオブジェクトを使って、いかにしてRDBから情報を取得するか

# 演習 - ユーザーオブジェクトの作成

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

```
>> user.name.class
=> String

>> user.email.class
=> String
```

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

```
>> user.created_at.class
=> ActiveSupport::TimeWithZone

>> user.updated_at.class
=> ActiveSupport::TimeWithZone

>> user.updated_at.class.superclass
=> Object
```

`ActiveSupport::TimeWithZone`というクラスです。以下のような特徴があります。

* ActiveSupport名前空間に所在する
    * RailsのActive Supportライブラリにより実装されたクラスである
* `Object`クラスを直接継承している

# ユーザーオブジェクトを検索する

Active Recordにおいて、RDB上に存在するレコードを検索する方法は多数用意されています。Railsチュートリアルでは、以下のメソッドが紹介されています。これらはいずれもモデルオブジェクトのクラスメソッドです。

* `find`メソッド
* `find_by`メソッド
* `first`メソッド
* `allメソッド`

## `find`メソッド

レコードを検索するActive Recordのメソッドのうち、最も基本的なものです。引数にはNumeric型の値をとり、引数で指定した`id`のオブジェクトを検索します。

まずはRDB上に存在するオブジェクトを検索した場合の例です。

```
>> User.find(1)
  User Load (1.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
=> #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2019-09-29 22:50:02", updated_at: "2019-09-29 22:50:02">
```

SQLの`SELECT`文がRDBに渡され、その結果に対応するユーザーオブジェクトが返されているのがわかりますね。

今度はRDB上に存在しないオブジェクトを検索した場合の例です（先ほどの学習で、`id=3`というユーザーは削除されました）。

```
>> User.find(3)
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 3], ["LIMIT", 1]]
ActiveRecord::RecordNotFound (Couldn't find User with 'id'=3)
```

SQLの`SELECT`文がRDBに渡されましたが、その結果となるレコードが返ってこなかったというエラーが返ってきました。

### 余談 - `find`メソッドに複数の引数を与えてみる

余談ですが、`find`メソッドは複数の引数を取ることができます。複数の引数を取った場合、引数として渡された整数に対応する`id`を持つ全てのレコードが、引数の順序に対応する順番で格納された配列を返します。

```
>> User.find(1,2)
  User Load (0.7ms)  SELECT "users".* FROM "users" WHERE "users"."id" IN (1, 2)
=> [#<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2019-09-29 22:50:02", updated_at: "2019-09-29 22:50:02">, #<User id: 2, name: "A Nother", email: "another@example.org", created_at: "2019-10-01 22:40:17", updated_at: "2019-10-01 22:40:17">]
```

RDB上に存在しない`id`を含む`find`メソッドを実行した場合、引数が複数であっても、引数1つの場合と同様、エラーが返ってきます。ただ、複数引数の`find`においては、エラーメッセージの内容が1引数の場合と異なります。

```
>> User.find(1,3)
  User Load (0.4ms)  SELECT "users".* FROM "users" WHERE "users"."id" IN (1, 3)
Traceback (most recent call last):
        1: from (irb):30
ActiveRecord::RecordNotFound (Couldn't find all Users with 'id': (1, 3) (found 1 results, but was looking for 2))
```

## `find_by`メソッド

特定の属性を検索条件として、RDB上のレコードを検索するメソッドです。検索条件として与える属性と属性値はハッシュとして与え、当該ハッシュが`find_by`メソッドの引数となります。

```
>> User.find_by(email: "mhartl@example.com")
  User Load (0.3ms)  SELECT  "users".* FROM "users" WHERE "users"."email" = ? LIMIT ?  [["email", "mhartl@example.com"], ["LIMIT", 1]]
=> #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2019-09-29 22:50:02", updated_at: "2019-09-29 22:50:02">
```

`find_by`メソッドの引数として与える属性・属性値は、SQL文の`WHERE`条件式に対応します。

RDB上にカラムとして存在する属性をキーとして与え、かつ引数として与えた属性値を持つレコードが存在しない場合、`find_by`メソッドは`nil`を返します。

```
>> User.find_by(email: "foobar@example.com")
  User Load (4.1ms)  SELECT  "users".* FROM "users" WHERE "users"."email" = ? LIMIT ?  [["email", "foobar@example.com"], ["LIMIT", 1]]
=> nil
```

RDB上にカラムとして存在しない属性をキーとして与えた場合、`find_by`メソッドはエラーとなります。

```
>> User.find_by(group: "")
  User Load (49.5ms)  SELECT  "users".* FROM "users" WHERE "users"."group" = ? LIMIT ?  [["group", ""], ["LIMIT", 1]]
ActiveRecord::StatementInvalid (SQLite3::SQLException: no such column: users.group: SELECT  "users".* FROM "users" WHERE "users"."group" = ? LIMIT ?)
```

## `first`メソッド・`last`メソッド

`first`メソッドは、データベースの最初のレコードを返します。

```
>> User.first
  User Load (0.3ms)  SELECT  "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ?  [["LIMIT", 1]]
=> #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2019-09-29 22:50:02", updated_at: "2019-09-29 22:50:02">
```

引数として整数値を与えた場合、データベースの最初から、引数として与えた個数のレコードを返します。

```
>> User.first(2)
  User Load (0.2ms)  SELECT  "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ?  [["LIMIT", 2]]
=> [#<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2019-09-29 22:50:02", updated_at: "2019-09-29 22:50:02">, #<User id: 2, name: "A Nother", email: "another@example.org", created_at: "2019-10-01 22:40:17", updated_at: "2019-10-01 22:40:17">]
```

`first`とは逆に、データベースの最後のレコードを返す`last`というメソッドもあります。

```
>> User.last
  User Load (0.3ms)  SELECT  "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT ?  [["LIMIT", 1]]
=> #<User id: 2, name: "A Nother", email: "another@example.org", created_at: "2019-10-01 22:40:17", updated_at: "2019-10-01 22:40:17">
```

## `all`メソッド

RDB上に存在する、当該モデルクラスに対応する全てのレコードの組を返します。

```
>> User.all
  User Load (1.3ms)  SELECT  "users".* FROM "users" LIMIT ?  [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2019-09-29 22:50:02", updated_at: "2019-09-29 22:50:02">, #<User id: 2, name: "A Nother", email: "another@example.org", created_at: "2019-10-01 22:40:17", updated_at: "2019-10-01 22:40:17">]>
```

戻り値が`ActiveRecord::Relation`という型である、というのは重要な事実です。

# 演習 - ユーザーオブジェクトを検索する

## 1.1. `name`を使ってユーザーオブジェクトを検索してみてください。

```
>> User.find_by(name: "Michael Hartl")
  User Load (0.3ms)  SELECT  "users".* FROM "users" WHERE "users"."name" = ? LIMIT ?  [["name", "Michael Hartl"], ["LIMIT", 1]]
=> #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2019-09-29 22:50:02", updated_at: "2019-09-29 22:50:02">
```

`name`の属性値としてRDB上に存在しない値を与えた場合、`find_by`メソッドの戻り値は`nil`となります[^ヒットしなかった場合にエラーを返すには]。

```
>> User.find_by(name: "Foo Bar")
  User Load (0.3ms)  SELECT  "users".* FROM "users" WHERE "users"."name" = ? LIMIT ?  [["name", "Foo Bar"], ["LIMIT", 1]]
=> nil
```

[^ヒットしなかった場合にエラーを返すには]: 「RDB上に存在するカラムにおいて、RDB上に存在しない値」を検索したときにエラーを返したい場合は、感嘆符つきの`find_by!`メソッドを使います。

## 1.2. `find_by_name`メソッドが使えることも確認してみてください。

```
>> User.find_by_name("Michael Hartl")
  User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."name" = ? LIMIT ?  [["name", "Michael Hartl"], ["LIMIT", 1]]
=> #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2019-09-29 22:50:02", updated_at: "2019-09-29 22:50:02">
```

なお、Railsのモデルオブジェクトにおいて、`find_by`と名のつくオブジェクトは、`find_by`および`find_by_name`以外にもいくつか存在します。

```
>> User.methods.grep(/find_by/)
=> [:find_by_name, :find_by, :find_by!, :initialize_find_by_cache, :find_by_sql]
```

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

```
>> User.all.class
=> User::ActiveRecord_Relation
```

### `User::ActiveRecord_Relation`クラスの継承関係

`User::ActiveRecord_Relation`は`ActiveRecord::Relation`を継承しており、`ActiveRecord::Relation`は`Object`を直接継承しています。

```
>> User.all.class.superclass
=> ActiveRecord::Relation
>> User.all.class.superclass.superclass
=> Object
```

![User  ActiveRecord_Relation.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/323778/92176d77-b569-97d6-7c74-f6ae6a83bdf5.png)

### `User::ActiveRecord_Relation`クラスのオブジェクトから`Array`クラスのオブジェクトを得るには

なお、`User::ActiveRecord_Relation`クラスのオブジェクトでは`to_a`メソッドが利用可能であり、`to_a`メソッドを実行することによって、本当に`Array`クラスのオブジェクトを得ることができます。

```
>> User.all.to_a
  User Load (0.8ms)  SELECT "users".* FROM "users"
=> [#<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2019-09-29 22:50:02", updated_at: "2019-09-29 22:50:02">, #<User id: 2, name: "A Nother", email: "another@example.org", created_at: "2019-10-01 22:40:17", updated_at: "2019-10-01 22:40:17">]

>> User.all.to_a.class
  User Load (0.2ms)  SELECT "users".* FROM "users"
=> Array
```

## 3. User.allに対してlengthメソッドを呼び出すと、その長さを求められることを確認してみてください (4.2.3)。

```
>> User.all.length
  User Load (0.2ms)  SELECT "users".* FROM "users"
=> 2
```

### ダックタイピングとは

[Railsチュートリアルの本文](https://railstutorial.jp/chapters/modeling_users?version=5.1#sec-exercises_finding_user_objects)では、以下のように言及されています。

> Rubyの性質として、そのクラスを詳しく知らなくてもなんとなくオブジェクトをどう扱えば良いかわかる、という性質があります。これをダックタイピング (duck typing) と呼び、よく次のような格言で言い表されています「もしアヒルのような容姿で、アヒルのように鳴くのであれば、それはもうアヒルだろう」。

# ユーザーオブジェクトを更新する

## 属性を個別に代入することによって更新する方法

```
>> user  # userオブジェクトが持つ情報のおさらい
=> #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2019-09-29 22:50:02", updated_at: "2019-09-29 22:50:02">

>> user.email = "mhartl@example.net"
=> "mhartl@example.net"

>> user.save
   (0.7ms)  SAVEPOINT active_record_1
  SQL (0.5ms)  UPDATE "users" SET "email" = ?, "updated_at" = ? WHERE "users"."id" = ?  [["email", "mhartl@example.net"], ["updated_at", "2019-10-02 23:05:12.413976"], ["id", 1]]
   (0.1ms)  RELEASE SAVEPOINT active_record_1
=> true
```

このとき、更新日時を表すマジックカラムである`updated_at`も更新されていることは重要です。

```
>> user.created_at
=> Sun, 29 Sep 2019 22:50:02 UTC +00:00
>> user.updated_at
=> Wed, 02 Oct 2019 23:05:12 UTC +00:00
```

### `save`しなかった場合

モデルオブジェクトの変更内容は、`save`メソッドを実行して保存しなければRDBには反映されません。保存しないで`reload`メソッドを実行した場合、RDBの内容を元にオブジェクトを再読み込みするので、次のように変更が取り消されます。

```
>> user.email
=> "mhartl@example.net"
>> user.email
=> "mhartl@example.net"
>> user.email = "foo@bar.com"
=> "foo@bar.com"
>> user.reload.email
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
=> "mhartl@example.net"
```

## `update_attributes`メソッドを使って更新する方法

```
>> user.update_attributes(name: "The Dude", email: "dude@abides.org")
   (0.2ms)  SAVEPOINT active_record_1
  SQL (0.3ms)  UPDATE "users" SET "email" = ?, "updated_at" = ?, "name" = ? WHERE "users"."id" = ?  [["email", "dude@abides.org"], ["updated_at", "2019-10-02 23:12:19.673874"], ["name", "The Dude"], ["id", 1]]
   (0.2ms)  RELEASE SAVEPOINT active_record_1
=> true
>> user.name
=> "The Dude"
>> user.email
=> "dude@abides.org"
```

`update_attributes`メソッドには、以下のような特徴があります。

* 引数は属性のハッシュである
* 必要な属性がすべて定義されていないと成功しない
* 成功時には更新と保存を続けて同時に行う
    * `save`メソッドを別に実行する必要はない
* 保存まで成功した場合、戻り値は`true`となる

`update_attributes`メソッドによってRDBに新しいレコードを生成することも可能です。

```
>> User.new.update_attributes(email:"foo@bar.com")
   (0.2ms)  SAVEPOINT active_record_1
  SQL (0.3ms)  INSERT INTO "users" ("email", "created_at", "updated_at") VALUES (?, ?, ?)  [["email", "foo@bar.com"], ["created_at", "2019-10-03 11:06:14.875963"], ["updated_at", "2019-10-03 11:06:14.875963"]]
   (0.2ms)  RELEASE SAVEPOINT active_record_1
=> true
```

## `update_attribute`メソッドを使う方法

特定の属性のみを更新したい場合は、`update_attribute`というメソッドを使います。

```
>> user.update_attribute(:name, "El Duderino")
   (4.4ms)  SAVEPOINT active_record_1
  SQL (4.0ms)  UPDATE "users" SET "updated_at" = ?, "name" = ? WHERE "users"."id" = ?  [["updated_at", "2019-10-03 10:56:18.366072"], ["name", "El Duderino"], ["id", 1]]
   (2.1ms)  RELEASE SAVEPOINT active_record_1
=> true
```

かくして、Userモデルの1番目のレコードの中身は以下のようになりました。

```
>> User.find(1)
  User Load (0.5ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
=> #<User id: 1, name: "El Duderino", email: "dude@abides.org", created_at: "2019-09-29 22:50:02", updated_at: "2019-10-03 10:56:18">
```

# 演習 - ユーザーオブジェクトの更新

## 1. userオブジェクトへの代入を使ってname属性を使って更新し、saveで保存してみてください。

```
>> user.name
=> "El Duderino"
>> user.name = "Reimu Hakurei"
=> "Reimu Hakurei"
>> user.save
   (0.2ms)  SAVEPOINT active_record_1
  SQL (0.3ms)  UPDATE "users" SET "updated_at" = ?, "name" = ? WHERE "users"."id" = ?  [["updated_at", "2019-10-03 10:58:44.709664"], ["name", "Reimu Hakurei"], ["id", 1]]
   (0.3ms)  RELEASE SAVEPOINT active_record_1
=> true
>> User.find(1)
  User Load (0.3ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
=> #<User id: 1, name: "Reimu Hakurei", email: "dude@abides.org", created_at: "2019-09-29 22:50:02", updated_at: "2019-10-03 10:58:44">
```

## 2. 今度はupdate_attributesを使って、email属性を更新および保存してみてください。

```
>> user.update_attributes(email: "foo@foobar.net")
   (0.1ms)  SAVEPOINT active_record_1
  SQL (1.4ms)  UPDATE "users" SET "email" = ?, "updated_at" = ? WHERE "users"."id" = ?  [["email", "foo@foobar.net"], ["updated_at", "2019-10-03 11:02:33.452008"], ["id", 1]]
   (0.1ms)  RELEASE SAVEPOINT active_record_1
```

`update_attributes`メソッドを使った場合、検証が成功した時点で直ちにRDBに変更が保存されます。

## 3. 同様にして、マジックカラムであるcreated_atも直接更新できることを確認してみてください。

> ヒント: 更新するときは「1.year.ago」を使うと便利です。これはRails流の時間指定の１つで、現在の時刻から１年前の時間を算出してくれます。

```
>> user.update_attributes(created_at: 1.year.ago)
   (0.3ms)  SAVEPOINT active_record_1
  SQL (0.2ms)  UPDATE "users" SET "created_at" = ?, "updated_at" = ? WHERE "users"."id" = ?  [["created_at", "2018-10-03 11:12:02.215696"], ["updated_at", "2019-10-03 11:12:02.228937"], ["id", 1]]
   (0.2ms)  RELEASE SAVEPOINT active_record_1
=> true
```

`created_at`が確かにUTC2018年10月3日になっていますね（この演習を行ったのはUTC2019年10月3日）。