モジュール　[Rubyチェリー本 8章まとめ]

## モジュールの概要

### モジュールの主な用途

> **継承を使わずにクラスにインスタンスメソッドを追加**する、もしくは上書きする（ミックスイン）。
**複数のクラスに対して共通の特異メソッド**（クラスメソッド）を追加する（ミックスイン）。
クラス名や定数名の衝突を防ぐために**名前空間を作る**。
**関数的メソッド**を定義する。
シングルトンオブジェクトのように扱って**設定値などを保持**する。

### モジュールの定義

```ruby
module モジュール名
 # モジュールの定義
end
```

クラスとは異なる点

1. インスタンスを作成することはできない
2. 他のモジュールやクラスを継承することはできない

## ミックスイン - include

Rubyは単一継承を利用している

> コードが重複しているからといって安易に継承を使ったりしてはいけません。「製品はユーザである」または「ユーザは製品である」という関係（isaの関係）が成り立たないのであれば、継承の使用は避けるべきです。

共通の機能は持たせたいが、継承は使うべきでない、そんな時に最適なのがモジュール
モジュールで定義したメソッドが、インスタンスメソッドとして呼び出せるようになります。以下のようにこの機能を扱い、この機能の名前をミックスイン、と言う。

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

class Product
	include Loggable
	def title # logメソッドはLoggableモジュールで定義したメソッド
		log 'title is called.'
		'A great movie'
	end
end
class Use
	include Loggable
	def name # Loggableモジュールのメソッドが使える
		log 'nameiscalled.'
		'Alice'
	end
end
product=Product.new
product.title
# =>[LOG]titleiscalled.
# "A great movie"
user=User.new
user.name
# =>[LOG]name is called.
# "Alice"
```

インスタンスメソッドを`private`にしたい場合は、module内で`private`しておく。

### extend

> extendを使うと、モジュール内のメソッドをそのクラスの特異メソッド（つまりクラスメソッド）にすることができます。

## ミックスインに関してもっと詳しく

### includeされているモジュールを確認する

#### include?(モジュール名)

```ruby
Product.include?(Loggable)#=>true
```

#### include_modules

```ruby
Product.included_modules#=>[Loggable,Kernel]
```

#### ancestors

モジュールだけではなく、スーパークラスの情報も含まれる

```ruby
Product.ancestors#=>[Product,Loggable,Object,Kernel,BasicObject]
```

### include先に特定のメソッドが定義されている前提のモジュール

これはRubyがダックタイピングという考え方で構成されている為に適用される話。

> Productクラスのpriceメソッドと連携して目的の処理を実行する

```ruby
module Taggable
	def price_tag
		"#{price}円" # priceメソッドはinclude先で定義されているはず、という前提
	end
end

class Product
	include Taggable
	def price 
		1000
	end
end

product=Product.new
product.price_tag # =>"1000円"
# 呼び出しているのはmodule内のメソッド、ただそのメソッド内でクラスのメソッドを活用している
```

### 上記の利用方法を採用したRubyの具体的なモジュール

#### Enumerableモジュール

> Enumerableモジュールは配列やハッシュ、範囲（Range）など、何かしらの繰り返し処理ができるクラスにincludeされているモジュールです。

`map`, `select`, `find`, `count`など

> Enumerableモジュールをincludeして、モジュールに定義されたメソッドを使えるようにする条件はたった1つだけです。それはinclude先のクラスでeachメソッドが実装されていることです。Enumerableモジュールのメソッドはいずれもinclude先のクラスに実装されたeachメソッドを使います。

#### Comparableモジュール

> Comparableモジュールのメソッドを使えるようにするための条件は、include先のクラスで<=>演算子を実装しておくことです。

<, ≤, ==, >, ≥など

#### Kernelモジュール

puts, p , print, require, loopなどについて

これらの当たり前に利用していたメソッドが何故これまで利用できていたか？
それは**KernelモジュールをObjectクラスがincludeしているから**。

### トップレベルでは、mainという名前のオブジェクト

Rubyのirbを起動した直後、
またclassやmoduleの定義の外側、これらの場所はどういう扱い？

```ruby
$ irb
irb(main):001:0>ここはどこ？私は誰？

================================================================================

# ここはどこ？誰？
class User
 # Userクラス内
end
```

これらの一番外側の部分を**トップレベル**という。

> トップレベルにはmainという名前のObjectクラスのインスタンスがselfとして存在しています。つまり、「私は誰？」の答えは「Objectクラスのインスタンス」になります。

### モジュール内での、インスタンス変数の扱い

> モジュール内で定義したメソッドの中でインスタンス変数を読み書きすると、include先のクラスのインスタンス変数を読み書きしたことと同じになります。
ただし、モジュールとミックスイン先のクラスでインスタンス変数を共有するのはあまり良い設計ではありません。なぜなら、インスタンス変数は未定義の状態でも自由に代入したり参照したりできるからです。
一方、メソッドであれば未定義のメソッドを呼び出したときにエラーが発生します。
なので、ミックスイン先のクラスと連携する場合は特定のインスタンス変数の存在を前提とするより、特定のメソッドの存在を前提とするほうが安全です。

## モジュールを利用した名前空間の作成

### 名前空間の衝突を防ぐ

> モジュール構文の中にクラス定義を書くと「そのモジュールに属するクラス」という意味になる

```ruby
module Baseball
	class Second
		def initialize(player, uniform_number)
			@player = player
			@uniform_number = uniform_number
		end
	end
