💎 Rubyの基本

# はじめに
これはRuby言語について学習したことの記録です。

# Rubyってどういう存在？
- オブジェクト指向言語(すべてがオブジェクトでできている)
- スクリプト言語(コンパイル抜きで実行できる)

# Rubyファイルの実行

```rb
ruby ファイル名.rb
```

# 外部ファイルの読み込み

```rb
requre "ライブラリ名"
```

```rb
# 自分で作ったファイルを読み込む
require "./ファイル名" # 拡張子「.rb」は省略可
```

```rb
# Ex. 同じディレクトリ下にある「sample.rb」というファイルを読み込む場合
require "./sample"
```

# コメントアウト

```rb
# 1行のコメントアウト

=begin
複数行のコメントアウトはこうです。
ここに書いたことは実行されません。
=end

# これでも
# いいです
```

# マジックコメント

プログラムの先頭行に入れたりする呪文。

```rb
# frozen_string_literal: true
```

いかにもコメントアウトのように見えますが…

```rb
str1 = "hello"
str2 = "hello"

p str1.frozen?                     #=> false
p str1.object_id == str2.object_id #=> false
```

```rb
# frozen_string_literal: true
str1 = "hello"
str2 = "hello"

p str1.frozen?                     #=> true
p str1.object_id == str2.object_id #=> true
```

このように出力に差が出ます。
Rubyではオブジェクトに対してユニークな整数(オブジェクトID)が割り当てられるのですが、`# frozen_string_literal: true`を記述しておくと、デフォルトで「文字の内容が同じなら、オブジェクトIDも同じにします」という設定が働きます。
オブジェクトに対して`.object_id`メソッドを使うとIDの中身が確認できます。

# irb (インタラクティブRuby)

コンソールに`irb`コマンドを入力してEnter

```console
irb
irb(main):001> # ここにRubyのコードを記述する
```

簡単なRubyのプログラムを試すのに便利です。ここで行った操作は特に保存されません。

`exit`で終了でき、元のコンソールに戻ります。

```console
irb(main):001> exit
```

### プロンプトの矢印をシンプルにするオプション

```console
irb --simple-prompt
>> # 見やすくて便利
```

# 入力 / 出力

- 入力に使うコマンド: `gets`
- 出力に使うコマンド: `p`, `print`, `puts` 

### 入力: gets

```rb
input = gets # inputという変数に入力値を格納
```

`gets`: **一行**を読み込んで、読み込み成功時にはその**文字列**を返します。
つまり、複数行を読み込みたい場合はgetsコマンドを複数回実行する処理を書いたり、getsで取得した値を整数に変えたい時は`to_i`メソッドを使うなどの工夫が必要です。
改行文字を除去する`chomp`メソッドと組み合わせて`gets.chomp`とするのはよくやる手です。

### 出力: p, print, puts

`p`: 文字列かそうでないかを見分けられる形で出力します。

```rb
# 型情報を一緒に出力
p "Hello" #=> "Hello"
p 1       #=> 1
```

`print`: 末尾に改行文字を入れずに出力します。

```rb
print "こんにちは"
print 21
print "世紀少年"
#=> こんにちは21世紀少年
```

`puts`: 引数と改行を順番に出力します。引数なしの場合は改行のみを出力します。

[サンプルコード]
```rb
puts 1
puts "H"
puts "E"
puts "L"
puts "L"
puts "O"
puts # 引数なし
puts ["毎日あいさつ", "大事です"] # 引数が配列
```
[サンプルアウトプット]
```console
1
H
E
L
L
O

毎日あいさつ
大事です
```

# メソッド定義と実行

メソッド定義
```rb
def メソッド名(引数)
  処理
end
```

サンプルコード
```rb
def hello # 引数のないメソッドを定義
  puts "Hello"
end

hello # 引数なしでメソッド実行
#=> Hello

def greeting(name)
  puts "Hello, #{name}."
end

greeting("Mike") # 引数を渡してメソッド実行
#=> Hello, Mike.
```

# 式展開

`#{変数名や式}`: ダブルクォートで囲んだ文字列の中で、式の実行や変数の値を文字列に埋め込む形で表示できます。

```rb
name = "Nick"
puts "My name is #{name}."
#=> My name is Nick.

puts "#{1 + 2}"
#=> 3
```

