开发者

Calling a method of a Ruby Singleton without the reference of 'instance'

I would like to call a method of a Singleton Object without the reference to its instance

SingletonKlass.my_method

instead of

SingletonKlass.instance.my_method

i've came up with this solution (using method_missing in the class):

require 'singleton'    

class SingletonKlass
  include Singleton

  def self.method_missing(method, *a开发者_开发知识库rgs, &block)
    self.instance.send(method, *args)
  end

  def my_method
     puts "hi there!!"
  end
end

is there any drawback to this ? is there any better solution ? any recommendation from you ?

thank you.

UPDATE:

my aim is to have a module to be mixed-in with singleton classes:

module NoInstanceSingleton
   def method_missing(method, *args)
      self.instance.send(method, *args)
   end
end

end then use it in the class:

class SingletonKlass
   include Singleton
   extend NoInstanceSingleton

  def method1; end
  def method2; end
  ...
  def methodN; end
end

i want to be able to directly call:

SingletonKlass.method1


Using forwardable and def_delegators:

require 'singleton'    
require 'forwardable'

class SingletonKlass
  include Singleton
  class << self
    extend Forwardable
    def_delegators :instance, :my_method
  end

  def my_method
     puts "hi there!!"
  end
end

SingletonKlass.my_method

Edit: If you want to include all the methods you've defined yourself, you could do

require 'singleton'    
require 'forwardable'

class SingletonKlass
  include Singleton

  def my_method
     puts "hi there!!"
  end

  class << self
    extend Forwardable
    def_delegators :instance, *SingletonKlass.instance_methods(false)
  end
end

SingletonKlass.my_method


The problem with your method_missing solution is that it will only redirect calls to instance if there does not exist a method of that name on SingletonKlass, which will cause problems if people want to access, for example, instance.__id__ through this interface you provide. There is not much of a problem with accessing SingletonKlass.instance the normal way, but if you really want to make a shortcut, the safest would be a constant:

KlassInstance = SingletonKlass.instance

If you want to define the constant dynamically, use Module#const_set:

const_set :KlassInstance, SingletonKlass.instance

You can extend upon this, too. For example, you can create a method that will create constants like this for you:

def singleton_constant(singleton_class)
  const_set singleton_class.name, singleton_class.instance
end

Of course, because Module#const_set is a method of a module, this specific technique can only be performed in the context of a module or class. Another possibility is by a mixin module with an overloaded hook method:

module SingletonInstance
  def included(base_class)
    const_set base_class.name, base_class.instance
    super
  end
end


Here is an alternative that does not really fall under the scope of my first answer. You can create a mixin module that undefines all the methods of the base class and then uses the method_missing technique:

module SingletonRedirect
  def included(base_class)
    instance = base_class.instance
    base_class.class_eval do
      methods.each &undef_method

      define_method :method_missing do |name, *arguments|
        instance.public_send name, *arguments
      end
    end
  end
end

To implement the idea, instance is a local variable that is passed via closure to the blocks in the calls to Class#class_eval and Module#define_method. Then, we do not need to refer to it by base_class.instance, so we can clear out all the methods, including that one (a technique known as a blank slate). Then, we define a method redirection system that takes advantage of the flat scope to refer to instance as a variable and call only methods that are publicly available via Object#public_send.


Instead of SingletonKlass, I'll use the name UrlHelper as a more concrete example.

require 'singleton'    

class UrlHelperSingleton
  include Singleton

  def my_method
     ...
  end
end

UrlHelper = UrlHelperSingleton.instance

# then elsewhere
UrlHelper.my_method


Why don't you use a "static" class? e.g.:

class StaticKlass

  class << self
    def method1; end
    def method2; end
    ...
    def methodN; end
  end

end

StaticKlass.method1
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