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:
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
call
ed.The
def
keyword resets the local variable scope. To get a value into a function, you can either define it usingdefine_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
Best Way to Debug Third-Party Gems in Ruby
What Are the Differences Between "Private", "Public", and "Protected Methods"
How to Calculate the Distance Between Two Gps Coordinates Without Using Google Maps API
Create_Or_Update Method in Rails
What's the Best Way to Handle Exceptions from Net::Http
Warning: Can't Verify Csrf Token Authenticity in Case of API Development
Ruby and "You Must Recompile Ruby with Openssl Support or Change the Sources in Your Gemfile"
How to Build a Rubygems Mirror Server
Why Does Ruby Have Trueclass and Falseclass Instead of a Single Boolean Class
Ruby on Rails "Invalid Byte Sequence in Utf-8" Due to Bot
Routing Nested Resources in Rails 3
Install Rails 3 on Osx with Rvm
Active Record - Find Records Which Were Created_At Before Today