Skip to content

Instantly share code, notes, and snippets.

@kachick
Created July 2, 2012 05:34
Show Gist options
  • Save kachick/3031285 to your computer and use it in GitHub Desktop.
Save kachick/3031285 to your computer and use it in GitHub Desktop.
昔お勉強したこと - 2010年 Module#define_method
#! ruby -Ks
# -*- coding: Shift_JIS -*-
# メタプログラミング / Module#define_method
# test / Ruby 1.8.7
# 下準備
def self.border
@border_num ||= 1
puts "---------------#{@border_num}---------------"
@border_num += 1
end
# define_methodについてもうちょい突っ込みます
# このメソッド気楽に使ってたんですが、ちょっと考えてみるとブロックスコープの扱いがかなり特殊でした。
class MettaMeta
names = [:メタ男, :メタ女, :メタ介]
define_method :name do
names.choice
end
end
border
person1 = MettaMeta.new
5.times do
p person1.name
end
#=>p いろいろ
# こんな感じで、ローカル変数はブロック呼び出し時のスコープから持ってきます。
# これは一般的なブロックの扱いと同じ
# 問題は次
class MettaMeta
# MettaMetaクラスのインスタンス用アクセサ
attr_accessor :age
class << self
# MettaMetaクラスオブジェクトの特異メソッド
def age
99
end
end
# MettaMetaクラスオブジェクトのインスタンス変数
@age = 20
define_method :secret do
age
end
define_method :機密事項 do
@age
end
end
border
person1.age = 50
p person1.secret
#=>p 50
p person1.機密事項
#=>p 50
# 自然に感じられるし、実際書く時はこう動いてくれるほうがうれしい
# でもこれはフツーのブロックとスコープの扱いが全然違ってる
# フツーのブロックスコープで考えると、person1.secretは99を、person1.機密事項は20を返すべき
# このように動く理由はブロック内でinstance_evalを叩いているかららしいのだけれど・・・慣れるまでちょっと戸惑うなぁ
# とりあえず、「define_method時。ローカル変数以外のスコープは、クラスでなくインスタンスがselfになる」と覚えておく感じですかね。
# 最後に、ふつーに書いたときと比較して、selfの扱いを覗いてみます。
class MettaMeta
define_method :僕が悪いんじゃないんです do
self
end
oresama = self
define_method :言われてやっただけなんです do
oresama
end
# フツーのブロックスコープ挙動例
my_proc = Proc.new do
self
end
eval "
def そっかー・・・
#{my_proc.call}
end
"
end
border
p MettaMeta.__id__
p person1.__id__
border
p person1.僕が悪いんじゃないんです.__id__
#=>person1.__id__と同じ
p person1.言われてやっただけなんです.__id__
#=>MettaMeta.__id__と同じ
p person1.そっかー・・・.__id__
#=>MettaMeta.__id__と同じ
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment