Ruby: is_a? and instance_of? in BasicObject - ruby

How do is_a? and instance_of? methods work with a subclass of BasicObject ?
class My < BasicObject
DELEGATE = [:is_a?, :instance_of?]
def method_missing(name, *args, &blk)
superclass unless DELEGATE.include? name
::Kernel.send(name,*args, &blk)
end
end
my = My.new
my.is_a? BasicObject #=> true
my.is_a? My #=> false ???
my.instance_of? My #=> false ???

You can steal is_a? from Kernel:
class My < BasicObject
define_method(:is_a?, ::Kernel.method(:is_a?))
end
m = My.new
m.is_a?(My) #=> true
m.is_a?(BasicObject) #=> true
m.is_a?(Object) #=> false
If you're going to build your own object hierarchy, you could also define your own Kernel, something like:
module MyKernel
[:is_a?, :instance_of?, :class].each do |m|
define_method(m, ::Kernel.method(m))
end
end
class My < BasicObject
include ::MyKernel
end

::Kernel.send(name,*args, &blk) calls the method name on the class Kernel with the arguments args and the block &blk.
When you run my.is_a? My name is :is_a?, *args is My, and &blk is nil. You're really running Kernel.is_a? My.
Instead, if you want to reimplement is_a? for BasicObject you can walk your class's ancestors...
def is_a?(target)
# I don't know how to get the current class from an instance
# that isn't an Object, so I'm hard coding the class instead.
return ::My.ancestors.include?(target)
end

Related

def vs. define_method in 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

How do I make a class whose constructor looks like the constructor of a built-in class?

Complex is a built-in class. To make a Complex object, I write:
Complex(10, 5)
But if I create my own class Thing:
class Thing
def initalize()
end
end
to create a new Thing, I have to write:
Thing.new(...)
Is it possible to create a constructor for Thing so I can write:
Thing(...)
and have it act just like a built-in class such as Complex(1,1)?
Complex can refer to either the Complex class, or to the Complex method defined in Kernel:
Object.const_get(:Complex) #=> Complex
Object.method(:Complex) #=> #<Method: Class(Kernel)#Complex>
The latter is called a global method (or global function). Ruby defines these methods in Kernel as both, private instance methods:
Kernel.private_instance_methods.grep /^[A-Z]/
#=> [:Integer, :Float, :String, :Array, :Hash, :Rational, :Complex]
and singleton methods:
Kernel.singleton_methods.grep /^[A-Z]/
#=> [:Integer, :Float, :String, :Array, :Hash, :Rational, :Complex]
Just like any other method in Kernel:
These methods are called without a receiver and thus can be called in functional form
You can use module_function to add your own global method to Kernel:
class Thing
end
module Kernel
module_function
def Thing
Thing.new
end
end
Thing #=> Thing <- Thing class
Thing() #=> #<Thing:0x007f8af4a96ec0> <- Kernel#Thing
Kernel.Thing() #=> #<Thing:0x007fc111238280> <- Kernel::Thing
class Dog
def initialize(name)
#name = name
end
def greet
puts 'hello'
end
end
def Dog(x)
Dog.new(x) #Create a new instance of the Dog class and return it.
end
d = Dog("Rover")
d.greet
--output:--
hello

Setting not initialized instance variable

Is it elegant to use instance variables in a class which are not initialized and setting them using other methods? Or maybe there is a better way to do that?
class Klass
def initialize(a)
#a = a
end
def set_b(b)
#b = b
end
end
In contrast to other languages, If you do not initialize an instance variable it will always be nil (whereas in certain other languages you could get something undefined).
As long as other methods of Klass do not depend on the instance variable actually having a value, this should be ok.
As for getters and setters, there are attr_accessor, attr_reader and attr_writer (see the docs).
class Klass
attr_accessor :b
# there's also attr_reader and attr_writer
def initialize(a)
#a = a
end
end
k = Klass.new :foo
k.b = :bar
k.b
#=> :bar
k.a
#=> undefined method `a' for #<Klass:0x007f842a17c0e0 #a=:foo, #b=:bar> (NoMethodError)
The way you are doing it works but Ruby defined attr_accessor, attr_reader and attr_writer for that purpose.
attr_reader: create method to read 'a'
attr_writer: create method to write 'a'
attr_accessor: create methods to read and write 'a'
I think the best way to do that is to use attr_accessor:a
class Klass
attr_accessor:a
def initialize(a)
#a = a
end
end
Then you can do:
k = Klass.new "foo" #example
k.a = "bar"

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!

`respond_to?` vs. `respond_to_missing?`

What is the point of defining respond_to_missing? as opposed to defining respond_to?? What goes wrong if you redefine respond_to? for some class?
Without respond_to_missing? defined, trying to get the method via method will fail:
class Foo
def method_missing name, *args
p args
end
def respond_to? name, include_private = false
true
end
end
f = Foo.new
f.bar #=> []
f.respond_to? :bar #=> true
f.method :bar # NameError: undefined method `bar' for class `Foo'
class Foo
def respond_to? *args; super; end # “Reverting” previous redefinition
def respond_to_missing? *args
true
end
end
f.method :bar #=> #<Method: Foo#bar>
Marc-André (a Ruby core committer) has a good blog post on respond_to_missing?.
It's a good practice to create respond_to_missing? if you are overriding method_missing. That way, the class will tell you the method you are calling exists, even though it's not explicitly declared.
respond_to? should probably not be overriden :)

Resources