Using class's method can be making instance in RUBY? - ruby

I'm wondering what means 'making instance' in ruby.
I wrote 2 sample codes.
I learned # 1 makes class' instance,
# 1.
now = Time.now
# this makes instance
# 2.
if Time.now.strftime('%Y/%m/%d') > '2022/12/19'
# blabla
end
But in case # 2, using Time class' method can be equal to 'making instance'?
Or in this case, it is just "using class' method"?

You seem to be confused about class methods and instance methods.
Time.now is a class method, and returns an instance of Time.
Time#strftime is an instance method of the Time class, which formats the instance's time.
Time.now.strftime(...) first calls Time.now which returns an instance of Time, then calls that instance's strftime method, to format the time into a string.
A class method can return anything, so of course it can return an instance of that class.
E.g.
class Foo
def self.return_an_instance
self.new
end
def return_three
3
end
end
p Foo
# Foo (the class)
p Foo.return_an_instance
# #<Foo:0x196a> (some instance of the class)
p Foo.return_an_instance.return_three
# 3 (return value of instance method)

Related

Ruby - How to access instance variables from classes with "self" methods?

Sorry that I have no clue how to title this, I'm having a hard time looking this up because I don't know how to say this. Anyway...
Let's say I have a class that looks like this for example:
class Run
def self.starting
print "starting..."
end
def self.finished
print "Finished!"
end
end
All of the methods in Run have self before them, meaning that I don't have to do run = Run.new and I can just do Run.starting. Now let's say that I wanted to add some instance variables...
class Run
attr_accessor :starting, :finished
def self.starting
print "starting..."
#starting = true
#finished = false
end
def self.finished
print "finished!"
#starting = false
#finished = true
end
end
What if I wanted to access those instance variables from outside the class? I know that something like print "#{Run.finished}" or print "#{Run.starting}" won't do anything. Can I do that without run = Run.new? Or should I just remove self and then use run = Run.new? (Sorry if this question is a mess.)
All of the methods in Run have self before them, meaning that I don't have to do run = Run.new and I can just do Run.starting
There's much more to it than this. In your case you're calling class methods. If you did runner = Runner.new - then you'd be calling instance methods (those are defined without self.
In general, if you need "the thing" to hold some kind of state (like #running = true) then you'd rather want to instantiate an object, and call those methods.
Now, #whatever are instance variables, and you don't have the access to them in class methods.
class Run
attr_reader :running
def start
#running = true
end
def stop
#running = false
end
end
runner = Run.new
runner.running # nil
runner.start
runner.running # true
runner.stop
runner.running # false
I'd recommend you doing some tutorial or basic level book on rails programming, find a chapter about objects and classes. Do some exercises.
In Ruby instance variables are just lexical variables scoped to an instance of a class. Since they are scoped to the instance they always act like a private variable.
If you want to provide access to an instance variable from the outside you create setter and getter methods. Thats what attr_accessor does.
class Person
attr_accessor :name
def initialize(name:)
#name = name
end
def hello
"Hello my name is #{#name}"
end
end
john = Person.new(name: 'John')
john.name = "John Smith"
puts john.hello # "Hello my name is John Smith"
puts john.name # "John Smith"
Methods defined with def self.foo are class methods which are also referred to as singleton methods. You can't access variables belonging to an instance from inside a class method since the recipient when calling the method is the class itself and not an instance of the class.
Ruby also has class variables which are shared by a class and its subclasses:
class Person
##count = 0
def initialize
self.class.count += 1
end
def self.count
##count
end
def self.count=(value)
##count = value
end
end
class Student < Person
end
Person.new
Student.new
puts Person.count # 2 - wtf!
And class instance variables that are not shared with subclasses:
class Person
#count = 0 # sets an instance variable in the eigenclass
def initialize
self.class.count += 1
end
def self.count
#count
end
def self.count=(value)
#count = value
end
end
class Student < Person
#count = 0 # sets its own class instance variable
end
Person.new
Student.new
puts Person.count # 1
Class variables are not used as often and usually hold references to things like database connections or configuration which is shared by all instances of a class.
You can't access instance variables from outside the instance. That is the whole point of instance variables.
The only thing you can access from outside the instance are (public) methods.
However, you can create a public method that returns the instance variable. Such a method is called an attribute reader in Ruby, other languages may call it a getter. In Ruby, an attribute reader is typically named the same as the instance variable, but in your case that is not possible since there are already methods with the names starting and finished. Therefore, we have to find some other names for the attribute readers:
class Run
def self.starting?
#starting
end
def self.finished?
#finished
end
end
Since this is a common operation, there are helper methods which generate those methods for you, for example Module#attr_reader. However, they also assume that the name of the attribute reader method is the same as the name of the instance variable, so if you were to use this helper method, it would overwrite the methods you have already written!
class << Run
attr_reader :starting, :finished
end
When you do this, you will get warnings (you always have warning turned on when developing, do you?) telling you that you have overwritten your existing methods:
run.rb:19: warning: method redefined; discarding old starting
run.rb:2: warning: previous definition of starting was here
run.rb:19: warning: method redefined; discarding old finished
run.rb:5: warning: previous definition of finished was here

What's the difference between Kernel.require and Kernel.gem_original_require?

Consider the following script:
module Kernel
unless defined?(gem_original_require_2)
alias gem_original_require_2 require
private :gem_original_require_2
end
def require(path)
return gem_original_require_2(path)
end
end
p method(:require) # #<Method: main.require>
p method(:require).owner # Kernel
p method(:require).receiver # main
p method(:require).source_location # ["1.rb", 7]
puts '-' * 10
p Kernel.method(:require) # #<Method: Kernel.require>
p Kernel.method(:require).owner # #<Class:Kernel>
p Kernel.method(:require).receiver # Kernel
p Kernel.method(:require).source_location # nil
puts '-' * 10
p Kernel.method(:gem_original_require) # #<Method: Kernel.gem_original_require(require)>
p Kernel.method(:gem_original_require).owner # Kernel
p Kernel.method(:gem_original_require).receiver # Kernel
p Kernel.method(:gem_original_require).source_location # nil
puts '-' * 10
p Kernel.method(:gem_original_require_2) # #<Method: Kernel.gem_original_require_2(require)>
p Kernel.method(:gem_original_require_2).owner # Kernel
p Kernel.method(:gem_original_require_2).receiver # Kernel
p Kernel.method(:gem_original_require_2).source_location # ["/home/yuri/.rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb", 34]
I've got a lot of questions about the output. Why is Kernel sometimes a class, sometimes a module? Why do they have different receivers? Does receiver become self when method gets called?
But more importantly, are Kernel.require and Kernel.gem_original_require the same method? Is there another place where Kernel.require gets overridden? If you can answer the rest of the questions, that would be awesome.
Let me put it another way. Let's try to reproduce this issue with run-of-the-mill classes and methods. As stated in the other question defining a method on Kernel creates 2 methods (instance and singleton). So:
class MyKernel
# original require
def require; puts 'require'; end
def self.require; puts 'require'; end
# copy original require
alias gem_original_require require
class << self
alias gem_original_require require
end
end
main = MyKernel.new
Kernel is in fact a module, but supposedly that doesn't matter here.
p MyKernel.method(:require)
== MyKernel.method(:gem_original_require)
# true
p main.method(:require)
== main.method(:gem_original_require)
# true
p main.method(:require)
== MyKernel.method(:require)
# false
So, supposedly to compare methods you've got to access them both either via a class, or via an instance. Let's override require:
class MyKernel
# override one of the original require's
def require; puts 'require'; end
end
Now we've got 3 requires:
main.require (original)
MyKernel.require
main.require
and 2 gem_original_requires:
main.gem_original_require
MyKernel.gem_original_require
We can compare like with like (instance methods with instance methods, singleton with singleton). But we no longer have access to the original main.require, so that leaves only singleton methods. And the following still holds:
p MyKernel.method(:require)
== MyKernel.method(:gem_original_require)
# true
But not in case of the real require:
p Kernel.method(:require)
== Kernel.method(:gem_original_require)
# false
I think you'll find part of the answer here: https://stackoverflow.com/a/57236134/6008847
Rubygems code replaces the included version of require.
When you call Kernel.require you get the original require method.
When you cal require you get the method from Rubygems (the receiver will be the context from where you call require).
gem_original_require is an alias for the original require method: https://github.com/ruby/ruby/blob/v2_6_3/lib/rubygems/core_ext/kernel_require.rb#L16
To avoid ambiguity I'm going to call methods owned by singleton classes singleton methods, the rest are instance methods.
class A
def m; end # instance method
def self.m; end # singleton method
end
(Although one might say that a singleton method is an instance method of the corresponding singleton class.)
One might call singleton methods class methods, but that would be oversimplification. Objects can also have singleton methods:
a = A.new
def a.m2; end
p a.singleton_methods(false).include? :m2 # true
p a.singleton_class.instance_methods(false).include? :m2 # true
Instance methods are often called via an instance of a class/module (e.g. [].compact, compact is owned by the Array class). But not necessarily so (an example is one of the reasons which led to me creating the question). What adds to the confusion are the methods/instance_methods methods. A.methods returns singleton methods of the object A, and A.new.instance_methods... is unavailable. So generally you want to call methods on an instance of a class, and instance_methods on a class/module, but... that depends. For those willing to understand this better I suggest these two links.
Now, as stated in the answer suggested by Gimmy, require is a global function. Which means that it's defined as both a Kernel's private instance method, and a Kernel's singleton method.
$ ruby --disable-gems -e 'p [
Kernel.private_instance_methods(false).include?(:require),
Kernel.singleton_class.instance_methods(false).include?(:require),
Kernel.instance_method(:require) == Kernel.singleton_class.instance_method(:require)
]'
[true, true, false]
Which means that you can call it basically from anywhere with require as an instance method (since almost all the objects are inherited from Object, and Kernel is included into Object):
require 'time'
Alternatively, you can call it (the other instance of the method) with Kernel.require as a singleton method:
Kernel.require 'time'
That, in its turn, means that rubygems create an alias of only the private instance method require.
About the second part:
class MyKernel
# original require
def require; puts 'require'; end # (1)
def self.require; puts 'require'; end # (2)
# copy original require
alias gem_original_require require # (3)
class << self
alias gem_original_require require # (4)
end
end
main = MyKernel.new
class MyKernel
# override one of the original require's
def require; puts 'require'; end
end
p MyKernel.method(:require)
== MyKernel.method(:gem_original_require)
# true
p Kernel.method(:require)
== Kernel.method(:gem_original_require)
# false
r.method(:m) returns the method that would be called if you called r.m.
As such MyKernel.method(:require) refers to the singleton method of the MyKernel class (2).
And MyKernel.method(:gem_original_require) refers to its alias (4).
In the second statement Kernel.method(:require) refers to the singleton method of the Kernel module. But Kernel.method(:gem_original_require) refers to an alias of the private instance method of the module Kernel. That is, the Kernel module itself doesn't have a singleton method gem_original_require, but since Kernel is also an object, and Object has a private instance method gem_original_require (included from the Kernel module), that is what is returned by Kernel.method(:gem_original_require):
p \
Kernel.singleton_class
.instance_methods(false).include?(:gem_original_require),
# false
Kernel.singleton_class
.private_instance_methods(false).include?(:gem_original_require),
# false
Kernel.class.ancestors,
# [Module, Object, Kernel, BasicObject]
Kernel.private_instance_methods(false).include?(:gem_original_require)
# true
To make it close enough to what happens with require:
module Kernel
def m; end
private :m
def self.m; end
alias malias m
end
p Kernel.method(:m) == Kernel.method(:malias) # false
About the first part:
def pmethod n, m
puts n + ': ' + m.inspect
puts ' owner: ' + m.owner.inspect
puts ' receiver: ' + m.receiver.inspect
puts ' source_location: ' + m.source_location.inspect
end
def hr
puts '-' * 3
end
module Kernel
unless defined?(gem_original_require_2)
alias gem_original_require_2 require
private :gem_original_require_2
end
def require(path)
return gem_original_require_2(path)
end
end
pmethod 'require', method(:require)
pmethod 'Kernel.require', Kernel.method(:require)
pmethod 'Kernel.gem_original_require', Kernel.method(:gem_original_require)
pmethod 'Kernel.gem_original_require_2', Kernel.method(:gem_original_require_2)
Let me first say a couple of words about the way ruby outputs objects:
p Object #=> Object
p Object.new #=> #<Object:0x000055572f38d2a0>
p Kernel #=> Kernel
In case of singleton classes the output is of the form:
#<Class:original_object>
Given a singleton class S, original_object is an object such that original_object.singleton_class == S.
p Kernel.singleton_class #=> #<Class:Kernel>
p Object.new.singleton_class #=> #<Class:#<Object:0x000055cad6695428>>
Instances of Method/UnboundMethod are output this way:
#<Method: receiver(owner)#method(original_method)>
r.method(:m) returns the method that would be called if you did r.m. r is the receiver, m is the method. Depending on the type of the method (singleton/instance) the receiver in the output means different things: for instance methods it's the class of the receiver, for singleton methods the receiver itself. owner is the class/module that owns the method (the class/module o such that o.instance_methods(false).include?(:m) == true):
class A
def m; end # owner is A
# A.instance_methods(false).include?(:m) == true
def self.m; end # owner is the singleton class of A
# A.singleton_class
# .instance_methods(false).include?(:m) == true
end
If an owner is a singleton class, its original object is displayed (an object o such that o.singleton_class == owner).
When the receiver and the owner match, the owner is not duplicated in parenthesis.
If the method is an alias, then the original method is specified in parenthesis.
. is used in place of # for singleton methods (methods owned by singleton classes).
Now the output should be self-explanatory:
require: #<Method: Object(Kernel)#require>
owner: Kernel
receiver: main
source_location: ["a.rb", 17]
An instance method (owned by the Kernel module), the receiver is main (of the Object class).
Kernel.require: #<Method: Kernel.require>
owner: #<Class:Kernel>
receiver: Kernel
source_location: nil
A singleton method (owned by the singleton class of the Kernel module), the receiver is Kernel. Considering that the owner is a singleton class, its original class (Kernal) is taken for the owner part. And since the resulting owner and receiver are both Kernel, the name is not duplicated.
Kernel.gem_original_require: #<Method: Module(Kernel)#gem_original_require(require)>
owner: Kernel
receiver: Kernel
source_location: nil
Kernel.gem_original_require_2: #<Method: Module(Kernel)#gem_original_require_2(require)>
owner: Kernel
receiver: Kernel
source_location: ["/usr/local/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb", 34]
These two are aliases of the method require. They are instance methods, which means the originals are also instance methods. The receiver is Kernel (of the Module class). The owner is Kernel.
To answer the rest of the question:
Why is Kernel sometimes a class, sometimes a module?
#<Class:Kernel> is the ruby's way of outputting a singleton class of Kernel.
Why do they have different receivers?
The receiver is the object whose method is being called. If the receiver is specified explicitly (e.g. Kernel.require), it's the object before the dot. In other cases it's self. And at the top level self is the main object.
Does a receiver become self when a method gets called?
Supposedly yes.
are Kernel.require and Kernel.gem_original_require the same method?
No, the first one is the Kernel's singleton method (an instance method of the Kernel's singleton class), the second one is a private instance method of the Kernel module.
Is there another place where Kernel.require gets overridden?
Not that I know of.
So, supposedly to compare methods you've got to access them both either via a class, or via an instance.
We can compare like with like (instance methods with instance methods, singleton with singleton).
They must refer to the same instance of a method, and the receivers must match:
class A
def m; end
alias ma m
end
class B < A; end
a = A.new
b = B.new
p a.method(:m) == a.method(:ma) #=> true
p a.method(:m) == b.method(:m) #=> false
# receivers do not match
p A.instance_method(:m) == B.instance_method(:m) #=> false
# receivers do not match
p a.method(:m) == A.instance_method(:m) #=> false
# #<Method:...> can't be equal #<UnboundMethod:...>
The issue with Kernel.method(:require) != Kernel.method(:gem_original_require) is that they belong to different classes/modules (Kernel.singleton_class and Kernel), although the receivers and method definitions match.

