Rails Tutorialsで勉強のメモ　その２

デモアプリケーションを`scaffold`ジェネレータですばやく作成

# デモアプリの準備

```bash
# デモアプリの初期化
$ cd rails_projects
$ rails new demo_app
$ cd demo_app

# Gemfileの書き換え
$ vim Gemfile
```

```rb:Gemfile
source 'https://rubygems.org'
ruby '2.0.0'
#ruby-gemset=railstutorial_rails_4_0

gem 'rails', '4.0.4'

group :development do
  gem 'sqlite3', '1.3.8'
end

gem 'sass-rails', '4.0.2'
gem 'uglifier', '2.1.1'
gem 'coffee-rails', '4.0.1'
gem 'jquery-rails', '3.0.4'
gem 'turbolinks', '1.1.1'
gem 'jbuilder', '1.0.2'


group :doc do
  gem 'sdoc', '0.3.20', require: false
end

group :production do
  gem 'pg', '0.15.1'
  gem 'rails_12factor', '0.0.2'
end
```

```bash
# gemのインストール
$ bundle install --without production
$ bundle update
$ bundle install

# gitリポジトリの作成とコミット
$ git init
$ git add .
$ git commit -m "Initial commit"

# GitHubにpush
# 事前にGitHubにdemo_appというリポジトリを作成
$ git remote add origin https://asam-3@github.com/asam-3/demo_app.git
$ git push -u origin master
```

## モデル設計

* マイクロブログのモデル
    * ブログのユーザ
    * マイクロポスト

* usersデータモデル

| 列名 | 型 | 備考 |
| ---- | ---- | ---- |
| id | integer | 一意のキー |
| name | string | |
| email | string | |

* micropostsデータモデル

| 列名 | 型 | 備考 |
| ---- | ---- | ---- |
| id | integer | 一意のキー |
| content | string | |
| user_id | integer | |

# Usersリソース

* UsersデータモデルとWebインタフェースが組み合わさったもの
* ユーザをHTTPプロトコル経由で自由に作成／読み出し／更新／削除できるオブジェクト
* `scaffold`ジェネレータでUsersリソースを生成する
    * `rails generate`スクリプトに`scaffold`コマンドを渡す

```bash
# Usersリソースを作成（Usersデータモデルが作成される）
$ rails generate scaffold User name:string email:string

# データベースをmigrateし、usersデータモデルを作成する
# bundle execを使用するのは、現在のGemfileに対応するバージョンのRakeが確実に実行されるようにするため
$ bundle exec rake db:migrate


# サーバを起動
$ rails s
```

## 参考：rakeコマンドについて

* Ruby版のmakeのようなもの

```bash
# rakeのデータベースタスクを参照
$ bundle exec rake -T db
rake db:create              # Create the database from DATABASE_URL or conf...
rake db:drop                # Drops the database using DATABASE_URL or the ...
rake db:fixtures:load       # Load fixtures into the current environment's ...
rake db:migrate             # Migrate the database (options: VERSION=x, VER...
rake db:migrate:status      # Display status of migrations
rake db:rollback            # Rolls the schema back to the previous version...
rake db:schema:cache:clear  # Clear a db/schema_cache.dump file
rake db:schema:cache:dump   # Create a db/schema_cache.dump file
rake db:schema:dump         # Create a db/schema.rb file that can be portab...
rake db:schema:load         # Load a schema.rb file into the database
rake db:seed                # Load the seed data from db/seeds.rb
rake db:setup               # Create the database, load the schema, and ini...
rake db:structure:dump      # Dump the database structure to db/structure.sql
rake db:version             # Retrieves the current schema version number
rake test:all:db            # Run tests quickly, but also reset db

# rakeの全てのタスクを参照
$ bundle exec rake -T
（省略）
```

## ユーザページの表示

* `rails generate scaffold User`でUsersリソースを作成すると、ページも自動生成される

| URL | アクション | 用途 |
| --- | --- | --- |
| /users | index | 全てのユーザ一覧を表示 |
| /users/1 | show | id=1のユーザを表示 |
| /users/new | new | 新規ユーザを作成 |
| /users/1/edit | edit | id=1のユーザを編集 |

* ユーザ関連操作を一通り実施
    * 作成
    * 表示
    * 編集
    * 削除


* RailsにおけるMVCの挙動（/usersにアクセスした際のフロー）
    1. /usersというURLへのリクエストを発行
    2. Railsルータが/usersをUsersコントローラ内のindexアクションに割り当てる（ルーティング）
    3. indexアクションは、Userモデルに「全てのユーザを取得する」と指示する（User.all）
    4. Userモデルは全てのユーザをDBから取り出す
    5. Userモデルはユーザの一覧をコントローラに返す
    6. コントローラはユーザの一覧を@users変数に保存し、indexビューに渡す
    7. ビューはコードの中に埋め込まれているRubyを使用してHTMLを生成する
    8. コントローラは生成されたHTMLをブラウザに返す


* Railsルータのソース
    * URLをUsersリソースで使用するコントローラアクションにマッピング
    * URLとアクションの組合せを作成

```rb:config/routes.rb
DemoApp::Application.routes.draw do
  resources :users
end
```

* scaffoldで生成したコントローラ
    * Usersのアクションがまとめられている

