How to Define a Ruby Singleton Method Using a Block

Is it possible to define a Ruby singleton method using a block?

Here's an answer which does what you're looking for

def define_say(obj, msg)
# Get a handle to the singleton class of obj
metaclass = class << obj; self; end

# add the method using define_method instead of def x.say so we can use a closure
metaclass.send :define_method, :say do
puts msg
end
end

Usage (paste from IRB)

>> s = "my string"
=> "my string"
>> define_say(s, "I am S")
=> #<Proc:0xb6ed55b0@(irb):11>
>> s.say
I am S
=> nil

For more info (and a little library which makes it less messy) read this:

http://viewsourcecode.org/why/hacking/seeingMetaclassesClearly.html

As an aside, If you're a ruby programmer, and you HAVEN'T read that, go do it now~!

Ruby Singleton methods for class and objects

First of all, the way to list singleton methods is with singleton_methods. The methods method returns a list of the names of public and protected methods of the object. Also, it is defined in the Object class. Try extending an instance. It is one of the most elegant ways, as it supports code reuse and seems to me very object-oriented:

class Foo
def bar
puts "Hi"
end
end

module Bar
def foo
puts "Bye"
end
end

f = Foo.new
f.bar
#=> hi

f.extend Bar
f.foo
#=> bye

f.methods(false)
#=> []
# the module we extended from is not a superclass
# therefore, this must be empty, as expected

f.singleton_methods
#=> ["foo"]
# this lists the singleton method correctly

g = Foo.new
g.foo
#=> NoMethodError

Edit: In the comment you asked why methods(false) returns nothing in this case. After reading through the C code it seems that:

  • methods returns all the methods available for the object (also the ones in included modules)
  • singleton_methods returns all the singleton methods for the object (also the ones in included modules) (documentation)
  • singleton_methods(false) returns all the singleton methods for the object, but not those declared in included modules
  • methods(false) supposedly returns the singleton methods by calling singleton_methods, but it also passes the parameter false to it; whether this is a bug or a feature - I do not know

Hopefully, this clarifies the issue a little. Bottom line: call singleton_methods, seems more reliable.

Is it possible in Ruby to add singleton methods to an existing object, using variables in the local scope?

Adding a singleton method that holds a reference to a local variable isn't idiomatic Ruby, but you can do it. You just have to define the method using a block, and the closure will remember the value of a.

a = { 'code' => 200, 'body' => 'text' }
b = a['body']

b.send(:define_singleton_method, :code) { a['code'] }

b.code # => 200

I want to add a singleton method with a closure to a Ruby object

You can define a method with a block using define_method.

Example:

class Object
def eigenclass
class <<self; self end
end
end

a = "Hello"
other_word = "World"
a.eigenclass.class_eval do
define_method(:cliche) {"#{self} #{other_word}"}
end
a.cliche # => "Hello World"
"Goodbye".cliche # => NoMethodError: undefined method `cliche' for "Goodbye":String

Here is an implementation of a define_singleton_method method:

class Object
def define_singleton_method(name, &block)
eigenclass = class<<self; self end
eigenclass.class_eval {define_method name, block}
end
end

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

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

Are all singleton methods public?

Singleton methods do not necessarily need to be public. Private/protected singleton methods are useful in the same situations as regular private/protected methods - for example as a helper method that you do not intend to be called outside of the class.

class Foo
end

f = Foo.new

class << f
def foo
helper
# other stuff
end

private
def helper
end
end

Not use self in every class method and not use singleton model in ruby

(this was already mentioned by engineersmnky in the comments)

You could extend your class with a module that holds your class methods (that module can be nested right within your class):

class Foo
module ClassMethods
def bar
# Do something
end
end

extend ClassMethods
end

You can also move it outside your class, even in its own file (this can be useful structure-wise or if there are many class methods):

# foo/class_methods.rb
class Foo
module ClassMethods
def bar
# Do something
end
end
end

# foo.rb
class Foo
extend ClassMethods
end

Methods defined via extend can even be overridden in the base class:

class Foo
module ClassMethods
def bar
123
end
end
end

class Foo
extend ClassMethods

def self.bar
super * 2
end
end

Foo.bar #=> 246

This is especially useful if you want to add custom functionality when extending multiple classes with the same module (think of shared class methods).

Define singleton methods on initialization using instance variables

Adding to Jörg's answer: define_singleton_method is Ruby 1.9+. If you want to run it in pre 1.9, the following works:

class Object
def metaclass
class << self; self; end
end
end
class TestingSingletonMethodsWithVariable
METHODS = %w(a b c d)

def initialize(favorite_method)
METHODS.each do |method_name|
if( favorite_method == method_name )
metaclass.send(:define_method, method_name, Proc.new do
puts "#{method_name} its my favorite method"
end)
else
metaclass.send(:define_method, method_name, Proc.new do
puts "#{method_name} its not my favorite method"
end)
end
end
end
end

t = TestingSingletonMethodsWithVariable.new('b')
t.a
t.b
t.c
t.d


Related Topics



Leave a reply



Submit