newを書かずにインスタンス生成
newが嫌いな理由 -- リテラル好き好きを読んで、newを書かずにインスタンス生成する実験をRubyでやってみます。
class Person def initialize(name) @name = name end def hello puts "Hello, #{@name}!" end end def Person(*args) Person.new(*args) end # 以上は仕込み。 x = Person('Tonkichi') # Person.newとしない x.hello #=> Hello, Tonkichi!
うまくいきました♪
ところで、def Personを自動化しようと思い、Class#inheritedの中に埋め込もうと試みたのが次のプログラムです。でもうまくいきませんでした。
class Class def inherited(*args) eval " def #{args}(*a) #{args}.new(*a) end " end end class Person def initialize(name) @name = name end def hello puts "Hello, #{@name}!" end end x = Person('Tonkichi') #=> undefined method `Person' for main:Object (NoMethodError) x.hello
次のようにすればうまく行くんですけれどね…。
class Class def inherited(*args) $func = " def #{args}(*a) #{args}.new(*a) end " end end class Person def initialize(name) @name = name end def hello puts "Hello, #{@name}!" end end eval $func x = Person('Tonkichi') x.hello #=> Hello, Tonkichi!
追記:スーパーな方々からアドバイスが入って、勉強になりまくりです(へんな日本語)。以下、アドバイスを反映したものを順に書きます。
id:sumimさんから、evalの第二引数とTOPLEVEL_BINDINGを教えていただきました。ri evalをやらなかったわたしのミスでしたね。
class Class def inherited(*args) eval(" def #{args}(*a) #{args}.new(*a) end ", TOPLEVEL_BINDING) end end class Person def initialize(name) @name = name end def hello puts "Hello, #{@name}!" end end x = Person('Tonkichi') x.hello #=> Hello, Tonkichi!
artonさんからは、module_evalを教えていただきました。
class Class def inherited(*args) module_eval(" def #{args}(*a) #{args}.new(*a) end ") end end class Person def initialize(name) @name = name end def hello puts "Hello, #{@name}!" end end x = Person('Tonkichi') x.hello #=> Hello, Tonkichi!
id:iwadonさんから、Kernel.instance_evalを教えていただきました。あらっ? inheritedは引数一つでよいのか…。ここまでのプログラムでわたしはinheritedを*argsで受けていましたね。
class Class def inherited(subclass) Kernel.instance_eval do define_method(subclass.to_s) do |*args| subclass.new(*args) end end end end class Person def initialize(name) @name = name end def hello puts "Hello, #{@name}!" end end x = Person('Tonkichi') x.hello #=> Hello, Tonkichi!
みなさま、ご教示ありがとうございます!
(ツ、ツンデレなんかじゃないんだからねっ!)