他言語経験者による初めてのrails

仕事でrailsを使うことになったので、調べたり書籍読んだりして学んだことをプロジェクト作成、マイグレーション、モデルあたりを中心に備忘メモ（コントローラ辺りで疲れて雑になってる）
ruby自体もほぼ未経験(Vagrantfileとかchefレシピ書く時使った程度)です、今の自分の知識レベルを残す意図も兼ねて残しておく


## 自分のspec

### 経験言語(業務で)
* Java
* PHP
* ワタシ、SQL(MySQL)、チョットワカル

### 経験フレームワーク(業務で)
* 何となくMVCぽいの。名前は書いて良いのか分からないので伏せる(Java)
* 何となくMVCぽいの。名前は書いて良いのか分からないので伏せる(PHP)

## 学習期間
* 1週間ほど

## 準備
* railsでググってはてブ数多めの記事をほとんど読まずにひたすらブクマ
    * 何でrails関連記事はこんなに100ブクマ超えだらけなんだ？
* 環境構築 - [他言語経験者による初めてのrails（環境構築編） - Qiita](http://qiita.com/jpshadowapps/items/3fd4b3c194ed3510bd3e)

## 学習の変遷
* パーフェクトRuby on Rails, 1章→2章と目を通し、6章→7章と目を通しつつ写経
    * rubyとrailsに対する事前知識が無さ過ぎた為か、内容に敷居の高さを感じる事があった
* 優し目の入門記事を複数読んで知識の補完・リカバリ
    * [開発現場でちゃんと使えるRails 4入門（1）：簡単インストールから始める初心者のためのRuby on Railsチュートリアル (1/3) - ＠IT](http://www.atmarkit.co.jp/ait/articles/1402/28/news047.html)
    * [Ruby on Rails入門](http://www.rubylife.jp/rails/)
    * [Ruby on Rails入門 ~ミニブログを作りながら学ぶWebアプリケーション制作 第1回~ 鳥井 雪 先生 - 無料動画学習｜schoo（スクー）](https://schoo.jp/class/1943)
        * チャプター から見れる質疑応答が、割りと自分の疑問と被ってる事が多かった
        * 

## 簡易ブログアプリ作成
* DBはMySQLを使う
    * 本番でデフォルトのSQLiteを使ってるイメージが余り無いし、だったら最初から事例多そうなmysqlを使いたかった
* テストはRSpecで書く（事を想定はしたが、この記事ではRSpecには触れてない）
* git管理下に置く前提で（完成したら`git commit`してゴール、のイメージ）

### プロジェクトの新規作成
* `rails new PROJECT_NAME -T -d mysql --skip-bundle`で作成する
    * 雛形作成 ~~と使用するgem(`Gemfile`に記載されている)のインストール~~ が走る
    * `-T`オプション - デフォルトのテストフレームワーク`Test::Unit`に関連するファイル作成をスキップする
        * テストを RSpec で書きたい場合とかは指定すると良い
    * `-d mysql`オプション - DBにMySQLを使いたい場合に指定
        * mysql2 というgemを使うっぽい（Gemfileに記載されている）
        * DBの設定ファイル`config/database.yml`もmysql向けのものになっている（後で修正）
    * `--skip-bundle`オプション - 通常`rails new`時には関連するgemのインストールが __プロジェクト・ローカルではない場所に__ インストールされるが、それをスキップする
        * プロジェクト・ローカルで閉じた環境に置くことで管理しやすくなる
        * もう一つ挙げるなら`ctags -R .`するだけでライブラリ込みのタグ作ってくれるのも良かった（当方 vimmer）

```
$ rails new blog -T -d mysql --skip-bundle
           :
           :
      create  ....
```

### rails new時にスキップしたgemのインストール
* `cd PROJECT_ROOT`して`bin/bundle --path vendor/bundle`する
    * `--path`オプションでインストール先を指定。インストール先情報とかは`.bundle`ってファイルに保存されていて、以降gemを追加した際は普通に`bin/bundle`するだけで同じ場所にインストールしてくれる
    * パスに`vendor/bundle`を指定するのは、目に付いた事例がそうだったからで、この名前に意味があるのかは分かってない
    * [Gem - Bundler概要 - Qiita](http://qiita.com/hisonl/items/162f70e612e8e96dba50)

### git init
* `rails new`は`.gitignore`も吐いてくれるんだけど、`git init`までしてくれるわけではないので自分でやる
* デフォルトの`.gitignore`は不足があるので追加
    * `/vendor/bundle`を追加
    * `*.swp`を追加（vimmerなので！）
* なんかいじったら`git add`でstagingしておくとして、これだけファイルが色々あると`git commit`をどのタイミングでやるべきか悩みそう

### config/database.yml の修正
mysqlサーバの場所やスペック、ymlの項目についての詳細は省略。とりあえずvagrantで立てたmysqlサーバを使った

* `default`,`development`,`test`,`production`という4セクションあって、`default`で汎用的な設定を記述し、個別上書きが必要ならその他3つで記述するっぽい
    * `<<: *default`これが「defaultの内容を継承する」って意味だろう（調べてない）
* DB名は`DBNAME_env`って形式。DB名を環境別に変えるので本番DBを丸っとdevにインポートって事をしてもそのままでは動かない
    * DB名を全環境で揃える事は出来ない（出来てもやらない方が良い）のか
    * そもそもrailsは環境識別を何処でどのようにやってるのか？

```yaml:config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8
  pool: 5
  username: root
  password:
  host: 192.168.56.150

development:
  <<: *default
  database: blog_development
```

* そういえば`encoding`をutf8mb4に変更出来るんだっけ？
    * おお出来るっぽい！railsパナぃ！ [ActiveRecordをutf8mb4で動かす - Qiita](http://qiita.com/kamipo/items/101aaf8159cf1470d823)

### DBを作成する
* `rake db:create`で作成
    * CREATE DATABASE はこのコマンドで動かす。つまり`rake db:create`を叩くサーバ（と接続に使うmysqlユーザ）にCREATE権限が必要ということ（巻き戻しも考えたらDROP権限もいる）
        * CREATE/DROP TABLEまでWAFのコマンドでやる事には正直違和感ある。。本番でもこのコマンド使うのだろか
        * __rails/mysql使用時に最低限必要っぽい権限 = "CREATE,DROP,INDEX,ALTER,SELECT,INSERT,UPDATE,DELETE"__
    * コマンドは`PROJECT_ROOT/bin`下のものを使う習慣を付けとくべきなのか。`rails new`した時点のスナップショット、という立ち位置であるのなら。

```
$ bin/rake db:create
```

* 正常終了したら`DBNAME_development`と`DBNAME_test`の2つのDBが作成される
* わざわざmysqlサーバに入らなくても`rails db`を叩けばmysqlにログイン出来る
    * これも同じく、接続を可能にする権限設定をサーバ側で行っている事が前提だけど

```sql
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| blog_development   |
| blog_test          |
| mysql              |
| performance_schema |
| test               |
+--------------------+
```

### アプリケーションサーバの起動と確認
* `rails s` でアプリケーションサーバを起動
* WEBrick という、rails付属のアプリケーションサーバが起動する
    * [WEBrickを使うためのメモ - 再帰の反復](http://d.hatena.ne.jp/lemniscus/20090722/1248261257)
    * 本番運用時にはApacheやNginxの後ろで動いている、というイメージで良いのかな
    * そもそもこのWEBrickは本番でどの程度利用されているのだろうか？
        * 仮に別のものに差し替える時に`rails s`の挙動（起ち上げるサーバ）をどうやって変更するのだろか？
* `http://localhost:3000`で確認

### アプリに機能を追加していく
railsアプリの動きはザックリこういう事だろうと理解している

* ユーザーがURL指定でリクエストする
* `config/route.rb`の内容を元に、URLと実行すべきコントローラの紐付けを行い、指定したコントローラ（のアクション）を実行する
* 実行されたコントローラ（のアクション）内では、DBのCRUD操作を呼び出しモデルにその内容をセットする
* コントローラ（のアクション）の処理が完了したら、ビューをレンダリングする、この際モデルをビューに渡す
* ユーザーにビューのレンダリング結果を表示する

#### rails g コマンドで必要なファイルを生成
`rails g 何か`と叩くだけで、rails的にヨサゲな雛形群を生成してくれる便利コマンドさん

##### コントローラ(とその中のアクションメソッド)を生成 
* `rails g controller CTL ACT`
    * 名前ミスった！とかで巻き戻したい - `rails d controller CTL`

##### ルーティング
* `config/route.rb`にURLとそのコントローラのマッピング設定を追加する

##### モデルを生成
* `rails g model MODEL COLUMNS`
    * `db/migrate`ディレクトリにテーブル用のマイグレーション（作成・修正）ファイルが生成される
    * `MODEL`で指定する名前は単数形を指定。z生成されるテーブル名は`MODELs`と複数形の名前になる模様
    * INDEX追加とか各種制約が欲しければこのファイルに追記すれば良い
    * ファイル名にタイムスタンプが含まれてるんだけど、タイムゾーンがUTCっぽいのでどうにかしたい
    * `app/model`ディレクトリにモデルクラスが生成される
    * 名前ミスった！とかで巻き戻したい - `rails d model MODEL`

##### マイグレーション適用後に、さらなるマイグレーション
* `rails g migration MIGRATION COLUMNS`
    * このとき生成されるマイグレーションファイル（のクラス）には`up`(適用する),`down`(戻す),`change`(変更する)といったメソッドが定義されていて、それぞれに必要な処理を記述すれば良いらしい
        * [新しいマイグレーションを追加してテーブルを変更 - Ruby on Rails入門](http://www.rubylife.jp/rails/model/index7.html)
    * 名前ミスった！とかで巻き戻し（削除）したい - `rails d migration MIGRATION`

##### コントローラ生成＋`route.rb`修正＋モデル生成 まで一発でやる
* `resource`サブコマンド - `rails g resource MODEL COLUMNS`
    * 修正後のroute.rbを見れば分かるが、 __RESTfulを意識したアプリ向けのファイルを生成してくれるのが`rails g resource`__
        * GET(show),POST(create),PUT(update),DELETE(destroy)
    * 名前ミスった！とかで巻き戻したい - `rails d resource MODEL`

##### さらにビュー関連ファイルまでも生成してくれる "全部入りgenerator"
* `scaffold` - `rails g scaffold MODEL COLUMNS`
    * `rails g resource` + ビュー生成 で良いのか
    * 名前ミスった！とかで巻き戻したい - `rails d scaffold MODEL`
    
    > Rails4の「Getting Start」からは、scaffold(スキャフォールド)の説明がとり除かれたようです。 これは、おそらくscaffoldを使用することで、かえって初心者の理解を妨げる要因になると考えられてのことだと思われます。

* __今回の開発では`rails g {model,controller}`を用い、一括生成系の`rails g {resource,scaffold}`は使わないことにした__
    * 学びながら作る、のが目的なら、こうすべきなのかなと思ったので

#### マイグレーションをDBに適用する
* `rails g model`とかで生成されたマイグレーションファイルの内容を、`rake db:migrate`を実行して適用し、テーブル定義の修正を行う
    * カラム定義間違えた！とかで巻き戻したい - `rake db:rollback`
        * rollback前 にマイグレーションファイルをいじると、rollback 実行時にエラーになる。いつ・どんな内容のマイグレーションを行ったかを管理しているからだろう
* `rake db:migrate:status`すると、DB適用済/未適用のマイグレーションを確認可能
* __仕様変更等でTBLの変更が発生した場合は、まず`rails g migrate`でマイグレーションファイルを生成し、それを適用する__ というのがrails流なんですかね

実際に作ってみる

TBLのPKにはintegerのidがデフォルトで使われるらしい。`rails g`のカラム指定で明示する必要はない

```
$ bin/rails g model article title:string content:text  # id は未指定
$ bin/rails g model comment article_id:int comment:text  # id は未指定
```

で生成すると、下記のようなマイグレーションファイル（実体はクラス）が出来る。
最低限のCREATE TABLE処理だけが定義されるので、INDEX付与したい場合とかは __専用のメソッドで__ 指定する

* index追加 - `add_index TBL_NAME, COLUMNS`
* その他使用できるメソッドやオプションを探すには [マイグレーション(migration) - Railsドキュメント](http://railsdoc.com/migration) を見ると良さげ

```rb
class CreateArticles < ActiveRecord::Migration
  def change
    create_table :articles do |t|
      t.string :title
      t.text :content

      t.timestamps null: false
    end
  end
end

class CreateComments < ActiveRecord::Migration
  def change
    create_table :comments do |t|
      t.integer :article_id
      t.text :comment

      t.timestamps null: false
    end

    add_index :comments, :article_id  # 手動で追記
  end
end
```

これを`rake db:migrate`でDBに適用。railsがマイグレーションクラスをDBMS毎に良しなにDDLに変換して実行してくれる

```
$ bin/rake db:migrate

== 20150227171411 CreateArticles: migrating ===================================
-- create_table(:articles)
   -> 0.0158s
== 20150227171411 CreateArticles: migrated (0.0160s) ==========================

== 20150227173657 CreateComments: migrating ===================================
-- create_table(:comments)
   -> 0.0276s
-- add_index(:comments, :article_id)
   -> 0.0381s
```

DBサーバ側でarticlesのDDLを覗いてみる。`:string`型指定したカラムは`varchar(255)`になる。長さを指定したければ`rails g model`の際に、`title:string{100}`と`{NUM}`形式で指定すれば良い

```sql
mysql> show create table articles;

CREATE TABLE `articles` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `content` text COLLATE utf8_unicode_ci,
  `created_at` datetime NOT NULL,
  `updated_at` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
```

ここまでして、 __テーブル間のリレーション設定には`rails g model`の際に`NAME:references`と`references`を使えば良い事を知った__ 。
ので戻して再作成する
戻しの流れがちと分かりにくかったが、`rake db:rollback`して`rails d model`でイケた。 → 参考: [間違ったrake db:migrateを元に戻す | Morizotter Blog](http://blog.morizotter.com/2014/05/05/rails-rollback-migration/)
(実際の運用ではSQLでいうところの`ALTER`相当の操作は`rails g migration``rake db:migrate`で行うのだろな、と想像している)

```
$ bin/rails g model comment article:references comment:text  # references でリレーション指定
$ bin/rake db:migrate
```

`references`指定のあるテーブルはmysqlではこういう`FOREIGN KEY`指定がなされたCREATE文になるらしい。 ~~FOREIGN KEY 出来ればあまり使いたくない人です~~

```sql
CREATE TABLE `comments` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `article_id` int(11) DEFAULT NULL,
  `comment` text COLLATE utf8_unicode_ci,
  `created_at` datetime NOT NULL,
  `updated_at` datetime NOT NULL,
  PRIMARY KEY (`id`),
  KEY `index_comments_on_article_id` (`article_id`),
  CONSTRAINT `fk_rails_f4641449be` FOREIGN KEY (`article_id`) REFERENCES `articles` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
```

#### モデル
そもそもモデル(`/app/model`下のクラス)には何を書くのかというと、ザックリこの3つっぽい

* モデル間のリレーション(association)
* モデルで保持する値のバリデーション
* モデル固有のビジネスロジック

##### リレーション
__マイグレーションで設定したリレーション(railsではassociationと呼んでいるぽい)を、モデルクラスにも独自DSL使って明記する__。
こうすることで、モデルを使う側(V層C層)にはDB(インフラ層)を隠蔽するという意図が見える
実は comment のモデルクラスには`rails g model`時点でリレーションについての処理が入っている。それが下記。`belongs_to`は名前の通り「所属」つまり一対多リレーションの一側を指定する __メソッド__ 。引数には __対象モデルクラス名を小文字にしたシンボル__ を指定するようだ

```rb
belongs_to :article
```

もう一方のarticle（一対多の一側）はどうするか？
`rails g model`時点では空のクラスが定義されているだけだが、同じくリレーションについての処理を追加する。
これは開発の流れとしてはよく有るケースっぽくて、一対多な関係性を持つモデルを`rails g model`で生成する際、一を生成した時点ではまだ多側がどのモデルになるか分からない。なので多側を追加生成する度に一側に`has_many`を追加していくのかと
例えば `dependent: :destroy`オプションは、「削除の際に関連する多側も一緒に削除する」事を指定する

```rb:app/model/article.rb
class Article < ActiveRecord::Base
  has_many :comment, dependent: :destroy
end
```

##### バリデーション
取りうる値に制限を設ける。`validates`か`validate`メソッドを使う。カラムごとの指定には前者、より複雑な・別途メソッドを呼んだりするような場合は後者を使うっぽい
バリデーションで使えるメソッド・オプションは [検証(validation) - Railsドキュメント](http://railsdoc.com/validation) を見ると良さげ

```rb:app/model/article.rb
class Article < ActiveRecord::Base
  has_many :comment, dependent: :destroy

  # 記事のタイトルは必須(presence)で50文字(length)まで
  validates :title, length: { maximum: 50 }, presence: true
  # 記事の本文は必須(presence)
  validates :content, presence: true

end
```
```rb:app/model/comment.rb
class Comment < ActiveRecord::Base
  belongs_to :article

  # コメントは必須(presence)
  validates :comment, presence: true

end
```

#### コントローラ（とビュー）
`rails g controller`すると、コントローラと対応するビューが生成される。コントローラ名は複数形で指定すれば良いらしい

```
$ bin/rails g controller articles
```

コントローラクラスにアクションメソッドを追加し、`config/routes.rb`にURLとアクションの紐付けも追加していく。`rails g {resource,scaffold}`を使えばこの辺りまで自動生成してくれて便利なのだろう

articleの一覧を拾って表示するだけの処理を書いてみる

```rb:app/controler/articles_controller.rb
class ArticlesController < ApplicationController
  def index
    # modelの all メソッドで取得した全件を @付きの変数(インスタンス変数)にセット
    @articles = Article.all

    respond_to do |format|
      format.html
      format.json
    end
  end
end
```
```rb:config/routes.rb
Rails.application.routes.draw do
  # "http://localhost:3000/articles" にアクセス時に起動するアクションを 'コントローラ名#メソッド名' で定義
  get '/articles' => 'articles#index'  
```

ビューにはデフォルトでerbと呼ばれるテンプレートが使われるらしい。jspっぽい（と言っていいのか）。
画面ごとのビューファイルには、その画面固有の内容だけ書けば良くて、ヘッダとかフッタとか所謂共通部分は`app/views/layouts`下のファイルを使ってくれる。デフォルトでは`application.html.erb`が使われるようで、このレイアウトファイル内の`<%= yield %>`と記述した箇所に固有のビューが埋め込まれる

```erb:app/views/article/index.html.erb
<div>
  記事
  <div>
    <% @articles.each do |article| %>
      <ul>
        <li><%= article.title %> | <%= article.content %></li>
      </ul>
    <% end %>
  </div>
</div>
```

作成・編集・削除とか、コメント機能の追加も同様にコントローラとビューを追加してガシガシ作っていけば良い。それはまたおいおい纏めようと思う

所感として、自分のような他言語/他FW経験者は、「どういう仕組み・流れで、どういう機能を実現したいか？」は分かっていても「どう書けばよいか？」が分からない/調査に時間が掛かる事が多いと思う。
__見やすい・探しやすい__ API一覧とか逆引きリファレンスを手元に置いておくとだいぶ効率が違ってくるはず


最後にRailsとは？系の記事を読んで、目に止まった言葉をいくつか

> 全てのプログラマがWebアプリケーションを作り始めるにあたって、必要であろう物を仮定することによって簡単にプログラム出来るように設計されています

>  他の多くの言語やフレームワークと比べ、なるべくコードを書かずに済むようになっています

> Railsは固執(?)的なソフトウェアです。 Railsは、それを行うのはこれが"ベスト"な方法であると仮定してしまい、その方法を奨励し別の方法は推奨しないように設計されています。 もし、"The Rails Way"を学ぶと、生産性が飛躍的に向上するかもしれません。 もし、Railsの開発に他の言語の古い手法を用いるのであれば、幸福な体験を得ることは難しいでしょう。