# オブジェクトのクラス(型のようなもの)を調べる

`.class`: オブジェクトのクラス名を返します。

```rb
p 123.class      #=> Integer
p 0.1.class      #=> Float
p "sample".class #=> String
p nil.class      #=> NilClass
p true.class     #=> TrueClass
```

`.is_a?(クラス名)`: オブジェクトが、引数で与えられたクラスまたはサブクラスのインスタンスであるかを判定し、ブーリアン値を返します。

```rb
p "text".is_a?(String) #=> true
p 0.is_a?(String)      #=> false
```

# 条件分岐

`if` ~ `elsif` ~ `else` ~ `end`: Rubyは**elsif**という表記なので注意。

```rb
if 条件式A
  条件式Aがtrueの時に実行する処理
elsif 条件式B
  条件式Bがtrueの時に実行する処理
else
  これまでの条件式がfalseの時に実行する処理
end
```

# 繰り返し

`each`文: 配列によく使います。配列の要素を1つずつ取り出し、取り出した要素に対しての処理を書きます。

[サンプルコード]
```rb
strings = ["オラ", "無駄"]

strings.each do |string|
  puts string * 10
end
```

[サンプルアウトプット]
```console
オラオラオラオラオラオラオラオラオラオラ
無駄無駄無駄無駄無駄無駄無駄無駄無駄無駄
```

### ハッシュを対象にしたeach文

[サンプルコード]
```rb
hsh = { ruby: "ルビー", perl: "パール", crystal: "クリスタル" }

hsh.each do |key, value|
  puts "キー: #{key}, バリュー: #{value}"
end
```

[サンプルアウトプット]
```console
キー: ruby, バリュー: ルビー
キー: perl, バリュー: パール
キー: crystal, バリュー: クリスタル
```
<br>

`while`文: 条件がtrueである間ループします。条件がfalseになればループが終了します。

[サンプルコード]
```rb
i = 18
good_night = 21

while i <= good_night
  puts "今は#{i}時か..."
  i += 1
end

puts "よし、寝よう！"
```

[サンプルアウトプット]
```console
今は18時か...
今は19時か...
今は20時か...
今は21時か...
よし、寝よう！
```
<br>

`until`文: 条件がfalseの間、ループします。while文の逆のような存在です。

[サンプルコード]
```rb
day = 1
num = 1

until day >= 31 # day が31以上であるか判定し、falseの間ループ
  puts "#{day}日目: お米を#{num}粒もらったよ"
  num *= 2
  day += 1
end
```

[サンプルアウトプット]
```console
1日目: お米を1粒もらったよ
2日目: お米を2粒もらったよ
3日目: お米を4粒もらったよ
4日目: お米を8粒もらったよ
... (中略) ...
28日目: お米を134217728粒もらったよ
29日目: お米を268435456粒もらったよ
30日目: お米を536870912粒もらったよ
```

# 配列

```rb
# 空の配列を生成
arr = []

# いろいろなものが格納できてしまう
sample_arr = [1, "hoge", { "name" => "Taro", "age" => 18 }]
```

配列への要素の格納や取り出し方法、また、配列に用意されたメソッドは多くの種類があります。奥が深いので個別記事にしたいです。

# ハッシュ

連想配列ともいいます。`キー`と`値`の組み合わせで関連付けができるところが配列と異なる点です。配列がインデックスという添字をもとに値を取り出せるのに対して、ハッシュでは`user1`をもとに`Michael`を取り出したり、`country1`をもとに`日本`を取り出したりなど、人間が理解しやすい形式で値を操作できます。

```rb
hsh = { キー1 => 値1, キー2 => 値2 }
```

[サンプルコード]
```rb
product = { "name" => "涼感シート", "price" => 350 }

def price_half(item)
  item["price"] /= 2
end

price_half(product) # 半額メソッドを実行

puts "なんと、#{product["name"]}を#{product["price"]}円で販売中！"
```

[サンプルアウトプット]
```console
なんと、涼感シートを175円で販売中！
```

# シンボル

`:symbol`のような、先頭にコロンが付いた文字列のことです。見かけは文字列ですが、内部ではユニークな整数として扱われ、「シンボルというオブジェクト」として存在しています。(Symbolクラス)

### シンボルを使う場面

シンボルは一度作ると不変であり、文字列よりもメモリ効率が良いため値の比較が高速です。その性質から、**ハッシュのキーとしてシンボルが使われる**場面が多いです。

