JavaScriptを有効にしてください

メタプログラミングRuby

 ·   ·  ☕ 7 分で読めます  ·  🐯 ブシトラ

気になっていた。メタプロ。
感想を書いていく。

はじめに

RubyやRuby on Railsを使いたい人は読むといいぽい

メタプログラミングはLispからやってきたもの。

I部では、

メソッド検索、Rubyのクラス
lambda
スコープ

Ⅱ部では、

メタプログラミングのケーススタディ

Ruby上級者向けの本ではないぽい

第2章

2-1.オープンクラス

メソッドが文字列でしか使われないと明確なため、Stringクラスに変更

1
2
3
def to_alphanumeric
  gsub(/[^\w\s]/, '')
end

2-1.1 クラス定義の内部

Rubyのclassは、クラス宣言というよりスコープ演算子のようなもの。
オープンクラスという。

ただStringとかを書き換えるモンキーパッチは危険そう。(感想)

[Ruby] メタプログラミングの入り口、オープンクラスを理解する

2.1.2 Monetizeの例

Monetizeというgemを利用してオープンクラスを確認した

2.1.3 オープンクラスの問題点

replaceメソッドを定義しArrayクラスに寄せたが、すでにArrayにreplaceが元から生えているため、壊れる。モンキーパッチの悪い例

2-2 オブジェクトモデルの構造

クラスやオブジェクトのクラスには見た目以上に隠されているものが多い

2.2.1 オブジェクトの中身

同じメソッドでも、クラスに着目しているときはインスタンスメソッド で、 オブジェクトに着用しているときはメソッドと呼ぶ

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class MyClass
  def my_method
    @v = 1
  end
end

obj = MyClass.new
obj.my_method
# obj は my_method というメソッドを持っていると言える

# MyClass は my_method というメソッドを持っているのではなく、
# my_methodのインスタンスメソッドを持っていると言える
# (メソッドを持っているというと、クラスメソッドとして使えるような文脈になる)

2.2.2 クラスの真相

クラスはオブジェクトである

1
2
3
4
 Array.superclass
 # Object
 Object.superclass
 # BasicObject

すべてのクラスはModule

1
2
Class.superclass
# Module

2.2.3 定数

定数と変数の違いは、スコープ。

2.2.4 オブジェクトとクラスのまとめ

オブジェクトのメソッドは、オブジェクトではなくオブジェクトのクラスに住んでいて
クラスのインスタンスメソッドと呼ばれる

クラスとは、オブジェクトにインスタンスメソッドの一覧とスーパークラスへの
リンクがついたものである

ここらへんこんがらがるからまた読み返したい

2.2.5 ネームスペースを使う

Action Mailer には Text クラスがあるので、
自前で Textクラスを実装することは出来ないが、moduleでラップすればいける

2.4 メソッドを呼び出すときに何がおきているのか

メソッドを探す時、Rubyは以下のことを行う

1.メソッドを探す これはメソッド検索と呼ばれる
2.メソッドを実行する これにはselfと呼ばれるものが必要

2.4.1 メソッド探索

karnel は Objectから継承されており、printがどこでも使えるよねという説明

2.4。2 メソッドの実行

メソッドはレシーバのオブジェクトに依存する

2.4.3 Refinements

https://docs.ruby-lang.org/ja/latest/method/Module/i/refine.html
3からはrefine になったっぽい。

2-6 まとめ

学んだことが羅列されている。
2章はここだけ復習すればよさそう

クラス、継承、インスタンス変数、メソッド、モジュール、レシーバ、あたり
Rubyのオブジェクトの実態など

3章 メソッド

動的言語はコンパイラ時にエラーが出ない。

3.2.1 メソッドを動的に呼び出す

