あなたがRails触る人なら見ておきたい「体系的な」豆知識


ここ最近複数の専門書を断片的に読む機会やら、(自分にとって)新たな技術にチャレンジする機会やらを頂いたため、最近数ヶ月で学んだ内容をせっかくなので体系的にまとめました。気づいたら大分ボリュームを増やしていましたので、必要なとき必要な箇所に行き当たってくれることを祈っています。

なお、以下の5記事を姉妹記事として同時に書いています。
文中でもリンクが出てきますのでよろしければ併せて目を通して頂けると幸いです。

- [**YAMLとは何か？ - いつもRailsの設定ファイルで出てくる奴の正体**](http://qiita.com/Yama-to/items/587544993fb62610528a)
- [**redirect_to使った時にフラッシュメッセージを表示させる**](http://qiita.com/Yama-to/items/4d19a714d8bf5bfbabdd)
- [**JavaScriptを卒業してCoffeeScriptで飛躍しよう - 基本などまとめ**](http://qiita.com/Yama-to/items/a947cfa2565b03ddc9de)
- [**モモンガでも使えるAjax（えいじゃっくす） − 投稿したコメントをリロードせずに表示しよう**](http://qiita.com/Yama-to/items/377f8a92cf30a3ebf454)
- [**Railsでメール自動配信機能をつくるまでの道程**](http://qiita.com/Yama-to/items/823baf26bba3193712ea)

# ■ Modelに関するあれこれ

## activerecord関連

### O/Rマッパー

- modelがデータベースとのやり取りをする際に欠かせないのがO/Rマッパー
- リレーショナルデータベースとオブジェクト指向言語の橋渡しをする
- 例えばオブジェクトのプロパティ名をテーブルのカラム名と一致させたりする
- Railsではデフォルトで `ActiveRcord` がO/Rマッパーとして使われる

### yamlとは何か

- `database.yml` とか設定ファイルに時々でてくる奴
- まとめたら長すぎたので別記事にしました（下記）

「[**YAMLとは何か？ - いつもRailsの設定ファイルで出てくる奴の正体**](http://qiita.com/Yama-to/items/587544993fb62610528a)」

## SQL関連

### whereメソッドのバリエーション

- プレイスホルダ(`?`)を使用して検索条件を後置できる
- プレイスホルダ(`?`)の個数分引数として値を渡してあげる必要がある
- 具体例： `Book.where('title = ? AND author = ?', <値1>, <値2>)`

```bash
$ Book.where('title = ? AND author = ?', params[:title], params[:author])

Book Load (0.3ms)  SELECT `books`.* FROM `books` WHERE (title = '白鯨' AND author = 'メルヴィル')
```

- `?` を使用した検索条件指定は名前なしパラメータと呼ばれる
- 名前付きパラメータを使用すると上記の例は以下のように書き換えられる

```bash
$ Book.where('title = :title AND author = :author', title: params[:title], author: params[:author])
```

- 検索条件には `not` を付与し「●●以外のもの全て」という条件式をつくることができる

```bash
$ Book.where.not(title: "白鯨")

Book Load (0.3ms)  SELECT `books`.* FROM `books` WHERE (`books`.`title` != '白鯨')
```

### 取得するプロパティの制限

- 多くの情報を扱うテーブルにおいて不要な列まで無条件に取り出すことは処理速度の遅延にもつながる
- `select` メソッドを併せて使用してあげると取得するプロパティを制限することができる

```bash
$ Book.all.select(:id, :title)

Book Load (0.3ms)  SELECT `books`.`id`,`books`.`title` FROM `books`

#=> [#<Book:0x007fb5ea28a4b8 id: 2, title: "白鯨">,
 #<Book:0x007fb5ea28a1c0 id: 3, title: "そして">]
```

### 指定したカラムの値で配列をつくる

- 取得してきたレコードに `pluckメソッド` を使用すると指定の値だけを取り出して配列にすることができる

```bash
$ Book.all.select(:id, :title).pluck(:title)

Book Load (0.3ms)  SELECT `books`.`title` FROM `books`

#=> ["白鯨", "そして"]
```


## config関連

### アプリケーションやアクティブレコードで使用するタイムゾーンを変更する

- `application.rb` 内に記述する
- デフォルトでコメントアウトされている部分の記述を以下のように直す

```ruby
# Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
# Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
# config.time_zone = 'Central Time (US & Canada)'

#=>
config.time_zone = 'Tokyo'
```

## console関連

### コンソールで検証した内容を実際のデータベース等に反映させたくない時

- 通常コンソールは `rails cコマンド` を用いて起動しレコードの生成・保存等の挙動を検証できる

```bash
$ rails c

Loading development environment (Rails 4.2.1)
[1] pry(main)> 
```

- `rails c --sandboxオプション` を使用するとコンソール終了時にデータベースに対する全ての変更をロールバックする

```bash
$ rails c --sandbox

Loading development environment in sandbox (Rails 4.2.1)
Any modifications you make will be rolled back on exit
[1] pry(main)>
```

### コード中のコメントを集めてTODO化する

- ひっそりと存在しているコマンド `rake notes`
- ルールに則りコメントを記述することでTODOを一覧化できる
- 書き方は `# [TODO/FIXME/OPTIMIZE]: hogehoge`

```ruby
class Users < ActiveRecord::Base
  # TODO:  set sign-in validations
  # FIXME: fix class name as singular
end
```

- 出力形式は `[行番号] [TODO/FIXME/OPTIMIZE] コメント`
- 特定の種類のコメントだけを出力することも可能

```bash
$ rake notes
#=>
app/models/user.rb:
  * [2] [TODO] set sign-in validations
  * [3] [FIXME] fix class name as singular

$ rake notes:fixme
#=>
app/models/user.rb:
  * [3] [FIXME] fix class name as singular
```

## migrationファイル関連

### 各カラムに追加設定できるオプション

- データベースに変更を加える際にmigrationファイルにはオプションを設定することができる
- 主なオプションとして以下の5つが挙げられる

|フラグ名|オプション内容|使用可能なデータ型|
|:--|:--|:--|
|limit|列の桁数|全て|
|default|デフォルト値|全て|
|null|null値を許可するか|全て|
|precision|少数以下含めた数値の全体桁|decimal型|
|scale|小数点以下の桁数|decimal型|

- 以下のように実行前のmigrationファイルに追記することでデータベースに反映させることができる


```ruby
def change
  create_table :books do |t|
    t.string  :title,     limit: 9, null: false
    t.decimal :price,     precision: 5, scale: 0
    t.string  :publisher, default: 'HBS'
    
    t.timestamps
  end
end
```

- 上記の指定はテーブルに対し以下の制約を与える
  - titleには9文字以下で何かしらの値が入っていなければ保存できない
  - priceは小数点以下0桁かつ全体5桁までの数字のみ保存可能
  - publisherには何も指定しなくともデフォルトで文字列'HBS'が代入される

### has_and_belongs_to_many関連の中間テーブルを生成する

- 中間テーブルは外部キー以外の列を持ってはいけないという制約がある
- migrationファイルにおいて `create_join_tableメソッド` を使用すれば制約を気にせずにテーブルを生成できる
- `rails g migration CreateJoinTableTable1AndTable2 <テーブル1名> <テーブル2名>` コマンドによってファイル中の記述も自動生成できる

```bash
$ rails g migration CreateJoinTableAuthorsAndBooks authors books

# invoke  active_record
# create    db/migrate/20150728175239_create_join_table_authors_and_books.rb
```

- migrationファイルの中身は以下のようになっている

```ruby
class CreateJoinTableAuthorsAndBooks < ActiveRecord::Migration
  def change
    create_join_table :authors, :books do |t|
      # t.index [:author_id, :book_id]
      # t.index [:book_id, :author_id]
    end
  end
end
```

# ■ Viewに関するあれこれ

## viewヘルパー関連

### html.erbは何の略か

- `erb` = **Embedded Ruby**
- html以外にもxmlやplain textにも使えるらしい
- 使ったことはない

### Rubyタグ直後の改行を無くす

- `-%>` のように閉じることでデザインに影響を与えるようなタグ直後の空白・改行を打ち消せる

```erb
<%= ... -%> || <% ... -%>
```

### コメントアウトの方法5種類

```erb
①
<%# 一般的なコメントアウトです %>

②
<%#= これもコメントアウトです 
     この行もコメントアウトです
     複数行に渡りコメントアウトできます %>

③
<% これはコメントではありません
　　# この行がコメントになります %>

④
<% if false %>
<%= この行はコメントアウトになります %>
<% end %>

⑤
<%
=begin
%>
<%= この間の部分は全てコメントになります %>
<%
=end
%>
```

### 改行文字を&lt;p&gt;/&lt;br&gt;タグで置き換える

- `simple_formatメソッド` によって改行文字を含むテキストをブラウザ上でも正しく表示できる
- 以下の4つの基準に従って文章を整形する
  - 文字列全体を `<p>` タグで括る
  - 単一の改行文字には `<br />` タグを付与
  - 連続した改行文字には `</p><p>` タグを付与
  - 元の改行文字の前後に追加され元の改行文字は破棄されない

```erb
# @text = 
# 本日の天気
#
# 関東地区の天気をお知らせします。
# 明日は全体的に猛暑日となる見込みです。
# 以上です。

<%= simple_format(@text) %>

# => 出力結果
<p>本日の天気</p>

<p>
関東地区の天気をお知らせします。<br />
明日は全体的に猛暑日となる見込みです。<br />
以上です。
</p>
```

### 条件に応じてリンクを生成する

- `link_to_if/unlessメソッド` を使用すると条件式がtrueの場合のみリンクを生成しfalseの場合はテキストを表示する
- `link_to_unless_currentメソッド` を使用するとリンク先が現在のページの場合はテキストのみを出力する

```erb
<%= link_to_if @user.present, "MY PAGE", user_path(@user) %>
<%= link_to_unless_current "HOME", root_path %>
```

## form関連

### リンク・ボタンクリック時に確認ダイアログを表示
- `link_to` に `data-confrimオプション` を指定する
- `OK` を選択した場合のみ画面遷移・処理を行うようにできる
- データの削除等、不可逆な処理に対する誤操作を極力無くすことができる

```erb
<%= link_to '削除', book, method: :delete, data: {confirm: '本当によろしいですか？'} %>
```

- `submitタグ` に `data-confirmオプション` を使用しても同様の処理を行える

### form_for配下でのform_tagヘルパーの使用

- モデルに紐付いたオブジェクトを扱うform_for内部でもモデルに紐付かない `form_tagヘルパー` を使用可能
- 例えばレビューを投稿するフォームの中にタグの入力欄を並列で作る時など有効

### form_forにおけるsubmitボタンの自動表示
- form_forメソッドは扱うモデルオブジェクトによってHTTPメソッドを使い分けてくれる
- 実はform内 `submitボタン` の表示文字も使い分けてくれる
- valueを設定しない場合初期値で「`Create (モデル名)`」または「`Update (モデル名)`」が自動で入る

### 存在検証機能の二重化
- コメント投稿欄に文字列が入っていなければデータを保存できないという検証機能をつける場合を考える
- 対象の入力欄に `required属性` を付与すると指定の入力欄が空の状態では警告メッセージが表示されデータを送信できなくなる

```erb
<%= f.text_filed :comment, required %>
```

- 警告メッセージを非常に簡易に実装でき便利だがブラウザ側から開発者ツール等を使用すれば容易に `required` 指定を外し空のデータを送れてしまう
- そのためにクライアントサイドだけでなくきちんとサーバーサイドでもバリデーションをかけなければならない

```ruby
class Comment < ActiveRecord::Base
  validates :text, presence: true
end
```

## layout関連

### 部分テンプレートを保存するべき場所

- 部分テンプレートの保存先には規則が存在する
- 規則に従うことでシンプルな記述で呼び出せるようになる

|用途|保存先フォルダ|
|:--|:--|
|特定のコントローラでのみ共有|/views/コントローラ名|
|アプリケーション全体で共有|/views/application|
|リソースに強く関連づいた部品|/views/リソース名(複数形)|

- `views/application` ディレクトリに保存された部分テンプレートはパスを指定せずとも `render 'review'` のように呼び出せる
- 同名の部分テンプレートが複数存在する場合にはメインテンプレートと同ディレクトリ配下の部分テンプレートが優先される

# ■ Controllerに関するあれこれ

## controllerの挙動関連

### ルーティングに対応するアクションが存在しない場合

- アクションの定義が存在せずともエラーにはならない
- 対応するテンプレートつまりビューファイルを直接読み込みにかかる

## 便利メソッド関連

### sleepメソッドによる処理中の演出

- `sleepメソッド` を使用すると指定秒数間処理を休止する
- formの `submit` において `data-disable_withオプション` と併用すると処理中の演出ができる
- 下記の場合「投稿する」ボタンを押した後3秒間ボタンが押せなくなくと同時に「処理中」の文字が表示される

```ruby
class ReviewsController < ApplicationController
  def create
    sleep 3
    Review.create(review_params)
  end

  #- (中略) -#
end
```

```erb
<%= form_for @review do |f| %>
  <!-- (中略) -->
  <%= f.submit "投稿する", data: {disable_with: "処理中..."} %>
<% end %>
```

### around_actionフィルタで記述をコンパクトにする

- フィルタ発火のタイミングはアクション実行直前直後の二種類

```ruby
class BooksController < ApplicationController
  before_action :start_log
  after_action  :end_log
  
  def create
    Book.create(book_params)
  end
  
  private
  
  def start_log
    logger.debug("[START: #{Time.now.to_s}]")
  end
  
  def end_log
    logger.debug("[END: #{Time.now.to_s}]")
  end
end
```

- `before_action` + `after_action` は **`around_action` + `yield`** で書き換えられる

```ruby
class BooksController < ApplicationController
  around_action :output_log
  
  def create
    Book.create(book_params)
  end
  
  private
  
  def output_log
    logger.debug("[START: #{Time.now.to_s}]")
    yield #アクションを実行
    logger.debug("[END: #{Time.now.to_s}]")
  end
end
```

## parameter関連

### アクセスしたユーザーのIPアドレスを取得する

- ユーザーのログイン認証を必要としない投票ボタンを実装する時に活躍
- アカウント機能は作りたくないけれど極力ひとり一票までにしたいときはIPアドレスで識別しよう
- `request.remote_ip` をコントローラーで使用すれば簡単にIPアドレスを取得できる

```ruby
class BooksController < ApplicationController
  #--(中略)--#
  
  private

  def set_ip
    @ip = request.remote_ip
  end
end
```
## status関連

### 条件に応じて意図的に404エラーを表示させる

- 条件分岐内にrenderメソッドでファイル名を指定してあげればok
- status
- `and return` を付けてあげないと痛い目を見る事が多い

```ruby
class BooksController < ApplicationController
  def show
    @book = Book.find(params[:id])
    if @book.status == '1'
      render file: Rails.root.join('public/404.html'), status: :not_found and return
    end
    render 'top/show'
  end
end
```

### 404以外のステータスコード

- logを見るときにもステータスコードを知っていると読解量が違ってくる
- SEOの観点からも302なのか301なのか正しいステータスコードを付与することは大切

|シンボル|コード|意味|
|:--|:--|:--|
|:ok|200|成功|
|:created|201|リソースの生成に成功|
|:moved_permanently|301|リソースが恒久的に移動した|
|:found|302|リソースが一時的に移動した|
|:see_other|303|リソースが別の場所にある|
|:unauthorized|401|認証を要求|
|:forbidden|403|アクセスが禁止されている|
|:not_found|404|リソースが存在しない|
|:method_not_allowed|405|HTTPメソッドが許可されていない|
|:internal_server_error|500|サーバーエラー|

## helper関連

### 現在のコントローラーに対応したヘルパーファイルのみ読み込む

- デフォルトではどのヘルパーファイルにヘルパーメソッドを記述してもアプリケーション全体で使用できてしまう
- アプリケーションが肥大化した場合名前の競合が意図せず起きてしまうことも考えられる
- 現在のコントローラー名に対応したヘルパーファイルだけを読み込む設定は `config/application.rb` に記述する

```ruby
class Application < Rails::Application
  # Do not share helper methods
  config.action_controller.include_all_helpers = false
end
```

- こうすることで、現在 `UsersController` に関連した動作を行っている場合は `users_helper.rb` が読み込まれる

## 認証関連

### ダイジェスト認証の設定

- コントローラーに記述するだけで簡単に実装できるセキュリティには二種類ある
- ベーシック認証とそれよりもセキュリティ強度の増した `ダイジェスト認証` の2つ
- `before_action` を利用し簡単に実装可能かつ記述も簡潔なダイジェスト認証の実装を紹介
- サイトにアクセスする際に共通ユーザー名と共通パスワードの入力が必須となる

```ruby
class ApplicationController < ActionController::Base
  before_action :digest_auth
  
  private

  def digest_auth
    members = { '<ユーザー名>' => '<パスワード>' }
    authenticate_or_request_with_http_digest('<アプリケーション名>') do |name|
      members[name]
    end
  end
end
```

## flashメッセージ関連

### redirect_toのオプションとしてフラッシュを設定する

- Bootstrapのalertのスタイルに当たるように任意のキーで送信できるようにする
- 長くなったので別記事にまとめました（下記）

「[**redirect_to使った時にフラッシュメッセージを表示させる**](http://qiita.com/Yama-to/items/4d19a714d8bf5bfbabdd)」

# ■ Routingに関するあれこれ

## ルーティング表示関連

### 検索機能付きルーティング一覧を表示する

- 最も使われるルーティング表示方法は `rake routesコマンド`
- 大規模なアプリケーションではルーティングが肥大化し検索機能を使いたい時も
- そんな時は `http://localhost:[ポート番号]/rails/info/routes` にアクセスすると良い
- リアルタイム検索機能付きのルーティング一覧を表示してくれる

### (.:format)の記述をルーティングから外す

- resourcesを利用してルーティングを設定すると勝手に `(.:format)` のオプションが付与される
- 大変親切なのだがjsonとかxmlとか今回使わねーしという場合外したくなる時も
- `format: false` オプションを指定し	て外してあげよう

```routes.rb
resources :users, format: false, only: [:index]

#=> users GET /users users#index
```

### routes.rbに記述すべき順番

- `routes.rb` に記載した順番がそのまま優先順位になる
- そのため汎用性の高いルーティングほど下に書くべきである
- 例えば `rootパス` の設定は非常に汎用性が高いと言えるので多くの場合最下部に記述する

## アクション追加関連

### resourcesの7つのアクション以外にアクションを定義する

- resourcesメソッドで生成される7つのアクションでは補いきれない場合がある
- その時は `collectionブロック` または `memberブロック` を使用してアクションを追加できる
- `collection` は複数のオブジェクトを扱うアクションに(ex:index)
- `member` は単一のオブジェクトを扱うアクションに(ex:edit)

```routes.rb
resources :books do
  collection do
    get :newest
  end
  member do
    get :archive
  end
end

#=> $ rake routes
# newest_books GET /books/newest      books#newest
# archive_book GET /books/:id/archive books#archive
```

- ブロック配下のアクションがひとつである場合には `onパラメータ` を使用してシンプルに記述することも可能

```routes.rb
resources :books do
  get :newest,  on: :collection
  get :archive, on: :member
end
```

# ■ フロントエンドに関するあれこれ

## 読み込み関連

### application.js/.cssの役割

- ファイル内に記述された `マニフェスト` と呼ばれるリストを元にアセットを読み込む
- 読み込む動作自体は `Sprockets` というgemが担っている
- `application.js` に記述してある標準ライブラリは `turbolinks` というgemの中に配置されている

```application.js
//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require_tree .
```

```application.css
/*
 *= require_tree .
 *= require_self
 */
```

### application.html.erbの役割

- 用意したJavaScriptやStyleSheetをviewで読み込む際には個々のファイル名は記述しない
- `application.html.erb` にマニフェストのファイル名を指定する

```application.html.erb
<!DOCTYPE html>
<html lang="ja">
  <head>
    <!-- 中略 -->
    <%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => true %>
    <%= javascript_include_tag "application", "data-turbolinks-track" => true %>
  </head>
```

## アセットパイプライン関連

### アセットパイプラインのしくみ

- Sprocketsによって提供されるJavaScriptやStyleSheetをまとめて管理する仕組み
- 大きく4つの段階が存在する

  1. コンパイル
    > オリジナルソースである複数の
    > `.js.coffee`形式で存在するファイルたちを`.js`形式に
    > `.css.scss`形式で存在するファイルたちを`.css`形式に
    > それぞれ変換する。
    > (※実行速度が遅延するためproduction環境ではスキップ)
     
  2. 統合
    > コンパイル済みである複数の
    > `.js`ファイルたちを`application.js`に
    > `.css`ファイルたちを`application.css`に
    > それぞれ統合する。
    > (※デバッグしやすいためdevelopment環境ではここで出力)
    
  3. 圧縮
    > 統合された`application.js`と`application.css`から
    > コメント・改行・空白等取り除き圧縮する。
    
  4. ダイジェスト付与
    > 圧縮された`application.css`・`application.js`と個別の画像たちに
    > `ハッシュ値`というそのファイル固有の文字列を付与する。
    > これによりアセットに編集作業を加えアップデートするごとに
    > 全く別のファイルだと認識され予期せぬキャッシュの適用を防ぐ。

- コンパイルの対象となるCoffeeScriptの正体に関しては以下のまとめを参照されたい

「[**JavaScriptを卒業してCoffeeScriptで飛躍しよう - 基本などまとめ**](http://qiita.com/Yama-to/items/a947cfa2565b03ddc9de)」

### アセットプリコンパイル

- アセットをアプリケーションで使う際には `コンパイル` して形式を統一する必要がある
- しかしproduction環境では処理速度に対する影響を避けるためコンパイル処理を自動で行わない
- そのため事前に手動でコンパイルを実行しなければならない
- 事前に手動でコンパイルを行っておくことを `アセットプリコンパイル` と呼ぶ
- ターミナルから以下のコマンドで実行できる

```bash
$ rake assets:precompile
```

_※WEBrick環境では `/config/environments/production.rb` 中の `config.serve_static_assets = false` をコメントアウトして静的アセットファイルの処理を放棄する_

## Turbolinks関連

- ページ遷移に際してページ全体ではなくタイトルと本文のみを置き換える仕組み
- このことによりページの読み込みを高速化させている
- 内部的にAjax通信（非同期通信）を発生させている
- デフォルトで有効にされているため無効にする場合はgemfileに記載の `gem 'turbolinks'` をコメントアウトし `$ bundle` する

## Ajax関連

- HTMLだけで完結する内容でもなかったので別記事にまとめたものを参照されたい

「[**モモンガでも使えるAjax（えいじゃっくす） − 投稿したコメントをリロードせずに表示しよう**](http://qiita.com/Yama-to/items/377f8a92cf30a3ebf454)」

# ■ その他Railsに関するあれこれ

## Mailer関連

- 例えば記事が更新された際に読者全員にお知らせメールを自動配信するような機能を簡単に実装できる。
- 導入からTipsまで若干体系的な話になり別記事にまとめたため下記を参照されたい。

「[**Railsでメール自動配信機能をつくるまでの道程**](http://qiita.com/Yama-to/items/823baf26bba3193712ea)」

## Gemfile関連

### 数字の意味

- gem名の隣には様々数字が書いてある場合があるがそれには意味がある
- 各々の数字はgemのバージョンを指定している
- 記号と意味は下表のとおり

|指定例|概要|
|--:|:--|
|2.4.3|特定のバージョンで固定|
|>= 2.1.2|特定のバージョン以上のものが必要|
|~> 3.1|3.x は良いが 4.x は不可|

### バージョンの意味
- gemに限らず何かしらのバージョンは最大で数字4つを点で区切って表す事が多い
- 「3.1.4.2」というバージョンがあったとする
- 左から「`メジャーバージョン`.`マイナーバージョン`.`リビジョン`.`ビルド`」と呼ばれる
- 各バージョンの変更が持つべき意味は下表のとおり。上に行くほど変化の角度が高い。

|バージョン名|概要|
|:--|:--|
|メジャーバージョン|抜本から変更を加える|
|マイナーバージョン|大幅な仕様変更や機能追加を行う|
|リビジョン|仕様変更や機能追加を行う|
|ビルド|修正パッチを追加する|