Pythonとサラリーマンと

2020年6月にPythonを始めたサラリーマンのブログです。

メモ:プロを目指す人のためのRuby入門

はじめに

『プロを目指す人のためのRuby入門』を進める中で、用語や気になった記法をメモする。

第2章 Railsの基礎を理解する

rubyでは全てがオブジェクト

・メソッドの呼び出し

オブジェクト.メソッド(引数1,引数2,引数3)

 カッコはなくてもOK

オブジェクト.メソッド 引数1,引数2,引数3

 引数がない場合もある

オブジェクト.メソッド

・改行が文の区切り。セミコロン(;)による区切りも可能だが使う頻度は少ない

・カッコのなかで改行しても、文の区切りとはならない

10.to_s(
16
)
#=>"a"

・識別子 = 変数やクラスやメソッドにつける名前のこと

予約語 = 識別子として使用できない単語

・空白文字 = 識別子や予約語を区切るために使われる
 半角スペースやタブなど。
 いくつ連続しても、違いはない。
 演算子は前後に空白文字がなくても正常に動作する。
 (入れたほうが見やすい)

(5   +       12)*    3

def

    add    (a,b)


a+    b

      end

add(  4, 5  )

リテラル = ソースコードに埋め込む値のこと
 (数値、文字列、配列、ハッシュ、正規表現など)

・変数(ローカル変数)
 宣言と代入は同時に行う。

s = 'Hello'

 変数を宣言するだけの構文はない。

#変数を宣言する目的で変数名だけ書くと、エラーになる
x

#=>NameError:undefinedlocalvariableormethod`x'formain:Object

・変数はアフファベットの小文字とスネークケースで書くのが慣習

・文字列
 シングルクオートかダブルクオートを使う。
 ダブルクオートは式展開や改行が使える。

・文字列の比較は==と!=を使う

'ruby'=='ruby' #=>true

'ruby'=='Ruby' #=>false

'ruby'!='perl' #=>true

'ruby'!='ruby' #=>false

〜数値〜
・3, 4.2, -3. -3.8など使ってOK

・変数の前に-をつけると正負を反転できる

-n = 2
#=> -2

・整数同士の割算の答えは整数になる(小数点以下切り捨て)
 (小数点以下が必要な時は5.0など小数点以下も記載する)

・整数が入っている変数にはn.to_fで小数に変更できる

・割算の余りを求めるには%を使う

演算子は色々あり、優先順位が定められている

・小数点の値の計算には注意
 コンピュータは10進数を2進数に変換して計算するため、丸め誤差が生じる。
 これにより想定と異なる比較結果が出てくることがある。

〜真偽値と条件分岐〜
・ルールは以下  falseかnilであれば偽。
 それ以外は全て真。

・論理演算子
 && = かつ
 || = または
 ここでも注意すべきは演算子の優先順位。
 &&は||よりも優先順位が高い。
 優先順位を変えたければ()を使う。
・!演算子 = 真偽値を反転させる

t1 = true
!t1 #=> false

・if文

if 条件A
  条件Aが真の場合だった時の処理
elsif 条件B
  条件Bが真の場合だった時の処理
else
  それ以外の場合の処理
end

・メソッド

def メソッド名(引数1, 引数2)
  必要な処理
end

・メソッド名のルールは変数名と同じ

・メソッドの戻り値  Rubyでは最後に評価された式がメソッドの戻り値になる

・returnを使うとメソッドを途中で脱出できる

defgreeting(country)

#countryがnilならメッセージを返してメソッドを抜ける
#(nil?はオブジェクトがnilの場合にtrueを返すメソッド)

  return 'countryを入力してください' if country.nil? 

  if country == 'japan'
   'こんにちは'
  else
    'hello'
  end

end

greeting(nil)
#=>"countryを入力してください"
greeting('japan')
#=>"こんにちは"

・引数のない場合はdef メソッド()の()は省略可能

〜文字列〜

・文字列はStringクラスのオブジェクト

・%記法を使うとシングルクオート/ダブルクオートがそのまま使える

