せかいや

いまいるここを、おもしろく http://sekai-in-the-box.appspot.com/

【Ruby】パーフェクトRuby 学習感想文 ~余談:クラス自体も実行可能な文

このブログをみた師匠から、
インスタンス変数が分かっていない」と謎なメールを頂きまして。


「こういうことでしょうか」と、勉強しなおした記事を送ったところ

大体あってる

というお返事。良かった。

その後に

クラス定義文自体もjavaのように静的感じではなく、
クラス定義自体も実行可能な文。こんなクラス定義がかける
	class A
		puts 111
	end

これが分かれば、attr_accessorとかがメソッドだということもわかるかも

というお告げが引き続き書いてあり。

ということで、今回は改めて本章に入る前に仕切り直し。

 
(参考・経緯など)
パーフェクトRuby 学習感想文 ~はじめに


 

classもただの文?

class Hoge
	p "jjjj"
end

hoge = Hoge.new

■実行結果

"jjjj"

なるほど。initializeしなくても、コンストラクタ生成時に文は実行されるんだ。
 ⇒⇒⇒この理解は間違いでした。ただしくはこちら

 

これが分かれば、attr_accessorとかがメソッドだということもわかるかも

ってところは、だから、
自分でattr_accessorメソッドをつくってみればいいのか


 

attr_accessor はメソッド?

まず「attr_accessorはメソッド」の事実確認から。

class Hoge
	attr_accessor: @name
end

hoge = Hoge.new
p hoge.methods

■実行結果

[:nil?, :===, :=~, :!~, :eql?, :hash, :<=>, :class, :singleton_class, :clone, :dup, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :freeze, :frozen?, :to_s, :inspect, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variables, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :tap, :send, :public_send, :respond_to?, :extend, :display, :method, :public_method, :define_singleton_method, :object_id, :to_enum, :enum_for, :==, :equal?, :!, :!=, :instance_eval, :instance_exec, :__send__, :__id__]

attr_accessorメソッド、ありませんけど?!

 
使ってないから?再チャレンジ。

class Hoge
        attr_accessor :name
end

hoge = Hoge.new
hoge.name = "tanaka"
p hoge.name
p hoge.methods()

 
■実行結果

"tanaka"
[:name, :name=, :nil?, :===, :=~, :!~, :eql?, :hash, :<=>, :class, :singleton_class, :clone, :dup, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :freeze, :frozen?, :to_s, :inspect, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variables, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :tap, :send, :public_send, :respond_to?, :extend, :display, :method, :public_method, :define_singleton_method, :object_id, :to_enum, :enum_for, :==, :equal?, :!, :!=, :instance_eval, :instance_exec, :__send__, :__id__]

やっぱりattr_accessorメソッドはない。

 

attr_accessorメソッドはModuleで定義されている

公式リファレンスを見たところ、Moduleで定義されている

 

メソッドの定義場所を調べる

6章P265に載っているメソッド定義場所の調べ方を参考に。

class Hoge
        attr_accessor :name
end

hoge = Hoge.new
hoge.name = "tanaka"
p hoge.name
p hoge.method(:attr_accessor)

■実行結果

undefined local variable or method `attr_accessor' for main:Object

やっぱりエラーか。
hogeオブジェクトが持っているメソッド一覧に存在しないんだから、
当たり前の結果か。


 

モジューから生成されたクラスは触れない

6章P254にはこうある

includeされたモジュールは・・・モジュールの機能を持った無名クラスが作られ
継承ツリーの中に埋め込まれます。この作成されたクラスにはRubyの
プログラムからは触れることが出来ません。

そうなのかー。ならModuleモジュールで定義されていると分かったところで
これ以上は探索できないのか?

 

実際にモジュールを作成してみる

module Momo
	def gohan
		"gohandayo"
	end
end

class Hoge
	include Momo
end

hoge = Hoge.new
p hoge.gohan
p Hoge.ancestors
p Hoge.methods
p Momo.methods
"gohandayo"
[Hoge, Momo, Object, Kernel, BasicObject]
[:allocate, :new, :superclass, :freeze, :===, :==, :<=>, :<, :<=, :>, :>=, :to_s, :inspect, :included_modules, :include?, :name, :ancestors, :instance_methods, :public_instance_methods, :protected_instance_methods, :private_instance_methods, :constants, :const_get, :const_set, :const_defined?, :const_missing, :class_variables, :remove_class_variable, :class_variable_get, :class_variable_set, :class_variable_defined?, :public_constant, :private_constant, :module_exec, :class_exec, :module_eval, :class_eval, :method_defined?, :public_method_defined?, :private_method_defined?, :protected_method_defined?, :public_class_method, :private_class_method, :autoload, :autoload?, :instance_method, :public_instance_method, :nil?, :=~, :!~, :eql?, :hash, :class, :singleton_class, :clone, :dup, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :frozen?, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variables, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :tap, :send, :public_send, :respond_to?, :extend, :display, :method, :public_method, :define_singleton_method, :object_id, :to_enum, :enum_for, :equal?, :!, :!=, :instance_eval, :instance_exec, :__send__, :__id__]
[:freeze, :===, :==, :<=>, :<, :<=, :>, :>=, :to_s, :inspect, :included_modules, :include?, :name, :ancestors, :instance_methods, :public_instance_methods, :protected_instance_methods, :private_instance_methods, :constants, :const_get, :const_set, :const_defined?, :const_missing, :class_variables, :remove_class_variable, :class_variable_get, :class_variable_set, :class_variable_defined?, :public_constant, :private_constant, :module_exec, :class_exec, :module_eval, :class_eval, :method_defined?, :public_method_defined?, :private_method_defined?, :protected_method_defined?, :public_class_method, :private_class_method, :autoload, :autoload?, :instance_method, :public_instance_method, :nil?, :=~, :!~, :eql?, :hash, :class, :singleton_class, :clone, :dup, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :frozen?, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variables, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :tap, :send, :public_send, :respond_to?, :extend, :display, :method, :public_method, :define_singleton_method, :object_id, :to_enum, :enum_for, :equal?, :!, :!=, :instance_eval, :instance_exec, :__send__, :__id__]
  • Hogeの継承先にはMomoモジュールが存在する
  • gohanメソッドはHogeクラスにも、Momoクラス(モジュール?)にも存在しない。

本によると、このときのクラスの関係はこうなる↓

hoge -> Hoge -> Momoをラップしたクラス -> Object

「モジュールの機能を持った無名クラス」だから、
実際はどこに、gohanメソッドが定義されているかは分からないのか?

うーん、最初のお題

attr_accessorとかがメソッドだということもわかるかも

は、なんとなく分かったけど、定義場所まで突き止めることは出来なかった。
 プライベートメソッドだから取れなかった。こちらの記事に加筆。
 

attr_accessorを自作してみる

やってみましょう。
 

クラスの中でモジュールに定義したメソッドを呼び出すには?

(1)モジュール内でモジュール関数を定義し実行 ⇒エラー
(2)モジュール内でクラスメソッドを定義し実行 ⇒OK

module Sekai
	def self.attr_sekai(attr)
		p "hogehoge"
	end
end

class Hoge
	include Sekai
	Sekai.attr_sekai :name
end

hoge = Hoge.new

■実行結果

"hogehoge"

 
クラスメソッドで定義する方法だとOKなのは、クラスメソッドが静的だからだと思う。
Rubyは「呼ばれたタイミングでメソッドを生成して実行する」という、
動的なプログラミング思想があるから、
クラスの地の文で実行したいメソッドは先にコンパイルされている必要がある、という理解。

もとのattr_accessorとは違う呼び出し方法(モジュール名を明記している)だけど
これ以上はちょっと分からない。

attr_accessorメソッドの中身を作る。

引数から動的にsetter getter を作ればいい。

module Sekai
	def self.attr_sekai(attr)
		eval <<-EOD
			@#{attr}
			def #{attr}=(val)
				@#{attr} = val
			end
			def #{attr}
				@#{attr}.upcase
			end
		EOD
	end
end

class Hoge
	include Sekai
	Sekai.attr_sekai :name
end

hoge = Hoge.new
hoge.name= "tanaka"
p hoge.name

■実行結果

"TANAKA"

ちょっとはattr_accessorぽく作れたかな。

attr_accessorはどうして、Module.attr_accessor
とレシーバをつけなくても呼べるんだろう。
「Moduleモジュールはすごく最初にコンパイルしている」っていうルールがあるのかな。