🫲

【実用性皆無】 Rubyでクラスを動的に生成する(継承もできるよ!)

2023/06/28に公開

クラスを動的に定義したいな〜って時ありませんか?

例えば、Ramen というクラスを動的に定義するには以下のようにします。

Object.const_set("Ramen", Class.new)

こうすると、Ramen クラスを使えるようになります。

Object.const_set("Ramen", Class.new)
ramen = Ramen.new

でも、あまりこういう使い方は必要ないかもですね🤔

クラスを動的に定義するときに、継承したいな〜って時ありませんか?

例えば、以下のような Ramen クラスがあったとします。

class Ramen
end

これを継承した、MisoRamen を作る場合はこうです。

class Ramen
end

Object.const_set("MisoRamen", Class.new(Ramen))

こうすると、MisoRamen クラスを使えるようになります。

class Ramen
end

Object.const_set("MisoRamen", Class.new(Ramen))
miso_ramen = MisoRamen.new

でも、こういう使い方もあまり必要ないかもですね🤔

moduleをネストして、クラスを動的に定義したいな〜って時ありませんか?

例えば、Miso モジュールをネストして Miso::Ramen というクラスを動的に定義するには以下のようにします。

module Miso
end

Miso.const_set("Ramen", Class.new)

こうすると、Miso::Ramen クラスを使えるようになります。

module Miso
end

Miso.const_set("Ramen", Class.new)

miso_ramen = Miso::Ramen.new

これもどこで使うんでしょう🤔

動的に定義したmoduleをネストして、動的にクラスを定義したいな〜って時ありませんか?

例えば、Miso モジュールを動的に定義し、Miso モジュールをネストして Miso::Ramen というクラスを動的に定義するには以下のようにします。

Object.const_set("Miso", Module.new)
Miso.const_set("Ramen", Class.new)

こうすると、Miso::Ramen クラスを使えるようになります。

Object.const_set("Miso", Module.new)
Miso.const_set("Ramen", Class.new)

miso_ramen = Miso::Ramen.new

ほんとにこれはどこで使うんでしょう🤔

動的に定義したmoduleをネストして、動的にクラスを定義するときに、継承したいな〜って時ありませんか?

例えば、以下のような Ramen クラスがあったとします。

class Ramen
end

例えば、Miso モジュールを動的に定義し、Miso モジュールをネストして Miso::Ramen というクラスを動的に定義して、Ramen クラスを継承するには以下のようにします。

class Ramen
end

Object.const_set("Miso", Module.new)
Miso.const_set("Ramen", Class.new(Ramen))

こうすると、Miso::Ramen クラスを使えるようになります。

class Ramen
end

Object.const_set("Miso", Module.new)
Miso.const_set("Ramen", Class.new(Ramen))

miso_ramen = Miso::Ramen.new

普段使う場所あるんでしょうか🤔

どこで使うねーん!

どこで使いましょうね、無理矢理考えてみましょう・・・

あっ!!

これらの技術を使えば、プロジェクトに定義されていないはずの
「ぼくのかんがえたさいきょうのラーメン」 クラスを作れますね!!

class Ramen
end

ideas = %w(Boku No Kangaeta Saikyo No)

ideas.size.times do |i|
  parent = i.zero? ? Object : Object.const_get(ideas[0...i].join("::"))
  parent.const_set(ideas[i], Module.new)
end

Object.const_get(ideas.join("::")).const_set("Ramen", Class.new(Ramen))

idea_ramen = Object.const_get(ideas.join("::"))::Ramen.new

puts idea_ramen.class
#=> Boku::No::Kangaeta::Saikyo::No::Ramen

いや使わないからーーーー!!!!!!

最後に

「なんでこんなことをしようとしたか想像つかない」って?自分もそう思います。

株式会社mofmofの「水曜日の個人開発」という取り組みの中でアプリを作っている時に、特定のモデルを継承したモデルを何個も定義するの面倒だな・・・って思ったんです。
https://indie-dev.mof-mof.co.jp

それで調べていたら、「できるやーん!」ってなって実装してたんですけど、
結局使うのはやめました。
(やめたんかーーーい!!!)

面倒だけど、ちゃんと定義した方が将来の自分のためになると思いました。
(保守性の観点とかね!)
(忘れっぽいから「これなんだっけ?」ってなっちゃうからね!)
(メタプロしたかっただけなんじゃないの??)
(そう思う!!)
(でも、楽しかったから記事にしちゃった!!)

Discussion