2章 Ruby on RailsとMVC
雑感
この章では基本的なMVCの仕組みを押さえつつ、今まで抜けていたより詳細な基本的な知識を再確認することができました。特にjbuilder周りは知っていれば個人開発もっと楽にできたなあと感じたのと、業務でもすぐに使いそうなのでしっかり押さえていきたいと思います。以下、初見の知識とメモしたかった箇所を中心とした忘備録です。
1章でもそうだったのですが、私の環境(以下参照)でscaffoldするとエラーとなりました。解決策は以下の記事を参考にしております。次章以降も毎回rails new実行時に同様の操作をしています。
$ ruby -v
ruby 2.7.8p225 (2023-03-30 revision 1f4d455848) [x86_64-darwin23]
$ bin/rails -v
Rails 6.0.6.1
「Webpacker::Manifest::MissingEntryError in Tasks#index」エラーが発生する
単数系のresourceでルーティングを作成
複数存在しない単一のリソースはresourcesの単数系のresourceでルーティングを定義でき、以下のような違いがある。
- 基本のルーティングから
indexを除いたルーティングが定義される - 単一のリソースなのでURLに
:idが含まれない
resource :profile
new_profile GET /profile/new(.:format) profiles#new
edit_profile GET /profile/edit(.:format) profiles#edit
profile GET /profile(.:format) profiles#show
POST /profile(.:format) profiles#create
PATCH /profile(.:format) profiles#update
PUT /profile(.:format) profiles#update
DELETE /profile(.:format) profiles#destroy
rescue_fromによってRailsが自動対応するもの以外の例外に対する挙動を指定
- Railsはいくつかの特定の例外クラスに関してはそれぞれに対応したエラーコードを返す
- それ以外の例外が発生した場合は
rescue_fromというクラスメソッドを用いることでアプリケーション全体の振る舞いとして特定のレスポンスを返却することができる
class LoginFailed < StandardError
end
with:オプションで例外発生時に呼ばれるアクションを指定する。
class ApplicationController < ActionController::Base
rescue_from LoginFailed, with: :login_failed
def login_failed
render template: "shared/login_failed", status: 401
end
end
ログインが失敗した場合に定義した例外を発生させる。
class LoginController < ApplicationController
def create
@user = User.where(name: params[:name], password: params[:password]).find
raise LoginFailed unless @user # ログイン失敗時に例外発生
end
variantsによるテンプレートの切り替え
コントローラ内でrequest.variantに:tabletや:mobileの値を入力することで展開されるビューテンプレートファイルをindex.html.erbからindex.html+mobile.erbへ、といった具合に、"+"とrequest.variantの値を合わせた名称のテンプレートへと変更できる。
class ApplicationController < ActionController::Base
before_action :detect_mobile_variant
private
def detect_mobile_variant
request.variant = :mobile if request.user_agent =~ /iPhone/
end
end
<補足> chromeでPCからUserAgentをiPhoneとしてアクセスする方法
1. Chrome開発者ツールを開く(F12 or Command + Option + I)
2. 開発者ツールの「...」(その他)メニューをクリック
3. More tools → Network conditions を選択
4. User agentセクションで「Custom」を選択
5. 以下のようなiPhoneのUser-Agentを入力:
Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1
コンソールでURLとパスの取得をする
- ルーティング表示した時の各Prefixへ
_pathをつけるとドメインやポートを除いた/から始まるパス部分を、_urlとすると完全なURLを取得できる - コントローラーやビューでは単に
edit_profile_urlと記述するとURLを取得できるがコンソール上ではappオブジェクトを経由することで取得可能
irb(main):001:0> app.edit_profile_url
=> "http://www.example.com/profile/edit"
irb(main):002:0> app.edit_profile_path
=> "/profile/edit"
irb(main):003:0> app.publisher_url(id: 1)
=> "http://www.example.com/publishers/1"
irb(main):004:0> app.publisher_path(id: 1)
=> "/publishers/1"
独自のヘルパーメソッドの定義とコンソールでの動作確認
- HTMLタグの付与や整形などビューに特有の加工をする場合はモデルクラスに処理を書くよりヘルパーメソッドを利用するのが一般的
- 独自のヘルパーメソッドの定義は
app/helpers配下に定義する
module ApplicationHelper
def to_hankaku(str)
str.tr("A-Za-z", "A-Za-z")
end
end
- ヘルパーメソッドをコンソールで利用する場合は、
helperオブジェクトを呼び出す
irb(main):002:0> helper.to_hankaku("A-Z")
=> "A-Z"
jbuilderでAPIとして返却するJSONデータを整形する
コントローラーのrebderメソッドはjsonへオブジェクトを変換できるが、より複雑なJSONデータを構築する場合は、Rails組み込みのjbuilderを利用する。その場合、コントローラー内ではrenderメソッドの呼び出しを止める。
class BooksController < ApplicationController
def show
respond_to do |format|
format.html
- format.json { render json: @book } # 単純なJSONを返却
+ format.json # jbuilderを利用する場合
end
end
end
対応するアクションごとの.json.jbuilderという拡張子のファイルを用意する。
# 指定したパラメータのJSONデータを構築できる
json.extract! @book, :id, :name, :price
# !で終わらないメソッドはそのままJSONのキーとなる
json.name_with_id "#{@book.id} - #{@book.name}"
# ブロックを渡すことでネストしたJSONデータを構築
json.publisher do
json.name @book.publisher.name
json.address @book.publisher.address
end
# Rubyのコードなのでifやunlessで制御できる
unless @book.high_price?
json.low_price true
end
上記の出力は以下のようになる。
{
"id": 1,
"name": "Book 1",
"price": 1000,
"name_with_id": "1 - Book 1",
"publisher": {
"name": "Gihyo inc",
"address": "Ichigaya"
},
"low_price": true
}