def vs. define_method in Ruby - ruby

I'm experimenting with Ruby and am confused by the following behavior. In a file test.rb I have the following code:
def my_method
puts "Output of my_method"
end
define_method(:my_other_method) do
puts "Output of my_other_method"
end
puts methods
I run this file from the command line using ruby test.rb, and the output is a list of methods:
to_s
inspect
my_other_method
nil?
===
=~
!~
eql?
hash
<=>
class
singleton_class
clone
dup
itself
taint
tainted?
untaint
untrust
untrusted?
trust
freeze
frozen?
methods
singleton_methods
protected_methods
private_methods
public_methods
instance_variables
instance_variable_get
instance_variable_set
instance_variable_defined?
remove_instance_variable
instance_of?
kind_of?
is_a?
tap
send
public_send
respond_to?
extend
display
method
public_method
singleton_method
define_singleton_method
object_id
to_enum
enum_for
==
equal?
!
!=
instance_eval
instance_exec
__send__
__id__
As you can see, my_other_method is in the list, but my_method is not. What causes this?

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]

Calling def on the top level declares a method as a private method of the Object class, thus you don't see it in the list of public methods. But you still can call it directly anywhere on the top level and from inside of an object of any class.
Example:
def my_method
puts "Output of my_method"
end
class Foo
def use_my_method
my_method # it can be called there
end
end
Foo.new.use_my_method
my_method

Related

Variables in class methods