end
```

> クラス名の衝突を防止する名前空間として使われています。モジュールに属するクラスを参照する際は“モジュール名::クラス名”のように、::でモジュール名とクラス名を区切ります

```ruby
Baseball::Second.new('Alice', 13)
```

### 名前空間でグループやカテゴリを分ける

> クラスが何十、何百もあるような大きなプログラムになってくると、カテゴリ別にモジュール（名前空間）を作って整理しないと、どれが何のクラスなのかぱっと把握しにくくなるためです。

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

1. 名前空間として利用するモジュールを定義する
2. クラスの定義の際の構文を変更する

```ruby
# moduleを定意義しておく
module Baseball
end
# 名前空間を定義
class Baseall::Second
end
```

## モジュールを単体で使う

### 特異メソッドを定義する

わざわざ他のメソッドに組み込むことなく、直接`モジュール名.メソッド名`とする事で、メソッドを呼び出す事ができる。
ただし、モジュールではインスタンスが作成できない為、「単なる関数の集まり」を作りたいケースに向いている。

また、クラスと同様に定数を定義することもできる。

### module_function

単体の特異メソッドとしての利用、かつモジュールをミックスインとしても利用できるようにするメソッド。

```ruby
module Loggable
	def text
		# 処理
	end
	module_function :log # どちらでも利用可能に
end
```

## 状態を保持する為に、モジュール自身に設定値を持たせる

> クラスインスタンス変数を使って、クラス自身にデータを保持する方法を説明しました。この方法はモジュールでも使うことができます。外部ライブラリ（gem）では、そのライブラリを実行するための設定値（config値）をモジュール自身に保持させたりすることがよくあります。

```ruby
# AwesomeApiという外部ライブラリ
AwesomeApi.base_url # => "http://example.com"
AwesomeApi.debug_mode # => false
```

> インスタンスを作って何か操作する必要がないのであれば、モジュールにしておいたほうがほかの開発者に変な勘違いをさせる心配がありません。
ところで、ライブラリの実行に必要な設定値などはアプリケーション全体で共通の値になることが多いです。
そのため、アプリケーション内ではいつでもどこでも同じ設定値を設定したり、取得したりする必要があります。
そのためには設定値の情報はアプリケーション内で「唯一、1つだけ」の状態になっていることが望ましく、「唯一、1つだけ」のオブジェクトを作る手法のことを、シングルトンパターンと呼びます。

## その他モジュールについて

#### 1.メソッド探索のルール

同名のメソッドを扱う際に、どのモジュールやクラス内のメソッドを参照しているか調べる方法が記載されている

#### 2.moduleにmoduleをinclude

#### 3.prepend

> prependの特徴は同名のメソッドがあったときに、ミックスインしたクラスよりも先にモジュールのメソッドが呼ばれるところです。

この活用方法は、既存のメソッドを置き換える際に活用することができる。具体例を見た方が早い・再読必要

#### 4.refinements - 有効範囲を限定

moduleとclassに跨る形で、それぞれ`refinements`, `using`を利用することで、module内のメソッドの利用可能範囲を限定する事ができる。具体例を見た方が早い・再読必要

## 例題 - deep_freezeメソッドの作成

ここでは基本的なモジュールの理解として、複数のclass内における共通のメソッドの利用、について学んだ。

```ruby:test/deep_freezable_test.rb
require 'minitest/autorun'
require '../lib/team.rb'
require '../lib/bank.rb'

class DeepFreezableTest < Minitest::Test
  def test_deep_freeze_to_array
    assert_equal ['Japan', 'US', 'India'], Team::COUNTRIES
    assert Team::COUNTRIES.frozen?
    assert Team::COUNTRIES.all? { |country| country.frozen? }
  end

  def test_deep_freeze_to_hash
    assert_equal(
      {'Japan' => 'yen', 'US' => 'doller', 'India' => 'rupee'},
      Bank::CURRENCIES,
    )
    assert Bank::CURRENCIES.frozen?
    assert Bank::CURRENCIES.all? { |key, value| key.frozen? && value.frozen? }
  end
end
```

```ruby:lib/deep_freezable.rb
module DeepFreezable
  def deep_freeze(array_or_hash)
    case array_or_hash
    when Array
      array_or_hash.each do |element|
        element.freeze
      end
    when Hash
      array_or_hash.each do |key, value|
        key.freeze
        value.freeze
      end
    end
    array_or_hash.freeze
  end
end
```

```ruby:lib/team.rb
require '../lib/deep_freezable.rb'

class Team
  extend DeepFreezable

  COUNTRIES = deep_freeze(['Japan', 'US', 'India'])
end
```

```ruby:lib/bank.rb
require '../lib/deep_freezable.rb'

class Bank
  extend DeepFreezable

  CURRENCIES = deep_freeze({'Japan' => 'yen', 'US' => 'doller', 'India' => 'rupee'})
end
```