[Ruby]おすすめ標準添付ライブラリ！

# はじめに
Rubyには、標準添付ライブラリというものがあります。これは、Rubyをより便利に活用出来るツールのことです。
これらを活用出来ることで、自分のプログラミングスキルの幅が広がり、メリットしかありませんので、日々の開発のぜひ参考にしてみてください！


## ファイル/ディレクトリ操作を極める

### Pathname
ファイルパスを表現するクラス。
※Fileクラスと同様のメソッドが使用可能。([こちら](https://qiita.com/ren0826jam/items/24d28839d307ffa825fc#iofile)の記事を参照)

```ruby
require 'pathname'

# Pathnameクラスの生成
Pathname.new('/tmp') #=> #<Pathname:/tmp>
Pathname('/tmp') #=> #<Pathname:/tmp>

home = Pathname.new('~')

# 連結する
foo = home.join('foo.txt') #=> #<Pathname:~/foo.txt>

# 部分的なパスを得る
foo.dirname #=> #<Pathname:~>
foo.basename #=> #<Pathname:foo.txt>
foo.expand_path #=> #<Pathname:/Users/tanaka/foo.txt>
foo.expand_path.parent #=> #<Pathname:/Users/tanaka>
foo.expand_path.parent.parent #=> #<Pathname:/Users>

# 文字列に変換する
foo.to_path #=> "~/foo.txt"
foo.to_s #=> "~/foo.txt"
```

### fileutils
ファイル操作を行うための処理を集めたモジュール。

```ruby
require 'fileutils'

# ファイルを再帰的にコピーする(第1引数にコピー元のファイル、第2引数にコピー先のディレクトリ)
FileUtils.cp_r('src/dir', 'dest/dir')

# コピー元を配列で指定
FileUtils.cp_r(%w(one two three), 'dest/dir')

# ファイル所有者、グループの変更
FileUtils.chown('tanaka', 'staff', 'dir')
FileUtils.chown_R('tanaka', 'staff', 'dir')

# パーミッションの変更
FileUtils.chmod(0700, 'dir')
FileUtils.chmod_R(0700, 'dir')

# ディレクトリの作成
FileUtils.mkdir('foo')
FileUtils.mkdir_p('foo/bar/baz')

# ファイルやディレクトリの移動
FileUtils.mv('foo', 'bar')

# ファイルやディレクトリの削除
FileUtils.rm_r('dir')

# ハードリンクの作成
FileUtils.ln('src', 'dest')

# シンボリックリンクの作成
FileUtils.ln_s('src', 'dest')
```

#### FileUtilsのメソッドで使用されるオプション一覧

| オプション名 | 意味 |
|:-:|:-:|
| :dereference_root  | trueだとシンボリックの指す内容コピー、falseはシンボリック自体コピー  |
| :force  | trueだとStandardErrorを無視する  |
| :nocreate  | trueだとファイルを作成しない  |
| :noop  | trueだと実際の処理を行わない  |
| :mtime  | 最終時刻をTimeか秒数で指定  |
| :mode  | パーミッションを８進数で指定  |
| :preserve  | trueだと更新時刻もコピー  |
| :remove_destination | trueだとコピーを行う前に、コピー先を削除  |
| :secure  | trueだとファイルの削除にFileUtils.#remove_entry_secureを使用  |
| :verbose  | trueだと詳細を出力  |

### tmpdir
一時的な作業ディレクトリを作成出来ます。

```ruby
require 'tmpdir'

# 一時ディレクトリの絶対パスを取得
Dir.tmpdir #=> "/var/folders/4h/..."

# 一時ディレクトリを作成してファイルパスを取得
dir = Dir.mktmpdir  do |dir|
  File.directory?(dir) #=> true
  dir #=>  "/var/folders/4h/..."
end

# 第1引数にディレクトリ名のプリフィックス、第2引数に親ディレクトリを指定出来る
dir = Dir.mktmpdir('prefix', '/tmp/sample')  do |dir|
  File.directory? #=> true
  dir #=>  "/tmp/sample/prefix..."
end
```

### tmpfile
一時ファイルを作成して操作する事ができます。
※Fileクラスと同様のメソッドが使用可能。([こちら](https://qiita.com/ren0826jam/items/24d28839d307ffa825fc#iofile)の記事を参照)

```ruby
require 'tmpfile'

# 第1引数にディレクトリ名のプリフィックス、第2引数に親ディレクトリを指定出来る
path = Tempfile.open('prefix', '/tmp/sample') do |f|
  f.puts 'foobar'
  f.path
end

path #=> "/tmp/sample/..."
File.read(path) #=> "foobar\n"

# Tempfileをopenする
tmpfile = Tempfile.open('prefix', '/tmp/sample') #=> #<File:/tmp/sample/...>
tmpfile.path #=> "/tmp/sample/..."
tmpfile.close
tmpfile.close! # 再オープンできない
```

## 特定のフォーマットのテキストデータを扱う

### YAML
構造化されたデータをプレーンな文字列で表現するためのデータ形式の1つです。
文字列・整数・浮動小数点・真偽値・nil・配列・ハッシュなどを表現できます！

```yml
require 'yaml'

yaml_string = <<EOS
---
remote:
  host:localhost
  port: 22
  ssh: true
  username: user
  password: null
EOS

# YAMLをオブジェクトとしてロード
p setting = YAML.load(yaml_string) #=> {"remote"=>{"host"=>"localhost", ...}

# 内容の変更
setting['remote'].update(host: 'example.com'

# オブジェクトをYAMLとしてダンプ
p YAML.dump(setting) #=> "---\nremote:\n host: example.com\n ..."

内容をYAMLとしてダンプ
p setting.to_yaml #=> "---\nremote:\n host: example.com\n ..."

```

### JSON
YAML同様、人気のあるデータのダンプ形式の1つです。
こちらは、文字列・整数・浮動小数点・真偽値・nil・ハッシュなどを表現できます！

```json
require 'json'

data = [1, 2.0, {key: 'value'}, nil, true, false]

# JSONをダンプする
json = data.to_json #=> "[1,2.0,{\"key\": \"value\"},null,true,false]"

# ロードする
p JSON.load(json) #=> [1, 2.0, {key: 'value'}, nil, true, false]

# TimeオブジェクトをJSONにダンプ
Time.now.to_json #=> "\"2021-05-14 12:34:56 +0900\""

# RegexpオブジェクトをJSONにダンプ
/ruby[0-9]/.to_json #=> ""\"(?-mix:ruby[0-9])\""

# ObjectオブジェクトをJSONにダンプ
Object.new.to_json #=> "{}"

# RangeオブジェクトをJSONにダンプ
(1..10).to_json #=> "\"1..10\""

# SymbolオブジェクトをJSONにダンプ
:symbol.to_json #=> "\"symbol\""
```

### CSV
コンマと改行で区切ることで表現する形式です。

```ruby
require 'csv'

# 配列をCSV形式の文字列に変換
['foo', 'bar', 'baz'].to_csv #=> "foo,bar,baz"

# CSV形式の文字列を配列に変換
'foo,bar,baz'.parse_csv #=> ["foo", "bar", "baz"]

# 複数行のCSVをparseする

data = <<EOS
foo,bar,baz
hoge,fuga,hoge
EOS

CSV.parse(data) do |row|
  puts row.join('|') #=> "foo|bar|..."
end

# ヘッダを指定する
data = <<EOS
name,age,gender
tanaka,21,man
yoko,32,woman
sato,44,man
EOS

CSV.parse(data, headers: :first_row) do |row|
  # ヘッダ名をキーにしてアクセス可能
  name = row['name']
  age, gender = row.values_at('age', 'gender')

  puts "#{name} is #{gender} (#{age})" #=> "tanaka is man (21)", ...
end

# CSVを書き出し
headers = %w(name age gender)
people = [
  ['tanaka', 21, 'man'],
  ['yoko', 32, 'woman'],
  ['sato', 44, 'man']
]

csv_str = CSV.generate('', write_headers: true, headers: headers) {|csv|
  people.each do |person|
    csv << person
  end
}

puts csv_str #=> name,age,gender,\ntanaka,21,man,...
```

#### CSV.parseに指定出来るオプション

| オプション名 | 意味 |
|:-:|:-:|
| :col_sep |  フィールド間のセパレータとして使用する文字列 |
| :row_sep  | レコード間のセパレータとして使用する文字列  |
| :quote_char  | シングルクォーとでもフォイールドを囲めるようになる  |
| :encoding  | エンコーディングを指定  |
| :unconverted_fields  | 変換前の行を配列で得ることができる  |
| :headers  | 1行目をヘッダーとみなす  |
| :return_headers  | ヘッダとみなした1行目もCSVとして読み込む  |
| :headers_converters  | ヘッダ専用のコンバータをProcオブジェクトまたはシンボルを指定  |
| :skip_blanks  | 空行を飛ばす  |
| :force_quotes  | フィールドの生成時に必ずクォートする  |
| :write_headers  | ヘッダを出力  |

## 上級者のための文字列オブジェクト
本来IOではない文字列をIOオブジェクトの代わりに用いることができます。

```ruby
require 'stringio'

def read_upcase(io)
  io.read.upcase
end

io = open('|uname')
stringio = StringIO.new('tanaka')

p read_upcase(io) #=> "DARWIN\n"
p read_upcase(stringio) #=> "TANAKA"
```

## 集合を扱う
重複の内容そのコレクションを扱うことができるクラスです。
Setを用いることで、集合の比較や要素の追加などを高速で行うことができます。

```ruby
require 'set'

# Enumerable#to_set
(1..5)..to_set #=> #<Set: {1,2,3,4,5}>

# Set.new
Set[1, 3, 5, 7] #=> #<Set: {1, 3, 5, 7}>
Set.new(%w(tanaka sato suzuki)) #=> #<Set: {"tanaka", "sato", "suzuki"}>

# 長さを返す
set = Set[1, 3, 5, 7]
set.length #=> 3

# 空にする
set = Set[1, 3, 5, 7]
set.clear #=> #<Set: {}>

# 空か確認
set = Set[1, 3, 5, 7]
set.empty? #=> false

# 要素の追加
set = Set[1, 3, 5, 7]
set << 10 #=> #<Set: {1, 3, 5, 7, 10}>

# 要素の削除
set = Set[1, 3, 5, 7, 10]
set.delete(10) #=> #<Set: {1, 3, 5, 7}>

# 要素が存在しなければ追加し、あれば何もしない
set = Set[1, 3, 5, 7]
set.add?(9) #=> #<Set: {1, 3, 5, 7, 9}>
set.add?(1) #=> nil

# 要素が存在すれば削除し、なければ何もしない
set = Set[1, 3, 5, 7]
set.delete(1) #=> #<Set: {3, 5, 7, 9}>
set.delete(100) #=> nil

# Setの連結
set = Set[1, 3, 5, 7]
set.merge(Set[11, 12, 13]) #=> #<Set: {1, 3, 5, 7, 9, 11, 12, 13}>

# 要素をまとめて削除
set = Set[ 1, 3, 5, 7]
set.subtract(1, 3) #=> #<Set: {5, 7}>

# 真となった要素を削除
## 何か削除されれば自信を返す
set = Set[0, 1, 2, 3, 4, 5, 6, 7]
set.reject! {|v| v.zero? } #=> #<Set{1, 2, 3, 4, 5, 6, 7}>

## 常に自身を返す
set.delete_if {|v| v.even? } #=> #<Set{1, 3, 5, 7}>

# 新たな集合を作る
a = %w(a b c).to_set
b = %w(c d e f).to_set

## どちらかに含まれる要素の集合(和集合)
p a | b #=> #<Set: {"a", "b", "c", "d", "e", "f"}>

## 両方に含まれる要素のみ集合(積集合)
p a & b #=> #<Set: {"c"}>

## 片方だけに含まれている集合(対称差)
p a ^ b  #=> #<Set: {"a", "b", "d". "e", "f"}>

## aの中でbに含まれない要素の集合(差集合)
p a - b  #=> #<Set: {"a", "b"}>
```

## 日付と時刻をもっと詳しく！

### Time

```ruby
require 'time'

# 時刻を表す文字列をTimeオブジェクトに変換
time = Time.parse('2021-05-14 12:34') #=> 2021-05-14 12:34:00 +9000

# 各種フォーマットで表現された文字列を返す
time.iso8601 #=> "2021-05-14T12:34:00+09:00"
time.httpdate #=> "Fri, 14 May 2021 03:34:00 GMT"
time.rfc2822 #=> "Fri, 14 May 2021 12:34:00 +0900"

# 第1引数に時刻の文字列、第2引数にフォーマットを指定して、Timeオブジェクトを生成
Time.strptime('2021年5月14日', '%Y年%m月%d日') #=> 2021-05-14 00:00:00 +0900
```

### Date/DateTime
日付を表現するクラス。

```ruby
require 'date'

# 年月日からDateオブジェクトを得る
date = Date.new(2021, 5, 14)

# 日付を指定した書式の文字列で表現
date.strftime('%Y年%m月%d日') #=> "2021年05月14日"

# 妥当な日付か真偽値で確認
Date.valid_date?(2000, 1, 1) #=> false

# うるう年か真偽値で確認
Date.leap?(2004) #=> true

# 今日の日付を取得
Date.today #=> Fri, 14 May 2021

# 年月日を確認
date.year #=> 2021
date.month #=> 5
date.day #=> 14

# TImeオブジェクトからDateTimeオブジェクトを作成
Time.new.to_date #=> Fri, 14 May 2021
```

# おわりに
いくつかのライブラリを紹介しましたが、かなり便利な機能があったんではないでしょうか？
標準添付ライブラリを味方につけてRubyマスターになりましょう！