```rb:app/controllers/users_controller.rb（クラス・メソッド定義のみ）
class UserController < ApplicationController
  # 全てのユーザを表示するページ
  def index
  end

  # ユーザを表示するページ
  def show
  end

  # ユーザを新規作成するページ
  def new
  end

  # ユーザを作成するアクション
  def create
  end

  # ユーザを編集するページ
  def edit
  end

  # ユーザを更新するアクション
  def update
  end

  # ユーザを削除
  def destroy
  end
end
```

* UsersコントローラとUserモデルの関係を確認
    * indexアクション
        * Userモデルから全てのユーザの一覧を取り出し、`@users`変数に格納

```rb:app/controllers/users_controller.rb（indexメソッドのみ）
class UserController < ApplicationController
  def index
    @users = User.all
  end
end
```

* Userモデル
    * 継承や`ActiveRecord`ライブラリにより、Userモデルは`User.all`というリクエストに対して全てのユーザを返すことができる

```rb:app/models/user.rb（Userモデル）
class User < ActiveRecord::Base
end
```

* ユーザーのビュー
    * `@users`変数に一覧が保存されると、コントローラが下記のビューを呼び出す

```erb:app/views/users/index.html.erb
<h1>Listing users</h1>
<table>
  <tr>
    <th>Name</th>
    <th>Email</th>
    <th></th>
    <th></th>
    <th></th>
  </tr>
<% @users.each do |user| %>
  <tr>
    <td><%= user.name %></td>
    <td><%= user.email %></td>
    <td><%= link_to 'Show', user %></td>
    <td><%= link_to 'Edit', edit_user_path(user) %></td>
    <td><%= link_to 'Destroy', user, method: :delete, data: { confirm: 'Are you sure?' } %></td>
  </tr>
<% end %>
</table>
<br />
<%= link_to 'New User', new_user_path %>
```

* Usersリソースの問題
    * データのバリデーションチェックが行われていない
    * ユーザ認証がない
    * テストコードがほぼない
    * レイアウトがちょっと
    * 理解しにくい

# Micropostsリソース

## マイクロポストのページを表示

```bash
# scaffoldでMicropostを作成
$ rails generate scaffold Micropost content:string user_id:integer

# 新しいデータモデルでDBを更新
$ bundle exec rake db:migrate
```

* `Micropost`を追加したことによるソースの追加／変更
    * Railsルータに`Microposts`が追加される
    * `MicropostsController`など、Usersと同様に各種ファイルの作成
* とりあえずマイクロポストをブラウザから2個作ってみる

```rb:config/routes.rb
DemoApp::Application.routes.draw do
  resources :microposts

  resources :users
end
```

## 文字数制限の追加

* `validates`を使用して入力制限を追加
* micropostモデルに入力制限のロジックを追加
    * 140文字以内

```rb:app/models/micropost.rb
class Micropost < ActiveRecord::Base
  validates :content, length: { maximum: 140 }
end
```

## UserとMicropostの関連付け

* 一人のユーザに対して複数のマイクロポストを関連付ける
    * `has_many :microposts`
* 一つのマイクロポストは一つのユーザにのみ属する
    * `belongs_to :user`

```rb:app/models/user.rb
class User < ActiveRecord::Base
  has_many :microposts
end
```

```rb:app/models/micropost.rb
class Micropost < ActiveRecord::Base
  belongs_to :user
  validates :content, length: { maximum: 140 }
end
```

### Railsアプリケーションの対話的な操作（`rails console`）

* Railsの`console`
    * Railsアプリケーションを対話的に操作
    * `rails console`で起動
* `User`と`Micropost`の関連付けを確認

```bash
$ rails console

# first_user変数に一人目のユーザを設定
> first_user = User.first
  User Load (0.6ms)  SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 1
 => #<User id: 2, name: "テストユーザー", email: "admin@localhost", created_at: "2014-08-02 06:36:09", updated_at: "2014-08-02 06:36:09">

# ユーザに関連付けられているマイクロポストを参照
> first_user.microposts
  Micropost Load (2.4ms)  SELECT "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ?  [["user_id", 2]]
 => #<ActiveRecord::Associations::CollectionProxy [#<Micropost id: 1, content: "テスト", user_id: 2, created_at: "2014-08-02 06:34:54", updated_at: "2014-08-02 06:35:06">]>

# コンソールを終了
> exit
```

## 継承の階層

### モデルの継承構造

* `ActiveRecord::Base`クラスを継承
    * 作成したオブジェクトはDBにアクセスできるようになり、DBのカラムを`Ruby`の属性のように扱えるようになる

### コントローラの継承構造

* `ApplicationController`を継承
* `ApplicationController`は`ActionController::Base`を継承
    * モデルオブジェクトの操作
    * インバウンドHTTP requestのフィルタ
    * ビューをHTMLとして出力
* Railsのコントローラは必ず`ApplicationController`を継承
    * Applicationコントローラで定義した内容は全てのアプリケーションに反映される
    * サインイン・サインアウトのヘルパーメソッドを定義する、といった用途に使用できる

## デモアプリケーションのデプロイ

* リポジトリをGitHubに登録

```bash
$ git add .
$ git commit -m "Finish demo app"
$ git push
```

* Herokuにデプロイ

```bash
$ heroku create
$ git push heroku master
```

* 本番DBのマイグレーション

```bash
$ heroku run rake db:migrate
```

# まとめ

* この章でやれたこと
    * Rails全体を高度なレベルで概観
    * MVCモデルを確認
    * RESTアーキテクチャに触れる
    * データベースモデルを作成
    * データベースを背後に持つWebアプリケーションを本番環境で動作させる