### ハッシュのキーにシンボルを使う例

```rb
sample_hash = { :Ruby => "Rails", :PHP => "Laravel" }
```

表記が違うが同じ意味↓

```rb
sample_hash = { Ruby: "Rails", PHP: "Laravel" }
```

[サンプルコード]
```rb
shinsei_b_komachi = [
  { name: "ルビー", age: 16 },
  { name: "かな", age: 17 },
  { name: "MEMちょ", age: 25 }
]

shinsei_b_komachi.each do |member|
  if member[:age] >= 18
    puts "#{member[:name]}は成人です"
  else
    puts "#{member[:name]}は未成年です"
  end
end
```

[サンプルアウトプット]
```console
ルビーは未成年です
かなは未成年です
MEMちょは成人です
```

# クラスベースのオブジェクト指向

モノに注目して現実世界を捉えてプログラムに落とし込むやり方としてオブジェクト指向プログラミング(OOP: Object-Oriented Programming)があります。
「モノの性質(インスタンス変数)」と「モノの操作(メソッド)」を「クラス」という設計図に定義して、クラスをもとに「インスタンス」を生成する手法がクラスベースのオブジェクト指向です。

```rb
# クラス定義
class クラス名
  # attr_accessor[reader/writer] でゲッターやセッターメソッドが楽に作れる

  def initialize # インスタンス生成時に呼ばれるメソッド
    大体はここでインスタンス変数を用意する
  end

  def 何らかのメソッド
  end
end
```

```rb
# インスタンスを生成
変数 = クラス名.new(引数)
```

[サンプルコード]
```rb
class Bird
  def initialize(name)
    @name = name
  end

  def fly
    puts "#{@name}は空へ飛び立つ"
  end

  def eat(food)
    puts "#{@name}は#{food.name}を#{food.quantity}食べた"
  end
end

class Penguin < Bird
  def fly
    puts "#{@name}が空へ飛び立つことは叶わなかった..."
  end
end

class Food
  attr_reader :name, :quantity

  def initialize(name)
    @name = name
    @quantity = 0
  end

  def set_quantity(num)
    @quantity = num.to_i
  end
end

# 実行
crow = Bird.new("カラス")
crow.fly
penguin = Penguin.new("ペンギン")
penguin.fly
fish = Food.new("魚")
fish.set_quantity(23)
penguin.eat(fish)
```

[サンプルアウトプット]
```console
カラスは空へ飛び立つ
ペンギンが空へ飛び立つことは叶わなかった...
ペンギンは魚を23食べた
```

オブジェクト指向を上手く使えるようになると、
- 実行文が簡潔で何をしているかがわかりやすくなる
- コードの保守性・柔軟性が上がる
- 同じようなものを使い回せる
- ある箇所を変更した場合に、その影響範囲が1つの責任内に収まる

などのメリットがあります。
現在は関数型プログラミングの人気が伸びていますが、そうはいっても「オブジェクト指向を知っているし使える」前提で現場は動いているようなので、頑張って使いこなせるようになりたいです。難しい…。

# 例外処理

例外(exception)とは、処理の実行中に発生するエラーのことです。
例外時に出る例外判定をもとに、適切な例外処理を行うことでシステム停止やデータ破損を防ぎましょう。
かの有名な「ぬるぽ」はJavaの「NullPointerException」というnull値の参照型変数を参照しようとした時に出てくる例外から来ています。

### Rubyが用意している例外のクラス

- Exception
    - StandardError: 基本はこの子を補足する
        - ZeroDivisionError
        - ArgumentError
        - NoMethodError
        ほか

`begin` ~ `end`で例外が発生しそうな処理を囲み、`rescue`でエラーを補足し、`rescue`後の部分に例外処理を記述します。

```rb
begin # 例外の予感がしたらbeginで囲む
  何らかの処理
rescue StandardError # 上記処理でStandardErrorが吐き出された場合に例外処理を実行
  例外処理を書く
else
  正常な時の処理を書く
ensure
  正常であろうと例外であろうと実行する処理を書く
end
```

### 変数に例外オブジェクトを割り当てる構文

`rescue`で例外を捕捉した時に、変数`e`に格納する特殊な構文があります。
しばしばエラーメッセージを取り出すのに使われます。

