creating method on fly as a parameter while calling singleton method - ruby

How's it possible in ruby ?
class Test
# Creating singleton method
def self.some_singleton_method(param1)
puts param1
end
end
# calling singleton method by creating method on fly as a parameter to it
Test.some_singleton_method def method_name(some_param)
# do something
end
## method_name
I've tried many places looking around, can't come up with an idea how's it's working.
Thanks!

It is possible, since def is keyword, that creates new method in current scope, which is Object since you're calling it on the "top" level, i.e. not inside any class. Starting from Ruby 2.1, def returns method name as a symbol, so your code is actually equivalent to
name = def method_name(some_param)
// do something
end
Test.some_singleton_method(name) # outputs "method_name"
EDIT: Thanks to Cary Swoveland for clarification that def is actually a keyword and not a method.

Here are two ways to do that.
#1
class Test
def self.doit(m)
send(m) yield
end
end
Test.doit(:hello) do
puts 'hi'
end
#=> :hello
Test.new.hello
#=> "hi"`.
#2
class Test
def self.doit(str)
eval(str)
end
end
Test.doit "def hello; puts 'hi'; end"
#=> :hello
Test.new.hello
#=> "hi"`.

Related

Make methods dynamic in ruby

Define a class as follows. I want to call one_method dynamically. By default, wow.one_method calls the first one_method. If I want to change the behavior of the method, just call redefine.
I can implement the method as a function type property, but that is not what I want.
If I use the following code directly, it would report errors. Could you modify it slightly.
class Wow
def one_method
puts "hello Ruby"
end
def redefine(what="none")
define_method :one_method do
puts what
end
end
end
wow = Wow.new
wow.redefine("Whatever I want.")
You can achieve that via class_eval or instance_eval:
class Wow
def one_method
puts "hello Ruby"
end
def redefine(what="none")
self.class.class_eval do
define_method :one_method do
puts what
end
end
end
end
wow = Wow.new
wow.one_method #=> hello Ruby
wow.redefine("Whatever I want.")
wow.one_method #=> Whatever I want.
Reason is that define_method defines instance method on the receiver and is a class's instance method so you'll have to call it on the eigen class of the object that you want to redefine a method on.
I would recommend achieving your goal in a more canonical way, just redefine the method on the instance itself:
class Wow
def one
:one
end
end
w = Wow.new
w.one
#=> :one
def w.one
:two
end
w.one
#=> :two
Drawbacks are:
your methods lookup table caches will be dropped
the code is becoming more obscure and hard to debug
Alternatives:
I don't know your real problem, but for your particular question it is better to parameterize your one_method method just to receive an argument for puts. Also, you can pass a block, so you will receive more grained control over the behavior.

Generic methods/Default methods in Ruby, when some method is not defined

I want to do something but I'm not sure if it is possible. I want to use "generic methods" or "default methods" in case when some method is called but is not defined. This is a simple example so you can understand my point:
This is the class:
class XYZ
def a
#...
end
def b
#...
end
end
The instance of the class XYZ:
n = XYZ.new
n.a
n.b
n.c
As you can see, I'm calling the method "c" which is not defined and it will throw an error. Can I do something in the class XYZ so when someone calls a method not defined get the name of the method and do something, in base of the name of the method? And, is this possible in another languages (not making a compiler)? If this is possible, how is it called (theory speaking)?
Use method_missing:
class XYZ
def a; end
def b; end
def method_missing(name, *args)
"Called #{name} with args: #{args}"
end
end
XYZ.new.c #=> "Called c"
You should also define respond_to_missing? to get respond_to? to work nicer in 1.9.2+. You should read more about respond_to?/respond_to_missing? when using method_missing.
This, by the way, would be considered to be metaprogramming. This isn't typically possible in compiled languages because of the way they call functions.
its called method_missing.
When you call a method that is not defined on object, ruby redirects the call to method_missing method which raises the error for you.
you can do this:
class XYZ
def method_missing(method, *args, &blck)
puts "called #{method} with arguments #{args.join(',')}"
end
end
now instead of error you will get output to your console.

Ruby Metaprogramming: creating a method by a method

I just wondered about some metaprogramming.
Actually I need to create a method within a method, or just create a method in the root of a class by a block. example:
["method_a", "method_b"].each do |m|
Marshal.generate_a_method_called(m)
end
Does somebody know how this is possible? And where to place what the method does? I need one argument for my method.
Yours,
Joern.
You could use define_method:
[:method_a, :method_b].each do |m|
define_method(m) do
# your method stuff
end
end
I don't understand your example. Are you generating the source for the method as well?
So I will start with an example from the book Perrotta: Metaprogramming Ruby
class MyClass
define_method :my_method do |my_arg|
my_arg * 3
end
end
obj = MyClass.new
obj.my_method(2) # => 6

Problem with accessing superclass methods in method redefinitions

