この記事でわかること
本記事では、このコードを深掘りしていきます。
normalizes :name, with: ->(value) { value.strip.presence }
結論からいうと、ユーザー名の前後に入った空白を削除して保存したい場合は、Userモデルに上記のコードを記述します。
例えば、フォームから以下のような値が送られてきた場合、
" 山田太郎 "
前後の空白が取り除かれ、
"山田太郎"
という形に整えてから扱うことができます。
この1行を書けば目的自体は実現できるのですが、このコードを見たとき、
「この1行、一体何をやっているんだろう?」
「->(value)って何? あまり見慣れない書き方だな…」
と疑問に感じました。
そこで、このコードを一つずつ分解して調べてみると、normalizes、ラムダ、stripなど、Rubyの基礎的な要素がこの1行に詰まっていると感じました。
自分の理解を整理する意味も込めて、調べた内容を記事としてまとめます。
※ 私は現在、エンジニアを目指して学習中のため、内容に誤りや分かりにくい表現が含まれている可能性がありますので、ご容赦ください。
実装したいこと
Userモデルにnameカラムがあるとします。
例えばユーザーが、
" 田中太郎 "
と入力した場合、そのまま保存するのではなく、
"田中太郎"
として保存したいです。
また、
" "
のように空白しか入力されなかった場合には、
nil
として扱いたいとします。
そこで、Userモデルに以下を記述します。
class User < ApplicationRecord
normalizes :name, with: ->(value) { value.strip.presence }
end
今回はこのコードを一つずつ見ていきます。
全体像
まず、ざっくり分解すると以下のようになります。
normalizes :name, with: ->(value) { value.strip.presence }
| コード | 役割 |
|---|---|
normalizes |
属性の値を一定の形に整える |
:name |
今回整える対象の属性 |
with: |
どのような処理で整えるか指定する |
->(value) |
値を受け取るラムダ |
value.strip |
文字列の前後の空白を削除する |
.presence |
空になった場合はnilを返す |
日本語にすると、
nameの値を受け取り、前後の空白を削除し、空文字になった場合はnilに変換する
という処理です。
normalizesとは?
normalizesは、Railsでモデルの属性(カラム)を、正規化(一定の形に整える)するための、Active Recordが提供しているメソッドです。
normalizes :name
上記コードは、「name属性を正規化します」という指定をしているということになります。
:nameとは?
normalizes :name, ...
このコードの:nameは、正規化する対象を指定しています。
今回の場合は、normalizesの引数として:nameを指定することで、
Userモデルのname属性(カラム)が正規化の対象となっています。
with:とは?
with:は、
具体的にどのような処理で正規化するか
を指定するためのオプションです。
with:の後ろには、「値をどのように整えるのか」を指定する処理を書きます。
そのため、
normalizes :name,
だけでは、「nameを正規化する」という対象しか分かりません。
with: ->(value) { value.strip.presence }
を追加することで、
前後の空白を削除して、空になった場合はnilにする
という具体的な処理をnormalizesメソッドに指定しています。
with:には、値を受け取って加工し、その結果を返す処理を指定します。
今回、その処理を書いているのが、
->(value) { value.strip.presence }
というラムダという記法です。
->(value)とは?
今回、一番「何これ?」と感じたのがここでした。
->(value) { value.strip.presence }
これはRubyのラムダ(lambda)というものです。
簡単に言うと、処理をひとまとまりにして、値として扱える仕組みです。
このコードは、
1. `value`を受け取る
2. `strip`する
3. `presence`する
4. その結果を返す
という小さな処理を作っています。
普通のメソッドと見比べるとイメージしやすいです。
例えば、普通のメソッドなら、
def normalize_name(value)
value.strip.presence
end
と書けます。
これも、
値を受け取る
↓
処理する
↓
結果を返す
という点では似ています。
一方、ラムダを使うと、
->(value) { value.strip.presence }
と、短い処理をこの1行で定義できます。
今回のように処理が1行で済む場合は、わざわざ別の名前を付けたメソッドを用意せず、ラムダでコンパクトに書けるわけです。
valueという名前である必要はない
ここで最初に勘違いしていたのですが、
->(value)
のvalueは特別な名前ではありません。
これはラムダが受け取る引数名です。
そのため、
normalizes :name, with: ->(name) { name.strip.presence }
と書いても動きます。
ただ個人的には、
->(value)
このようにvalueを使うことで、
「受け取った値を処理している」と認識しやすく、分かりやすいと感じました。
normalizes :name, with: ->(value) { value.strip.presence }
今回のこのコードでは、
:name→ 正規化する属性
value→ name属性に設定された値を受け取る引数
という違いがあります。
stripとは?
続いて、このコードについてです。
value.strip
stripはRubyの文字列(String)で使えるメソッドで、文字列の前後の空白を削除します。
例えば、こうなります。
" 田中太郎 ".strip
# => "田中太郎"
また、空白やスペースだと、このようになります。
" ".strip
# => ""(空文字)
まとめると、stripメソッドは文字列の前後の空白を削除する
また、空白だけの文字列の場合は、空文字にするメソッドです。
presenceとは?
presenceはRailsのActive Supportが提供しているメソッドです。
値が存在すればその値を返し、空ならnilを返すという動きをします。
例えば、こうなります。
"田中太郎".presence
# => "田中太郎"
"".presence
# => nil
そのため、
" ".strip.presence
を順番に見ると、
" "
↓ strip
""
↓ presence
nil
となります。
これによって、空白しか入力されなかった場合をnilとして扱えるようになります。
ラムダをもう少し深掘りしてみる
今回のコードを見て、
->(value) { value.strip.presence }
はnormalizes専用の特殊な書き方なのかな?と思いました。
しかし、調べてみると、そうではありませんでした。
->はRailsではなく、Rubyのラムダを作るための記法です。
例えば、今回の処理やRailsとは関係なく、こう書くこともできます。
greeting = ->(name) { "こんにちは、#{name}さん" }
greeting.call("田中")
これを実行すると、以下の結果が返ります。
こんにちは、田中さん
つまり、
変数 = ->(引数) { 処理 }
で処理を作って変数に格納して、
変数.call(値)
で実行できます。
今回深掘りしてみた
normalizes :name, with: ->(value) { value.strip.presence }
このコードも、
normalizesのwith:に、正規化するための処理を渡している
と考えることで、理解しやすくなりました。
with:がないとどうなる?
normalizesは、
normalizes :name, with: ...
のように、
- 何を正規化するか
- どのように正規化するか
を指定します。
今回の場合だと、:nameが「何を」、with: ->(value) { value.strip.presence }が「どのように」に当たります。
そのため、
normalizes :name
だけでは、どのように値を変換するのか指定されていません。
with:はnormalizesで正規化処理を指定するために必要なものです。
まとめ
normalizes :name, with: ->(value) { value.strip.presence }
を一つずつ見ていくと、
normalizes → name属性を正規化する
:name → 対象はname属性
with: → この方法で正規化する
->(value) → 値を受け取るラムダ
value.strip → 前後の空白を削除する
presence → 空ならnilにする
という処理になっていました。
最初は、
normalizes :name, with: ->(value) { value.strip.presence }
という1行を「Rails特有のよく分からない書き方」として見ていました。
しかし分解してみると、normalizesというRailsの機能の中に、
- ラムダ
- Stringの
strip - Railsの
presence
といった要素が組み合わさっていることが分かりました。
個人的には、こういった短い1行でも、分からない部分を一つずつ分解していくことでRubyそのものの理解にもつながると感じました。
今後も「動いているけど、なぜ動くのか分からない」というコードに出会ったら、一度分解して調べてみようと思います。