プロを目指す人のためのRuby入門を読んで

はじめに

Rubyの有名な技術書「プロを目指す人のためのRuby入門」を読了したのでその感想を書きます。

良かったところ

解説が丁寧でわかりやすいです。
各章で例題が配置されている点、現場目線で注意書きがされているのがありがたいなと感じました。 (例えば、現場ではXXは滅多に使わず、XXXをよく使います。といった感じで記載してくれている。)
筆者の人柄や気遣いが伝わる書籍でした。

学んだこと

6章

  • ハッシュの展開(**)
    • **をハッシュの前につけるとハッシュリテラル内で他のハッシュの要素を展開できる
h = { us: 'dollar', india: 'rupee' }
{ japan: 'yen', **h } #=> {:japan=>"yen", :us=>"dollar", :india=>"rupee"}

mergeメソッドも同じ働き

h = { us: 'dollar', india: 'rupee' }
{ japan: 'yen' }.merge(h) #=> {:japan=>"yen", :us=>"dollar", :india=>"rupee"}
  • ハッシュの擬似キーワード引数

ハッシュを受け取ってキーワード引数のように見せる

def buy_burger(menu, options = {})
  drink = options[:drink]
  potato = options[:potato]
  # 省略
end

buy_burger('cheese', drink: true, potato: true)
# buy_burger('cheese', { drink: true, potato: true })と同じ
# 最後の引数が {}の時だけ省略可能

’擬似’キーワード引数だと、未定義のキーワードでもエラーにならない。

buy_burger('fish', salad: true)

ただし未定義の’キーワード引数’だとエラーになる。

def buy_burger(menu, drink: true, potato: true)
  # 省略
end

# saladとchickenは無効なキーワード引数なのでエラーになる
buy_burger('fish', drink: true, potato: false, salad: true, chicken: false)
#=> ArgumentError: unknown keywords: salad, chicken

エラー回避(任意のキーワード引数が必要の場合)は**を使う

**引数

def buy_burger(menu, drink: true, potato: true, **others)
  # othersはハッシュとして渡される
  puts others
  # 省略
end
#=> {:salad=>true, :chicken=>false}

呼び出し元の引数で設定する例

params = {drink: true, potato: true}
buy_burger('fish', params)
#=>エラー

# **を使う
buy_burger('fish', **params)

7章

  • self.nameと@nameを基本的に同じという理解
  • privateメソッドになるのはインスタンスメソッドのみでクラスメソッドはprivateにならない
  • .freezeは、オブジェクトを変更不可能にするメソッドで、対象のオブジェクトは後からの変更が禁止される(エラー)。文字列や配列などのミュータブルなオブジェクトに対して使用され、不用意な変更を防ぐのに役立つ。
  • ダックタイピングとはオブジェクトの型やクラスよりも、その振る舞いや機能が重要であるプログラミングスタイルのこと

8章

  • モジュールの用途
    • 名前空間の作成
    • ミックスイン
    • 特異メソッドの追加
    • モジュール関数の提供
  • Enumerable モジュールは、配列やハッシュなど繰り返し処理を行うクラスにincludeされるモジュール
  • Comparable モジュールは、クラスに対して比較演算子<, <=, ==, >=, >)を可能にするモジュール

9章

  • 例外処理の構文
begin
 # 例外が起きうる処理
rescue
 # 例外が発生した場合の処理
end
  • 例外自身もオブジェクトとなる
    • 例えばmessageメソッド、backtraceメソッドなどがある
  • 情報を取得したい場合の構文
begin
 # 例外が起きうる処理
rescue => 例外オブジェクトを格納する変数
 # 例外が発生した場合の処理
end

例文

begin
  1 / 0
rescue => e
  puts "エラークラス: #{e.class}"
  puts "エラーメッセージ: #{e.message}"
  puts "バックトレース -----"
  puts e.backtrace
  puts "-----"
end

省略形としてeやexを使用することが多い

  • クラスを指定して補足する例外を限定させることも可能
begin
 # 例外が起きうる処理
rescue 補足したい例外クラス
 # 例外が発生した場合の処理
end

例文

0で徐算した場合に発生するZeroDivisionError

begin
  1 / 0
rescue ZeroDivisionError
  puts "0で除算しました"
end
#=> 0で除算しました

ZeroDivisionError以外のエラーが発生した場合はどうなるかと言うと例外は捕捉されない=プログラムが以上終了する

以下は、ZeroDivisionErrorを記載しているコードで存在しないメソッドを呼び出した例

begin
  # NoMethodErrorを発生させる
  'abc'.foo
rescue ZeroDivisionError
  puts "0で除算しました"
