ruby - create singleton with parameters? ruby - create singleton with parameters? ruby ruby

ruby - create singleton with parameters?


Here's another way to do it -- put the log file name in a class variable:

require 'singleton'class MyLogger  include Singleton  @@file_name = ""  def self.file_name= fn    @@file_name = fn  end  def initialize    @file_name = @@file_name  endend

Now you can use it this way:

MyLogger.file_name = "path/to/log/file"log = MyLogger.instance  # => #<MyLogger:0x000.... @file_name="path/to/log/file">

Subsequent calls to instance will return the same object with the path name unchanged, even if you later change the value of the class variable. A nice further touch would be to use another class variable to keep track of whether an instance has already been created, and have the file_name= method raise an exception in that case. You could also have initialize raise an exception if @@file_name has not yet been set.


Singleton does not provide this functionality, but instead of using singleton you could write it by yourself

class MyLogger  @@singleton__instance__ = nil  @@singleton__mutex__    = Mutex.new  def self.instance(file_name)    return @@singleton__instance__ if @@singleton__instance__    @@singleton__mutex__.synchronize do      return @@singleton__instance__ if @@singleton__instance__      @@singleton__instance__ = new(file_name)    end    @@singleton__instance__  end  private  def initialize(file_name)    @file_name = file_name  end  private_class_method :newend

It should work, but I did not tested the code.

This code forces you to use MyLogger.instance <file_name> or at least at the first call if you know it will be first time calling.


Here is an approach I used to solve a similar problem, which I wanted to share in case you or other people find it suitable:

require 'singleton'class Logger  attr_reader :file_name  def initialize file_name    @file_name = file_name  endendclass MyLogger < Logger  include Singleton  def self.new    super "path/to/file.log"  end  # You want to make {.new} private to maintain the {Singleton} approach;  # otherwise other instances of {MyLogger} can be easily constructed.  private_class_method :newendp MyLogger.instance.file_name# => "path/to/file.log"MyLogger.new "some/other/path"# => ...private method `new' called for MyLogger:Class (NoMethodError)

I've tested the code in 2.3, 2.4 and 2.5; earlier versions may of course exhibit divergent behavior.

This allows you to have a general parametrized Logger class, which can be used to create additional instances for testing or future alternative configurations, while defining MyLogger as a single instance of it following to Ruby's standardized Singleton pattern. You can split instance methods across them as you find appropriate.

Ruby's Singleton constructs the instance automatically when first needed, so the Logger#initialize parameters must be available on-demand in MyLogger.new, but you can of course pull the values from the environment or set them up as MyLogger class instance variables during configuration before the singleton instance is ever used, which is consistent with the singleton instance being effectively global.