#%q! !はシングルクオートで囲んだことと同じになる

puts %q!He said, "Don'tspeak."!
#=>He said, "Don't speak."

#%Q! !はダブルクオートで囲んだことと同じになる(改行文字や式展開が使える)
something="Hello."
puts %Q!He said, "#{something}"!
#=>He said, "Hello."

#%! !もダブルクオートで囲んだことと同じになる

something="Bye."
puts %!He said, "#{something}"!
#=>He said,"Bye."

・ヒアドキュメント
 複数行に渡る長い文章を書く時はヒアドキュメントを使うと便利。

a = <<TEXT
ここに文章を書きます。
改行もしっかりできます。
TEXT

 なお、TEXTを識別子として使ったが、文章の中に含まれていない文字列ならなんでも良い。

・<<-TEXTを使うと最後のTEXTをインデントさせられる

・ヒアドキュメントの中では式展開が有効

・Stringクラスに用意されている様々なメソッドを使って、文字列の表示方法をコントロールできる

〜数値 アゲイン〜
・ビット演算・指数表現・integer・float・有理数複素数など、どの種類の数値を扱うのかを意識する必要がある

〜真偽値と条件分岐 アゲイン〜 ・Rubyでは論理式を評価する場合、式全体が真または偽出ることを決定するまで順番に評価していく。
 真偽が決定すると、評価を終了して、最後に評価した式の値を返す。

・&&とand、||とor。それぞれ使いどころがある

・unless文は条件が偽の時だけ処理を実行する
 unless分にはelsifのようなものはないが、elseは使える。

・case分
 複数の条件を指定する場合にelseifよりも見やすくかける

case 対象のオブジェクトや式
when 値1
  値1に一致する場合の処理
when 値2
  値2に一致する場合の処理
when 値3
  値3に一致する場合の処理
else
  どれにも一致しない場合の処理
end

・条件演算子三項演算子

式 ? 真だった時の処理 : 偽だった時の処理

〜メソッド アゲイン〜
・メソッドの引数にはデフォルト値をつけれる

def greeting(coutry = '日本')

 こうすることで引数なしでメソッドを使ってもエラーにならない。

・デフォルト値あり、なしの引数の混在も可能

・?で終わるメソッド = 真偽値を返す

・!で終わるメソッド = 使用する際には注意

・破壊的メソッド = 呼び出したオブジェクトの状態を変更してしまうメソッド

〜その他の基礎知識〜 ・ガベージコレクション
 使用されなくなったオブジェクトを自動的に回収し、メモリを解放する。
 (これによりプログラマはメモリ管理を意識する必要なし)

エイリアスメソッド
 全く同じ処理をするが、名前が違うメソッド

・式(Expression)と文(statment)
 式 = 値を返す
 文 = 値を返さない

・擬似変数
 true, false, nil, self, FILE, など。
 擬似変数は文法上、変数のように見えるが代入するとエラーが発生する。

・参照
 Rubyの変数にはオブジェクトそのものではなく、オブジェクトへの参照が格納されている

・組み込みライブラリ、標準ライブラリ、gem

・requireで標準ライブラリやgemを読み込む
(自分で作成したRubyプログラムも)

・loadは何回でもプログラムを読み込める

・require_relativeを使えばファイルのある場所がパス参照の起点になる

・puts = 改行あり
・print = 改行なし
 これらは配列の表示の仕方も違う

第3章 テストを自動化する

・クラス名とファイル名は合わせる
 クラス名 = キャメルケース

class JapaneseCalenderTest < Minitest::Test

~~~

end

 ファイル名 = スネークケース
 japanese_calender_test.rb

・Minitestはtest_で始まるメソッドを探して実行する

def test_sample

end

・クラス内では複数のtestメソッドを作成して良い
 Minitestはクラス内の全てのtest
で始まるメソッドを実行する

・Minitestが提供しているメソッドを使ってテストする

assert_equal 期待する結果, テスト対象となる値や式

・この本で使うテストメソッドは以下の3つ