end
#=> NoMethodError: undefined method `foo' for "abc":String
  • 例外クラスにも継承関係がある
    • 全ての例外クラスはExceptionを継承している
  • rescue節に指定しない場合はStandardErrorとそのサブクラスが捕捉される。 その場合特殊なエラー(Nomemory ErrorやSystemExit)などは捕捉されない
  • 例外処理よりも条件分岐を使うべし
  • 例外が発生してもしなくても必ず実行したい処理がある場合はensureに記載
begin
 # 例外が発生するかもしれない処理
rescue #(※rescueは任意なので書かなくても問題ない)
 # 例外発生時の処理
ensure
 # 例外の有無に関わらず実行する処理
end
  • 例外が発生しなかった場合の処理はelseに記載
  • 例外処理にも戻り値があり、正常処理の場合はbegin内の最後、例外発生時はrescueの最後が戻り値になる

10章

  • yieldを使うと渡されたブロックを実行する ブロックに引数を渡したり、ブロックの戻り値を受け取ったりができる
  • yieldとブロックでやり取りする引数は個数の過不足に寛容
  • ブロックを引数として受け取る場合は引数名の前に&をつける ブロックを実行するときは.callを使う
def メソッド(&引数)
    引数.call
end
  • Procオブジェクトを作る方法
    • Proc.new
    • procメソッド
    • 構文 - >
    • lambdaメソッド

11章

  • パターンマッチとは「データ構造による条件分岐」「構成要素の取り出し」という要素を備えた機能とのこと。 条件分岐などの用途で使われる。
case 式
in パターン1
  パターン1にマッチした時の処理
in パターン2
  パターン1にマッチせず、パターン2にマッチした時の処理
else
  パターン1,2にマッチしなかったとき
end
  • ver3.0から正式に導入された

12章

  • エラーが発生するとバックトレースが出力される
    • 実行環境によって表示形式が変化する
    • 下に行くほどエラーが古い(上のエラーが新しい呼び出し)
  • よく発生する例外
    • NameError
      • 未定義のローカル変数や定義を呼び出したとき
    • NoMethodError
      • 存在しないメソッドを呼び出そうとしたとき
        • メソッド名違い
        • privateメソッドをクラス外から呼び出したとき
        • レシーバの型が想定していた型と異なるとき
        • レシーバが想定と違いnilのとき
    • ArgumentError
      • 引数の数が違ったとき、期待値ではなかったとき
    • SyntaxError
      • 構文エラー、endやカンマに過不足がある時など
  • デバッグでプログラムの途中経過を確認
    • printデバッグ
      • printメソッドをプログラムに埋め込んで、プログラムを実行し、ターミナルに出力される値を確認して不具合の原因を探る
    • logger.debugでデバッグ情報をログに出力する
    • デバッガ(debug.gem)を使う
      • プログラムを1行ずつ実行しながら変数の中身を確認したり、実行される条件分岐を確認したりできる
  • その他トラブルシューティング
    • irb上で簡単なコードを動かす
    • ログを調べる
    • 公式ドキュメントやリファレンスを読む
    • issueを検索する
    • ライブラリのコードを読む
    • テストコードを書く
    • (警戒しながら)ネット情報を参考にする
    • パソコンの前から離れる
    • 誰かに聞く

13章

  • 日付、時刻
    • Timeクラス
      • 組み込みライブラリのためrequireの必要なし
    • Dateクラス
      • require必須
  • ファイルやディレクト
    • Fileクラス、Dirクラス
      • 組み込みライブラリのためrequireの必要なし
  • Rake
    • Rubyで作られているビルドツール、「何かしらのまとまった処理」を簡単に実行するためのツールとして使われる
task :hello_world do
  # ブロックの中がタスクとして実行される処理になる
  puts 'Helloc'
end
$ rake hello_world
Hello, world!
  • gemとBundler
    • Rubyのライブラリはgemという形式でパッケージングされる 作成したgemはRuby.Gems.orgにてアップロード、インストールできる
      # fakerというgemをインストールする場合
      $ gem install faker
      $ gem install faker -v 2.16.0
    
    • Bundlerを使うと1つのコマンドで大量のgemをどの環境でも同一のバージョンでインストールすることができ、PJでgemが統一される
    • Gemfile.lock
      • Bundlerで管理すべきgemとそのバージョン番号が記載されている
      • Bundle installすると自動的に作成される

難しかった所

初心者向けではないと本に記述されている通り、全体的に私には難しい技術書でした。
6章まではわかりやすく、理解も大丈夫そうでしたが、7章以降どんどん難しくなってきたので「無理に全部理解しようとせず、頭の中にインデックスを作る読書スタイルに切り替えてもOKです」という筆者の言葉通りに読み進めました。
正直、読了後の自身の理解度は5割に満たないように思いますが、今後課題などを進めていく時にまたこの本を振り返れるようにしたいと思います。