2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[Ruby] Singleton なクラスのインスタンスを再初期化する

Last updated at Posted at 2025-10-05

やりたいこと

YAML に書かれた設定値を読み込む AppData クラスがあります。

config/app.yml
---
version: 0.9.9
app_data.rb
class AppData
  attr_reader :version

  def initialize
    @version = yaml.fetch('version')
  end

  private

  def yaml = YAML.load_file(Pathname(__dir__).join('config/app.yml'))
end

途中で YAML を更新しても、インスタンスを生成しなおせば AppData#version が更新されます。

app_data = AppData.new
app_data.version
#=> "0.9.9"

# config/app.yml の 0.9.9 を 1.0.0 に更新する。
puts(Pathname(__dir__).join('config/app.yml').read)
# ---
# version: 1.0.0

app_data.version
#=> "0.9.9"

app_data = AppData.new
app_data.version
#=> "1.0.0"

では AppDataSingleton モジュールを include している場合はどうでしょう?

app_data.rb
class AppData
  include Singleton

  attr_reader :version

  def initialize
    @version = yaml.fetch('version')
  end

  private

  def yaml = YAML.load_file(Pathname(__dir__).join('config/app.yml'))
end
app_data = AppData.instance
app_data.version
#=> "0.9.9"

# config/app.yml の 0.9.9 を 1.0.0 に更新する。
puts(Pathname(__dir__).join('config/app.yml').read)
# ---
# version: 1.0.0

app_data.version
#=> "0.9.9"

# version が更新されない。
app_data = AppData.instance
app_data.version
#=> "0.9.9"

途中で YAML を更新すると、AppData.instance でインスタンスを取得して直しても古いバージョンのままです。(Singleton パターンを使っているので当たり前ではありますが) インスタンスが最初に AppData.instance を呼んだ時の状態のままだからですね。新しい Ruby プロセスでインスタンス化すれば AppData#version を更新できます。

# irb や Ruby アプリケーションを再起動して、新しい Ruby プロセスでインスタンス化する。
app_data = AppData.instance
app_data.version
#=> "1.0.0"

では同じ Ruby プロセスのままで「Singleton モジュールを include したクラスのインスタンスの状態を更新する」ということはできないでしょうか? 🤔

方法

Singleton.__init__ を使うと可能です。引数に Singleton なクラスを指定します。

app_data = AppData.instance
app_data.version
#=> "0.9.9"

# config/app.yml の 0.9.9 を 1.0.0 に更新する。
puts(Pathname(__dir__).join('config/app.yml').read)
# ---
# version: 1.0.0

app_data = AppData.instance
app_data.version
#=> "0.9.9"

# Singleton なクラスである AppData の唯一のインスタンスを初期化する。
Singleton.__init__(AppData)
#=> AppData

# version が更新された!
app_data = AppData.instance
app_data.version
#=> "1.0.0"

Singleton.__init__ の実装は以下のとおりです。インスタンスをスレッドセーフに初期化しています。

# https://github.com/ruby/singleton/blob/v0.3.0/lib/singleton.rb#L162-L168
def __init__(klass) # :nodoc:
  klass.instance_eval {
    set_instance(nil)
    set_mutex(Thread::Mutex.new)
  }
  klass
end

ただし、ソースコード中に :nodoc: とコメントされている様に、これはドキュメント化されていない (undocumented) メソッドです。Singleton::VERSION の変更に伴い、内部実装が変わってこのメソッドの挙動が変わったりメソッド自体が廃止されたりする可能性もあるので注意してください。

バージョン情報

RUBY_VERSION
#=> "3.4.6"

Singleton::VERSION
#=> "0.3.0"

参考

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?