#aとbの値は等しければバスする
assert_equal b, a

#aが真であればパスする
assert a

#aが偽であればパスする
refute b

配列や繰り返し処理を理解する

・配列内のデータは順番に並んでいて添字を指定することでデータを取り出せる

・配列は[]を使って作成する

[要素1, 要素2, 要素3]

・配列はArrayクラスのオブジェクト

・変数に代入する書き方

a = [1, 2, 3]

a[0] #=> 1
a[1] #=> 2
a[2] #=> 3


b = [
  4,
  5,
  6
]

・配列には数値以外でもどんなオブジェクトでも格納できる

・素材しない添字を指定したらnilが返る

・要素の変更

配列[添字] = 新しい値

・元の大きさよりも大きい添字を指定すると、間の値はnilで埋まる

・配列の最後に要素を追加する

a = []
a << 1
a << 2
a << 3

a #=> [1, 2, 3]

・配列の中の特定の要素を削除するdelete_atメソッド

a = [1, 2, 3]

a.delete_at(1) 

a #=> [1, 3]

・配列を使った多重代入ができる

a, b = [1, 2]

a #=> 1
b #=> 2

・ブロックはメソッドの引数として渡せる処理のかたまり

・繰り返し処理

・他の言語ではforで実行する繰り返し処理をRubyではeachを使って行う

・each = 配列の要素を最初から最後まで順番に取り出す

・eachで要素を取り出して、その要素をどう扱うかをブロックに記述する

numbers = [1, 2, 3, 4]
sum = 0

numbers.each do |n|
  sum += n
end

・|n|はブロック引数
 配列から取り出された要素が順番に格納される

・ブロックの中で初めて定義した変数は、ブロックの中だけでしか呼び出せない

・ブロックの外で定義したローカル変数は、ブロックの中でも呼び出せる

・ブロックはdo..endと{}の何でも書くことができる

numbers.each { |n| sum += n }

・mapメソッド(collectメソッド)
 配列使うメソッド。
 各要素の対してブロックを評価した結果を新しい配列にして返す。

numbers = [1, 2, 3, 4]

new_numbers = numbers.map { |n| n * 10 }

print new_numbers
#=> [10, 20, 30, 40]

・selectメソッド(find_allメソッド)
 各要素に対してブロックを評価し、その戻り値が真の要素を集めた配列を返すメソッド。

numbers = [1, 2, 3, 4, 5, 6]

even_numbers = numbers.select { |n| n.even? }

print even_numbers
#=> [2, 4, 6]

rejectメソッドはselectメソッドの反対

・findメソッド(detectメソッド)
 ブロックの戻り値が真になった最初の要素を取り出す

numbers = [1, 2, 3, 4, 5, 6]

even_numbers = numbers.select { |n| n.even? }

print even_numbers
#=> 2

・injectメソッド(reduceメソッド)  たたみ込み演算を行うメソッド

numbers = ['Mon', 'Tue', 'Wed', 'Thu']

sum = numbers.inject('Sun') { |result, n | result + n}

print sum
#=> SunMonTueWedThu

・範囲(Range)オブジェクト

最初の値..最後の値(最後の値を含む)
最初の値...最後の値(最後の値を含まない)

・範囲オブジェクトの使いどころ

#配列の一部を取り出す
a = [1 ,2, 3, 4]
a[1..2]
#=> [2, 3]

#2文字目から4文字目を取り出す
a = 'abcdef'
a[2..4]
#=> cde

 このように「X以上、Y以下(未満)」といった時に使うと便利

・文字列の配列の場合[]ではなく%wや%Wを使える
 (各要素をシングルクオートやダブルクオートで挟まなくてよくなる)

%w!apple melon orange!
#=> ["apple", "melon", "orange"]

%w(apple melon orange)
#=> ["apple", "melon", "orange"]

・配列、ブロック、繰り返し処理には様々なメソッドが用意されていて、値の操作の仕方や書き方が多様に存在している

ハッシュやシンボル

・ハッシュはキーと値の組み合わせでデータを管理するオブジェクト

