プロを目指す人のためのRuby入門(通称チェリー本)をやっと一通り読むことができましたため、読んでみて、良かったところや学んだこと、難しかったところを記載します!
良かったところ
文法の説明にとどまらず、ベストな使用タイミングについても説明がされている
例外処理の章で、raise
やrescue
の文法的な説明だけではなく、例外処理を実装する際のベストプラクティスについても触れられていた点がとても良かったです😊
過去に文法的には理解できましたが、どのタイミングで学んだことを使用した方が良いのかが分からなかった経験がございましたが、本書ではそういったことは少ないのではと思います!
例外処理では安易にrescue
を使用して、プログラムの処理を継続しない方がよいです。なぜなら、本来はプログラムが異常終了するものを継続して処理を動かしているため、そのことによってデータ構造をおかしくしてしまうことにより、2次災害が起きる可能性があるためです。そのため、rescue
を使用せずに異常終了したら、その原因を調べて解決するほうが良いです。
人間でも同じことが言えますが、病気や怪我をしている状態で無理に体を動かそうとすると、患部以外の箇所も悪くなってしまうという状態です😩
そういった場合は、まず患部の原因を調べて、治してから活動した方が良いです。
それでも例外処理を実装する場合には以下の点を意識した方が良いです。
- バックトレースログを出力させる。
- エラーメッセージを出力させる。
- エラークラスを出力させる。
後で例外が発生した場合に原因調査が必要になった際に情報がないと調査に時間がかかったり、例外の原因がわからない状況になってしまうからです。
過去に実務でエラーメッセージがでないエラーに遭遇したことが何度かありました。エラーメッセージがでる場合よりもエラーメッセージが出ない方が格段にエラー解決が大変ですので、メッセージは出しておいた方が良いですね😅
Rubyと他の言語との比較で説明がされていたこと
Rubyだけの説明にとどまらず、Javaのような他言語との比較で説明されていて、わかりやすかったです!
印象的だったのが、定数とprivateの説明です。
私自身、過去にJavaを使用していた経験がありますが、Javaだと一度定数に値を定義したら、その後は定数の値を変更できません。
Rubyの場合、定数でも値を変更できてしまいます😳
それって定数っていうんですかね?とも思ったのですが、、
以下に試してみた結果、ワーニングメッセージは出ますが、定数の中身は変わっております。
irb(main):015:0* HOGE = 1
=> 1
irb(main):016:0> HOGE
=> 1
irb(main):017:0* HOGE = 2
(irb):17: warning: already initialized constant HOGE
(irb):14: warning: previous definition of HOGE was here
=> 2
irb(main):018:0> HOGE
=> 2
irb(main):019:0>
private
についてですが、こちらもJavaですと、クラスの中でprivateメソッドを定義した場合、呼び出せるのは同じクラス内のメソッドです。
一方、Rubyだとprivateメソッドを呼び出せるのは同じクラス内のメソッドだけでなく、サブクラス(子クラス)のメソッドからも呼びせます。
irb(main):102:1* class Food
irb(main):103:1* private
irb(main):104:1*
irb(main):105:2* def name
irb(main):106:2* 'Delicious meals'
irb(main):107:1* end
irb(main):108:0> end
=> :name
irb(main):109:0>
irb(main):110:0>
irb(main):111:1* class Sushi < Food
irb(main):112:2* def to_s
irb(main):113:2* "name: #{name}"
irb(main):114:1* end
irb(main):115:0> end
=> :to_s
irb(main):116:0>
irb(main):117:0> sushi = Sushi.new
=> #<Sushi:0x00007fa83a0616b8>
irb(main):118:0>
irb(main):119:0> sushi.to_s
=> "name: Delicious meals"
irb(main):120:0>
学んだこと
曖昧だった用語について理解を深められた
- レシーバ
- ミュータブル
- イミュータブル
レシーバ
はインスタンスやオブジェクトとほとんど同じような意味で使われております。レシーバという言葉を英訳すると「受け取る人」ですが、何を受け取るなのか疑問でしたが、インスタンスメソッドを呼び出した際にインスタンスメソッドがインスタンスに対してメッセージを送っているため、レシーバと呼ばれているそうです🤔
ミュータブル
は変更可能なという意味。以下のようにString型は破壊的なメソッドで破壊的な変更ができます。
irb(main):103:0> aaa = "testtest"
=> "testtest"
irb(main):109:0> aaa.upcase!
=> "TESTTEST"
irb(main):110:0>
irb(main):111:0> aaa
=> "TESTTEST"
irb(main):112:0>
一方、イミュータブル
は変更不可能なという意味。Integer型はString型のように破壊的な変更ができません。
例外処理でresucueした例外を再度発生させる
実務の中でよく以下のようなコードを見かけますが、本書を読むことでちゃんと理解できていないことに気づきました、、
def hogehoge
処理内容
rescue => e
エラー内容をSentryというイベントログを管理できるソフトに送信
raise
end
hogehogeメソッドの中にある処理内容でエラーが発生したら、例外を捕捉して、また例外を発生させているというところまでは理解できたのですが、腑に落ちていない感覚がありました😵
なんでまた例外をわざわざ発生させているのかという点が疑問でした、、
本書を読むことで、やっと腑に落ちました!
まずhogehogeメソッドの中にある処理内容でエラーを捕捉したら、hogehogeメソッドの後に続く処理が継続してしまいます。処理の継続はしたくないですが、エラーメッセージをSentryに情報は送りたいという要望があります。その要望を満たすために、raiseでわざわざ例外を発生させることで、hogehogeメソッドの後に続く処理が継続しないようにしているということでした。
ご参考までにSentryの詳細は以下にございます。
Sentryでイベントログ収集をしよう! - Vue.js編 -
bundle execコマンドの意味について知れたこと
実務でよくbundle exec rails routes
やbundle exec rails c
などのコマンドを使用しておりましたが、そもそもなんでbundle exec
を先頭につけなえればいかないのかが疑問でした😅
(ただのおまじないなのかと、、)
上記の疑問を解消するためにはまずBundlerというgemを理解する必要がありました。Bundlerはgemの依存関係やバージョンを管理してくれます。例えば、Aというgemを使用するためには、Bというgemをインストールしなければいけませんが、ユーザがわざわざBのgemもインストールしなくてもBundlerが空気を読んで、Bというgemをインストールしてくれます。
そして、Bundlerで管理されているgemのコマンドを使用したい場合にはbundle exec
を先頭につけなければいけません。私が毎回叩いていたrails routes
やrails c
はrailsのgemに関するコマンドでしたため、それをBundler経由で使用するためにはbundle exec
をつけなければいけなかったということを知れたのは目からウロコでした😳
selfについて
self
がメソッドの前についていたら、クラスメソッドと思っていたのですが、そうではない場合もあることを知りました。
例えば、以下コードです。
class Test
def self.hogehoge
puts "hogehoge"
end
def hoge
self.huga
end
def huga
puts "hugahuga"
end
以下の通りに実際にコードを実行してみましたところ、def self.hogehoge
はTest.hogehoge
でうまく呼び出せているので、クラスメソッドです。
irb(main):032:1* class Test
irb(main):033:2* def self.hogehoge
irb(main):034:2* puts "hogehoge"
irb(main):035:1* end
irb(main):036:1*
irb(main):037:2* def hoge
irb(main):038:2* self.huga
irb(main):039:1* end
irb(main):040:1*
irb(main):041:2* def huga
irb(main):042:2* puts "hugahuga"
irb(main):043:1* end
irb(main):044:1* end
=> :huga
irb(main):045:0>
irb(main):046:0>
irb(main):047:0> Test.hogehoge
hogehoge
=> nil
irb(main):048:0>
一方、self.huga
もクラスメソッドと思いきやTest.huga
で実行してみたところ、エラーが出ました😱
irb(main):053:0> Test.huga
Traceback (most recent call last):
5: from /Users/yokoyamagen/.rbenv/versions/3.0.0/bin/irb:23:in `<main>'
4: from /Users/yokoyamagen/.rbenv/versions/3.0.0/bin/irb:23:in `load'
3: from /Users/yokoyamagen/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/irb-1.3.0/exe/irb:11:in `<top (required)>'
2: from (irb):49:in `<main>'
1: from (irb):50:in `rescue in <main>'
NoMethodError (undefined method `huga' for Test:Class)
irb(main):054:0>
self.huga
はインスタンスメソッドなのかと思い、以下のコードを実行したところ、うまくメソッドを呼び出せました!
irb(main):054:0>
irb(main):055:0> test = Test.new
=> #<Test:0x00007fe2a8884ae0>
irb(main):056:0>
irb(main):057:0> test.huga
hugahuga
=> nil
irb(main):058:0>
どうして上記のようになるのかを調べるためにself
の中身を以下コードで確認してみます。
class Test
puts "クラス構文直下のself: #{self}"
def self.hogehoge
puts "クラスメソッド直下のself: #{self}"
end
def hoge
puts "インスタンスメソッド直下のself: #{self}"
end
end
クラス構文直下とクラスメソッド直下のselfはクラス名であるTestを指しておりますが、インスタンスメソッド直下のselfは#Test:0x00007fe2a80244b8というインスタンを指していることが分かりました。
irb(main):061:0>
irb(main):062:1* class Test
irb(main):063:1* puts "クラス構文直下のself: #{self}"
irb(main):064:2* def self.hogehoge
irb(main):065:2* puts "クラスメソッド直下のself: #{self}"
irb(main):066:1* end
irb(main):067:1*
irb(main):068:2* def hoge
irb(main):069:2* puts "インスタンスメソッド直下のself: #{self}"
irb(main):070:1* end
irb(main):071:0> end
クラス構文直下のself: Test
=> :hoge
irb(main):072:0>
irb(main):073:0> Test.hogehoge
クラスメソッド直下のself: Test
=> nil
irb(main):074:0>
irb(main):075:0> test = Test.new
=> #<Test:0x00007fe2a80244b8>
irb(main):076:0>
irb(main):077:0> test.hoge
インスタンスメソッド直下のself: #<Test:0x00007fe2a80244b8>
=> nil
irb(main):078:0>
selfは定義する場所によって、クラスであったり、インスタンスであることを学びました☺️
インスタンスメソッドの前についているself
は暗黙的に定義されているため、明示的に書かなくてもよいです。
selfがメソッド名の前についていたら、クラスメソッドだけでなく、インスタンスメソッドの可能性もあるのだなと学びました!
selfが定義されているのがインスタンスメソッドの中なのか、クラス構文直下なのか、定義されている場所も把握する必要があると思いました。
難しかったこと
「クラスの作成を理解する」という箇所のcase文と===の関係がうまく理解できなかったため、また時間をおいて読み直した時には理解できるようになっていたいです😅
感想
一通り本書を読んでみて、以前よりも公式マニュアルを読むことが苦痛に感じることがなくなったのではないかと思いました🤩
今まで用語の理解をしっかりと行っていなかったのを今回の学習を通して、理解できるようになったことが大きいのではと思っております。
また、Railsを読む際にもRubyの名前空間や例外処理を理解したことによって、よりコードの内容が頭の中に入るようになったと感じております。
時間をおいて何度でも読み直したい一冊だと感じました!!