パーフェクト Ruby on Rails 2章 まとめ


* MVCアーキテクチャ
* モデル
* コントローラの役割
* ビューの役割
* MVCについてのまとめ

## MVCアーキテクチャ
### モデル
---

#### ActiveRecord::Relation

* Query Interface による捜査結果をオブジェクトとして表現したもの
* どんなSQLを発行するか、という情報だけを保持している
* そのSQLの実行結果が必要になるタイミングまではDBアクセスを行うことはない

どういうことか？

##### 動作

* ActiveRecord に対してQuery Interface が呼ばれると ActiveRecord::Relation のインスタンスが生成される
* 繰り返し呼び出した Query Interface は ActiveRecord::Relation のインスタンスに蓄積され、どんなSQLを発行するかの情報が更新されていく
* 実際にデータが必要になった時点で、蓄積された情報を元にSQLを発行しデータを取得する


###### データが必要になったタイミングで初めて実行される理由

=> メソッドチェーンによるクエリ構築を行うため。

最初のメソッド呼び出し時に検索条件が自明でない場合もあります。
このような場合、組み立てた時点で実行されてしまうと複雑な条件や外部からの引数でSQLを切り替えたい場合に記述が難しくなってしまいます。
このような理由により、データが必要になったタイミングで実行されるようになっています。
(明示的に任意の箇所でクエリを発行したい場合には `to_a` を呼び出すなどを行うとできます。返却されるものはモデルのインスタンスの配列です。)



#### Scope

検索結果に名前をつけてひとまとめにしたもの。

* クエリに名前をつけることで可動性が向上する
* 重複コードが減る

```ruby
scope :written_about, ->(theme) { where("name like ?", "%#{theme}%")}
```

#### バリデーション

エラーになったら、例えば book.errors とすることで取得できる


#### コールバック

* before_validation
* after_validation



### コントローラ
---

#### ルーティングとリソース

```bash
bundle exec rake routes
```

で設定されているルーティング一覧を見ることができます。


#### resources について

resources は REST にしたがって endpointを作成してくれる。

```ruby

resources :publishers
```


と書くと、RESTの統一インターフェースにしたがって

* リソースの取得(GET)
* リソースの作成(POST)
* リソースの更新(PATCH(PUT))
* リソースの削除(DELETE)

というendpointを作成する。

ちなみに、

```ruby
resources :publisher do
  resources :books
    member do
      get 'detail'
     end
  end
end
```

こんなかんじで親子関係をルーティングすることができます。


#### resources 以外

```ruby
resource :profile
```
一人のユーザーからみてアプリケーション上１つしか存在しないようなリソースの時にはこのように書くと便利です。
例えば、ログインユーザーが参照する自身のプロフィールなど。


表示と更新だけにしたいときとかそういう時には only を使って

```ruby
resource :profile, only: %i{show edit update}
```

とかくと `show`, `edit`, `update` を呼び出すendpointのみ作成します。


#### 例外処理

##### rescue_from

ApplicationController などに定義しておく。

```ruby
rescue_from LoginFailed, with: :login_failed

def login_failed
  render template: 'shared/login_failed', status: 401
end
```

すると、ApplicationController を継承したクラス内で、raise したときに、rescue_from してある例外はキャッチしてくれる。

```ruby
class LoginController < ApplicationController
 def create
   @user = User.where(name: params[:name], password: params[:password]).first
   raise LoginFailed unless @user
  end
end
```

##### StrongParameters とは

* Mass Assignment 機能を利用する際に起こりうる脆弱性に対抗する手段の一つ。
* Mass Assignment とはモデルの生成や更新の際に、以下の様なRubyのHashクラスを使って一括で属性を設定できる非常に便利な仕組み。


下記のようにすると、意図しない属性の変更を一般ユーザーに許してしまう。

```ruby
user = User.find(1)
user.update(name: "Bob", email: "bob@example.com")
```

params[:user] には外部からどんな値も送られてきうる。(params はユーザーが送ってきたHTTPリクエストから組み立てられるものなので)

```ruby
class ProfileController < ApplicationController
  def update
    user = current_user
    user.update(params[:user])
  end
end
```

これを防ぐための機構が StrongParameters です。
StrongParameters は Mass Assignment で、利用を許可する Hash の key を事前に検査する。

```ruby
class ProfileController < ApplicationController
  def update
    user = current_user
    user.update(user_params)
  end

  private

    def user_params
      params.require(:user).permit(:name, :email)
    end
end
```