Difference between #foo, self.foo, and foo?

class Artist
##song_count = []
attr_accessor :name, :songs
def initialize(name)
#name = name
#songs = []
end
def add_song(song)
#songs << song
end
def print_songs
songs.each {|song| puts song.name}
end
end
So in this example, it uses all two types, #songs and songs.
I'm having a hard time understanding why these are used, instead of using #songs for everything.
And then in this example,
def add_song(song)
self.songs << song
song.artist = self
##song_count +=1
end
Why is self.songs used instead of #songs?
Ok, so I forgot to say one more thing. In the first code snippet above,for method print_songs, why am I able to use songs.each instead of #songs.each? I was expected it to generate an error undefined songs.
Why is self.songs used instead of #songs
Using the method is more flexible. You're abstracting yourself from knowing how exactly it gets/stores data. The less you rely on implementation details, the easier it will be for you to change code later.
One small example, consider this implementation of songs
def songs
#songs ||= []
#songs
end
#songs may or may not have been assigned value prior to invocation of this method. But it doesn't care. It makes sure that #songs does have a sane default value. The concept is called "lazy initialization" and it's very tedious and error-prone to do if you use instance variables directly.
So, when in doubt, always use methods.
Difference between foo and #foo
Instance variables
Instance variables are defined within instance methods, and their names begin with #. Their value is only accessible within the specific object on which it was set. In other words, when we modify the value of an instance variable, the change only applies to that particular instance. Unlike local variables which are only available within the method where they were defined, instance variables are accessible by all methods within the object (instance methods of the class). Instance variables are the most commonly used type of variable in Ruby classes.
class Car
attr_reader :color
def set_color(color_receiverd_as_argument)
#color = color_receiverd_as_argument
end
end
car1 = Car.new
car1.color # Output: => nil
car1.set_color "black"
car1.color # Output: => "black"
car2 = Car.new
car2.set_color "silver"
car2.color # Output: => "silver"
In the example above, notice that:
Trying to access an instance variable before it's initialized will not raise an exception. Its default value is nil.
Changing the value of the color variable in one instance of the Car class does not affect the value of the same variable in the other instances.
Local variables
A local variable within a class is like any other local variable in Ruby. It is only accessible within the exact scope on which it's created. If defined within a method, it is only available inside that method.
class Car
def initialize
wheels = 4
end
def print_wheels
print wheels
end
end
c = Car.new
c.print_wheels # Output: NameError: undefined local variable or method `wheels'…
The self keyword
The self keyword is always available, and it points to the current object. In Ruby, all method calls consist of a message sent to a receiver. In other words, all methods are invoked on an object. The object on which the method is called is the receiver, and the method is the message. If we call "foo".upcase, the "foo" object is the receiver and upcase is the message. If we don't specify an object (a receiver) when calling a method, it is implicitly called on the self object.
Self keyword at class level
When used within a class but outside any instance methods, self refers to the class itself.
class Foo
##self_at_class_level = self
def initialize
puts "self at class level is #{##self_at_class_level}"
end
end
f = Foo.new # Output: self at class level is Foo
Self keyword at instance methods
When inside an instance method, the self keyword refers to that specific instance. In other words, it refers to the object where it was called.
class Meditation
def initialize
puts "self within an instance method is #{self}"
end
end
zazen = Meditation.new # Output: self within an instance method is #<Meditation:0x00000000ab2b38>
Notice that #<Meditation:0x00000000ab2b38> is a string representation of the zazen object, which is an instance of the Meditation class.

