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
}