#空のハッシュを作る
{}

#キーと値の組み合わせを3つ格納するハッシュ
{ キー1 => 値1, キー2 => 値2, キー3 => 値3}

・ハッシュの{}とブロックの{}は同じ書き方

・後から新しいキーと値を追加する

#ハッシュ[キー] = 値

currencies = {'japan' => 'yen', 'us' => 'doller'}

currencies['italy'] = 'euro'
#すでに存在しているキーの場合は上書きされる

currencies #=> {"japan"=>"yen", "us"=>"doller", "italy"=>"euro"}

・ハッシュからキーを取り出す

#ハッシュ[キー]
currencies = {'japan' => 'yen', 'us' => 'doller'}
currencies['japan']

・ハッシュを使った繰り返し処理
 eachメソッドを使うとキーと値の組み合わせを順に取得できる

currencies = {'japan' => 'yen', 'us' => 'doller'}
currencies.each { |key, value|
  puts "#{key} : #{value}"
}
#=> 
japan : yen
us : doller

・引数を1つだけにするとキーと要素が配列に格納される

・ハッシュの同値比較
 全てのキーと値が同じであれば並び順に関わらずtrueが返る。

・ハッシュの要素数の取得 = sizeメソッド、lengthメソッド

・ハッシュの要素の削除 = deleteメソッド

・ハッシュリテラルの注意点
 →ハッシュリテラルがメソッドの第一引数になる場合は、引数の()を省略できない
 →これは、ハッシュの{}がブロックの{}だと認識されてエラーが出るから
 →第二引数以降であれば、()なしでハッシュを使える

〜シンボル〜
・:シンボル、'文字列'

・シンボルは何個作ってもオブジェクトIDは同じ
 =>メモリ効率が良い

・シンボルはイミュータブルなので、破壊的な変更はできない

・ハッシュのキーではシンボルが好んで使われる
 => 文字列よりも高速に値を取り出せる

・シンボルを使ったハッシュの書き方

currencies = {:japan => 'yen', :us => 'doller'}

currencies = {japan: 'yen', us: 'doller'}

#どちらの書き方でもOK

・キーも値もシンボルの場合

currencies = {:japan => :yen, :us => :doller}
currencies = {japan: :yen, us: :doller}

・ハッシュのキーは文字列、シンボルの混在OK
 =>非推奨

・ハッシュの値は文字列、シンボルがよく混在される

・メソッドのキーワード引数

def buy_burger(menu, drink: true, potate: true)

 =>キーワード引数は省略可能で順番前後のOK
 =>キーワード引数のデフォルト値を省略することも可能

・キーワード引数を使うメソッドを呼び出す場合、キーワード引数に一致するハッシュを引数として渡せる

params{drink: true, potate: true)

def buy_burger('fish', params)

・キーワード引数の値をメソッドの中で呼び出すときは、シンボルを記述するのではなく変数のように記述するだけ

def convert_length(length, from:, to: )
  units = {m: 1.0, ft: 3.28, in: 39.37}

   (length/units[from]*units[to]).round(2)
end

〜ハッシュアゲイン〜
・ハッシュでよく使うメソッド
 keysメソッド = ハッシュのキーを配列として返す

n = {japan: 'yen', us: 'doller'}

n.keys
#=> [:japan, :us]

 valuesメソッド = ハッシュの値を配列として返す

n = {japan: 'yen', us: 'doller'}

n.values
#=> ["yen", "doller"]

 has_keys?メソッド = ハッシュの中に指定したキーがあるか確認する

n = {japan: 'yen', us: 'doller'}

n.has_key?(:japan)

#=>true

#keys?, inclueds?, members?は全てhas_keys?のエイリアスメソッド

・**を使うとハッシュ内で他のハッシュのキーと値を式展開

h = {india: 'rupee'}

n = {japan: 'yen', us: 'doller', **h}
#=> {:japan=>"yen", :us=>"doller", :india=>"rupee"}
# ハッシュのmergeメソッドでも同じことができる