Binding method to instance

Is there a way to bind an existing method to an existing instance of an object if both the method and the instance are passed as symbols into a method that does that if the instance is not a symbol?
For example:
def some_method
#do something
end
some_instance = Klass.new(something)
def method_that_binds(:some_method, to: :some_instance)
#how do I do that?
end
Your requirements are a little unusual, but it is possible to do this mostly as you say:
class Person; end
harry = Person.new
barry = Person.new
def test
puts 'It works!'
end
define_method :method_that_binds do |a_method, to|
eval(to[:to].to_s).singleton_class.send(:define_method, a_method, &Object.new.method(a_method))
end
method_that_binds :test, to: :harry
harry.test
# It works! will be sent to STDOUT
barry.test
# undefined method 'test'
This doesn't actually use a named parameter, but accepts a hash with a to key, but you can see you can call it in the way you want. It also assumes that the methods you are defining are defined globally on Object.
The API you want doesn't easily work, because you have to know from which scope you want to access the local variable. It's not quite clear to me why you want to pass the name of the local variable instead of passing the content of the local variable … after all, the local variable is present at the call site.
Anyway, if you pass in the scope in addition to the name, this can be accomplished rather easily:
def some_method(*args)
puts args
puts "I can access some_instance's ivar: ##private_instance_var"
end
class Foo; def initialize; #private_instance_var = :foo end end
some_instance = Foo.new
def method_that_binds(meth, to:, within:, with: [])
self.class.instance_method(meth).bind(within.local_variable_get(to)).(*with)
end
method_that_binds(:some_method, to: :some_instance, within: binding, with: ['arg1', 'arg2'])
# arg1
# arg2
# I can access some_instance's ivar: foo
As you can see, I also added a way to pass arguments to the method. Without that extension, it becomes even simpler:
def method_that_binds(meth, to:, within:)
self.class.instance_method(meth).bind(within.local_variable_get(to)).()
end
But you have to pass the scope (Binding) into the method.
If you'd like to add a method just to some_instance i.e. it's not available on other instances of Klass then this can be done using define_singleton_method (documentation here.)
some_instance.define_singleton_method(:some_method, method(:some_method))
Here the first use of the symbol :some_method is the name you'd like the method to have on some_instance and the second use as a parameter to method is creating a Method object from your existing method.
If you'd like to use the same name as the existing method you could wrap this in your own method like:
def add_method(obj, name)
obj.define_singleton_method(name, method(name))
end
Let's say we have a class A with a method a and a local variable c.
class A
def a; 10 end
end
c = '5'
And we want to add the method A#a to c.
This is how it can be done
c.singleton_class.send :define_method, :b, &A.new.method(:a)
p c.b # => 10
Explanations.
One way to add a method to an object instance and not to its class is to define it in its singleton class (which every ruby object has).
We can get the c's singleton class by calling the corresponding method c.signleton_class.
Next we need to dynamically define a method in its class and this can usually be accomplished by using the define_method which takes a method name as its first argument (in our case :b) and a block. Now, converting the method into a block might look a bit tricky but the idea is relatively simple: we first transform the method into a Method instance by calling the Object#method and then by putting the & before A.new.method(:a) we tell the interpreter to call the to_proc method on our object (as our returned object is an instance of the Method, the Method#to_proc will be called) and after that the returned proc will be translated into a block that the define_method expects as its second argument.

