Ruby: Define_Method VS. Def

Ruby: difference between def and define_method

Let's make it simple.

define_method is a method. Or I should say a private class method of Object class. You invoke it by giving it an argument as instance method name you are going to define, and a block which contains the code of the method. apidock has very clear definition. You may want to read documentation.

def is a keyword. You use this to define methods just as you do all the time. Not really related to meta-programming.

If you are trying define class method, use class_eval, and give it a string. As its name indicates, instance_eval defines stuffs on instance level. In your code, if you do Foo.instance_methods, you will find the bar method. So if you do Foo.new.bar it returns 1, as TK-421 answered you. But since define_method defines instance_method, as indicated by documentation, regardless if you use class_eval or instance_eval, you will get instance method.

Here's the documentations you can read and they will answer all you question.

class_eval: http://apidock.com/ruby/v1_9_3_392/Module/class_eval

define_method: http://apidock.com/ruby/Module/define_method

instance_eval: http://apidock.com/ruby/Object/instance_eval

And don't forget this all mighty: http://www.google.com :D

Ruby: define_method vs. def

define_method is a (private) method of the object Class. You are calling it from an instance. There is no instance method called define_method, so it recurses to your method_missing, this time with :define_method (the name of the missing method), and :screech (the sole argument you passed to define_method).

Try this instead (to define the new method on all Monkey objects):

def method_missing(m)
puts "No #{m}, so I'll make one..."
self.class.send(:define_method, :screech) do
puts "This is the new screech."
end
end

Or this (to define it only on the object it is called upon, using the object's "eigenclass"):

def method_missing(m)
puts "No #{m}, so I'll make one..."
class << self
define_method(:screech) do
puts "This is the new screech."
end
end
end

def vs. define_method in Ruby

As you can see, my_other_method is in the list, but my_method is not.
What causes this?

Ruby treats defs at the toplevel differently than defs inside a class: toplevel defs become private methods of the Object class. Yet:

Object#methods: Returns a list of the names of public and protected
methods...

def my_method
puts "Output of my_method"
end

define_method(:my_other_method) do
puts "Output of my_other_method"
end

print "public and protected:\n\t"
puts methods.grep(/my/)
print "private:\n\t"
puts private_methods.grep(/my/)

--output:--
public and protected:
my_other_method
private:
my_method

It's undocumented, but define_method() actually creates a public method:

print "public:\n\t"
puts public_methods.grep(/my/)

--output:--
public:
my_other_method

And:

p Object.public_methods.grep(/my/)

--output:--
[:my_other_method]

Understanding Ruby define_method with initialize

Short answer, yes, long answer:

First, let's start explaining in a really (REALLY) simple way, how metaprogramming works on Ruby. In Ruby, the definition of anything is never close, that means that you can add, update, or delete the behavior of anything (really, almost anything) at any moment. So, if you want to add a method to Object class, you are allowed, same for delete or update.

In your example, you are doing nothing more than update or create the initialize method of a given class. Note that initialize is not mandatory, because ruby builds a default "blank" one for you if you didn't create one. You may think, "what happens if the initialize method already exist?" and the answer is "nothing". I mean, ruby is going to rewrite the initialize method again, and new Foo.new calls are going to call the new initialize.

Ruby performance: define method with define_method or eval

When you use define_method, the method you're defining can't accept a block.

It’s pretty well known that because of
a deficiency in blocks arguments in
Ruby 1.8 Class#define_method cannot
define methods that take blocks.

def x *args, █ end  # => works!
define_method(:x) {|*args,&block| } # => SyntaxError: compile error

The method being defined requires a block:

"def #{prefix}#{method}(*args, &block)" # def customer_name(*args, &block)

So define_method can't be used.

question regarding define_method and method_missing

You defined do_call as an instance method while you probably intended to define it as a class method. Which is why it calls method_missing for do_call as well and you get the error you're getting.

Also note that when you do self.class.send, self.class will be Class, so the method will be available on all classes, not just meta. You probably rather want:

class <<self
self
end.send

Edit in response to your update:

'a' is a class method and it is not passed on to the new Meta object (c). Why?

Because a is a class method[1]. Instances of a class only get the class's instance methods.

You're defining a as an instance method on Class and then you try to call it on an instance of Meta, which does not work. In ruby instance methods of Class as well as singleton methods defined on a class can only be called by doing TheClass.the_method, not instance_of_the_class.the_method. If you want to call a method on instances of Meta, define it as an instance method. If you want to be able to do Meta.a as well as Meta.new.a you have to define both an instance as well as a class method a.

[1] Actually, as I already said, the way you're defining it isn't even a class method of Meta. It's an instance method of Class (which means you can also call it as e.g. String.a).

Ruby define method to return proc which makes use of an argument

Your example has two problems:

  1. You can't call a "proc full of methods" like that -- it'll work as an association extension, but there the block is evaluated as a module body, not called.

  2. The def keyword resets the local variable scope. To get a value into a function, you can either define it using define_method instead (that block retains surrounding scope), or put the value somewhere else the function will be able to find it (a class variable, for example).


def test(word)
proc do
define_method(:hello) do
puts word
end
end
end

Class.new(&test("hello")).new.hello

Separately, if you're defining approximately the same method on several associations, there might be a simpler path by defining them as class-level scopes.

Define method parameters for meta programming in Ruby

Try passing an array or dictionary.

UPDATE:

if condition1
class_eval <<-EVAL
def #{"my_method"}(arg1)
end
EVAL
else
class_eval <<-EVAL
def #{"my_method"}
end
EVAL
end

UPDATE2:

if condition1
self.instance_eval <<-EVAL
def #{"my_method"}(arg1)
end
EVAL
else
self.instance_eval <<-EVAL
def #{"my_method"}
end
EVAL
end

UPDATE3:

# or
self.instance_eval("def method1(arg1) puts 'hellowa' + arg1.to_s; end")
self.instance_eval("def method2() puts 'hellowa2'; end")

# and then
method1(33) # => hellowa33
method2 # => hellowa2

Ruby: define method with args

I think eval is the better choice to write less code

def construct_payload(id, file_name, type)
eval("base(id).merge(#{type}(file_name))")
end

def base(_id)
{
"id": _id,
}
end

def radio(file)
{
"mode": "ds",
"file": file
}
end

def pan(file)
{
"mode": "pr",
"file": file
}
end

Ruby define_method

You may define methods the first time they're called in method_missing.

Whether or not you should is open to some debate, but it's a better option than respond_to?.

class Foo
def method_missing(sym)
puts "Method missing; defining."
self.class.send(:define_method, sym) do
puts "Called #{sym}."
end
end
end

Sanity check:

f = Foo.new
=> #<Foo:0x007fa6aa09d3c0>
f.wat
=> Method wat missing; defining.
f.wat
=> Called wat.
f2 = Foo.new
=> Called wat.


Related Topics



Leave a reply



Submit