〜コラム:よく使うイディオム〜
・&.演算子を使ってメソッドを呼び出すとオブジェクトがnilだった場合にエラーではなくnilを返す

・||=を使うと変数がfalseかnilの時にリテラルを代入する

limit ||= 10

・!!を使うとtrue/falseを簡単に返せる

!!1 #=> true
!!false #=> false
!!nil #=> false

limit = 10
!!limit => true

正規表現

Rubyでは正規表現を/正規表現/という風に書く

正規表現と文字列を比較する場合は=~をよく使う
 真の場合、0が返る。
 偽の場合、nilが返る。  なお、0が返るのは文字列中の正規表現がマッチした文字列の位置が返るから。

・比較に!~を使うとマッチすればtrue、マッチしなければfalse

正規表現のキャプチャを使うとマッチした文字列を連番で抜き出せるようになる

正規表現は、後に回すことにする。理解が苦しいのと、必要に迫られていない

クラスの作成を定義する

・クラスは内部にデータを保持し、自分が保持するデータを利用する独自のメソッドを持つことができます
(データをメソッドを一緒に持ち運べる)

・オブジェクト = インスタンス = レシーバ  →クラスをもとに作られたデータのかたまり

・メソッド = メッセージ
 →オブジェクトが持つ動作・振る舞い・処理のこと

・オブジェクトの状態 = オブジェクトの各データ

・オブジェクトから取得したり、設定したりできる値のことを属性・アトリビュート・プロパティと呼ぶ

alice = User.new(Alice, Jackson, 30)

alice.first_name #<= 取得してる

alice.first_name = "アリス" #<= 設定してる

#first_nameの部分はattr_accessorで定義される = 値
#使い方はメソッドと似ているが、別物

・クラス名は必ず大文字で始める

class User

end

class OrderItem

end

・クラスからオブジェクトを作成する = newメソッド

user = User.new

・newメソッドを使った時、initializeメソッドが呼び出される
 →必要であればクラス内で定義しておく

class User

 def initialize
   puts 'initialized'
 end

end

User.new
#=> initialized

・initializeメソッドは特殊で、外部から呼び出せない
 (デフォルトでprivateメソッド)

・initializeメソッドに引数をつけると、newメソッドを呼ぶ時にも引数が必須になる

class User

 def initialize(name, age)
   puts "name :#{name}, age: #{age}"
 end

end

user = User.new
#=> wrong number of arguments (given 0, expected 2) (ArgumentError)

インスタンスメソッド = クラスの内部で定義されたメソッド  →そのクラスのインスタンスに対して呼び出すことができる

・クラスの内部ではインスタンス変数を定義できる
 →インスタンス変数は@で始める
 →インスタンス変数は同じインスタンスの内部で共有される

class User

 def initialize(name)
   #インスタンス作成時に渡された名前をインスタンス変数に保存する
   @name = name
 end

 def hello

   puts "Hello, #{@name}."

 end
end

user = User.new('Alice')

puts user.hello
#=> Hello, Alice.

・メソッドやブロックの中で作成される変数 = ローカル変数
 →ローカル変数はメソッドやブロックの内部でのみ有効
 →メソッドやブロックが呼び出されるたびに毎回作り直される

・ローカル変数は参照する前に必ず値を代入しないとエラーになる

インスタンス変数は値が代入されずに参照されてもエラーにならず、nilが返る

class User

 def initialize(name)
   #インスタンス作成時に渡された名前をインスタンス変数に保存する
   #@name = name
 end

 def hello

   puts "Hello, #{@name}."

 end
end

user = User.new('Alice')

user.hello
#@name部分が空で帰ってくる
#=>Hello, .

 つまり、インスタンス変数をタイプミスするとエラーにもならず不具合につながる

class User

 def initialize(name)
   #インスタンス作成時に渡された名前をインスタンス変数に保存する
   @name = name
 end

 def hello

   puts "Hello, #{@mame}."

 end
end

user = User.new('Alice')

user.hello
#@mameになってもうてるやん!
#=>Hello, .

