Ruby
Rails
RubyonRails5の上手な使い方

一連の記事の目次
rails、君に決めた!!~目次

前回の記事
rails、君に決めた!!~3

コントローラ

コントローラの命名規則

リソースの複数形Controller
リソースがpersonの時はPeopleController

複数形分からないよって時はpluralizeを使う

$ bin/rails c
Loading development environment (Rails 5.1.5)
[1] pry(main)> 'person'.pluralize
=> "people"
[2] pry(main)> 'chocolate'.pluralize
=> "chocolates"

単数形はsingularize
至れり尽くせりで笑う

ApplicationController

全てのコントローラの生みの親
共通処理はここに書く(セキュリティ設定、例外処理、コールバックなど)
app/controllers以下に色々なコントローラが生成されている

HTTPリクエストのパラメータを受け取る

HTTPリクエストで利用できるパラメータ送信方法は4種類

1. パスパラメータ
2. queryパラメータ
3. Body
4. カスタムヘッダ

このうち1,2,3はparams[]で扱うことができる。
実際にparamsの中身を確認してみよう。
app/controllers/application_controller.rbを下のように変更し、

class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception

  # 追加
  def hello
    text = "PARAMS: #{params}"
    render plain: text
  end
end

http://localhost:3000/hello?p=foo&q=bar
にアクセスすると、画面にPARAMS: {"p"=>"foo", "q"=>"bar", "controller"=>"application", "action"=>"hello"}と表示される。

注意点
text = "PARAMS: #{params}"のダブルクオート""をシングルクオート''にすると、変数の埋め込みができなくなる。。。引っかかったで笑

Strong Parameters

強いパラメータとはなんぞや

Railsアプリに送信されたパラメータを安全に扱うための仕組み

強さとは安全を意味するらしい、真理だ笑

例として、新規ユーザー登録を行う機能について考える。
処理の流れ
フォーム画面
→userオブジェクトをPOST
→サーバーがデータを適切な形に変換してデータベースに保存

脆弱性を含む例

def create
  # params[:user] => {name: 'Ichiro', email: '~@.com'}
  User.create(params[:user])
end

なぜこれはダメなのか。

例えば、悪意のあるユーザーがuserオブジェクトに、管理者を意味する{admin: true}のような属性を付加し、サーバーにPOSTしたとする。もしデータベースにadminというカラムが存在していたら、admin属性のユーザーが作成されてしまう。

なるほど、name,email属性だけ送ってくればいいのに悪い人がadmin属性も加えて送って来たりすると管理者権限とられちゃうってことか!

Strong Parametersを使った例

def create
  User.create(user_params)
end

private

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

params[:user]を直接呼ばずにuser_paramsメソッドを呼んでいる。

  • requireメソッド paramsの中にちゃんとuserオブジェクトが必須パラメータとして入っているか検証した後、userの中身を返す
  • permitメソッド 更新や追加していい属性をホワイトリスト方式で制限

これでadmin属性を除外できてよきよき。

コントローラをもうちょっと詳しく作ってみる

コントローラ=アクション+フィルタ
アクション: index,showなどのメソッド
フィルタ: アクションの前後に共通処理を実行する仕組み

user系を作り直すので、以下のコードを実行する

bin/rails d scaffold user
bin/rails db:migrate:reset

scaffoldで生成したuserを削除して、データベースもリセット
新たにusers コントローラを作る

bin/rails g controller users index show

生成されたusres_controller.rbの中身をみてみる。

class UsersController < ApplicationController
  def index
  end

  def show
  end
end

指定したindex,showアクションあるね、よしよし。
config/routes.rbを変更
ビフォー

Rails.application.routes.draw do
  get 'users/index'

  get 'users/show'

  resources :articles
  get 'home/index'
end

アフター

Rails.application.routes.draw do
  resources :users, only: [:index, :show]

  resources :articles
  get 'home/index'
end

ビフォーだとURLがRESTじゃないんだと。

ビフォーのルーティング

Prefix Verb        URI Pattern            Controller#Action
users_index GET   /users/index(.:format)   users#index
users_show GET    /users/show(.:format)    users#show

アフターのルーティング

Prefix Verb      URI Pattern           Controller#Action
users GET    /users(.:format)            users#index
user GET    /users/:id(.:format)         users#show

ルートを見比べてみると確かにって感じだ

フィルタを設定する

フィルタは以下の3種類

before_action
after_action
around_action

前、後ろ、両方ってだけ

実装のイメージ

class UsersController < ApplicationController
  before_action :require_login, only: [:index]

  def index
    # 何か処理
  end

  private

  def require_login
    reject_to login_url unless singed_in?
  end
end

indexアクションを実行する前にrequire_loginメソッドを実行する。未ログインならindexアクションを実行せず、ログインページに飛ばしてくれる。

引数を取るメソッドをフィルタにしたい場合は、
blockでもフィルタを定義できる

親クラスで定義したフィルタは、子クラスでskip_xxx_actionで例外的に無効化できる

疲れた〜ー
次回はセッション管理!
ついにredisを使うときがきた