What is the difference between class_eval, class_exec, module_eval and module_exec?

I am reading the Module documentation but can't seem to understand their differences and which should be used where.
How is the eval different than exec?
I'm going to answer a bit more than your question by including instance_{eval|exec} in your question.
All variations of {instance|module|class}_{eval|exec} change the current context, i.e. the value for self:
class Array
p self # prints "Array"
43.instance_eval{ p self } # prints "43"
end
Now for the differences. The eval versions accepts a string or a block, while the exec versions only accept a block but allow you to pass parameters to it:
def example(&block)
42.instance_exec("Hello", &block)
end
example{|mess| p mess, self } # Prints "Hello" then "42"
The eval version does not allow to pass parameters. It provides self as the first parameter, although I can't think of a use for this.
Finally, module_{eval|exec} is the same as the corresponding class_{eval|exec}, but they are slightly different from instance_{eval|exec} as they change what is the current opened class (i.e. what will be affected by def) in different ways:
String.instance_eval{ def foo; end }
Integer.class_eval { def bar; end }
String.method_defined?(:foo) # => false
String.singleton_methods.include?(:foo) # => true
Integer.method_defined?(:bar) # => true
So obj.instance_{eval|exec} opens the singleton class of obj, while mod.{class|module}_{eval|exec} opens mod itself.
Of course, instance_{eval|exec} are available on any Ruby object (including modules), while {class|module}_* are only available on Module (and thus Classes)
To answer your last question first, eval (in all its variations) is completely different from exec. exec $command will start a new process to run the command you specify and then exit when that finishes.
class_eval and module_eval have the power to redefine classes and modules -- even those that you yourself did not write. For example, you might use class eval to add a new method that did not exist.
Fixnum.class_eval { def number; self; end }
7.number # returns '7'
class_eval can be used to add instance methods, and instance_eval can be used to add class methods (yes, that part is very confusing). A class method would be something like Thing.foo -- you're literally calling the foo method on the Thing class. An instance method is like the example above, using class_eval I've added a number method to every instance of Fixnum.
Okay, so that's the *_eval class of methods. The exec methods are similar, but they allow you to look inside a class and execute a block of code as though it was defined as a method on that class. Perhaps you have a class that looks like this:
class Foo
##secret = 'secret key'
##protected = 'some secret value'
def protected(key)
if key == ##secret
return ##protected
end
end
end
The class Foo is just a wrapper around some secret value, if you know the correct key. However, you could trick the class into giving you its secrets by executing a block inside the context of the class like so:
Foo.class_exec { ##secret = 'i'm a hacker' }
Foo.protected('i'm a hacker') #returns the value of ##protected because we overwrote ##secret
In general, with a lot of the tools in ruby, you could use any of these to solve a lot of problems. A lot of the time you probably won't even need to unless you want to monkey patch a class some library you use has defined (although that opens up a whole can of worms). Try playing around with them in irb and see which you find easier. I personally don't use the *_exec methods as much as the *_eval methods, but that's a personal preference of mine.
To avoid ambiguity I'm going to call a method that belongs to (owned by) a singleton class a singleton method. The rest are instance methods. Although one might say that a singleton method of an object is an instance method of its singleton class.
tl;dr Use class_eval/module_eval on a class/module to define instance methods, and instance_eval on a class/module to define class methods (or to be more precise, use instance_eval to define singleton methods). Additionally you can use instance_eval to access instance variables.
A terminology is a bit lacking in this case. ruby maintains a stack of class references (cref for short). When you open/reopen a class, the corresponding class reference is pushed to the stack. And the current class refernece affects where def defines methods (to which class/module they're added).
Now, class_eval/module_eval and class_exec/module_exec are aliases.
The *_exec() variants don't accept strings, and allow to pass arguments to the block. Since the *_eval() variants are mainly used I'll focus on them.
class_eval/module_eval changes cref and self to the receiver (Thing in Thing.module_eval(...)):
rb_mod_module_eval() -> specific_eval()
yield_under() (for blocks)
vm_cref_push()
eval_under() (for strings)
vm_cref_push()
instance_eval changes cref to the singleton class of the receiver, and self to the receiver.
Let's see them in action:
class A
p self #=> A
#a = 1
def initialize
#b = 2
end
end
p A.instance_variables #=> [:#a]
p A.new.instance_variables #=> [:#b]
#a on a class level adds an instance variable to the class A as an object. I'm adding it here for completeness. But that's not how you add a class variable.
A.instance_eval do
p self #=> A
p #a #=> 1
def m() puts 'm' end
end
sclass = A.singleton_class
p sclass.instance_methods(false).include? :m #=> true
A.m #=> m
a = A.new
a.instance_eval do
p self #=> #<A:0x00007fc497661be8 #b=2>
p #b #=> 2
def m2() puts 'm2' end
end
sclass = a.singleton_class
p sclass.instance_methods(false).include? :m2 #=> true
a.m2 #=> m2
So, inside instance_eval def adds a singleton method to the receiver (an instance method to the singleton class of the receiver). For a class/module that means a class/module method. For other objects, a method that is available for that particular object.
A.class_eval do
p self #=> A
p #a #=> 1
def m() puts 'm' end
end
p A.instance_methods(false).include? :m #=> true
A.new.m #=> m
And, inside class_eval def adds an instance method to the receiver itself (the class/module). class_eval is only available for classes/modules.
Also, when class_eval is passed a block, constant/class variable lookup is not affected:
module A
C = 1
##c = 1
class B
C = 2
##c = 2
end
A::B.class_eval { p [C, ##c] } #=> [1, 1]
A::B.class_eval 'p [C, ##c]' #=> [2, 2]
end
The naming is confusing. I might guess that instance in instance_eval suggests that receiver is treated as an instance (allows to change things for a particular instance), and class in class_eval as a class (allows to change things for a class of objects).

Resources