現場で使えるRuby on Rails5 速習実践ガイド 読書メモ
https://scrapbox.io/files/64b658e8d636b0001c7caba8.png
まえがき
Optimize for programmer happiness - プログラマーの幸福を最優先にする
Convention over Configuration - 設定よりも規約を優先する
The menu is omakase - メニューはおまかせ
No one paradigm - 特定のパラダイムに縛られない
Exalt beautiful code - 美しいコードを称賛する
Provide sharp knives - 鋭いツールを提供する
Value integrated systems - 統合されたシステムを重視する
Progress over stability - 安定性よりも進歩を優先する
Push up a big tent - 大きなテントを広げる
/emoji/tea.icon Push up a big tentよく聞いたことなかった。
Having a big tent doesn’t mean trying to be all things to all people, though. It just means you welcome all people to your party, and allow them to bring their own drinks. We need to lose none of our soul or values by offering others to join us, and we may well learn how to mix a new delicious drink or two.
/emoji/tea.icon 全員にマッチするものを提供するわけではなくて、テント(人々が利用できる場所)を広げていって、ドリンクの持ち込み(拡張やライブラリ)を可能にするみたいな意味だと考えた
敷居を低くして感謝を伝えていくことを大切にという解説があった
1章
オブジェクトID
code: ruby
puts "hoge"
puts 1
hogeの方は毎回変わる。
日本語で表現
クラスはオブジェクトの設計図
インスタンスはそこからできた実体
インスタンス変数はデータ
メソッドは振る舞い
/emoji/tea.icon unless elseは流石にわかりにくいわな。。。
ハッシュって数字キーに出来るんだ
code:plain
❯ irb
irb(main):001> {11=>1}
=> {11=>1}
irb(main):002> {11=>1}.class
=> Hash
irb(main):003>
allocate
イミュータブルなインスタンス作れる
Q.キーワード引数とデフォルト引数を比較して何か述べてください
デフォルト引数は、例えば2つの引数があって2つ目だけ渡したい場合も1つ目の引数を省略できない
キーワード引数は省略できたり、渡す順序が関係なかったりする
一方で、どのキーワードに対応する値か書かないといけないし、キーワードも当然省略できない
/emoji/tea.icon ここが唯一のデメリットな気がするが、キーワード引数の方が好きだなぁ
ちなみに、キーワード引数は必ずデフォルト値を設定しなければいけないというわけではない
/emoji/bulb.icon ちなみに、デフォルト値は厳密にはデフォルト式
毎回設定しなければいけない引数を設定、デフォルト式を規約として捉えることもできるとのこと CoC
has_manyはクラスメソッド
フレームワーク独自の構文がクラスメソッドだったりするケースも多い
エラーの捕捉の省略
code:ruby
begin
hoge
rescue => e
# StandardError及びその子クラスのエラーを捕捉
end
nilガード
code: ruby
hoge || hoge = 10
nilが入ってしまう場面でデフォルト値を決める際に良い書き方!
/emoji/tea.icon Rubyらしい書き方もっと考えてみる
Hash.new { |h, k| h[k] = [] }
array.map(&:to_s)
hash.dig(:user, :profile, :name)
rangeの利用とかもそうかも?
2章
bin/railsがbundle exec railsよりも一般的
YAMLのエイリアスとアンカー
<< これってそういう名前だったんだ。&エイリアス、それを<<エイリアスで内容を再利用できる。
code:yaml
default_settings: &default_settings
theme: "dark"
language: "ja"
timezone: "Asia/Tokyo"
user1:
name: "Alice"
settings:
<<: *default_settings
3章
migrationでVERSIONを指定すると、そこまでが実行された状態にできる
エラーメッセージの日本語化
renderとredirectの簡単な違いはブラウザがリクエストし直すかどうか
redirect時のflashはセッションの仕組みで実装でき〜ている
simple_format
Two or more consecutive newlines (\n\n or \r\n\r\n) are considered a paragraph and wrapped in <p> tags. One newline (\n or \r\n) is considered a linebreak and a <br /> tag is appended. This method
あとはsanitizeをtrueにすると危険なタグを除外してくれる。
4章
MySQLのバージョンによっては、ロールバックが効かないこともある
change_columnを使う場合up/downの中で使用する
バージョン戻す処理を反転するだけでは作れないから
/emoji/tea.icon limit300の逆ってなんだみたいな話か
ユニークインデックス
code:ruby
class AddIndexToUsersEmail < ActiveRecord::Migration5.0 def change
add_index :users, :email, unique: true
end
end
組み合わせがユニークならこうする
code:ruby
class CreateBookmarks < ActiveRecord::Migration6.0 def change
create_table :bookmarks do |t|
t.references :user, null: false, foreign_key: true
t.references :post, null: false, foreign_key: true
t.timestamps
# こう
end
end
end
カスタムバリデーター
そもそもDBレベルのバリデーションは「内容の範囲」
モデルレベルは検証と考えるといいかも
カスタムバリデーターはapp/validatorsにおくと良さそう
再利用可能
複数カラム横断の条件可能
code:ruby
class MyValidator < ActiveModel::Validator
def validate(record)
unless record.name.start_with? 'X'
record.errors.add :name, "名前はXで始まる必要があります"
end
end
end
# 呼び出し
class Person
include ActiveModel::Validations
validates_with MyValidator
end
valid?やpersisted?が便利
valid?メソッドを使って、バリデーションを手動でトリガすることもできます。valid?を実行するとバリデーションがトリガされ、オブジェクトにエラーがない場合はtrueを返し、エラーの場合はfalseを返します。 これは以下のように実装できます。
persistedはこっち
update_all
検証は行われない、コールバックも呼ばれない
p145のコールバックの表便利
around_destroy
code:ruby
class User < ApplicationRecord
around_destroy :log_destroy_action
private
def log_destroy_action
puts "削除処理を開始します"
yield # ここで実際の削除処理 (destroy) が実行される
puts "削除処理が完了しました"
end
end
コールバックで他のモデルに依存させまくるとあまり良くない
そういうのいい感じに管理するgemあるだろうか。
sessionとcookie
sessionはアプリケーション側での実装。cookieはブラウザとWebサーバーの間で使われる。
別物というよりも、sessionの仕組みの一部がcookieによって実現されている。
セッションIDのデフォルトの保存先はcookie。
digest
一方向的な変換(ハッシュ化)をしたもの
has_secure_passwordの機能とgem bcryptで認証機能作れる
current_userみたいなセッションを使うメソッドほどメモ化できる
skip_before_action
便利。ログイン画面だけログイン不必要とかできる。exceptとかもあったかも。
ちょっと脱線してroute.rbの書き方復習
namespaceの有無
scopeで指定すると、URLには含まれないがその指定のコントローラーのアクションを呼び出せる
リソースのネスティングは、ぜひとも1回にとどめて下さい。決して2回以上ネストするべきではありません。
code:ruby
resources :articles, shallow: true do
resources :comments
resources :quotes
resources :drafts
end
こうすればネストしているがURLにはarticleがつかない
concern機能もある
authenticate
ここかな?
rinku知ってたけど結構昔に更新止まっている?
aタグをリンク表示にしてくれる
5章
テストのメリット
言わずもがなたくさん
ドキュメントになる
変更、追加、リファクタリングに耐えうる
バージョンアップできる
変更範囲の理解
実装時に仕様を見直すきっかけになる
言い方
システムテスト
system, feature spec
結合テスト
request spec
機能テスト
controller spec
アクション単位
code:ruby
# spec/example_spec.rb
require 'rails_helper'
# System Test (Feature Spec)
RSpec.describe 'System Test', type: :system do
it 'displays the homepage' do
visit root_path
expect(page).to have_content('Welcome')
end
end
# Integration Test (Request Spec)
RSpec.describe 'API Request Test', type: :request do
it 'returns a successful response' do
get api_v1_some_resource_path
expect(response).to have_http_status(:success)
end
end
# Functional Test (Controller Spec)
RSpec.describe SomeController, type: :controller do
it 'assigns @items and renders the index template' do
items = create_list(:item, 3)
get :index
expect(assigns(:items)).to match_array(items)
expect(response).to render_template(:index)
end
end
end
Functionalが若干Systemと似ているが、1つのアクションをテストしていると考えれば粒度が高いことがわかる。
わかりやすかった/emoji/plus.icon
FactoryBotの実装例
code:ruby
# factories/users.rb
FactoryBot.define do
factory :user do
name { "Test User" }
email { "test@example.com" }
# posts関連データを作成するtrait
trait :with_posts do
after(:create) do |user, evaluator|
create_list(:post, evaluator.posts_count || 3, user: user)
end
end
end
end
# factories/posts.rb
FactoryBot.define do
factory :post do
title { "Sample Post" }
content { "This is a sample post content." }
association :user
end
end
関連データを作成する場合:
code:ruby
# postsが3件作成される
create(:user, :with_posts)
# postsが指定した数だけ作成される
create(:user, :with_posts, posts_count: 5)
関連データを作成しない場合:
code:ruby
# postsは作成されない
create(:user)
shared系のメソッド
こんなこともできる
code:ruby
RSpec.shared_context "shared stuff", :shared_context => :metadata do
before { @some_var = :some_value }
def shared_method
"it works"
end
let(:shared_let) { {'arbitrary' => 'object'} }
subject do
'this is the subject'
end
end
RSpec.configure do |rspec|
rspec.include_context "shared stuff", :include_shared => true
end
6章
putとpatch
PUT→その名の通り全部置き換えること
PATCH→リソースの一部を書き換えること
RESTFfulとは、を簡潔に説明できるか?
ステートレスな通信(HTTP)
個々のリソースへの操作がHTTPメソッドとして統一
個々のリソースへのアクセスが一意のURI
名前の由来がわからなかったので聞いてみた GPT
Railsのmemberとcollectionという呼び名は、それぞれが操作する対象(リソース)の範囲と特性からきています。ここでは、その背景と意味を説明します。
1. member
意味: 「メンバー」や「個別の要素」という意味を持ちます。
用途: ある特定のリソースの個別のインスタンスに対するアクションを定義します。
例: resources :users do member do get :profile end end
この例では、profileアクションは個別のユーザーに紐づいているため、/users/:id/profileというURLになります。この:idパラメータが個別のリソース(「メンバー」)を指すため、「member」と呼ばれています。
2. collection
意味: 「集合」や「コレクション」という意味です。
用途: リソース全体や特定の集合(リソースの全体)に対するアクションを定義します。
例: resources :users do collection do get :active end end
ここでは、activeアクションがユーザー全体に対して実行されるもので、URLは/users/activeとなります。個別のユーザーではなく、リソースの集合全体に対するアクションであるため「collection」と呼ばれています。
呼び名の理由
memberとcollectionは、リソースのどの部分を操作するのかを区別するために使われる用語です。
memberは特定の「個別リソース」を操作することを示し、
collectionは「リソース全体」やその集合に対する操作を示す、という設計に基づいています。
/emoji/tea.icon 由来を知ると覚えやすい
/emoji/tea.icon P237かなり重要
URLの構造ではなく、コントローラーの方に関心を持たせる
開発者の立場になって考える
namespaceはコントローラーも指定の方に縛る
時刻の話
utcにしておく場合もメリットがある
システムに依存しなくなる
デフォルトで扱いやすい
エラーハンドリング
falseにすると本番環境同様になる
show_detailed_exceptionsを自前で実装すれば、その中に詳細な内容が入ってくる
/emoji/tea.icon実際のエラー画面見る時に便利だな
/emoji/thinking.icon結局ログのフィルタリングしたい時、この処理の部分だけ隠すってどうやればよかったんだろう
パラメーター単位ではなく、処理単位でできるものなのだろうか
params.requireはそれが含まれていなければ例外を出す
code:ruby
# File actionpack/lib/action_controller/metal/strong_parameters.rb, line 519
def require(key)
return key.map { |k| require(k) } if key.is_a?(Array)
if value.present? || value == false
value
else
raise ParameterMissing.new(key, @parameters.keys)
end
end
再帰的に実行
CSRF
GETにはセキュリティチェックの仕組みがない
基本的に重要な処理はPOSTで行う
アセットパイプライン
/emoji/tea.icon ここむずい
前提:アセットパイプラインとは、その名の通りにパイプライン
パイプラインの中で何をしているか?
高級言語のコンパイル、アセットの連結、最小化、ダイジェストの付与
アセットとはJS, CSS, image, 高級言語(scssやcoffeeなど)
ダイジェストは、ファイル名が変わるのでブラウザが検知してキャッシュで更新できないみたいなのを防ぐ
連結と最小化はdevelopment環境では行わない
読み込ませるCSSは、ページごとに変えられる
マニフェストファイル
どのようにファイルをまとめるか
code:js
//= require rails-ujs
//= require activestorage
//= require_tree .
こう言ったものを書く
Rails.application.config.assets.pathsでどこを読み込んでいるのかチェックできる
どう言う時に動的コンパイルしたいんだろう
デバッグしたい時、コンパイルの動作試したい時
でも重いから本番は「プリコンパイル」なんだ
静的ファイルの配信はWEBサーバーで行う
アプリケーションの方で行わない
publicディレクトリに理由なく突っ込まない
7章
Productionでのメール配信
smtpなど設定すればActionMailerがいい感じにやってくれそう
非同期実行
/emoji/tea.icon そういえば、ワーカー用のサーバーとか使ったことないな
アプリケーションのサーバーとコードがズレるみたいなことないんだろうか
やりたいのはこう言うイメージ
余談だけどAWSを12月以降はやらないとなって思った
ActiveStorage
blobの方が実態と紐づく
key, file_name, content_typeなど
attachmentsが中間テーブル
何との?
Postモデルとか、そいうドメインのものとの
/emoji/tea.icon CSVのところ、サンプルだと行数ごとに保存してたけど、バリデーションかけて突破したものだけをinsert_allとかすると効率いいだろうか
非同期ジョブ
Active Job
フレームワーク
バックグラウンドで非同期処理を行うツールそのものではなく、インターフェースで扱う仕組み
/emoji/tea.icon バックエンドの非同期処理ツール(例: Sidekiq, Resque, Delayed Job)とアプリケーションコードの間をつなぐ「共通の窓口」か
SidekiqではredisをDBとして使用しているよ
8章
turbo周りわからんな、調べよう
Turbolinks
ページ遷移時にフルページの再読み込みをせず、AjaxでHTMLを取得して高速化。
サーバーからのHTMLレスポンスで、<body> 内のみを置き換える。
既存のJavaScriptやCSSは再読み込みせずそのまま利用。
落とし穴として、差分だけをajaxで読み込むようになるから、turbolinks:roadを使わないとページにアクセスした際に発火させたいコードが実行されない可能性がある
使うならscriptが再実行されないようにheadタグに書いておく<script>
Ajax
一応
Asynchronous JavaScript and XML
Webページをリロードせずに、サーバーとデータをやり取りする仕組み
remote: tureした時のやつ
9章
レビュー周りの注意
blank?の多用
render前にはflashではなくflash.now
リダイレクト先にまでデータ持っちゃうから
マイグレーションの追加
ロールバックできることまで確認しよう
我らがfind_each
大きいデータ扱う時に思い出して
初めて知った
10章
P401のようにmodelに寄せるべきコードはモデルに寄せよう
【重要】before_validation使うの綺麗
module側はNotimpIementErorr実装しておくと安心
カスタムヘルパーは小さく作る
コントローラーを関心ごとで分ける
同じOrderを扱うクラスでも、そのCSVなのか、Reportなのかとかそういうレベルまで