インスタンス変数は、通常は外部から参照できない  →参照する必要がある場合は、参照用のメソッドを作る

class User

 def initialize(name)
   @name = name
 end

#@nameを外部から参照するためのメソッド
 def name
   @name
 end

end

user = User.new('Alice')

puts user.name

インスタンス変数の値を外部から変更する場合も変更用のメソッドを定義する

class User

 def initialize(name)
   @name = name
 end

 def name
   @name
 end

#@nameを外部から変更するためのメソッド
 def name=(value)
   @name = value
 end

end

・アクセサメソッド = インスタンス変数を読み書きするメソッド
 (他の言語では「ゲッターメソッド」や「セッターメソッド」)

・attr_accessorを使えばアクセサメソッドの定義を省略できる
 (外部から読み書きさせるよ〜、のおまじない的な)

class User

 attr_accessor :name

 def initialize(name)
   @name = name
 end

end

user = User.new('Alice')

user.name ='bob'

puts user.name

・attr_reader = 読み込み専用
 →参照はできるが、変更はできない

・attr_writer = 書き込み専用
 →変更はできるが、参照はできない

・シンボルをカンマで区切ると複数のインスタンス変数へのアクセサメソッドを作成できる

class User

 attr_accessor :name, :age

 def initialize(name, age)
   @name = name
   @age = age
 end

end

インスタンスメソッド
 →インスタンスに含まれるデータの読み書きのために使う

・クラスメソッド
 →そのクラスと関係は深いがが、一つ一つのインスタンスのデータはつかはない場合  →書き方は以下の二つ

class クラス名

 def self.クラスメソッド
 処理
 end

end

 もしくは

class クラス名

 class << self
  def クラスメソッド
   処理
  end
 end

end   

 →クラスメソッドをたくさん定義する場合は、後者の方が便利

・String#to_i = Stringクラスのto_iというインスタンスメソッド

・File.exist? == File::exist? == Fileクラスのexist?というクラスメソッド

・クラスの中では定数を定義することができる

class User

 DEFAULT_PRICE = 0

 attr_accessor :name, :price

 def initialize(name, price = DEFAULT_PRICE)
   @name = name
   @price = price
 end

end

user = User.new('Nintendo Switch')

puts user.name
puts user.price
#=> Nintendo Switch
 => 0

Rubyはクラス名も定数の一つ

・同じクラス内のメソッドを呼び出す方法

name
self.name

 →これらはいずれも同じ
 →ただし、セッターメソッドを呼び出す場合は、selfをつけないとローカル変数への代入だと解釈されてしまう

・クラスメソッドの中ではインスタンスメソッドを呼び出せない

インスタンスメソッドの中ではselfを使ってクラスメソッドは呼び出せない
 →(クラス名.クラスメソッド名とちゃんと書かないといけない)

〜継承〜 / ・サブクラスのメソッド内でsuperを使うとスーパークラスの同名のメソッドを呼び出せる

・サブクラスで同名のメソッドを定義しなおすと、スーパークラスのメソッドをオーバーライドできる

・サブクラスではスーパークラスインスタンス変数も共有される
 →サブクラスで同名のインスタンス変数を定義するとオーバライドされてしまい、ややこしくなる

〜メソッドの公開レベル〜
・3つの公開レベルpublic/ protexted/ private

・publicメソッドはクラスの外部からでも自由に呼び出せる
 →initializeメソッド以外のインスタンスメソッドはデフォルトでpublic

・privateメソッドはクラスの外から呼び出せず、クラスの内部だけで呼び出せる
 →クラス内でprivateを定義すると、その下はprivateメソッドに
 →クラスの内部であっても、self.メソッド名では呼び出せない
 (privateメソッドは厳密にはレシーバを指定してメソッドを呼び出せないというルール)

Rubyではそのクラスだけでなく、サブクラスからでもprivateメソッドを呼び出せる

・クラスメソッドはprivateの下に書いてもprivateメソッドにならない

・クラスメソッドをprivateにする書き方は以下

