Rubyの凄く面白い特徴をRailsのコードを例に解説

これは[ドリコムAdventCalendar](http://www.adventar.org/calendars/518)の6日目です。  
5日目の記事は、[ドリコムの開発を支えるGitリポジトリ@gussan](http://gussan.hateblo.jp/entry/2014/12/05/%E3%83%89%E3%83%AA%E3%82%B3%E3%83%A0%E3%81%AE%E9%96%8B%E7%99%BA%E3%82%92%E6%94%AF%E3%81%88%E3%82%8BGit%E3%83%AA%E3%83%9D%E3%82%B8%E3%83%88%E3%83%AA)です。
7日目は、[般若心経F*ck、コピペで徳を高める話@おーはらさん](http://qiita.com/ohr486/items/bcd2c0513c82e909c3ef)です。

#### 自己紹介
ドリコムでアプリケーションエンジニアとしてネイティブゲームの開発を行ったりマネージメントをしたりしています。

その他の事はこちら参照:
https://gist.github.com/Shinya131/5d9e604d963177ee2cdc

# Rubyの凄く面白い特徴をRailsのコードを例に解説
## はじめに
この記事は、プログラミング言語Rubyが持つ凄く面白い特徴を、
Ruby on Rails の一部である`ActiveSupport core extensions`のソースコードを題材に解説する物です。

題材に使うActiveSupportは、`version 4.1`です。

## 対象とする読者
この記事は、以下のような読者に役立つ内容を目指して書かれています。
 1. ここ最近rubyを使い始めたエンジニア (例えば新入社員)
 2. 普段rubyは使っていないもののrubyに興味があるエンジニア

## 記事の目的・ゴール
この記事を読んだ人がRubyのプログラミング言語としてのデザインや挙動に「おもしろさ・興味深さ」を感じてもらい、
さらなる学習のきっかけになる事を目的としてます。

あわよくば、この記事を読んだ人が、後に紹介する資料や書籍に興味を持って、
さらなる学習に着手してくれる事がこの記事のゴールです。

## 記事の概要
rubyの凄く面白い特徴である以下の機能について解説します。

 1. **オープンクラス**
 2. **すべてのデータがオブジェクト**
 3. **ダックタイピング**

今はこれらの単語に馴染みがなくても戸惑う必要はありません。  
これらの単語が持つ意味と効果について解説するのがこの記事の主題です。  
> 逆に言うとこれらのコンセプトについて理解している人にとっては物足りない内容と思われます;)

解説には、Ruby on Railsの一部である`ActiveSupport core extensions`と呼ばれるライブラリのソースコードを題材に使います。  
このライブラリはシンプルですが、上に挙げたRubyの面白い特徴がフルに活用されています。

## 第一章:オープンクラス
それでは、最初にオープンクラスについて解説していきます。  
オープンクラスとは一言で言いうと「すでに定義されたクラスの振る舞いを、後から変える事ができる機能」です。

### 例
具体的にはどういう事でしょうか?  
例を示します。

---

クラス`A`を定義します。  
このクラスは`#one`というメソッドを持っています。それ以外に特にメソッドはありません。

```ruby
class A
  def one
    1
  end
end
```

---

`A`のインスタンスを生成し`#one`実行します。`1`が返ってきます。  
次に`#two`を実行します。`#two`などというメソッドは無いので当然エラーになります。  

```ruby
class A
  def one
    1
  end
end

a = A.new
a.one #=> 1
a.two # NoMethodError: undefined method `two'
```

---

では次のように書いたらどうなるでしょうか?  
一般的なプログラミング言語の場合`すでに定義されているclassであるAを再度定義しようとした`といった感じのエラーになりそうですが、  
rubyの場合はエラーになりません。

では、どのような挙動になるかというと、`A`のインスタンスが`#one`と`#two`両方のメソッドに応答できるようになります。

```ruby
class A
  def one
    1
  end
end

class A
  def two
    2
  end
end

a = A.new
a.one #=> 1
a.two #=> 2
```

---

なぜこうなるのでしょうか?  
大変ざっくりした言い方をしてしまうと、このように書いた場合...

```ruby
class A
  def one
    1
  end
end

class A
  def two
    2
  end
end
```

あたかもこのような定義を行ったような挙動を示すのです。

```ruby
class A
  def one
    1
  end

  def two
    2
  end
end
```

このように、**一度定義されたクラスの振る舞いを、後から変える事ができます**。  

> オープンクラスという特性を使うと、メソッドの追加以外にも様々な挙動の変更ができます。  
> しかし、今回はメソッドの追加に絞って説明します。

### オープンクラスの使用例
先ほど、新たに定義したクラス`A`に対してオープンクラスを使って挙動の変更を行いました。  
このような変更は、全てのクラスに対して行う事ができます。

組み込みクラスである `Array`, `Hash`, `Numeric`, `String`などのクラスも例外ではありません。

```ruby
class String
  def one
    1
  end
end

"文字列".one #=> 1 # `String`にメソッドを追加。もちろんこのような拡張は不適切です(；´Д｀)
```

---

#### `ActiveSupport core extensions`
さて、ではここで、`ActiveSupport core extensions`による`String`に対する拡張の1つを題材に
オープンクラスの活用例を見て行きましょう。  

そもそも`ActiveSupport core extensions`とは何なのでしょうか?  
これはWebアプリケーションフレームワークである`Ruby on Rails`を構成するライブラリの1つです。  

このライブラリは、rubyの各種組み込みクラスに対して、さまざまな拡張を行います。  
拡張の目的は、Webサービスを開発するときに良く行われる処理を、よりスムーズに行えるようにする事です。  

#### `String`に対する拡張: `#squish`
`ActiveSupport core extensions`によって`String`に追加される`#squish`を例にオープンクラスについて解説します。  

このメソッドは文字列の冒頭と末尾についた空白を削除します。  
また、複数の連続した空白を1文字にまとめます。

例:

```ruby
# ActiveSupport core extensionsの一部を読み込んで、その機能を有効にしています。
# これ以降の例では省略します。
require 'active_support/core_ext/string/filters.rb'

# Stringに対して、`#squish`を呼び出します。
" \n  foo\n\r \t bar \n".squish
# => "foo bar" # 冒頭と末尾の空白が消える。複数の空白が1文字にまとめられる。
```

ユーザーが入力した文字を処理する時などに活躍しそうですね。  

---

では、このメソッドがどのように実装されているか見て行きましょう。

おもむろに`String`がオープンされメソッドが追加されています。  
まずは、`#squish!`の挙動から読んだ方が分かりやすいでしょう。
これは、`#squish`の破壊的操作版です。

```ruby
# active_support/core_ext/string/filters.rb
class String
  def squish
    dup.squish!
  end

  def squish!
    gsub!(/\A[[:space:]]+/, '')
    gsub!(/[[:space:]]+\z/, '')
    gsub!(/[[:space:]]+/, ' ')
    self
  end
end
```

rubyの場合、レシーバーを明示せずにメソッドを呼び出すと`self`をレシーバーとしてメソッドが実行されます。  
つまり`#squish!`の中で実行されている`#gsub!`は`self.gsub!`と等価です。  

そのため、文字列自身に対して`#gsub!`が呼ばれます。  
文字列自身を破壊的に変更した後、`self`を`return`しています。  

`#squish`の場合は、`self`をコピーした物に対して`#suquish!`を実行して返却しています。  

`String`クラスをオープンして機能が拡張されている様子が分かると思います。  

### 本章のまとめ
 このようにオープンクラスの機能を使うと、すでに定義されたクラスの振る舞いを変更する事ができます。  
 この機能はrubyの組み込みクラス群に対しても使えます。

 これだけだでも面白いですが、これから紹介する2つの特徴と合わせて使うと更に面白い事が出来ます。

# 第2章:すべてのデータがオブジェクト
さて、2章の本題に入る前に少し寄り道をします。  
この章の理解に役立つrubyの組み込み機能についての解説です。

## 準備: 本章の理解に役立つツール
### rubyの事をrubyに聞く
次のコードを見てください。  

```ruby
1.class #=> Fixnum
```

Fixnumのインスタンスに対して、`#class`というメソッドを呼び出しています。  
これは、レシーバーとなったインスタンスのクラスを取得するメソッドです。  
すべてのインスタンスに対して使う事が出来ます。　　

---

さらにこのような事ができます。  
`1#class`の戻り値である`Fixnum`に対して`#superclass`というメソッドを呼び出しています。  
これは、あるクラスの親クラスを取得するメソッドです。  

```ruby
1.class #=> Fixnum
1.class.superclass #=> Integer
```

rubyの場合、プログラムがプログラム自身の情報を豊富に保持しており、簡単に取得する事ができます。  
以上で準備は終わりです。  

本章では、これらのメタ情報を見ながら、「すべてのデータがオブジェクト」という事の意味について解説していきます。  

## すべてのデータがオブジェクト
rubyにおいてはすべてのデータがオブジェクトです。  
メソッドを持たない`ただの値`が無いのです。  

例を見て行きましょう

```ruby
'a'.class  #=> String    # 文字列は文字列型ではありません。Stringクラスのインスタンスです。
1.class    #=> Fixnum    # 数値は数値型ではありません。Fixnumクラスのインスタンスです
```

どのデータも、クラスのインスタンスです。  
だから`#class`メソッドでクラスを取得する事ができます。

---

次の例を見てみましょう。  
rubyではすべてのデータがオブジェクトです。  
そして、rubyの記述のかなりの部分は、インスタンスに対するメソッドの呼び出しです。  

例

```ruby
# これは1(Fixnumのインスタンス)の`#+`メソッドの呼び出しです。2はメソッドの引数です。
1 + 2   #=> 3

# だからこのように書く事もできます。むしろ、上の書き方がシンタックスシュガーなのです。
1.+(2)  #=> 3
```

---

このようなデータもインスタンスです。　

```ruby
nil.class  #=> NilClass  # nilはNilClassクラスのインスタンスです。
true.class #=> TrueClass # trueはTrueClassクラスのインスタンスです。
false.class#=> FalseClass# falseはFalseClassクラスのインスタンスです。
```

---
さらに特徴的な挙動を紹介します。

ここに`String`クラスがあります。  
具体的な文字列では無くクラスです。  
`#new`すれば具体的なインスタンスを産みだす事ができますが、このままではただのクラスです。

```ruby
String
```

```ruby
String.new('a') #=> 'a'
```

さて、この`String`クラスですが、この`String`クラスにも「すべてのデータがオブジェクト」という法則が当てはまります。  
**`String`クラスはオブジェクト** なのです。

クラスがオブジェクトとはどういう事でしょうか?
確かめてみます。

```ruby
String.class #=> Class
```

**クラスである`String`もまた、`Class`クラスのインスタンスなのです**。
**rubyにおけるクラスとは、`Class`クラスのインスタンスなのです**。

---

「rubyにおけるクラスとは、`Class`クラスのインスタンス」  
この特徴を使うことで、
ちょっと面白いことが出来ます。

`Class`に対して`#new`を行うと何が帰ってくるでしょうか?  
もちろん`Class`クラスのインスタンスです。 **つまり、通常のクラスです。**  

なので、このような書き方でクラスを定義する事ができます。

```ruby
A = Class.new
```

これは、この書き方と等価です。

```ruby
class A
end
```

### Classクラスにも親がいる
先ほど登場した`Class`クラスですが、このクラスにも親クラスがあります。

```ruby
Class.superclass #=> Module
```

はい、見慣れた物が出てきました。`Module`です。実は、`Class`は`Module`を継承していたのです。  
これはこれで大変興味深い部分なのですが、本題と関係ないので無視します。  

さらに`Module`の親クラスも調べます。

```ruby
Module.superclass #=> Object
```

出ました。本章で最も重要な概念である`Object`です。

ここまで見てきたように、rubyにおいて全てのデータはインスタンスです。  
そして、全てのインスタンスの元となっているクラス群は、この`Object`を継承しています。  

`Object`にはすべてのインスタンスで共通して使われるメソッドが含まれます。  
例えばインスタンスのコピーを行う`#dup`メソッドなどは、`Object`に含まれます。

この`Object`を継承することで、すべてのインスタンスで、`Object`に定義されたメソッドを使う事ができます。

---

ここまで、rubyの重要な特徴について「すべてのデータがオブジェクト」と言ってきましたが少し言い換えます。  

より重要なのは、  
すべてのデータがインスタンスであり、すべてのインスタンスのクラスが`Object`を継承しているため、  
**「`Object`の振る舞いを変更することで、すべてのインスタンスの振る舞いを変更できる」**
という点なのです。

### 「`Object`の振る舞いを変更できる」の活用例
さて、先ほど上げた特徴ですが、どのように便利なのでしょうか?  
ここで`ActiveSupport core extensions`に定義されている`#try`メソッドを例に見て行きましょう。  

#### `#try`メソッド
`#try`は`#send`に似たメソッドです。

```ruby
# tryとは関係なく普通に`#next`メソッドを呼び出す。
@number = 1
@number.next #=> 2

# tryを使って`#next`メソッドを呼び出します。
@number.try(:next) #=> 2 # メソッド名をsymbolとして渡すと、そのメソッドが実行される。
```

これだけだと`#send`と何が違うのか分かりません。  
ここからが`#send`と違う所です。  

```ruby
# もしnilが入ったら?
@number = nil
#@number.next #=> NoMethodError: undefined method `next' for nil:NilClass # 普通にしたらエラー

@number.try(:next) #=> nil # レシーバーがnilだとnilが返る。
```

「`Fixnum`が入るんだけど、`nil`が入る場合もある変数に対する処理」みたいな物を書く場合に便利です。  

この`#try`メソッドは`Object`をオープンして追加されているメソッドです。  
そのため、あらゆるインスタンスが`#try`に応答する事ができます。

```ruby
# どのインスタンスにも`#try`メソッドがある。
'1'.try(:to_i)          #=> 1
1.try(:to_s)            #=> "1"
[1,2,3].try(:pop)       #=> 3
{a: 1, b: 2}.try(:keys) #=> [:a, :b]

# nilも`#try`メソッドを持っている。何を渡してもnilが返る
nil.try(:to_i) #=> nil
nil.try(:to_s) #=> nil
nil.try(:pop)  #=> nil
nil.try(:keys) #=> nil
```

さらに、新たに定義された未知のクラスにたいしても、この振る舞いが追加されています。

```ruby
class A
  def one
    1
  end
end

A.new.try(:one) #=> 1
```

### `#try`の実装
先ほどまでに解説したtryの実装を見て行きましょう。  

実物の`#try`の実装は、本章のテーマと関わらない部分も多いので、今回は実際の実装では無く大幅に抜粋したコードを使って解説します。

```ruby
class Object
  def try(method_name)
    public_send(method_name)
  end
end

class NillClass
  def try(method_name)
    nil
  end
end
```

本当は、もっと様々な処理がありますが、根本的にはこれだけです。  
たったこれだけのコードで、この章で例に挙げた`#try`の使用例をすべて実現できます。  

処理内容としては、以下のようになっています。

 1. `Object`を変更によって、すべてのインスタンスにtryを定義。
 2. `NillClass`についてだけさらに`#try`を再定義。`Object`で定義された`#try`は、`NilClass`に定義されたメソッドにオーバーライトされ、`nil`だけ独自の挙動を行う。

これも、

- オープンクラス: すべてのクラスは定義後でも振る舞いを変えられる
- すべてのデータがオブジェクト: `Object`の振る舞いを変えることで、すべてのインスタンスの振る舞いを変えられる

というrubyの特徴のおかげです。

### 本章のまとめ
 - `Object`の振る舞いを変えることで、すべてのインスタンスの振る舞いを変えられる。  
 - さらに、継承とオーバーライトを利用して、特定のクラスのインスタンスだけ振る舞いを変える事ができる。  

組わせて`#try`のようなメソッドの実現が可能になっている。

# 第3章:ダックタイピング
最後にダックタイピングについて解説します。  
これは、「あるインスタンスのクラスが何であろうと、必要なメソッドに応答さえ出来れば処理を行える」という特徴です。  

## `Object#blank?`
早速ですが例に入っていきます。  
今回解説に使うのは`ActiveSuppor`の`#blank?`メソッドです。  

これは「空っぽいインスタンスに対して呼び出すと`true`を返すメソッド」です。

```ruby
[].blank?     #=> true # 空っぽい
{}.blank?     #=> true # 空っぽい

[1].blank?    #=> false
{a: 1}.blank? #=> false

''.blank?     #=> true # 空っぽい
'1'.blank?    #=> false

nil.blank?    #=> true # 空っぽい
true.blank?   #=> false
false.blank?  #=> true # 空っぽい
```

## `#blank?`の実装
さて、多少端折っていますが、`#blank?`の実装は次の通りです。

```ruby
class Object
  def blank?
    respond_to?(:empty?) ? !!empty? : !self
  end
end

class NilClass
  def blank?
    true
  end
end

class FalseClass
  def blank?
    true
  end
end

class TrueClass
  def blank?
    false
  end
end

class Array
  alias_method :blank?, :empty?
end

class Hash
  alias_method :blank?, :empty?
end

class String
  BLANK_RE = /\A[[:space:]]*\z/
  def blank?
    BLANK_RE === self
  end
end

class Numeric
  # No number is blank
  def blank?
    false
  end
end
```

基本的には前の章で見た、`#try`の実装と似ています。

 - `Object`の振る舞いを変えることで、すべてのインスタンスの振る舞いを変える
 - 継承とオーバーライトを利用して、特定のクラスのインスタンスだけ振る舞いを変える。

このような特徴を利用しています。

注目して欲しいのはこの部分です。

```ruby
class Object
  def blank?
    respond_to?(:empty?) ? !!empty? : !self
  end
end
```

`self`に対して、`#respond_to?(:empty?)`というメソッドを呼び出し、  
結果が`true`なら`#empty?`の結果を`#blank?`の結果として返しています。  

この`#respond_to?`とはどんなメソッドなのでしょうか?  
これは、「あるインスタンスが、引数のメソッドに応答できるかどうかを返却するメソッド」です。  

例

```ruby
# レシーバーは#empty?に応答可能
[].respond_to?(:empty?)      #=> true
[1,2,3].respond_to?(:empty?) #=> true

''.respond_to?(:empty?)      #=> true
'a'.respond_to?(:empty?)     #=> true

# 実際呼び出せる
[].empty?      #=> true
[1,2,3].empty? #=> false

''.empty?      #=> true
'a'.empty?     #=> false

# レシーバーは#empty?に応答できない
1.respond_to?(:empty?) #=> false

# 実際呼び出せない
1.empty?               # NoMethodError
```

`#blank?`はこの`respond_to?`を使う事によって、

 - `#empty?`に応答できるインスタンスの場合はそれを使う
 - 応答できないインスタンスの場合は、また別の処理を行う

という挙動を実現しているのです。

そのため、この`#blank?`は全く未知のクラスに対しても「`#empty?`に応答できるインスタンスの場合はそれを使う」という挙動を示すことが出来ます。

```ruby
class EmptyClass
  def empty?
    true
  end
end

EmptyClass.new.empty? #=> true
EmptyClass.new.blank? #=> true
```

このように、rubyでは、
そのインスタンスのクラスの継承関係がどうなっていようと、ある特定のメソッドにさえ応答できれば、レシーバーとして扱えるようになっています。

この機能を「ダックタイピング」と呼び、以下のような言葉で比喩されます。  

**「もしもそれがアヒルのように歩き、アヒルのように鳴くのなら、それはアヒルである」**  
(たとえ実際にはロボットだろうが、鴨だろうが)

## 本章のまとめ
 - rubyではインスタンスのクラスがどのような継承関係をもっていても、ある特定のメソッドに応答できさえすればレシーバーとして振る舞うような処理がかける。(ダックタイピング)

# まとめ: 全体を通して
本記事では、冒頭で紹介rubyが持つ面白い3つの特徴について解説してきました。
### オープンクラス
 - すでに定義されたクラスの振る舞いを、後から変える事ができる。
 - この機能は、rubyの組み込みクラス群を含めたすべてのクラスに対して使える。

### すべてが`Object`
 - rubyにおけるすべてのデータはインスタンス。
 - そして、すべてのインスタンスのクラスは`Object`を継承している。
 - そのため、`Object`の振る舞いを変えることで、すべてのインスタンスの振る舞いを変更できる。

### ダックタイピング
 - あるインスタンスのクラスがどのような継承関係をもっていても、
 ある特定のメソッドに応答できさえすればレシーバーとして振る舞うような処理が記述できる。

---

これらの3つの特徴は個々につかっても面白い事ができますが、組み合わせて使うことでより強力な機能を実現しています。  
例えば、`Active Support core extensions`は、
ライブラリがが実装された時点では、ライブラリの利用者がどのような継承ツリーを持った、どんなクラス群を作るか全く不明なのに、あたかも言語仕様自体を拡張したかのような機能追加を実現しています。

しかもこれが、言語仕様の拡張専用の文法では無く、普通のrubyコードの記述で実現されています。  
この柔軟性は凄く面白く魅力的なものだと思います。  

---

以上で解説は終わりです。  
いかがでしたでしょうか? rubyに対して興味を深めて頂けましたか?  

もし、興味を深めて頂いたようでしたら次のステップへ行きましょう。

# 次のステップ
### 書籍を読む
今回この記事で解説したような、内容がより詳細に、正確に、幅広く解説されている書籍です。
#### はじめてのRuby
http://www.amazon.co.jp/%E5%88%9D%E3%82%81%E3%81%A6%E3%81%AERuby-Yugui/dp/4873113679

#### メタプログラミングRuby
http://www.amazon.co.jp/%E3%83%A1%E3%82%BF%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0Ruby-Paolo-Perrotta/dp/4048687158

### Rails Guidesを読む
例題として扱った、`Active Support`の公式解説サイトです。
主要な機能が大変丁寧に解説されています。
http://railsguides.jp/active_support_core_extensions.html

また、このサイトの章立ては、`Active Support`のソースコードとほとんど一対一で組み立てられており、`Active Support`のソースコードをリーディングする時の資料として大変参考になります。

`Active Support core extensions`の全てのソースコードはここから見ることが出来ます。
https://github.com/rails/rails/tree/master/activesupport