Meta-2章-オブジェクトモデル


![](https://ruby.tw/logo.gif)

## オープンクラス
* 標準クラスにmethod追加できる

```ruby
require 'test/unit'

def to_alphanumeric(s)
    s.gsub /[^\w\s]/, '' # [a-zA-Z0-9_空白]以外
end

class ToAlphanumericTest < Test::Unit::TestCase
    def test_steps_non_alphanumeric_characters
        assert_equal '3 the Magic Number', to_alphanumeric('#3, the ***Magic, Number*?')
    end
end
```
```ruby
require 'test/unit'

class String
    def to_alphanumeric
        gsub /[^\w\s]/, ''
    end
end

class ToAlphanumericTest < Test::Unit::TestCase
    def test_steps_non_alphanumeric_characters
        assert_equal '3 the Magic Number', '#3, the ***Magic, Number*?'.to_alphanumeric
    end
end
```

* method検索

```ruby
[].methods.grep /^re/
# [:repeated_permutation, :repeated_combination, :reject, :reverse_each, :replace, :reverse, :reverse!, :reject!, :reduce, :remove_instance_variable, :respond_to?]

"".methods.grep /^re/
[:replace, :reverse, :reverse!, :remove_instance_variable, :respond_to?]
```

## Objectとinstance変数
* objectのinstance変数はobjectにある(@vはそれぞれのobjectにある)
* objectのmethodはclassにある(my_methodはMyClassにある)
* instance変数は値が代入された時に初めて出現する

```ruby
class MyClass
	def my_method(v)
		@v = v
	end
end

obj1 = MyClass.new.my_method(5)
obj2 = MyClass.new.my_method(7)
obj3 = MyClass.new
obj3.instance_variables 
# []
obj3.my_method
obj3.instance_variables 
# [:@v]
``` 

## オブジェクトはinstance変数とclassへのリンクで構成されている

## オブジェクトのmethodはclassに住んでいる。classから見ればinstance methodと呼ばれる

## classはClassクラスのオブジェクト。クラス名は単なる定数。

## ClassはModuleのサブクラス
* module: methodをまとめたもの
* class: newしたり、superclassで階層構造を作れる

## moduleやclassの名前がディレクトリ、定数がファイルのような感じ
```ruby
 module MyModule
	 MyConstant = 'out 定数'
	 class MyClass
		 MyConstant = 'innner 定数'
	 end
 end
 
MyModule::MyConstant
# "out 定数"
MyModule::MyClass::MyConstant
# "inner 定数"
```

## require, load method
require: ファイルを一度だけ読み込む
load: 呼び出すたびにファイルを実行する

```ruby
require './a.rb'

# loadは定数が変数を汚染する、防ぐためにtrueオプション
load('a.rb', true)
```

## classはBasicObjectまでの継承チェーンを持っている

## method探索
* レシーバーのクラスに入り、メソッドを見つけるまで継承チェーンを上る
* レシーバ: `obj.my_method`の場合`obj`がレシーバ
* 継承チェーン: クラスをsuperclassに向かってたどる

```ruby
class MyClass
	def my_method
		@v = 1
	end
end

class SubClass < MyClass
end

obj = SubClass.new
obj.my_method
SubClass.ancestors
# SubClass, MyClass, Object, Kernel, BasicObject]
```

## classにmoduleをinclude, prepend
* include: includeするclassの真上に入る
* prepend: prependするclassの真下に入る

```ruby
module M1
	def my_method
		@v = 1
	end
end

class C 
	include M1
end

class C1
	prepend M1
end

class D < C
end

D.ancestors
# [D, C, M1, Object, Kernel, BasicObject]

class E < C1
end

E.ancestors
# [E, M1, C1, Object, Kernel, BasicObject]

```

## moduleがすでにチェーンに含まれていたら2回目は無視する
1. M3にM1がprepend [M1, M3]
2. M3にM2がinclude [M1, M3, M2]
3. M2にM1がincludeだが、M1はすでにチェーンにあるため無視

```ruby
module M1; end
module M2
	include M1
end
module M3
	prepend M1
	include M2
end

M3.ancestors
# [M1, M3, M2]
```

## Kernel
* どこからでも呼び出せるprint methodはObject classがKernel moduleをincludesしているから
* Kernelにmethodを追加すればどこからでも使えるようになる


```ruby
require 'awesome_print'

local_time = { :city => "Roma", :now => Time.now }
ap local_time, :indent => 2

#{
#  :city => "Roma",
#   :now => 2017-03-29 21:32:45 +0900
#}
# awesome_printがKernelにap methodを追加している
```

## methodを呼び出すときはレシーバがselfになる
* コードはcurrent object内部(self)で実行される
* methodを呼び出すときはmethodのレシーバがself
* その時点から全てのinstance変数はselfのinstance変数になり、レシーバを明示しないmethod呼び出しは全てselfに対する呼び出しになる
* 最後にmethodのレシーバになったobjectを追いかけると流れがわかりやすい

```ruby
class MyClass
	def testing_self
		@var = 10
		my_method
		self
		puts @var
	end

	def my_method
		@var = @var + 1
	end
end

obj = MyClass.new
obj.testing_self
# #<MyClass:0x007f83542e7cd0 @var=11>
# testing_selfを呼び出すとレシーバであるobjがselfになる
# そのためinstance変数@varはobjのinstance変数となる
# レシーバを明示していないmy_methodの呼び出しもobjに対するものとなる
# my_method時もselfはobjなので@varはobjのinstance変数のまま
# よって、@varは+1される
```

## トップレベル
* methodを呼び出してない、または呼び出したmethodが全て戻った時のselfはmain


```ruby
self # main
self.class # Object
```

## Refinements
* クラスのコードにパッチを当てて、通常のメソッド探索をオーバーライドする感じ
* Refinementsはusingを呼び出したところから、ファイルやモジュールの定義が終わるところまでの限られた部分でのみ有効になる

```ruby
module StringExtensions
	refine String do
		def to_alphanumeric
			gsub(/[^\w\s]/, '')
		end
	end
end

"my *1st* refinement!".to_alphanumeric
# NoMethodError
using StringExtensions
"my *1st* refinement!".to_alphanumeric
# "my 1st refinement!"
```

```ruby
module StringExtensions
	refine String do
		def reverse
			"esrever"
		end
	end
end

module StringStuff
	using StringExtensions
	"my *1st* refinement!".reverse
	# "esrever"
end

"my *1st* refinement!".reverse
# "!tnemenifer *ts1* ym"
```

## Documentのprintを実行したい場合
1. どちらかのprintの名前を変える
2. includeの順番を変える

```ruby
module Printable
	def print
		puts 1
	end	
	def prepare_cover
		puts 2
	end
end

module Document
	def print_to_screen
		prepare_cover # 2 Printableで発見
		format_for_screen # 3 Documentで発見
		print # 1
	end	
	def format_for_screen
		puts 3
	end	
	def print
		puts 4
	end
end

class Book
	include Document # Bookの真上に入る
	include Printable # Bookの真上に入る
	puts 5
end

# Book > Printable > Document > Object > Kernel > Basic Object


b = Book.new
b.print_to_screen
# 5 2 3 1

Book.ancestors
# [Book, Printable, Document, Object, Kernel, 
BasicObject]

b.method(:print).owner
# Printable
```

## Ctrl + L
pry画面クリア