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 extend
ing 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 modulesmethods(false)
supposedly returns the singleton methods by callingsingleton_methods
, but it also passes the parameterfalse
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
Get Server File Path with Paperclip
Capistrano 3 Execute Within a Directory
How to Alias a Class Method in Rails Model
Ruby: "&& Return" VS "And Return"
Ruby $Stdin.Gets Without Showing Chars on Screen
Ruby: How to Generate CSV Files That Has Excel-Friendly Encoding
Clarification on the Ruby << Operator
Ruby Convert String to Method Name
Error Running 'Requirements_Osx_Brew_Libs_Install...' on MAC 10.7
In Ruby/Rails, How to Encode/Escape Special Characters in Urls
Understanding Method_Added for Class Methods