could you please explain me why the class variable cannot be accessed by attribute_accessors?
As i am trying here to have the list of all methods of all subclasses in one array it works a little different. It created array #method_names in every subclass with specific methods for every class ... so i do need to do a loop through subclasses.
What kind of variable/attribute is #method_names?
Thanks!
module First
class First_class
class << self
def info
puts "First_class method info."
puts #subclasses
puts #method_names
end
def inherited(subclass)
puts "#{subclass} located ..."
subclasses << subclass
end
def subclasses
#subclasses ||= []
end
def method_added(method_name)
puts "Method located #{method_name} ..."
method_names << method_name
end
def method_names
#method_names ||= []
end
end
def initialize
puts "Instance of First_class is created."
end
def first_method
end
end
class Second_class < First_class
def self.info
puts "Second_class method info."
puts #subclasses
puts #method_names
end
def second_method
end
def initialize
puts "Instance of Second_class is created."
end
end
class Third_class < First_class
def third_method
end
def initialize
puts "Instance of Third_class is created."
end
end
end
First::First_class.subclasses.each {
|subclass| puts subclass
subclass.method_names.each {
|methodn| puts methodn
}
}
#################UPDATE#########
Ok, maybe I put the question incorrectly.
Basically what is the difference for ##method_names(class variable) and #method_names (instance variable) if i do not create the instance of object? After inserting more inputs into #method_names it still inserts into the same object_id. So what is benefit of ##method_names?
updated to answer updated question.
Classes in ruby can have class variables. However if you modify the class level variable, ALL instances will be modified. This is not recommended but will illustrate the point. But also see this answer
class Foo
##bar = 'bar'
attr_accessor :bar
def initialize
#bar = 'bar'
end
def class_bar
##bar
end
def change_class_bar string
raise ArgumentError unless string.is_a?(String)
##bar = string
end
end
a = Foo.new
b = Foo.new
# change the class variable ##bar
b.change_class_bar 'wtf?'
# see both instances are changed because objects are passed by referrence
print 'a.class_bar is: '
puts a.class_bar
print 'b.class_bar is: '
puts b.class_bar
# change one instance only
a.bar = 'only a has changed'
print 'a.bar is: '
puts a.bar
print 'b.bar is still: '
puts b.bar
run this and you should get output:
a.class_bar is: wtf?
b.class_bar is: wtf?
a.bar is: only a has changed
b.bar is still: bar
original answer left here
#method_names is an instance variable of an instance of the class from which it was instantiated. However it cannot be accessed for read/write unless those attributes are defined with getter or setter methods defined.
ff = First::First_class.new
Instance of First_class is created.
=> #<First::First_class:0x00007fde5a6867b8>
ff.method_names
NoMethodError: undefined method `method_names' for #<First::First_class:0x00007fde5a6867b8>
Did you mean? methods
Now if you call ff.methods you will see all methods defined through standard Ruby inheritance.
As a side note, class names in Ruby conventionally use PascalCase see PascalCase. Mixed_case is discouraged.

Calling original method from a redefined one

My question is based on an answer to the topic “redefining a single ruby method on a single instance with a lambda”.
How can I redefine a method and from within the new method call the original definition? Other instances of some_object's class should not become affected.
def some_object.some_method
# call original `some_object.some_method` here
do_something_else
end
If some_object.some_method is not a singleton method, then you can just call super in your redefined method.
def some_object.some_method
super
do_something_else
end
If some_object.some_method is a singleton method, then
Ruby >= 2.0.0
You can define that method in a module
module SomeModule
def some_method
super
do_something_else
end
end
And then prepend it to the singleton class of the object
some_object.singleton_class.prepend(SomeModule)
Ruby < 2.0.0
You have to make an alias then redefine, since there is no Module#prepend.
class << some_object # open the singleton class of some_object
alias some_method_original some_method
def some_method
some_method_original
do_something_else
end
end
class Klass
def greeting
puts "hiya"
end
def add_personal_greeting(str)
define_singleton_method(:greeting) do
super
puts str
end
end
end
Bob gets a handle and tries the standard greeting.
bob = Klass.new
#=> #<Klass:0x007fa66b084ad0>
bob.greeting
# hiya
He finds that too impersonal so he decides to add "I'm Bob" after the greeting. He does that by defining a method greeting on his singleton class that calls Klass's instance method greeting and then adds another line to the greeting.
bob.add_personal_greeting("I'm Bob")
He tries it.
bob.greeting
# hiya
# I'm Bob
Much better. Note
bob.singleton_class.superclass
#=> Klass
Meanwhile, Lucy tries the standard greeting.
lucy = Klass.new
#=> #<Klass:0x007fa66a9ed050>
lucy.greeting
# hiya
She's not wild about it, but it will do.
Bob decides to change his greeting.
bob.add_personal_greeting("I'm Robert")
and tries it.
bob.greeting
# hiya
# I'm Robert

Execute method like before_filter in Rails

I try to write a metaprogramming for execute a method before 'master' method. Why ? Because, I have several class and it's ugly to repeat the call in the head of the method
Case :
class MyClass
include MySuperModule
before :method, call: before_method
def before_method
puts "Before.."
end
end
class SomeClass < MyClass
def method
puts "Method.."
end
end
module MySuperModule
# the awesome code
end
Output :
SomeClass.new.method => "Before.. Method.."
So, I try write a module with ClassMethodsor method_missingwithout success.
You don't need a gem for simple metaprogramming like this. What you can do is redefine the "after" method to call the "before" method and then the original "after" method.
This works even when using before multiple times on the same method or when creating a chain of before calls.
module MySuperModule
def before meth, opts
old_method = instance_method(meth)
define_method(meth) do
send opts[:call]
old_method.bind(self).call
end
end
end
class MyClass
extend MySuperModule
def foo
puts "foo"
end
def bar
puts "bar"
end
def baz
puts "baz"
end
before :foo, call: :bar
before :bar, call: :baz
end
MyClass.new.foo
# baz
# bar
# foo
If it is just for subclassing purposes you can take advantage of Module#prepend:
class Superclass
def self.inherited(subclass)
# subclass.send :prepend, Module.new { on Ruby < 2.1
subclass.prepend Module.new {
def method
before_method
super
end
}
end
def before_method
puts 'Before'
end
end
class Subclass < Superclass
def method
puts 'Method'
end
end
Subclass.new.method
#=> Before
#=> Method
What you are looking for is Aspect oriented programming support for ruby. There are several gems implementing this, like aquarium.
Another way to do this is to use the rcapture gem.
It is pretty awesome.
Eg:
require 'rcapture'
class A
# Makes the class intercept able
include RCapture::Interceptable
def first
puts 'first'
end
def second
puts 'second'
end
end
# injects methods to be called before each specified instance method.
A.capture_pre :methods => [:first, :second] do
puts "hello"
end
n = A.new
n.first
n.second
produces:
hello
first
hello
second
Maybe you can use a decorator. In ruby there is a nice gem called 'drapeer'. See Drapper Link
Every call in ruby runs through set_trace_func so you can hook into that and call exactly what you want. Not the prettiest solution and there are better ways but it does work. Another option is the Hooks gem, though I haven't tried it myself, it looks like it should give you the ability to do what you want.
module MySuperModule
# the awesome code
end
class MyClass
include MySuperModule
def before_method
puts "Before.."
end
end
class SomeClass < MyClass
def method
puts "Method.."
end
end
set_trace_func proc { |event, file, line, id, binding, class_name|
if event == "call" && class_name == SomeClass && id == :method
caller = binding.eval("self")
caller.send(:before_method)
end
}
SomeClass.new.method
#=> Before..
#=> Method..

Dynamically creating class method

I am writing a class method to create another class method. There seems to be some strangeness around how class_eval and instance_eval operate within the context of a class method. To illustrate:
class Test1
def self.add_foo
self.class_eval do # does what it says on the tin
define_method :foo do
puts "bar"
end
end
end
end
Test1.add_foo # creates new instance method, like I'd expect
Test1.new.foo # => "bar"
class Test2
def self.add_foo
self.instance_eval do # seems to do the same as "class_eval"
define_method :foo do
puts "bar"
end
end
end
end
Test2.add_foo # what is happening here?!
Test2.foo # => NoMethodError
Test2.new.foo # => "bar"
class Test3
def self.add_foo
(class << self; self; end).instance_eval do # call explicitly on metaclass
define_method :foo do
puts "bar"
end
end
end
end
Test3.add_foo # => creates new class method, as I'd expect
Test3.foo # => "bar"
My understanding is that class methods are instance methods defined on the metaclass of the class in question (Test2 in this case). Based on that logic, I would expect the receiver for the class method call add_foo to be the metaclass.
What is self referring to inside the Test2.add_foo method?
Why does calling instance_eval on this receiver object create an instance method?
The main difference between instance_eval and class_eval is that instance_eval works within the context of an instance, while class_eval works within the context of a class. I am not sure how familiar you are with Rails, but let's look at this for an example:
class Test3 < ActiveRecord::Base
end
t = Test3.first
t.class_eval { belongs_to :test_25 } #=> Defines a relationship to test_25 for this instance
t.test_25 #=> Method is defined (but fails because of how belongs_to works)
t2 = Test3.find(2)
t2.test_25 #=> NoMethodError
t.class.class_eval { belongs_to :another_test }
t.another_test #=> returns an instance of another_test (assuming relationship exists)
t2.another_test #=> same as t.another_test
t.class_eval { id } #=> NameError
t.instance_eval { id } #=> returns the id of the instance
t.instance_eval { belongs_to :your_mom } #=> NoMethodError
This happens because belongs_to is actually a method call that happens within the context of the class body, which you cannot call from an instance. When you try to call id with class_eval, it fails because id is a method defined on an instance, not in a class.
Defining methods with both class_eval and instance_eval work essentially the same when called against an instance. They will define a method only on the instance of the object it is called against.
t.class_eval do
def some_method
puts "Hi!"
end
end
t.instance_eval do
def another_method
puts "Hello!"
end
end
t.some_method #=> "Hi!"
t.another_method #=> "Hello!"
t2.some_method #=> NoMethodError
t2.another_method #=> NoMethodError
They differ, however, when dealing with the class.
t.class.class_eval do
def meow
puts "meow!"
end
end
t.class.instance_eval do
def bark
puts "woof!"
end
end
t.meow #=> meow!
t2.meow #=> meow!
t.bark #=> NoMethodError
t2.bark #=> NoMethodError
So where did bark go? It got defined on the instance of the class' singleton class. I'll explain more below. But for now:
t.class.bark #=> woof!
Test3.bark #=> woof!
So to answer your question about what self is referring to within the class body, you can construct a little test:
a = class Test4
def bar
puts "Now, I'm a #{self.inspect}"
end
def self.baz
puts "I'm a #{self.inspect}"
end
class << self
def foo
puts "I'm a #{self.inspect}"
end
def self.huh?
puts "Hmmm? indeed"
end
instance_eval do
define_method :razors do
puts "Sounds painful"
end
end
"But check this out, I'm a #{self.inspect}"
end
end
puts Test4.foo #=> "I'm a Test4"
puts Test4.baz #=> "I'm a Test4"
puts Test4.new.bar #=> Now I'm a #<Test4:0x007fa473358cd8>
puts a #=> But check this out, I'm a #<Class:Test4>
So what is happening here is that in the first puts statement above, we see that inspect is telling us that self within the context of a class method body is referring to the class Test4. In the second puts, we see the same thing, it's just defined differently (using the self.method_name notation for defining class methods). In the third, we see that self refers to an instance of Test4. The last one is a bit interesting because what we see is that self is referring to an instance of Class called Test4. That is because when you define a class, you're creating an object. Everything in Ruby is an object. This instance of object is called the metaclass or the eigenclass or singleton class.
You can access the eigenclass with the class << self idiom. While you're in there, you actually have access to the internals of the eigenclass. You can define instance methods inside of the eigenclass, which is congruent with calling self.method_name. But since you are within the context of the eigenclass, you can attach methods to the eigenclass' eigenclass.
Test4.huh? #=> NoMethodError
Test4.singleton_class.huh? #=> Hmmm? indeed
When you call instance_eval in the context of a method, you're really calling instance_eval on the class itself, which means that you are creating instance methods on Test4. What about where I called instance_eval inside of the eigenclass? It creates a method on the instance of Test4's eigenclass:
Test4.razors #=> Sounds painful
Hopefully, this clears up a few of your questions. I know that I have learned a few things typing out this answer!

Ruby class_eval method

I'm trying to figure out how to dynamically create methods
class MyClass
def initialize(dynamic_methods)
#arr = Array.new(dynamic_methods)
#arr.each { |m|
self.class.class_eval do
def m(*value)
puts value
end
end
}
end
end
tmp = MyClass.new ['method1', 'method2', 'method3']
Unfortunately this only creates the method m but I need to create methods based on the value of m, ideas?
There are two accepted ways:
Use define_method:
#arr.each do |method|
self.class.class_eval do
define_method method do |*arguments|
puts arguments
end
end
end
Use class_eval with a string argument:
#arr.each do |method|
self.class.class_eval <<-EVAL
def #{method}(*arguments)
puts arguments
end
EVAL
end
The first option converts a closure to a method, the second option evaluates a string (heredoc) and uses regular method binding. The second option has a very slight performance advantage when invoking the methods. The first option is (arguably) a little more readable.
define_method(m) do |*values|
puts value
end

Resources