リクエストに :user という key が必要であることに注意。
上記のコードでは、user の中で受け付けても良いのは [:name, :email] の2つのkeyのみ。

require に指定したパラメータが params に含まれていなかった場合は、
ActionController::ParmeterMissing 例外が発生する。

###### StrongParameter が生まれた背景 = "mass assignment"

* http://blog.sorah.jp/2012/03/05/mass-assignment-vulnerability-in-github
* http://blog.sorah.jp/2012/03/14/rails-strong-parameter
* http://selfkleptomaniac.org/archives/2109
* https://github.com/blog/1068-public-key-security-vulnerability-and-mitigation


### ビュー
---

#### テンプレートの検索

render :show

* 描画するためのテンプレートを探す
* 探されたテンプレートを基に、データを展開し、最終的なHTMLを生成する

どうやってcontrollerからの指定でviewファイルを探索しているのか？
=> `RAILS_ROOT/app/views/コントローラ名/アクション名.html.erb`

ex) app/controllers/books_controller.rb -> `app/views/books/show.html.erb`


#### render を省略した場合

```ruby
def show
  @book = Book.find(params[:id])
end
```


#### コンテンツのタイプによって表示を出し分ける

HTML, JSON, XML などその他のフォーマットで表示させる。

```ruby
class BookController
  def show
    @book = Book.find(params[:id])
    respond_to do |format|
      format.html
      format.csv
    end
  end
end
```

app/views/books/show.csv.erb
=> `コントローラ名/アクション名.フォーマット.エンジン`

```ruby
format.xml { render xml: @book }
```

#### partialテンプレートとlayout

同じ内容を複数箇所で使いまわす。
-> `:partial` によって使いまわしたい部分を切り出せる。

`partial:` をつけると prefix `_` で始まるファイル名のテンプレートから検索する。


##### テンプレートの構造化

app/views/layouts/ というディレクトリにレイアウト用のテンプレートを配置する。
デフォルトでは application.html.erb というファイルが用いられる。

* pp/views/layouts/application.html.erb


```ruby

<%= yield %>
```

こうするとそれぞれのページのコンテンツが展開されるので、

* app/views/books/show.html.erb

^^ このファイルには body タグの中身だけ書けば良い。


#### variants によるテンプレートの切り替え

variants ... Rails 4.1 でテンプレートを切り替える機構として導入されたもの。

#### テンプレートエンジンの紹介
##### ERB

Ruby に標準添付されているテンプレートエンジン。
Railsでも標準のテンプレートエンジンとして採用されている。

```erb
<ul>
  <%= 3.times do |n| %>
    <li>Number = <%= n %></li>
  <% end %>
</ul>
```


##### Haml

railsで使いたいときには haml-rails 
indentベース

```haml
%html
  %head
    %title Hi
  %body
    %h1 #header Header
    - 3.times do |i|
      %p Item
      %p= i
```


##### Slim
railsで使うには slim-rails
indentベース
Hamlよりもさらに簡素な表記が可能

```slim
doctype html
html
  head
    title Hi
  body
    h1 id="header" Header
    - 3.times do
      p Item
```


#### ヘルパー

##### url_for

```ruby
url_for(controller: :users, action: :index)
#=> /users

url_for(controller: :users, action: :index, id: 1234, detailed: 'true')
#=> /users/1234?detailed=true
```

##### form_tag/form_for

* form_tag ... 単純なフォームを作る
* form_for ... モデルの情報を基に対応するフィールドの内容を埋めたり、エラーを表示したり

```erb
<% form_for(@publisher) do |f| %>

<% end %>
```

##### エスケープ処理

Railsのエスケープの仕組み。
XSSに対する対策がフレームワークとして組み込まれている。

そのまま出したいときには 'raw' を使う

```erb
<%= raw "<script>alert('sample');</script>" %>
```

raw ヘルパは内部的には String#html_safe というメソッドを読んで String オブジェクトを ActiveSupport::SafeBuffer のオブジェクトに変換している。

```erb
<%= "<script>alert('sample');</script>".html_safe %>
```

つまり、^^ともかける。


#### APIサーバにとってのビュー

最近ではJSONが主流。

* format.json

```ruby
json.extract! @book, :id, :name, :price, :created_at
```

* "!" => 終わるメソッドはJSON化する
* "!" => 終わらないメソッドはそのままJSONのキーになる。

```ruby
json.name_with_id "#{@book.id} - #{@book.name}"

json.publisher do
  json.name @book.publisher.name
  json.addredd @book.publisher.address
end

unless @book.high_price?
  json.low_price true
end
```