send を使うと得られるメリット → 動的ディスパッチ(コード実行時に呼び出すメソッドを決められる)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
[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の箇所を変数に変えればメソッド名は自由自在。

ゴーストメソッド

さらに「この名前で定義してね」とお願いするだけではなく「知らないメソッド名がきたらこの処理をしてね」ということもできる。

method_missingをオーバーライドすることでメソッド名が存在しない場合の処理を記載できる。

これを使う場合、respond_to_missing?メソッドもオーバーライドしてゴーストメソッドがrespond_to?に正しい値を返すようにするのがルール。ただ暗黙のルールとなっているため動的メソッドで済む場合はそちらを使った方がバグが入ってきづらい。

3.2.2 Pryの例

スパイダーマンのベンおじさんの 「大いなる力には、大いなる責任が伴う」がでてきて胸アツ

例では send はprivateも呼び出せるから強力すぎるといったことを説明している

3-6 まとめ

動的メソッドと動的ディスパッチ
repond_to? method_missing
ゴーストメソッド

あたりの話。途中の 3-5あたりまでスキップ

4章 ブロック

4.1.2 ブロックの基本

1
2
3
4
5
6
def a_method(a,b)
  a + yield(a,b)
end

a_method(1,2) {|x,y| (x + y) * 3 }
# 10

メソッドはyieldを使ってブロックをコールバックする。

4.5.1 proc と lambda

Procはブロックをオブジェクトにしたもの

1
2
3
inc = Proc.new { |x| x + 1 }
inc.call(2)
# 3

上記は遅延評価?

&演算子と、procと、Object#method について理解しなおす

&修飾は復習が必要そう

Procをlambdaの明確な違い

・return キーワードの意味が違う
・引数に関するチェック

Procの場合は、Procが定義されたscopeから戻る。
lambdaの方が、引数の扱いに厳しい

lamdbaでも同じように作れるがprocでよさそうと思っていたが、書籍ではlambda推しだった

クラス定義
モジュール定義
メソッド
という単位で切り替わる。

4-8 まとめ

スコープゲートとはなにか
フラットスコープと共有スコープを使い、スコープを横断して束縛を見えるようにする
オブジェクトのスコープで、コードを実行する方法や、クリーンルームでコードを実行する方法
ブロックとオブジェクトを相互に変換する方法
メソッドとオブジェクトを相互に変換する方法
呼出し可能オブジェクトの種類とその地以外 通常とのメソッドとの違い
独自の小さなDSLの書き方

正直この章むずかったので復習したい

第5章 クラス定義

Rubyのクラス定義は、実際にコードを実行している

この章では、クラスマクロとアラウンドエイリアスを学ぶ。

クラスマクロ

attr_accsessorのようなキーワードに見えるクラスメソッドの事

特異メソッド 特異クラス

【Ruby】特異メソッド・特異クラスを理解する

1つのインスタンス固有のメソッドのことだけが使えるメソッドのこと

オブジェクトの7つのルール

オブジェクトは1種類のみ: 通常のオブジェクトかモジュールになる
モジュールは1種類のみ: 通常のモジュールかクラス、特異クラスのいずれかになる
メソッドは1種類のみ: モジュールに住んでいる
全てのオブジェクトは「本物のクラス」を持つ: 通常クラスか特異クラスのどちらか
(BasicObject以外の)全てのクラスは1つの祖先を持つ: スーパークラスかモジュール
オブジェクトの特異クラスのスーパークラスはオブジェクトのクラス、クラスの特異クラスのスーパークラスはクラスのスーパークラスの特異クラス
メソッド探索はまず「本物のクラス」に向かって「右に」進み、その後継承チェーンを辿って「上に」進む

クラスメソッドの構文

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# その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番目の、特異クラスをオープンして、そこにメソッド 定義している

instance_eval

nstance_evalは、オブジェクトの特異クラスにインスタンスメソッドを定義したり、そのオブジェクト自身が参照できるインスタンス変数を定義、または上書きしたりすることができる。

instance_evalとclass_evalについての備忘録

instance method Object#extend

instance method Object#extend

イマイチぴんときてない。。

引数で指定したモジュールのインスタンスメソッドを self の特異メソッドとして追加します。

Module#include は、クラス(のインスタンス)に機能を追加しますが、extend は、ある特定のオブジェクトだけにモジュールの機能を追加したいときに使用します。

引数に複数のモジュールを指定した場合、最後の引数から逆順に extend を行います。

アラウンドエイリアス

アラウンドエイリアスとは再定義したメソッドから以前のメソッドをエイリアスとして呼び出すことです。

・元の処理の前後に処理を追加したり
・ラップした処理を作成したり
・既存機能を拡張しつつ、元のメソッドも利用可能にしたり

と便利。
しかし、既存処理を利用箇所がある場合は影響範囲の見極めが必要

確かにStringやIntの元のメソッド変えると大変なことになるね

1
2
3
4
5
6
7
8
9
class Integer
  # 既存の+をold_plusと定義します
  alias :old_plus :+
  # +を新しく定義してやります
  # ここでは、常に4をプラスしてやります
  def +(value)
    self.old_plus(value).old_plus(4)
  end
end
共有

busitora
著者
ブシトラ
エンジニア