この春頃から、Ruby / Rails を業務として使う・書くことになった。僕は、2016年6月まではソフトウェアエンジニアとして、主にWebアプリケーションのバックエンドを書いていたが、その時使っていたのが Ruby / Rails だったので、そのブランクにして実に9年弱になる。コードを書くこと自体は趣味の個人開発で続けてきてはいたものの、言語が違う(Go)なので、こと「Ruby を書く」ことに関していえば、やはり9年弱のブランクがあると言える。
当時の Ruby の最新は2.3系だったようなのだが、今はというとどうやら 3.4系まで進んでいるようで、そのあまりの差の大きさにクラクラする。これくらいのブランクがあると、「以前 Ruby / Rails を書いていたことがありました」「この9年弱は趣味で Go を書いてました」というのはむしろ弊害のほうが多いのでは?とすら思えてしまう。というわけで(?)、今回、改めて Ruby というプログラミング言語をキャッチアップすべく、「プロを目指す人のためのRuby入門[改訂2版] 言語仕様からテスト駆動開発・デバッグ技法まで Software Design plus」(俗に言う "チェリー本")を読み返すことにした。
現状、一通り通読し終えただけではあるけれど、とても素晴らしい一冊だった。
さらにこの本のありがたいことに、執筆当時は3.0系を前提として詳細な解説をしてくれてるのだけれど、その後のバージョンアップによって発生した差分についてはサポートサイトでカバーしてくれているので、それについても確認を行った。具体的には以下。
以下に、この本を読みながら「あ〜〜あったなぁこれ」「こんなのあったか?」「確かあったような気もするな......」「今はこんな書き方できるのか」「これの使い所が思い浮かばんのだが。。」などの、様々な感情を覚えた・不意を突かれたポイントを、自分用のメモとして(あとは、上記のような属性の人間が久々に Ruby を触ると、どういう点で不意を突かれるのかというのは面白そうだと思ったので)残しておきたい。
基礎
++
--
はない。+=
-=
を使う- 定数
- メソッド内にスコープを限定した定数は定義できない。
- 定数には再代入が可能。
- グローバル変数は
$
で変数名を始める。クラスの内外問わず、プログラムのどこからでも代入、参照が可能。 - Rational(有理数)クラスとBigDecimalクラス
0.1r * 3.0r #=> (3/10) 0.1r * 3r == 0.3 #=> true a = 0.1 b = 3.0 a.rationalize * b.rationalize #=> (3/10) (BigDecimal("0.1") * BigDecimal("3.0")).to_f #=> 0.3
- Ruby の if文は最後に評価された式を戻り値として返す
- Ruby で真偽値の偽として扱われるのは nil か false のみのため、そのことを活用し、式の戻り値や真偽値が確定した時点で評価が終了されることを活用する場面がある
- case文
- break のようなキーワードを用いなくても、次の when 節が実行されたりすることはない
- when節に複数の値を指定し、どれかに一致すれば処理を実行する、という条件分岐を書ける
!
の付くメソッドが常に破壊的変更をおこなう、というわけではない。!
がつかないメソッドよりも危険、という意味。- 破壊的メソッドと非破壊的メソッドの2種類が存在する場合には、前者に
!
が付く。
- エンドレスメソッド定義(1行メソッド定義)
def greet = 'Hello!'
require_releative
: 自ファイルからの相対パスで読み込むファイルを指定するときに使用require
を使って、実行時のカレントディレクトリからの相対パスで指定することもできるが、カレントディレクトリによって変わってくるため非推奨
pp
メソッドを使えば複雑なオブジェクトの内容を見やすく整形して出力してくれる
配列・繰り返し処理
<<
を使うと配列の最後に要素を追加できるmap
メソッド: 各要素に対してブロックを評価した結果を新しい配列にして返す&
とシンボルを使うことで、ブロックを使うメソッドは簡潔に書くことができる(以下の条件が揃ったときのみ。)- ブロックパラメータが1個だけ
- ブロックの中で呼び出すメソッドに引数がない
- ブロックの中では、ブロックパラメータに対してメソッドを1回呼び出す以外の処理がない
['ruby', 'java', 'python'].map { |s| s.upcase } ['ruby', 'java', 'python'].map(&:upcase) # 1. `&:upcase` はシンボルの `:upcase` に対して `to_proc` メソッドを呼び出す。 # 2. シンボルの `:upcase` が Proc オブジェクトに変換され、 `map` メソッドにブロックとして渡される。 # 3. 2. で作った Proc オブジェクトは `map` メソッドから配列の各要素を実行時の第1引数として受け取る。第1引数は `upcase` メソッドのレシーバとなる。つまり、配列の各要素に対して `upcase` メソッドを呼び出すことになる。 # 4. `map` メソッドは Proc オブジェクトの戻り値を順に新しい配列に詰め込む。 # 5. 3. と 4. の組み合わせにより、配列の各要素が大文字に変換された新しい配列が `map` メソッドの戻り値になる。
- 範囲オブジェクト
最初の値..最後の値
(最後の値を含む)最初の値...最後の値
(最後の値を含まない)最初の値..
(最初の値以上を表す範囲オブジェクト)..最後の値
(最後の値以下を表す範囲オブジェクト)
*
を使った多重代入のパターン
e, *f = 100, 200, 300 f #=> [200, 300] e, * = 100, 200, 300 # *の後ろの変数名を省略 e, = 100, 200, 300 # ,で終わっても同様に無視 a, *b, c, d = 1, 2, 3, 4, 5 b #=> [2, 3] a, *b, c, d = 1, 2, 3 b #=> []
- 配列の展開
a = [] b = [2, 3] a.push(1) a.push(*b) a #=> [1, 2, 3]
jp = ['japan', '日本'] country = '日本' case country when *jp 'こんにちは' end
- 自分で定義するメソッドで可変長引数を使う場合
def greet(*names) "#{names.join('と')、こんにちは" end
%
記法で文字列の配列を簡潔に作る:%w! apple melon orange!
- 添字付きの繰り返し処理: `hoge.each_with_index { |v, i| puts "#{i}: #{v}" }
with_index
メソッドを用いた添字付きの繰り返し処理: `list.map.with_index { |v, i| puts "#{i}: #{v}" }with_index
は Enumerator クラスのインスタンスメソッド。
- 2次元配列に対してシンプルに繰り返し処理を行う
dimensions = [ [10, 20], [30, 40], [50, 60], ] dimensions.each do |length, width] p length * width end
- 番号指定パラメータ: 明示的にブロックパラメータを指定する代わりに、パラメータの順番に応じて
_1
から_9
までの番号を使うことができる・_1
についてはit
も同じ役割を持つ。
['1', '2', '3'].map { _1.rjust(3, '0')} ['japan', 'us', 'italy'].map.with_index { [_2, _1] } #=> [[0, "japan"], [1, "us"], [2, "italy"]]
- 数値を1つずつ増やしながら/減らしながら処理をしたい場合:
upto
メソッド /downto
メソッド
10.upto(14) { |n| a << n } a #=> [10, 11, 12, 13, 14]
begin ... end
で囲むと最低1回は実行される
begin a << 1 end while false a #=> [1]
break
に引数を渡すと、while や for 文の戻り値になる。- 一気に外側のループまで抜けたい場合:
throw
メソッドとcatch
メソッドを使う。throw
メソッドに第2引数を渡すと、catch
メソッドの戻り値になる。- Ruby のこれらのメソッドは例外処理とは関係ない。
catch タグ do # do something throw タグ end
break
とreturn
の違いbreak
: 繰り返し処理から抜けるreturn
: メソッドから抜ける
- 繰り返し処理をやり直したい場合:
redo
ハッシュやシンボル
- ハッシュリテラルでは値を省略することもできる
name = 'Alice' age = 20 h = {name:, age:} # {name: name, age: age} と書いたのと同じ
- シンボル
- Ruby の内部では整数として管理される
- 同じシンボルであれば全く同じオブジェクトである
- シンボルはイミュータブルなオブジェクト
- 状態を変数に入れて管理するなどの場合、シンボルを使うことで可読性のみならず処理効率も向上させられる
**
をハッシュの前に付けると、ハッシュリテラル内でほかのハッシュの要素を展開できる。- 代わりに
merge
メソッドを使うこともできる。
- 代わりに
h = { us: 'dollar', india: 'rupee' } { japan: 'yen', **h } #=> { :japan => 'yen', :us => 'dollar', :india => 'rupee'} { japan: 'yen' }.merge(h) #=> { :japan => 'yen', :us => 'dollar', :india => 'rupee'}
- メソッドに定義されていない任意のキーワード引数をハッシュで受け取る
def buy_burger(menu, drink: true, potato: true, **others) # end
**
を使ってハッシュをキーワード引数に明示的に変換して渡す:buy_burger('fish', **params)
- メソッドへの最後の引数がハッシュであれば、ハッシュリテラルの
{}
を省略可能。 - 存在しないキーが指定されたときに nil 以外の値を返すハッシュを作りたいときは、
Hash.new
の引数に戻り値のデフォルト値を指定する:h = Hash.new('hello')
Hash.new
にブロックを与えると、ブロックパラメータとして「ハッシュ自身」と「見つからなかったキー」が渡される。このブロックパラメータを使って、ハッシュにキーとデフォルト値を同時に設定するケースも多い。
h = Hash.new { |hash, key| hash[key] = 'hello' }
- 通常 SyntaxError となるようなシンボルを作る(シングルクォートで囲む)
:'12345' :'ruby-is-fun' :'ruby is fun' :'()'
正規表現の利用
- 正規表現オブジェクトを作成する方法
Regexp.new('\d{3}-\d{4}') /https:\/\/example\.com/ %r!https://example\.com!
- 正規表現オブジェクト作成時のオプション:
/正規表現/オプション
orRegexp.new(正規表現, オプション)
Regexp.new
に渡すオプションは、Regexp::IGNORECASE
のような指定に加え、'i'
のような文字列でも指定可能。
=~
: 文字列中の最初にマッチした位置(0以上の数値)が返る・マッチしなかった場合は nil- キャプチャにメタ文字を使って名前をつける
m = /(?<year>\d+)年(?<month>\d+)月(?<day>\d+)日/.match(text) m[:year] m[:month] m[:day]
if /(?<year>\d+)年(?<month>\d+)月(?<day>\d+)日/ =~ text puts "#{year}/#{month}/#{day}" end
Regexp.timeout
で正規表現のタイムアウト秒数を指定できる。これはグローバルな設定だが、一部の正規表現だけにタイムアウト秒数を指定したい場合はRegexp.new
にtimeout
キーワードを指定する。- マッチした結果が代入される組み込み変数
$~
: MatchDataオブジェクト$&
: マッチした部分全体$1
$2
$3
... : 1,2,3番目のキャプチャを取得する$+
: 最後のキャプチャ文字列を取得する
match?
メソッドは組み込み変数を書き換えない- マッチすれば
true
しなければfalse
を返す
- マッチすれば
scan
メソッド: 引数で渡した正規表現にマッチする部分を配列に入れて返す- `'123 456 789'.scan(/\d+/) #=> ["123", "456", "789"]
- 正規表現に
()
があると、キャプチャされた部分が配列の配列になって返る。'1977年7月17日 2021年12月31日'.scan(/d+)年(\d+)月(/d+)日/) #=> [["1977", "7", "17"], ["2021", "12", "31"]]
[]
に正規表現を渡すと、文字列から正規表現にマッチした部分を抜き出す。
text = '郵便番号は123-4567です' text[/\d{3}-\d{4}/] #=> "123-4567"
split
に正規表現を渡すと、マッチした文字列を区切り文字にして文字列を分解し、配列として返す。
text = '123,456-789' text.split(/,|-/) #=> ["123, "456", "789"]
gsub
の置換ルールをハッシュとして第2引数で渡すことができる
text = '123,456-789' hash = { ',' => ':', '-' => '/' } text.gsub(/,|-/, hash) #=> "123:456/789"
クラス
- クラスメソッドを定義する方法
class クラス名 def self.クラスメソッド # do something end end
class クラス名 class << self def クラスメソッド # do something end end end
- 継承元を指定せずに作成したクラスはデフォルトで Object クラスを継承している
super
: スーパークラスの同名メソッドを呼び出せる・スーパークラスとサブクラスで引数の数が同じだった場合は、引数なしのsuper
を呼ぶだけで、自身に渡された引数をすべてスーパークラスに引き渡すことができる。- そのメソッドでは引数を使わないのであれば
*
で可変長引数として任意の数の引数を受け取り、そのままsuper
を呼び出すことで引数もそのまま渡すことができる - ただし、
super()
と書いた場合は「引数0個でスーパークラスの同名メソッドを呼び出す」という意味になるので注意。
- そのメソッドでは引数を使わないのであれば
- privateメソッド
- クラスの外からは呼び出せず、クラスの内部でのみ使えるメソッド(レシーバが
self
に限定されるメソッド)。サブクラスでも呼び出せる。 - クラス内で
private
キーワードを書くと、そこから下で定義されたメソッドは private メソッドになる。もしくは、既存のメソッド名をprivate
キーワード(メソッド)に渡すことでも、そのメソッドを private メソッドにすることもできる。この場合、その下に定義しためそっどの可視性は変更されない。 - privateなアクセサメソッドを定義したいときは
private attr_accessor :name
などと一行で書ける。 - クラスメソッドを private にしたい場合は、
class << self
構文を使う・もしくはクラスメソッドの定義後にprivate_class_method
で可視性を変更する。
- クラスの外からは呼び出せず、クラスの内部でのみ使えるメソッド(レシーバが
class User class << self private def hello 'Hello!' end end end
class User def self.hello 'Hello' end private_class_method :hello end
- protectedメソッド: そのメソッドを定義したクラス自身と、そのサブクラスのインスタンスメソッドからレシーバ付きで呼び出せる。
- 定数やクラスの可視性を private にしたい場合は、
private_constant
を使う。
class Foo class Bar # end private_constant :Bar end
- サブクラスでメソッドをオーバーライドすると、可視性も同時に変更できる。
- 特異メソッド: 特定のオブジェクトにだけ紐づくメソッドのこと。「クラスメソッド」も、「特定のクラスの特異メソッド」。
alice = 'I am Alice.' bob = 'I am Bob.' # aliceのオブジェクトにだけ、shuffleメソッドを定義する def alice.shuffle chars.shuffle.join end alice.shuffle #=> NoMethodErrorにならない
- Classクラスのスーパークラスは Module クラス。
モジュール
- 名前空間を切るためにも使用される
- クラスであるモジュールを include すると、モジュールで定義したメソッドがインスタンスメソッドとして呼び出せるようになる(ミックスイン)。
- extend を使うと、モジュール内のメソッドをそのクラスの特異メソッド(クラスメソッド)にすることができる。
クラス名.extend モジュール名
の形式でモジュールを include/extend することも可能。- ミックスインとしても使えて、なおかつモジュールの特異メソッドとしても使えるような定義の仕方:
module_function
メソッドを使う。- モジュール関数: ミックスインとしてもモジュールの特異メソッドとしても使えるメソッドのこと。
- モジュール関数は
モジュール名.#メソッド名
と書くこともある。
- モジュール関数は
- モジュール関数: ミックスインとしてもモジュールの特異メソッドとしても使えるメソッドのこと。
- 可視性は include したクラスでも引き継がれる。
prepend
でもミックスインすることができるが、これを使うとミックスインしたクラスよりも先に prepend したモジュールからメソッドが検索される。- prependするモジュール側で
super
を用いて同名のメソッドを実装することで、ミックスインするクラスの同名メソッドを直接書き換えることなく、かつ、元の実装を活かしながら振る舞いを変更することができる。
- prependするモジュール側で
- クラスやモジュールがどの順番でメソッド探索されるかを確認する: Classオブジェクトに対して
ancestors
メソッドを呼び出す - Enumerableモジュールを include するためには、include先のクラスで each メソッドが実装されていることが必要条件となる。
- Comparable モジュールを include できるようにするための条件は、include 先のクラスで
<=>
演算子(宇宙船演算子 / UFO演算子)を実装しておくこと。<=>
演算子の要件(a <=> b
を例に)- aがbより大きいなら正の整数を返す
- aとbが等しいなら0を返す
- aがbより小さいなら負の整数を返す
- あとbが比較できない場合はnilを返す
- refinements: オーバーライドや独自のメソッドの追加などの独自の変更の有効範囲(スコープ)を限定することができる。
refine
メソッドを使って refinements を適用するクラスを指定し、refinements を有効にするためにはusing
メソッドを使う。
module StringShuffle refine String do def shuffle chars.shuffle.join end end end class User using StringShuffle def shuffled_name @name.shuffle end end
例外処理
- 例外処理の最も単純な構文
begin # 例外が起きうる処理 rescue # 例外が発生した場合の処理 end
rescue
節でretry
文を実行することで、begin
節の最初からやり直すことができるrescue
節に何もクラスを指定しなかった場合に捕捉されるのはStandardError
とそのサブクラス。- 他の言語でいうところの
Exception
クラスが Ruby でいうところのStandardError
クラスと同じ扱いだったりすることがある - 通常のコードで捕捉するのは `StandardErrorP クラスか、そのサブクラスに限定すべき。
- 他の言語でいうところの
raise
メソッド- 文字列だけを渡したときは
RuntimeError
クラスの例外が発生する - 第1引数に例外クラスを、第2引数にエラーメッセージを渡すと任意の例外クラスで例外を発生させられる
- 例外クラスのインスタンスを渡すこともできる
- 文字列だけを渡したときは
full_message
メソッド: クラス名、エラーメッセージ、バックトレースの3つを一度に取り出せるrescue
は修飾子としても使える。例外が発生し得る処理 rescue 例外が発生したときの戻り値
- 捕捉する例外クラスを指定することはできない。
rescue
節内でraise
メソッドを使うこともできる。その際raise
メソッドの引数を省略すると、rescue
節で捕捉した例外をもう一度発生させることができる。- 最後に発生した例外は組み込み変数の
$!
に格納される / バックトレース情報は$@
に格納される。
yield と Proc
yield
を使うことで、渡されたブロックを実行できる。
def greet puts 'おはよう' yield puts 'こんばんは' end greet do puts 'こんにちは' end #=> おはよう # こんにちは # こんばんは
block_given?
メソッド: ブロックが渡されたかどうかを確認する- ブロックを引数として明示的に受け取る(ただしブロックの引数はメソッド定義につき1つしか指定できない・他にも引数がある場合には、ブロックの引数は必ず最後に指定):
def メソッド(&引数) # `&` の役割は Proc オブジェクトをブロックと認識させるだけではなく、厳密には、 # 右辺のオブジェクトに対して `to_proc` メソッドを呼び出し、その戻り値として得られた Proc オブジェクトを、ブロックを利用するメソッドに与える、ということをしている。 引数.call end
- 他のメソッドにブロックを渡すだけなら、メソッド定義時に引数名を付けなくても良い。
def foo(&) bar(&) end def bar(&b) b.call end
- Procオブジェクトの作成:
add_proc = Proc.new { |a = 0, b = 0| a + b } add_proc = proc { |a, b| a + b } # 以下2つは Proc クラスのオブジェクトではあるが、ラムダ。( `->` はラムダリテラル ->(a, b) { a+ b } lambda { |a, b| a + b }
- ラムダは
Proc.new
よりも引数の数のチェックが厳密。 - Procオブジェクトをブロックの代わりに渡す際には、変数名の戦闘に
&
を付ける。普通のオブジェクトとして受け渡しする場合には、渡す側/受け取る側両方において&
の指定は不要。 - Ruby のブロックや Proc オブジェクトはクロージャとして振る舞う: 生成時のコンテキスト(変数情報など)が保持され続ける
パターンマッチ
case / when
ではなく、case / in
を使う
case 式 in パターン1 パターン1にマッチしたときの処理 in パターン2 パターン1にマッチせず、パターン2にマッチしたときの処理 else パターン1にも2にもマッチしなかったときの処理 end
else
節がなく、どの条件にもマッチしなかった場合には例外NoMatchingPatternError
が発生するin
節で、事前に定義された変数とのマッチをさせたい場合にはピン演算子^
を用いる。インスタンス変数やクラス変数、グローバル変数も渡すことができる。式も渡すことができるが、その場合には()
で囲む必要がある。
alice = 'Alice' bob = 'bob' def charlie = 'charlie' case name in ^alice # in ^bob # in ^(charlie) # end
- ハッシュに対するパターンマッチ
case car in {name:, engine:, motor:} puts "#{name} / #{engine} / #{motor}" in {name:, engine:} puts "#{name} / #{engine}" in {name:, motor:} puts "#{name} / #{motor}" end
- 各要素のマッチ判定には
===
が使われるため、クラス名(クラスオブジェクト)や範囲オブジェクトをin
節で指定して「そのクラスのインスタンスか?」「その範囲に収まる値か?」といった条件を指定することもできる。
case ['Alice', 999, 3] in [String, 10.., n] "n=#{n}" end #=> "n=3"
- hashパターンはハッシュの各要素が
in
節で指定したパターン(キーと値 / キーのみ)に部分一致すればマッチしたと判定される。
case {name: 'Alice', age: 20, gender: :female} in {name: 'Alice', gender:} "gender=#{gender}" end
**
を使って「任意のキーと値」を指定することもできる。
case {name: 'Alice', age: 20, gender: :female} in {name: 'Alice', **rest} "gender=#{gender}" end
- asパターン: パターンマッチでマッチしたオブジェクトを変数に代入する・一番外側に
=> 変数名
と書くと、マッチしたオブジェクト全体を変数に代入できる
case {name: 'Alice', age: 20, gender: :female} in {name: String => name, age: 18.. => age} # end case {name: 'Alice', age: 20, gender: :female} in {name: String, age: 18..} => person # end
- alternativeパターン: 2つ以上のパターンを指定し、どれか1つにマッチすればマッチしたと見なす
case {name] 'Bob', age: 25} in {name: 'Alice' | 'Bob' => name, age:} # :name の値が 'Alice' または 'Bob' で、さらに :age というキーがあればマッチする・さらに、 :name と :age の値をそれぞれ変数 name と age に代入する # end
in
節に追加の条件式(ガード式)を追加できる・ガード式の中ではパターンマッチで代入された変数を参照することも可能。
case 式 in パターン if 条件式 パターンにマッチし、なおかつ条件式が真になった場合に実行する処理 end
- 1行パターンマッチ:
case
節を省略して評価したい式 in パターン
と書くことも可能。マッチすればtrue
しなければfalse
が返る。
person = {name: 'Alice', children: ['Bob']} # 右辺の括弧は省略する( `person = name: 'Alice', children: ['Bob']` と書く)ことも可能 if person in {name:, children: [_]} # :name と :children をキーにもち、かつ :children が要素1つの配列であれば処理を実行する end
- 1行パターンマッチのもう一つの記法:
式 => パターン
主にパターンマッチを使った変数代入を利用するために使う。あたかも左辺の式を右辺の変数に代入しているように見える場合があるため、「右代入」と呼ばれることもある。この記法でのパターンマッチ自体の戻り値は、マッチするとnilが返り、マッチしないと例外NoMatchingPatternError
が発生する。
{name: 'Alice', children: ['Bob']} => {name: , children: [child]} name #=> "Alice" child #=> "Bob"
words = 'Ruby is fun' words.split(' ').map { |word| word.upcase + '!' * 3 }.join(' ') => loudvoice loudvoice #=> "RUBY!!! IS!!! FUN!!!"
- Ruby のパターンマッチは独自の変数スコープを作らない。また、パターンマッチ内で新たに定義されたローカル変数はパターンマッチを抜けても使用可能。
- 自作クラスをパターンマッチ array パターンに対応させるためには
deconstruct
メソッドを、hash パターンに対応させるためにはdeconstruct_keys
メソッドをそれぞれ定義する。deconstruct
メソッドは自分自身の配列表現を戻り値として返すようにする。deconstruct_keys
メソッドは自分自身のハッシュ表現を戻り値として返すようにする。
クラス名(パターン)
またはクラス名[パターン]
という形式: arrayパターンやhashパターンを利用しつつ、マッチさせたいオブジェクトの形を限定する- MatchDataオブジェクト、Timeオブジェクト、Dateオブジェクトもパターンマッチで使える。
便利
- キーワード引数を指定する際、値を省略できる
chomp = true text.lines(chomp:) # text.lines(chomp: chomp) と書いたのと同じ
- nil かもしれないオブジェクトに対して安全にメソッドを呼び出したい場合:
&.
演算子
a = nil a&.upcase #=> nil
||=
を使った自己代入(nilガード)!!
: 値に対応する真偽値をtrue
またはfalse
として得る- エイリアスメソッドの定義:
alias 新しい名前 元の名前
equal?
メソッドはobject_id
が等しい場合に true を返す。===
の代表的な用途として、case 文の when 節がある。仮に独自に定義したクラスのオブジェクトをcase文のwhen節の中で使いたい場合は、===
を要件に合わせて再定義する必要がある。- メソッドの存在確認:
respond_to?
gets
メソッド: ターミナル上でユーザーの入力を受け付ける。Kernelモジュールで定義されているメソッド。chomp
メソッド: 改行文字を取り除く- ファイルの読み書きを行う場合は
open
メソッドにブロックを渡すことで、ensure
節やクローズ処理を書かずに済ませることができる - Ruby2.7以降、メソッドチェーンの行間にコメントを挟んでも構文エラーが発生しなくなった。
inject
メソッド: たたみ込み演算を行うメソッド。引数は、メソッドに渡すブロックの初回の第1引数に用いられる。
numbers = [1, 2, 3, 4] sum = numbers.inject(0) { |result, n| result + n) sum #=> 10
tap
メソッドはレシーバをそのまま返す。
'#043c78'.scan(/\w/\w/).tap { |rgb| p rgb }.map(&:hex) #=> ["04", "3c", "78"]
- ワンライナーを使う場合
- ワンライナーを使う場合は
ruby
コマンドに-e
オプションを渡す。 - ワンライナーで初期化処理や終了処理を記述する場合は
BEGIN
文やEND
文を使う。BEGIN
文はほかのどのコードよりも先に実行され、END
文はプログラムの終了時に実行される。
- ワンライナーを使う場合は
$ ruby -e 'BEGIN{$sum=0};[1,2,3,4].each{|n|$sum+=n};END{p $sum}'
- バッククオートリテラルは、それで囲まれた文字列をOSコマンドとして実行する。
send
メソッドはレシーバに対して指定したシンボル(または文字列)のメソッドを実行する。
str.send(:upcase) # str.upcase を呼ぶのと同じ str.send(:split, ',') # str.split(',') を呼ぶのと同じ
Data
クラスを使うことでイミュータブルなクラスを簡単に定義できる。==
メソッドやdeconstruct
メソッド、deconstruct_keys
メソッドも自動的に実装される。Data
クラスと似たものでStruct
クラスがある。Time.new
では日時文字列をパースできる。
その他
- Rubyはすべてがオブジェクト。
- インスタンス変数は、作成(値を代入)する前にいきなり参照してもエラーにならず、nilが返る
- メソッド名の表記法
- インスタンスメソッドを表す場合:
クラス名#メソッド名
- クラスメソッドを表す場合:
クラス名.メソッド名
またはクラス名::メソッド名
- インスタンスメソッドを表す場合:
- Rubyのクラスは変更に対してオープンなため、「オープンクラス」と呼ばれることもある
- Ruby にはメソッドのオーバーロード(多重定義)という考え方はない。
- メソッドのなかで引数のクラスをチェックしたり、引数のデフォルト値や可変長引数を用いることでメソッド呼び出し時の引数の個数を柔軟に変えることができることを利用し、オーバーロードと同じようなしくみは実現できる。
- シングルトンパターン: 「唯一、1つだけ」のオブジェクトを作る手法のこと
DateTime
クラスは非推奨クラスになっており、日時を扱う場合は Time クラスを使うように推奨されている。- Rake: Ruby で作られているビルドツール。
- Gemfile での
'~> 2.17.0'
のようなバージョン指定: 悲観的なバージョン指定、「2.17.0 以上かつ 2.18 未満」を指定したことになる。'~> 2.17'
であれば「2.17以上、3.0未満」を指定したことになる。 - Ruby 3.0 から、プログラム本体に新しい構文を導入することなく、なおかつ人間がなるべく型情報を書かずに済む独自のアプローチとして、「RBS」「TypeProf(
typeprof
コマンド)」「Steep(形検査のために使う外部の gem)」を採用している- 型情報はスクリプト本体(rbファイル)とは別の rbs ファイルに記述する
- TypeProfは、与えられた Ruby コードを解析し型情報を推定、rbs ファイルを生成する
- あくまで推定した結果であり、推定に失敗するケースもある。
- rbsファイルは Ruby の型情報を記述するための言語(RBS言語)で書かれている
- TypeProf で型情報を自動生成するためには、目的のメソッドやクラスを実行するプログラムが必要になる。解析対象の本体のrbファイルだけだと、TypeProfは型情報を生成できない。テストコードや実行用プログラムが存在しない場合は、開発者がrbsファイルを手書きする必要がある。
File.exists?
とDir.exists?
は削除された- 文字列の破壊的変更を行う破壊的メソッドが Ruby 3.4 で警告扱いになった
- rubyist: ruby に対して単なるお客さん以上の気持を持っている人