Can someone help me make sense of the following? I have the following code in test.rb:
class Dog
end
// bark is declared outside of Dog class
def bark
puts 'Woof!'
end
then in irb:
>> source 'test.rb'
>> a = Dog.new
=> #<Dog:0x117f614>
>> a.bark
Woof!
=> nil
Why does method bark exist in Dog instance even though it is declared outside of the class? Because it's in the same file? Thanks!
When you create a method in the "global" scope (outside of any class), that method is made a private method of Object:
#!/usr/bin/ruby1.8
class Dog
end
p Object.respond_to?(:bark, true) # => false
def bark
puts "Woof!"
end
p Object.respond_to?(:bark, true) # => true
Object is in the ancestry chain of all objects, including Dog:
dog = Dog.new
p dog.class.name # => "Dog"
p dog.class.superclass.name # => "Object"
Therefore dogs (and indeed all objects) now know how to bark. However, the method being private, you'll have to use instance_eval to call it with an explicit receiver:
dog.instance_eval { bark } # => "Woof!"
Or you can call it with an implicit receiver with no gymnastics needed:
bar # => "Woof!"
Your exact example doesn't work in Ruby 1.9. (Apart from the bad comment syntax.)
However, declaring a method in the top level scope will make it a private method on Object, apparently:
>> Object.private_methods.include? :bark
=> true
Perhaps in your Ruby (1.8?), this is a public method?
Related
My understanding of instance_eval was that if I have module M then the following were equivalent:
module M
def foo
:foo
end
end
class C
class << self
include M
end
end
puts C.foo
equivalent to:
module M
def foo
:foo
end
end
class C
end
C.instance_eval do
include M
end
puts C.foo
However, the first example prints :foo and the second throws a NoMethodError? (Ruby 2.3.0)
In both cases above, if I had replaced:
include M
with:
def foo
:foo
end
ie directly defining the method rather than including a module then both cases would have resulted in a C.foo method being defined. Should I be surprised at this difference between include and defining the method directly?
Or does it ever even make sense to call include within the context of instance_eval? Should it only ever be called within a class_eval?
In each of these cases, what object are you calling include on? In your first example, you're calling include on C's singleton class:
class C
class << self
p self == C.singleton_class
include M
end
end
# => true
p C.foo
# => :foo
...so your include line is equivalent to C.singleton_class.include(M).
In your second example, however, you're calling include on C itself:
class C
end
C.instance_eval do
p self == C
include M
end
# => true
p C.foo
# => NoMethodError: undefined method `foo' for C:Class
p C.new.foo
# => :foo
...so you're doing the equivalent of C.include(M), which is the same as:
class C
p self == C
include M
end
# => true
p C.new.foo
# => :foo
What would work like you want would be to call instance_eval on C's singleton class:
class D
end
D.singleton_class.instance_eval do
p self == D.singleton_class
include M
end
# => true
p D.foo
# => :foo
Module#class_eval() is very different from Object#instance_eval(). The instance_eval() only changes self, while class_eval() changes both self and the current class.
Unlike in your example, you can alter class_instance vars using instance_eval though, because they are in the object scope as MyClass is a singleton instance of class Class.
class MyClass
#class_instance_var = 100
##class_var = 100
def self.disp
#class_instance_var
end
def self.class_var
##class_var
end
def some_inst_method
12
end
end
MyClass.instance_eval do
#class_instance_var = 500
def self.cls_method
##class_var = 200
'Class method added'
end
def inst_method
:inst
end
end
MyClass.disp
#=> 500
MyClass.cls_method
#=> 'Class method added'
MyClass.class_var
#=> 100
MyClass.new.inst_method
# undefined method `inst_method' for #<MyClass:0x0055d8e4baf320>
In simple language.
If you have a look in the upper class defn code as an interpreter, you notice that there are two scopes class scope and object scope. class vars and instance methods are accessible from object scope and does not fall under jurisdiction of instance_eval() so it skips such codes.
Why? because, as the name suggests, its supposed to alter the Class's instance(MyClass)'s properties not other object's properties like MyClass's any object's properties. Also, class variables don’t really belong to classes—they belong to class hierarchies.
If you want to open an object that is not a class, then you can
safely use instance_eval(). But, if you want to open a class definition and define methods with def or include some module, then class_eval() should be your pick.
By changing the current class, class_eval() effectively reopens the class, just like the class keyword does. And, this is what you are trying to achieve in this question.
MyClass.class_eval do
def inst_method
:inst
end
end
MyClass.new.inst_method
#=> :inst
In Ruby, the Struct class's new method creates a subclass of Struct that behaves differently based on the parameters passed to it. How do I do something similar with my own class in Ruby? (I would have just copied Struct's source code, except it's written in C.)
irb(main):001:0> Foo = Struct.new(:foo, :bar)
=> Foo
irb(main):002:0> x = Foo.new
=> #<struct Foo foo=nil, bar=nil>
irb(main):003:0> Foo.superclass
=> Struct
class A
def self.new; Class.new(self) end
end
A.new # => #<Class:0x007f009b8e4200>
Edit This might better fit the OP's intention.
class A
singleton_class.class_eval{alias :old_new :new}
def self.new
Class.new(self){singleton_class.class_eval{alias :new :old_new}}
end
end
I was just playing to see the instance method calls from the subclass and used the below code of my test:
class Animal
def bark
p "hukkhh"
end
end
#=> nil
class Cow < Animal
end
#=> nil
Cow.public_instance_method(:bark)
#=> #<UnboundMethod: Cow(Animal)#bark>
class Cow
bark
end
#=> NameError: undefined local variable or method `bark' for Cow:Class
# from (irb):11:in `<class:Cow>'
# from (irb):10
# from C:/Ruby193/bin/irb:12:in `<main>'
From that code I was confirmed that instance method can't be executed without the object instance of the respective class.
But then I tried below code:
def talk
p "hi"
end
#=> nil
Object.public_instance_method(:talk)
#=> #<UnboundMethod: Object#talk>
class Foo
talk
end
# prints: hi
#=> "hi"
Here the output made me confused with my first test code output.
Could anyone help me to understand the fact behind these above?
Method talk you defined is belongs to Object which is root of all objects. So talk method is available to all object in ruby.
def talk
p "hi"
end
Object.respond_to? :talk #=> true
Now, you defined class Foo which is also an ruby object.
Foo.is_a? Object #=> true
Foo.respond_to? :talk #=> ture
So talk is available to Foo class.
Now you define a class Animal with method bark
class Animal
def bark
p "hukkhh"
end
end
bark method you define is belong to Animal class for now as instance level method not class level. It means you need to create instance of class Animal to call bark.
Animal.respond_to? :bark #=> false
Object.respond_to? :bark #=> false
Animal.new.respond_to? :bark #=> true
Animal.respond_to? :talk #=> true #talk is available to Animal also because Animal is also object of Class
Animal.new.respond_to? :talk #=> true
Now you create class called Cow inheriting from Animal. So bark is available to Cow unless it is overridden.
class Cow < Animal
end
Cow.respond_to? :bark #=> false
Cow.new.respond_to? :bark #=> true
Cow.respond_to? :talk #=> true
Cow.new.respond_to? :talk #=> true
Cow.new.bark #=> "hukkhh"
So to call bark you need to create instance of class.
If you want to call a method of parent which is overridden in child use super
class Cow < Animal
def bark
super
#do your stuff
end
end
Cow.new.bark #=> "hukkhh"
I'm new to programming. Right now I'm studying Ruby. To my understanding, global variables are defined in the global namespace (so outside of any classes or functions). I'm reading something and it says global variables have a $ sign before them. What does that mean? Does it mean when I define a function or class and want to reference my global variable (let's say it is edmund = 123) I would have to reference it like this: $edmund?
so:
edmund = 123
def my_function()
456 + $edmund
end
Also are class variables (the ones that begin with ##) like instance variables (#) where you can access them by calling them through Class.classvariable? What is their purpose?
Global scope is scope that covers the entire program. Global scope is enjoyed by global variables, which are recognizable by their initial dollar-sign ($) character. They’re available everywhere and creating your own global variables can be tempting, especially for beginning programmers. But they’re not always a good idea.
$gvar = "I'm a global!"
class C
def examine_global
puts $gvar
end
end
c = C.new
c.examine_global # I'm a global!
Class variables begin with two at signs: ##var, for example. Despite their name, class variables aren’t class scoped. Rather, they’re class-hierarchy scoped. At its simplest, the idea behind a class variable is that it provides a storage mechanism that’s shared between a class and instances of that class, and that’s not visible to any other objects.
class Parent
##value = 100
end
class Child < Parent
##value = 200
end
class Parent
puts ##value
end
What gets printed is 200. The Child class is a subclass of Parent, and that means Parent and Child share the same class variables—not different class variables with the same names, but the same actual variables. When you assign to ##value in Child, you’re setting the one and only ##value variable that’s shared throughout the hierarchy—
that is, by Parent and Child and any other descendant classes of either of them.
And to give credit where its due - This explanation comes from "The Well Grounded Rubyist" by David A Black, one of the best resources to learn about Ruby.
Excellent question. Unfortunately, you just jumped down a rabbit hole, but it's one that you have to fall through eventually in ruby to start understanding the real intricacies.
For your first question, regarding the $-prefixed global variables. They are truly global:
def mk_foo() $foo ||= "foo"; end
$foo # => nil
mk_foo # => "foo"
$foo # => "foo"
mk_foo.object_id # => 70299647799620
$foo.object_id # => 70299647799620
As you can see, when $foo is defined within the mk_foo method, it is defined in the global space, and you can access it anywhere:
class CanSeeFoo
def see_foo() $foo; end
end
CanSeeFoo.new.can_see_foo
# => "foo"
CanSeeFoo.new.can_see_foo.object_id
# => 70299647799620
As for the class variable question, this is where the rabbit-hole begins. First, you are correct that ##-prefixed variables are referred to as "class variables" and #-prefixed variables are referred to as "instance variables".
Class variables are static across all subclasses (at all sub-levels of the inheritance tree) of the defining class. The implication here is that if any subclass changes the class variable, it will change in all related subclasses and up to the defining class.
class A; end
class B < A; ##foo = "foo"; end
B.class_variable_get(:##foo) # => "foo"
A.class_variable_get(:##foo)
# => raises NameError "uninitialized class variable ##foo in A"
class C < B; end
C.class_variable_get(:##foo) # => "foo"
class D < C
def self.change_foo(); ##foo = "bar"; end
def change_foo(); ##foo = "baz"; end
end
D.class_variable_get(:##foo) # => "foo"
class E < D; end
E.class_variable_get(:##foo) # => "foo"
D.change_foo # => "bar"
D.class_variable_get(:##foo) # => "bar"
E.class_variable_get(:##foo) # => "bar"
C.class_variable_get(:##foo) # => "bar"
B.class_variable_get(:##foo) # => "bar"
D.new.change_foo # => "baz"
D.class_variable_get(:##foo) # => "baz"
E.class_variable_get(:##foo) # => "baz"
C.class_variable_get(:##foo) # => "baz"
B.class_variable_get(:##foo) # => "baz"
A.class_variable_get(:##foo)
# => raises NameError "uninitialized class variable ##foo in A"
As for accessing class and instance variables, neither is accessible without the use of #instance_variable_get or ::class_variable_get until an accessor is defined. At present, ruby only has methods for defining accessors on instance variables, but it is simple enough to define the appropriate methods for the class variables:
class A
##foo = "foo"
# the second argument `true` adds the writer method `#bar=`
attr :bar, true
def self.foo(); ##foo; end
def self.foo=(v); ##foo = v; end
def initialize()
#bar = "bar"
end
end
class B < A; end
A.foo # => "foo"
B.foo = "foobar"
A.foo # => "foobar"
B.foo # => "foobar"
a = A.new
a.bar # => "bar"
a.bar = "baz"
a.bar # => "baz"
a.foo
# => raises NoMethodError: undefined method `foo' for #<A:0x ...
You can see the attribute accessor methods here in the ruby core docs: http://www.ruby-doc.org/core-1.9.3/Module.html#method-i-attr. Also, ActiveSupport (http://rubygems.org/gems/activesupport) has "cattr" methods for defining class variable accessors http://api.rubyonrails.org/v3.2.5/classes/Class.html#method-i-cattr_accessor.
That's the simple stuff. The next step is understanding the "singleton class" also known as the "eigenclass" or "metaclass" (Wikipedia: Metaclass) (remember, everything in ruby is an Object, including the Class and Module constructs). Here I will point you to an excellent post by Yehuda Katz: Metaprogramming in Ruby: It’s All About the Self, and another Stack Overflow question: class << self idiom in Ruby.
As a preview: The singleton class (not to be confused with the singleton design pattern) allows you to access methods and instance data for a specific class or module. For some related documentation, see the core docs: http://www.ruby-doc.org/core-1.9.3/Object.html#method-i-singleton_class
class A; end
class B < A;
class << self
def foo() #foo end
def foo=(v) #foo = v; end
end
end
B.foo = "foo"
class C < B; end
A.foo
# => raises NoMethodError: undefined method `foo' for A:Class
B.foo # => "foo"
C.foo # => nil
B.foo = "baz"
B.foo # => "baz"
C.foo # => nil
C.foo = "foo"
C.foo # => "foo"
B.foo # => "baz"
Lastly, remember to make use of the Ruby-Core docs. Most useful for understanding the above are:
http://www.ruby-doc.org/core-1.9.3/Object.html
http://www.ruby-doc.org/core-1.9.3/Module.html
http://www.ruby-doc.org/core-1.9.3/Class.html
The dollar sign is part of the variable name, so it has to be declared like this:
$edmund = 123
This is the same things for the instance and class variables: their names begin with # or ##.
I expect the following code to work as expected but it gives me a NoMethodError (private method `foo' called for #<MyClass...)
class MyClass
end
my_object = MyClass.new
my_object.instance_variable_set(:#foo, "bar")
MyClass.send("attr_reader", :foo)
puts my_object.foo
The problem is I'm using literally identical code in a larger application and it works exactly as I expect, but when I simplify it to this most basic example it fails.
(I understand there are many other ways to do what I'm doing in Ruby)
Use Module#class_eval:
By doing this, the block puts you in the context of MyClass, thus allowing you to call attr_reader
ruby-1.9.2-p136 :028 > my_object.instance_variable_set(:#foo, "bar")
=> "bar"
ruby-1.9.2-p136 :029 > MyClass.class_eval{attr_reader :foo}
=> nil
ruby-1.9.2-p136 :030 > my_object.foo
=> "bar"
Let's assume for an instant that you want to create an accessor for that particular instance of the class (which I assume is the case since you are operating on the instance.
You can simply open up the instance's singleton class and use instance_eval to add the accessor
class MyClass
end
my_instance = MyClass.new
my_instance.singleton_class.instance_eval { attr_accessor :foo }
my_instance.foo = :bar
my_instance.foo # => :bar
Interesting problem, I found this solution works fine:
MyClass.class_eval("attr_reader :foo")
The answer by #MikeLewis is nice, but why not just re-open the class?
irb(main):001:0> class MyClass; end
#=> nil
irb(main):002:0> m = MyClass.new
#=> #<MyClass:0x2d43ae0>
irb(main):003:0> class MyClass; attr_accessor :foo; end
#=> nil
irb(main):004:0> m.foo = 42
#=> 42
Just force the method to become public:
MyClass.send("public", :foo)
I have no idea why it is private in some cases.
I'm puzzled at this, but one solution that works is to make the attribute_reader explicitly public using MyClass.send(:public, :foo), i.e.
class MyClass
end
my_object = MyClass.new
my_object.instance_variable_set(:#foo, "bar")
MyClass.send(:attr_reader, :foo)
MyClass.send(:public, :foo)
puts my_object.foo