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
精彩评论