```rb
begin
  # 処理
rescue StandardError => e
  puts "Error: #{e.message}"
end
```

`raise 発生させたい例外のクラス`: 明示的にエラーを出す場合に使えます。

[サンプルコード]
```rb
begin
  puts 1 / 0
rescue StandardError => e
  puts "Error: #{e.message}"
end
#=> Error: divided by 0

begin
raise ArgumentError.new("引数エラーです。")
rescue StandardError => e
  puts "Error: #{e.message}"
end
#=> Error: 引数エラーです。
```

# gem: 外部ライブラリ

Rubyでは、gemという形式で外部ライブラリが提供されており、自分でgemを作って提供することもできます。

gemのインストール
```console
gem install ライブラリ名
```

インストールされているgemの一覧表示
```console
gem list
```

gemのアンインストール
```console
gem uninstall ライブラリ名
```

### Bundlerで依存関係を解消

Bundlerは英語のbundle(束ねる)が由来のgemの依存関係を管理するツールです。
Bundler自身もgemですが、一度Bundlerをインストールすると、その後は主にBundlerを使って他のgemを導入するようになるほどの便利なやつです。

#### Bundlerを使ってgemをインストールする手順

ステップ1. `Gemfile`を作成する

`bundle init`が便利
```console
cd プロジェクトのディレクトリ
bundle init
# 上記コマンドでGemfileが作成されます。
```
ステップ2. Gemfileにインストールしたいgemの名前を文字列(ダブルクォートまたはシングルクォートで囲む書き方)で記述する

```rb
# -- Gemfile --
source "https://rubygems.org" # インストール元を指定

gem "インストールしたいgemの名前" # バージョンを指定しない場合
gem "インストールしたいgemの名前", "~> 4.2.0" # バージョンを指定する場合
```

ステップ3. `bundle install`を実行する

```console
bundle install
# Gemfileに記載されたgemを、Bundlerが一括でインストールする
# この時、バージョンを示すGemfile.lockが生成される
```

#### Bundlerでgemをアンインストールする手順

ステップ1. `bundle exec gem uninstall`を実行する

```console
bundle exec gem uninstall 不要なgemの名前

# バージョン指定版
bundle exec gem uninstall 不要なgemの名前 -v "バージョン名"
```

ステップ2. `Gemfile`から該当部分を削除する(手動で削除)

```rb
-- Gemfile --
# 手動で消せばOK
```

ステップ3. `Gemfile.lock`から削除する(bundle update を実行)

```console
bundle update
# または bundle install でも可
# Gemfile.lock の中身が更新される
```

# デバッグgem: `Ruby/debug`の活用

非常に便利なデバッグ用のgemです。
gemのインストールを実行後に、

#### Rubyコード内で使う

コードの先頭の方に`require "debug"`で読み込みを行い、デバッグコンソールを起動したい場所に`binding.b`を仕込むだけです。

```rb
# Rubyファイル
require "debug"

binding.b # この箇所でデバッグコンソールが起動する
```

デバッグコンソールでは、`n`で次のステップに進んだり、`i`(info)で変数一覧を表示したりできます。

# 静的解析ツールgem: `RuboCop`の活用

gem名`rubocop`: インストール後に、静的解析したいディレクトリ下で`rubocop`コマンドを使うと解析結果が表示されます。
デフォルトの設定はガチガチで厳しいので、プロジェクトのディレクトリ下に`.rubocop.yml`ファイルを作成し、その中に設定を記述すると、RuboCopは設定ファイルを優先するので、程よい加減になります。

```console
cd プロジェクトのディレクトリ
touch .rubocop.yml
# .rubocop.ymlファイルに適切な設定を書き込む

# 解析の実行
rubocop ファイル名
# ファイル名を指定しない場合はディレクトリ下の全ファイルを対象とします。
```

※静的解析ツールのため、あくまで「変な空白がありますよ」とか「この変数はどこからも使われていませんよ」などの「コーディングルールに従っているか」のチェックが中心であり、実行時のエラーをしっかり防いでくれる性質のものではありません。

# 参考

https://www.ruby-lang.org/ja/documentation/

https://www.sbcr.jp/product/4797399844/

https://zenn.dev/mamayukawaii/articles/20240626192527

https://qiita.com/YutoYasunaga/items/98111ef50927ecb4135d