class User

  class << self
    private
     #ここに処理を書けばprivateなクラスメソッド
  end
end

 もしくは

private_class_method :クラスメソッド名

 で定義する

・protectedメソッドは、そのクラスとサブクラスの中でだけレシーバ付きで呼び出せるメソッド

〜定数 アゲイン〜
・定数はクラスの外部からでも呼び出せる

クラス名::定数名

・定数はメソッドの内部では定義できない
 →必ずクラス構文の直下で定義

・定数は後から再代入することは可能
 (警告エラーはでる)

class User

  FIRST_NAME = 'Yamada'

  def say_hello
    puts self.hello
  end

end

User::FIRST_NAME = "Sato"
#warning: already initialized constant User::FIRST_NAME
#=> Sato

・クラスを凍結すれば定数の再代入を禁止できる

class User

  FIRST_NAME = 'Yamada'

  def say_hello
    puts self.hello
  end

  User.freeze

end

User::FIRST_NAME = "Sato"
#`<class:User>': can't modify frozen class (FrozenError)

モジュール

・モジュールの作り方

module モジュール名
  #メソッドや定数
end

 例えば

module Greeter

 def hello
  'hello'
 end

end

・モジュールからインスタンスを作成することはできない

・モジュールは、他のモジュールやクラスを継承することはで機内

・クラスの継承が使えない場合に、モジュールのincludeを使えば共通のメソッドを再利用できるようになる
 →ミックスインと呼ぶ

module Loggable
  def log(text)
    puts "[LOG] #{text}"
  end
end

class Product

  include Loggable

  def title
    log 'title is called'
    'A great movie'
  end

end

・1つのクラスに複数のモジュールをミックスインすることも可能

・モジュールの中でprivateメソッドにしておけば、ミックスインしたクラスの中でもprivateメソッドになる

・extendを使うと、そのモジュールの中のメソッドをそのクラスの特異メソッド(クラスメソッド)にすることができる

module Loggable
  def log(text)
    puts "[LOG] #{text}"
  end
end

class Product

  extend Loggable

  def self.create_products(names)
    #logメソッドをクラスメソッド内で呼び出す
    #つまり、logメソッドもクラスメソッドとなる
    log 'create_products is called'
  end

end

・UFO演算子 <=>

名前空間によりクラス名の重複を回避する
 →モジュールの中にクラスを定義することで、「そのモジュールに属するクラス」という定義ができる
 →これはモジュールをクラス名の衝突を避けるための名前空間として利用している
 →モジュールに属するクラスを参照する方法は以下

