うぃろぅです。
少し間が空いてしまいましたが今日も本を読んでいきます。
暑さに負けるな。
今日の本
- 作者: Paolo Perrotta,角征典
- 出版社/メーカー: オライリージャパン
- 発売日: 2015/10/10
- メディア: 大型本
- この商品を含むブログ (3件) を見る
Rubyの基本を理解したら読むといいらしい。
具体的には「ループ処理をeach
で書けるなら読める」と書いてある。ほんとぉ??
構成
Rubyの各機能についてのことが1章ずつ、その後Railsについて記載されている。
1章 頭文字M
多分「イニシャルエム」と読むと思う。夏は豆腐が美味しい。
メタプログラミングとは何かについてのイントロ。
「コードを書くコード」をプログラミングするのがメタプログラミングとしている。
「実行時に定義されるメソッド」とか「実行時に定義されるクラス」とかそういったものをこれから学習していくよ、というような内容となっている。
2章 オブジェクトモデル
オブジェクトモデルの内部
Rubyではクラスもメソッドもオブジェクトである。
また、すでに定義されているクラスにメソッドや変数を追加することもできる。自由度の鬼。
ということは、自分しか使わないと思って定義したクラス名が他のgemのクラス名と被るということが起き得る。
そのため、名前の衝突を回避するために
module MyModule class MyClass (何か処理) end end
こんな感じでクラスをネームスペースにラップすると良い。
メソッド探索
メソッド探索は「右へ一歩、それから上へ」となっている。
インスタンスから自分のクラスに一歩動き、それから繼承ツリーを上へ上へと登っていくイメージ。
[1] pry(main)> module M1; end => nil [2] pry(main)> module M2 [2] pry(main)* include M1 [2] pry(main)* end => M2 [3] pry(main)> module M3 [3] pry(main)* prepend M1 [3] pry(main)* include M2 [3] pry(main)* end => M3 [4] pry(main)> M3.ancestors => [M1, M3, M2]
また、同じモジュールを2回以上挿入しても読み込むのは最初の1回だけ。かしこめ。
3章 メソッド
動的メソッド
Module#define_method
を使えば動的にメソッドが生成できる。
[1] pry(main)> class MyClass [1] pry(main)* define_method :my_method do |my_args| [1] pry(main)* my_args * 3 [1] pry(main)* end [1] pry(main)* end => :my_method [2] pry(main)> obj = MyClass.new => #<MyClass:0x00007fe2feed0ba0> [3] pry(main)> obj.my_method(2) => 6
上記の例だとmy_method
がメソッド名、my_args
が引数。
my_method
の箇所を変数に変えればメソッド名は自由自在。
メソッド呼び出しもsend
を使えば文字列で呼べる。ちょっと自由度高すぎんよー。
ゴーストメソッド
さらに「この名前で定義してね」とお願いするだけではなく「知らないメソッド名がきたらこの処理をしてね」ということもできる。
method_missing
をオーバーライドすることでメソッド名が存在しない場合の処理を記載できる。
これを使う場合、respond_to_missing?
メソッドもオーバーライドしてゴーストメソッドがrespond_to?
に正しい値を返すようにするのがルール。ただ暗黙のルールとなっているため動的メソッドで済む場合はそちらを使った方がバグが入ってきづらい。
4章 ブロック
ブロックはクロージャ
スコープは
- クラス定義
- モジュール定義
- メソッド
という単位で切り替わる。
class Hoge
やdef fuga
という記述によってスコープが切り替わるため、ここをClass.new
やdefine_method
と書くことでスコープが切り替わらなくなる。
キーワードによってスコープが切り替わると覚えておくとわかりやすい。
スコープが切り替わるキーワードをスコープゲートと呼び、スコープゲートを越えたいときにブロックが使われる。
なのでブロックはクロージャである、と。
呼び出し可能オブジェクト
ブロックはオブジェクトではない。
ブロックをオブジェクトにしたものはProc
である。
Proc.new
にブロックを渡してProc
オブジェクトを生成し、Proc#call
を呼び出すことでブロックを評価する。
他には
- lambda
- proc
- ->(矢印ラムダ)
p ->(x) { x + 1 }
のような書き方になる
がある。
この辺りのことに関しては以下の記事が非常に詳しい。
とても助かる。
呼び出し可能オブジェクトとは、「評価ができて、スコープを持ち運べるコード」のことである。
呼び出し可能オブジェクトになれるのは以下。
- ブロック
- Proc
- lambda
- メソッド
DSLについては付録で詳しく学習することとする。
5章 クラス定義
オブジェクト指向といえばクラス、クラスといえばオブジェクト指向というくらい切っては切れないクラス定義について。
Rubyの場合、クラス定義にはあらゆるコードを記述できる。
class Hoge puts "こんにちは" end
これでも動く。
動的にクラスを宣言する
class Hoge
のようにclass
キーワードを使わないでクラスを宣言してみる。
[1] pry(main)> c = Class.new(Array) do [1] pry(main)* def my_method [1] pry(main)* "Hello!" [1] pry(main)* end [1] pry(main)* end => #<Class:0x00007faea61c9e20>
Class.new
でOK。引数は継承元。
ただしこれだと無名クラスであるため、他の変数で使い回せない。
ではどうすればいいかというと
[2] pry(main)> Hello = c => Hello
これでmy_method
を持ったHello
クラスということになる。便利。
[5] pry(main)> k = Hello.new => [] [6] pry(main)> k.my_method => "Hello!"
これでインスタンスも生成できると。
特異メソッド
特定のインスタンスにのみメソッドを追加することもできる。これが特異メソッド。
こう書くと何か特別なメソッドなのかと考えるかもしれないが、何の事は無い。
クラスメソッドはクラスの特異メソッドであるから使ったことはある。
def obj.a_singleton_method; end def MyClass.another_class_method; end
見た目からして同じに見える。
これを利用したものがアクセサ。
class Hoge attr_accessor :my_attribute end
少し違うけれどざっくりプロパティみたいな。getter
とsetter
の代わりと考えると覚えやすい。
このようなメソッドをクラスマクロと呼ぶらしい。
特異クラス
メソッドときたらクラス。
特異メソッドを宣言したとして、そのメソッドはどこに存在するのか。それがこの特異クラスとなる。
1] pry(main)> obj = Object.new => #<Object:0x00007f8c3c6d1648> [2] pry(main)> singleton_class = class << obj [2] pry(main)* self [2] pry(main)* end => #<Class:#<Object:0x00007f8c3c6d1648>> [3] pry(main)> singleton_class.class => Class [4] pry(main)> "abc".singleton_class => #<Class:#<String:0x00007f8c3c899200>>
class <<
やObject#singleton_class
で確認することができる。
インスタンスを一つしか持てないため、シングルトンクラスとも呼ばれる。
特異クラスはメソッド探索時に自分自身のクラスより先に探索される。オーバーライドした場合はそちらが先。
オブジェクトモデルの7つのルール
- オブジェクトは1種類のみ: 通常のオブジェクトかモジュールになる
- モジュールは1種類のみ: 通常のモジュールかクラス、特異クラスのいずれかになる
- メソッドは1種類のみ: モジュールに住んでいる
- 全てのオブジェクトは「本物のクラス」を持つ: 通常クラスか特異クラスのどちらか
- (BasicObject以外の)全てのクラスは1つの祖先を持つ: スーパークラスかモジュール
- オブジェクトの特異クラスのスーパークラスはオブジェクトのクラス、クラスの特異クラスのスーパークラスはクラスのスーパークラスの特異クラス
- メソッド探索はまず「本物のクラス」に向かって「右に」進み、その後継承チェーンを辿って「上に」進む
これをベースに考えれば大抵のことはうまくいく。特異クラスのところはややこしいからあとでまた読む。
クラスメソッド構文3つ
# その1 def MyClass.a_class_method; end # その2 class MyClass def self.another_class_method; end end # その3 class MyClass class << self def yet_another_class_method; end end end
どれも見たことがあったがその3の書き方にようやく合点がいった。なるほどこういう仕組みだったのね。
クラスメソッドを追加することをクラス拡張、オブジェクトに特異メソッドを追加することをオブジェクト拡張と呼ぶ。
先程までのように特異クラスをオープンしてメソッドを追加することももちろんできるが、使う機会が多いためObject#extend
という拡張用のメソッドが用意されている。そういうことだったのか…。
クラス定義に関しては何回か読まないと難しそう。
6章 コードを記述するコード
コードは単なるテキストである。テキストであれば出力できる。だからコードを出力するコードが書けるよね、という論理。
Kernel#eval
を使うと、コードの文字列を評価できる。
[1] pry(main)> arr = [10, 20] => [10, 20] [2] pry(main)> element = 30 => 30 [3] pry(main)> eval("arr << element") => [10, 20, 30] [4] pry(main)> arr => [10, 20, 30]
もはやこうなってくるとなんでもあり感ある。
eval
で実行するコードに別のスコープの変数を持ち込みたい場合、Binding
オブジェクトをeval
に渡すことで実現できる。
[5] pry(main)> class MyClass [5] pry(main)* def my_method [5] pry(main)* @x = 1 [5] pry(main)* binding [5] pry(main)* end [5] pry(main)* end => :my_method [6] pry(main)> b = MyClass.new.my_method => #<Binding:0x00007fd47001d118> [7] pry(main)> eval "@x", b => 1
しかし何でもかんでも使えばいいということではない。実行時にはコードとして評価されるが、書いている / 読んでいる際はただの文字列なのである。そのため、シンタックスハイライトや補完が使えず、バグに気付きにくい。
さらに大きな問題として、悪意あるユーザーが悪意あるコードを実行できる危険性がある。
eval
を使わないようにするのであれば、define_method
やsend
を使うことになる。適切なメソッド選択が大事。
社内でしか使わないからeval
で書いた、という機能もリリースする可能性がある。そのため、最終的にはeval
を使わないようにリファクタリングできる場合はすることも考えるべきである。
フックメソッド
オブジェクトに対して発生するイベントをキャッチすることができる。
イベント、というのは例えばクラスが継承されたりメソッドが定義されたりといったこと。
[1] pry(main)> class String [1] pry(main)* def self.inherited(subclass) [1] pry(main)* puts "#{self}は#{subclass}に継承された" [1] pry(main)* end [1] pry(main)* end => :inherited [2] pry(main)> class MyClass < String; end StringはMyClassに継承された => nil
こんな感じ。このように何かのイベントに対応して呼び出されるメソッドをフックメソッドと呼ぶ。
7章 エピローグ
メタプログラミングに必要な知識としては以上となる。
これ以降はRailsのコードを見ながら実際に使われている箇所を見ていく流れ。
これ以降は実際のコードについて見ていく箇所になるため、ここでのアウトプットに関しては以上とする。
まとめ
メタプログラミング、なんてかっこいい名前をつけられているが、Rubyの世界においてはそれもただのプログラミングに過ぎないよ、ということが丁寧に順を追って説明されている良書。
とりあえず知識のインデックスは作った。これからは実際に手を動かしてみる予定。
8章以降でRailsについて見たので次はRailsの本でも読もうかしら。
ではまた。