I am having a bit trouble to understand when "super" can be called and when not. In the below example the super method leads to a no superclass error.
class Bacterium
def eats
puts "Nam"
end
end
class Bacterium
def eats
super # -> no superclass error
puts "Yam"
end
end
b = Bacterium.new
b.eats
But this works:
class Fixnum
def times
super # -> works
puts "done"
end
end
5.times { |i| puts i.to_s }
Is 5 not just also an instance of Fixnum. And am I not redefining an existing method like in the Bacterium example above?
No, not really. Fixnum inherits from Integer class, and you are in fact overriding Integer#times, so super works, as it calls implementation from the parent.
In order to achieve something similar when monkeypatching, you should alias method before redefining it, and there call it by alias.
class Bacterium
alias_method :eats_original, :eats
def eats
eats_original # -> "Nam"
puts "Yam"
end
end
Class reopening is not a form of inheritance and super is of no use there.
Just as Mladen said, and you can check that with Class#superclass:
irb> Fixnum.superclass
=> Integer
And does Integer implement #times?:
irb> Integer.instance_methods.grep /times/
=> [:times]
Yes it does.
So, in a simplified way, we can say, that super invokes the method you are in of a superclass. In your case the superclass of a Bacterium is Object, which doesn't implement #eats.
I said this is very simplified, because look at this example:
module One
def hi
" World" << super()
end
end
module Two
def hi
"Hello" << super()
end
end
class SayHi
def hi
"!!!"
end
end
h = SayHi.new
h.extend(One)
h.extend(Two)
puts h.hi
#=> Hello World!!
Don't take to serious what I wrote here, it is actually tip of the iceberg of the Ruby object model, which is important to understand (I am still learning it) - then you will get most, or all of those concepts.
Use some Google-fu for "Ruby object model"...

Define a method that is a closure in Ruby

I'm re-defining a method in an object in ruby and I need the new method to be a closure. For example:
def mess_it_up(o)
x = "blah blah"
def o.to_s
puts x # Wrong! x doesn't exists here, a method is not a closure
end
end
Now if I define a Proc, it is a closure:
def mess_it_up(o)
x = "blah blah"
xp = Proc.new {||
puts x # This works
end
# but how do I set it to o.to_s.
def o.to_s
xp.call # same problem as before
end
end
Any ideas how to do it?
Thanks.
This works (tested in irb):
NOTE: This changes only str - not all instances of String. Read below for details as to why this works
another_str = "please don't change me!"
str = "ha, try to change my to_s! hahaha!"
proc = Proc.new { "take that, Mr. str!" }
singleton_class = class << str; self; end
singleton_class.send(:define_method, :to_s) do
proc.call
end
puts str.to_s #=> "take that, Mr. str!"
puts another_str.to_s #=> "please don't change me!"
# What! We called String#define_method, right?
puts String #=> String
puts singleton_class #=> #<Class:#<String:0x3c788a0>>
# ... nope! singleton_class is *not* String
# Keep reading if you're curious :)
This works because you are opening str's singleton class and defining a method there. Because this, as well as the call to Module#define_method, have what some call a "flat scope", you're able to access variables that would be out of scope if you used def to_s; 'whatever'; end.
You may want to check out some of these other "metaprogramming spells" here:
media.pragprog.com/titles/ppmetr/spells.pdf
Why does it only change str?
Because Ruby has a couple interesting tricks up it's sleeves. In the Ruby object model, a method invocation results in the reciever searching not only it's class (and it's ancestors), but also it's singleton class (or as Matz would call it, it's eigenclass). This singleton class is what allows you to [re]define a method for a single object. These methods are called "singleton methods". In the example above, we are doing just that - defining a singleton method name to_s. It's functionaly identical to this:
def str.to_s
...
end
The only difference is that we get to use a closure when calling Module#define_method, whereas def is a keyword, which results in a change of scope.
Why can't it be simpler?
Well, the good news is that you're programming in Ruby, so feel free to go crazy:
class Object
def define_method(name, &block)
singleton = class << self; self; end
singleton.send(:define_method, name) { |*args| block.call(*args) }
end
end
str = 'test'
str.define_method(:to_s) { "hello" }
str.define_method(:bark) { "woof!" }
str.define_method(:yell) { "AAAH!" }
puts str.to_s #=> hello
puts str.bark #=> woof!
puts str.yell #=> AAAH!
And, if you're curious...
You know class methods? Or, in some languages, we'd call them static methods? Well, those don't really exist in Ruby. In Ruby, class methods are really just methods defined in the Class object's singleton class.
If that all sounds crazy, take a look at the links I provided above. A lot of Ruby's power can only be tapped into if you know how to metaprogram - in which case you'll really want to know about singleton classes/methods, and more generally, the Ruby object model.
HTH
-Charles
Feature #1082 implemented in Ruby 1.9.2 makes this an easy task with Object#define_singleton_method:
def mess_it_up(o)
x = "blah blah"
# Use Object#define_singleton_method to redefine `to_s'
o.define_singleton_method(:to_s) { x }
end
The concepts involved are still the same as in my previous answer, which provides a more in-depth description of how this works in Ruby's object model, as well as a Object#define_method definition that is conceptually the same as Ruby 1.9.2's Object#define_singleton_method.
Other methods that you might find useful for similar tasks:
Object#singleton_class
Object#singleton_methods
Object#respond_to_missing? (great blog post here)
This seems to work.
class Foo
def mess_it_up(o)
x = "blah blah"
o.instance_variable_set :#to_s_proc, Proc.new { puts x }
def o.to_s
#to_s_proc.call
end
end
end
var = Object.new
Foo.new.mess_it_up(var)
var.to_s
The problem is that code in def is not evaluated until it's run, and in a new scope. So you have to save the block to an instance variable on the object first and retieve it later.
And define_method doesn't work because it's a class method, meaning you would have to call it on the class of your object, giving that code to ALL instances of that class, and not just this instance.

Resources