Skip to content

Instantly share code, notes, and snippets.

@libitte
Last active August 29, 2015 14:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save libitte/d1923aab95354a9a7c38 to your computer and use it in GitHub Desktop.
Save libitte/d1923aab95354a9a7c38 to your computer and use it in GitHub Desktop.

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

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

  • クエリに名前をつけることで可動性が向上する
  • 重複コードが減る
scope :written_about, ->(theme) { where("name like ?", "%#{theme}%")}

バリデーション

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

コールバック

  • before_validation
  • after_validation

コントローラ


ルーティングとリソース

bundle exec rake routes

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

resources について

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

resources :publishers

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

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

というendpointを作成する。

ちなみに、

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

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

resources 以外

resource :profile

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

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

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

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

例外処理

rescue_from

ApplicationController などに定義しておく。

rescue_from LoginFailed, with: :login_failed

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

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

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クラスを使って一括で属性を設定できる非常に便利な仕組み。

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

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

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

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

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

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"

ビュー


テンプレートの検索

render :show

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

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

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

render を省略した場合

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

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

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

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 => コントローラ名/アクション名.フォーマット.エンジン

format.xml { render xml: @book }

partialテンプレートとlayout

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

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

テンプレートの構造化

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

  • pp/views/layouts/application.html.erb
<%= yield %>

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

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

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

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

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

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

ERB

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

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

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

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

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

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

ヘルパー

url_for
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 ... モデルの情報を基に対応するフィールドの内容を埋めたり、エラーを表示したり
<% form_for(@publisher) do |f| %>

<% end %>
エスケープ処理

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

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

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

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

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

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

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

最近ではJSONが主流。

  • format.json
json.extract! @book, :id, :name, :price, :created_at
  • "!" => 終わるメソッドはJSON化する
  • "!" => 終わらないメソッドはそのままJSONのキーになる。
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
@libitte
Copy link
Author

libitte commented Dec 5, 2014

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment