【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メソッドはない。
メソッドの定義場所を調べる
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モジュールはすごく最初にコンパイルしている」っていうルールがあるのかな。