モジュール名::クラス名
Baseball::Second.new(’Alice', 13)

・大規模なブログラムになるとクラス名の重複が普通に起こるので、名前空間を作ってクラスを作成するのが普通

・ネストなしで名前空間付きのクラスを定義できる

module Baseball
end

class Baseball::Second
end

・モジュールに定義したメソッドをそのまま呼び出すこともできる  →モジュール内で特異メソッドを作っておく

module Loggable
  def self.log(text)
    "[LOG] #{text}"
  end
end

Loggable.log('test')
#=> "[LOG] test"

・モジュールはインスタンスを作れないので、単なるメソッドの集まりを作りたいときにモジュールの特異メソッドを作る
 (newする必要が全くないとき)

・モジュールでも以下の書き方OK

module Loggable

  class << self
  def log(text)
    "[LOG] #{text}"
  end

  #他の特異メソッド

  end
end

・モジュール関数 = ミックスインとしても、特異メソッドとしてもどちらでも使えるメソッド
 →module_functionメソッドで定義する
 →ミックスインした場合は自動的にprivateメソッドになる

例外処理

Rubyでは例外も例外クラスのインスタンス(例外オブジェクト)

Traceback (most recent call last):
2: from /Users/naoki/.rbenv/versions/2.5.0/bin/irb:11:in `<main>'
1: from (irb):14
NoMethodError (undefined method `new' for Loggable:Module)

 →NoMethodErrorは発生した例外のクラス名

・何もしなければ例外が発生した時点でプログラムが終了して、それ以下の処理は実行されない

・例外が発生しても、処理を続けさせることもできる

begin
 #例外が起こりうる処理
rescue
 #例外が発生した場合の処理
end

・例外が捕捉されない場合、プログラムは以上終了する

def method_1
  puts 'method_1 start'

  begin
    method_2
  rescue
    puts '例外が発生しました'
  end
    puts 'method_1 end'

end

def method_2

  puts 'method_2 start'
  method_3
  puts 'method_2 end'

end

def method_3
  puts 'method_3 start'

  1/0
  puts 'method_3 end'
end

method_1

 →この場合、method2の中のmethod3でエラーが発生し、method3/method2の順番で処理が終了し、method1で例外が捕捉される

・例外オブジェクトのメソッドを使って、例外に関する情報を取得することができる

begin
  1/0

rescue => e
  puts "エラークラス:#{e.class}"
  puts "エラーメッセージ:#{e.message}"
  puts "バックトレース____"
  puts e.backtrace
  puts "-----"
end
#=>
エラークラス:ZeroDivisionError
エラーメッセージ:divided by 0
バックトレース____
./lib/practice.rb:2:in `/'
./lib/practice.rb:2:in `<main>'
-----

 →変数eに例外オブジェクトを格納してメソッドを呼び出す

・捕捉するクラスを指定することができる

・例外クラスもスーパークラス・サブクラスの継承関係にある

・rescueの中でretryを使用すれば処理をやり直すことができる
 → カウンタをつけてリトライ回数も指定できる

・raiseを使って意図的に例外を発生させられる

def currency_of(country)

  case country

  when :japan
    'yen'
  when :us
    'dollar'
  when :india
    'rupee'
  else
    raise "無効な国名です。#{country}"
  end

end

 →デフォルトではRunTimeErrorクラスの例外
 →その他の例外クラスを指定することも可能
 →例外クラスのインスタンスを渡すことも可能

〜例外処理のベストプラクティス〜
・安易にrescueを使わない
 →例外はあくまで例外であって、一度処理を止めるのが普通
 (RailsのようなWebフレームワークでは例外処理を実装してくれてる)

・rescueするなら必ず情報を残す
 →例外クラス/メッセージ/バックトレースは最低でも

・例外処理を書く場合は対象範囲と対象クラスをできるだけ絞る
 →処理を続行させることを予測している例外だけを対象に
 →その他で例外が出たら処理を止めるべき

・例外処理よりも条件分岐を使って想定するエラーを処理する

・ifやcaseなどで予期しない条件が来たら例外を発生させて処理を止めるべき

例外処理の例題ともっと詳しくはスキップ。現時点で必要性が低い

yieldとProcを理解する

・メソッド内でyieldを使うとブロックで渡された処理を実行できる

・yieldを記載してブロックが渡されていなかったらエラー
 →block_given?メソッドでブロックの有無を確認

・yieldはブロックに引数を渡すことも可能

・Procクラスはブロックをオブジェクト化するためのクラス
 →Procクラスはブロック = 何らかの処理を表すクラス

・Procクラスのインスタンスを作る

hello_proc = Proc.new do 処理 end

・callメソッドでProcオブジェクトを実行する

hello_proc.call

yeildとprocもほどほどに。使い所がイメージできないので入ってこない

Rubyデバッグ手法を身に着ける

デバッグも、イメージできないのでスキップ

Railsに関して

・定数の自動読み込み機能
 →コード中に不明なクラスがあれば、特定のパスから条件に合いそうなクラスを自動的に読み込んでくれる

Rubyの標準ライブラリやgemをインストールしたライブラリも自動で読みこむ

名前空間としてのモジュールを自動で作成してくれる

# app/models/foo/bar.rb
class Foo:Bar < ApplicationRecord
 #クラス定義
end

 →Railsはfooというディレクトリの下にbar.rbがることから、「Fooは名前空間として使われている」と判断する

Railsrubyの標準クラスに対して数多くの独自拡張をしている

以上