Setting Variables within `self.included` - ruby

In Ruby, why can't I set the value of variables directly within a module's self.included method?
For example, the following code outputs NilClass (indicating that the #sound variable has NOT been set):
module Animal
def self.included(klass)
attr_accessor :sound
#sound = "Woof!" # <-- Variable assignment
end
def speak
puts #sound.class
end
end
class Dog
include Animal
end
dog = Dog.new
dog.speak # => NilClass
However, if I set the value of #sound within the module's speak method (instead of inside of self.included), then the variable is set correctly. For example, the following code outputs String:
module Animal
def self.included(klass)
attr_accessor :sound
end
def speak
#sound = "Woof!" # <-- Variable assignment
puts #sound.class
end
end
class Dog
include Animal
end
dog = Dog.new
dog.speak # => String
I would have expected both code samples above to output String.

In first example the receiver in self.included block is Dog class, not it's instance, meaning you are defining class instance variable #sound, not instance variable.
You can check it by running
Dog.instance_variable_get(:#sound) # with first example
speak method returns NilClass because dog does not have #sound instance variable defined.
In second example you are defining an instance variable #sound, thus it works as you expect.

Related

Can you omit the # when calling instance variables

When you have a class with a attr_accessor, can't you omit the # symbol when calling your instance variables since self.instance_variable would be implied if you don't use the # symbol?
attr_accessor :foo is basically just a macro that generates two methods (a getter and a setter) for you:
def foo
#foo
end
def foo=(foo)
#foo = foo
end
Can you omit the # when calling your instance variable? Kind of – calling the instance variable name without the # means you are calling the instance method generated by attr_accessor instead of calling the instance variable. This works as long as the getter method is not overridden or extended.
But when you would try to set an instance variable without the #, it would not work that way, because then Ruby would set a local variable with that name. To set the instance variable through the instance method generated by attr_accessor you need to write self. (or another receiver) instead of the #:
#foo = 'bar' # assigns 'bar' to the instance variable `#foo`
foo = 'bar' # assigns 'bar' to a local variable `#foo`
But to use the setter method generated by attr_accessor:
self.foo = 'bar' # passes 'bar' to the instance method `foo=`
In the case where you have an attr_accessor and you want to read the value, you'll get the same value. It isn't exactly that you're omitting the # but that you're calling a method with the same name that returns the instance variable.
Omitting the # when trying to set the value will not work the same way; it will just set a local variable with the same name. Using an object's setter within the object requires that you precede it with self..
class Foo
attr_accessor :bar
def getter_equivalent?
bar.equal?(#bar) # Returns true. They are the same object.
end
def set_with_at(value)
#bar = value # Will set the instance variable
end
def set_without_at(value)
bar = value # Will not set the instance variable
end
def set_with_self(value)
self.bar = value # Will set the instance variable
end
end
A class example with attr_accessor expanded for clarity. The usual initialize method has been omitted to focus on the task at hand:
class Foo
def bar
#bar
end
def bar=(bar)
#bar = bar
end
#the above two methods represent attr_accessor :bar
def call_method
bar
#same as self.bar
#self refers to the current object i.e. the current instance of Foo
#bar refers to the method bar defined above
end
def call_instance_var
#bar
#refers directly to the the instance variable
end
end
You can use either one, I personally prefer calling the method rather than the instance variable.
Example
foo = Foo.new
foo.bar = "bar"
foo.call_method #=> "bar"
foo.call_instance_var #=> "bar"
Although it's possible to use accessor methods within the class, it's better practice to use the # symbol to refer to instance variables.
Here's an example where using a reader method within a class would produce unexpected results:
class A
def var
#var
end
def defined_using_method?
defined?(var)
end
def defined_using_at?
defined?(#var)
end
end
A.new.defined_using_method? # => "method"
A.new.defined_using_at? # => nil

Class method is accessing instance variable

I'd like to understand this code. Why is it returning Hello instead of Howdy! ?
class Speaker
#message = "Hello!"
class << self
#message = "Howdy!"
def speak
#message
end
end
end
puts Speaker.speak
First off, your message #message is not an instance variable, or rather not the type of instance variable you may be thinking about: it's a class-level instance var, so an instance variable of Speaker itself, which as an object is an instance of class Class.
Here's a version of the code that does what you're trying to do with a local variable and a closure:
class Speaker
#message = "Hello!"
class << self
message = "Howdy!"
define_method(:speak) { message }
end
end
Speaker.speak
#=> "Howdy!"
And here's some code that illustrates the difference between the class-level instance variable and a "normal" instance variable:
class Speaker
#message = 'Howdy!' # class-level instance variable
def initialize
#message = 'Hello!' # instance variable of Speaker's instances
end
def speak
#message
end
class << self
def speak
#message
end
end
end
Speaker.speak
#=> "Howdy!"
Speaker.new.speak
#=> "Hello!"
Here is your code, except I've defined the class method in the usual way (def self.speak...). As a class method is nothing more than an instance method defined on the class' singleton class, this change is merely a different way of creating the same class method. (If you doubt that, run the code below both ways.) I made that change because I thought it would make my explanation of what is happening clearer. I also added a puts statement.
class Speaker
#message = "Hello!"
def self.speak
puts "self=#{self}"
#message
end
class << self
#message = "Howdy!"
end
end
The first line of the class definition creates a class instance variable #message:
Speaker.instance_variables
#=> [:#message]
Speaker.instance_variable_get(:#message)
#=> "Hello!"
By constrast,
#message = "Howdy!"
creates an instance variable on Speaker's singleton class:
Speaker.singleton_class.instance_variables
#=> [:#message]
Speaker.singleton_class.instance_variable_get(:#message)
#=> "Howdy!"
Now invoke speak on Speaker:
Speaker.speak
# self=Speaker
#=> "Hello!"
As self #=> Speaker, speak is obviously returning the value of the class instance variable.
For speak to return the value of the instance variable defined on Speaker's singleton class we can write the following:
class Speaker
#message = "Hello!"
def self.speak
puts "self=#{self}"
puts "singleton_class = #{singleton_class}"
singleton_class.instance_variable_get :#message
end
class << self
#message = "Howdy!"
end
end
puts Speaker.speak
# self=Speaker
# singleton_class = #<Class:Speaker>
# Howdy!
In the last expression, because self equals Speaker and self is the implied receiver when there is no explicit receiver, "singleton_class is equivalent to Speaker.singleton_class.
The reason this code returns 'Hello' is that it is attempting to change an instance variable in a class << self block.
Class methods are for anything that does not deal with an individual instance of that class - instance variables are tied to individual instances of a class, and we can't change instance variables at a class level.
Instead of using an instance variable in the speak method, we should use a class variable (denoted by ##).
As an example, the following code will return 'Howdy!' -
class Speaker
##message = "Hello!"
class << self
##message = "Howdy!"
def speak
##message
end
end
end
puts Speaker.speak

Ruby invoking attr method with array of attributes

Please explain me why i should define attr_list before attr? I can not understand why i should do that?
class Question
def self.attr_list
[:id, :name]
end
attr *self.attr_list
end
class Question
attr *self.attr_list
def self.attr_list
[:id, :name]
end
end
NoMethodError: undefined method `attr_list' for Question:Class
Unlike a def, a class is executed line by line immediately after you hit return to run your program:
class Dog
x = 10
puts x
end
--output:--
10
...
class Dog
puts x
x=10
end
--output:--
1.rb:2:in `<class:Dog>': undefined local variable or method `x'
In this line:
...
class Dog
def self.greet
puts 'hello'
end
greet
end
--output:--
hello
...
class Dog
greet
def self.greet
puts 'hello'
end
end
--output:--
1.rb:2:in `<class:Dog>': undefined local variable or method `greet'
Similarly, in this line:
attr *self.attr_list
you call self.attr_list(), yet the def comes after that line, so the method doesn't exist yet.
With a def, you can write this:
def do_math()
10/0
end
and you won't get an error until you call the method.
But by the time you create an instance of a class, all the code inside the class has already executed, creating the methods and constants that are defined inside the class.
By the way, you don't ever need to use attr because ruby 1.8.7+ has attr_accessor(reader and writer), attr_reader, and attr_writer.

What does ##variable mean in Ruby?

What are Ruby variables preceded with double at signs (##)? My understanding of a variable preceded with an at sign is that it is an instance variable, like this in PHP:
PHP version
class Person {
public $name;
public function setName($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
}
Ruby equivalent
class Person
def set_name(name)
#name = name
end
def get_name()
#name
end
end
What does the double at sign ## mean, and how does it differ from a single at sign?
A variable prefixed with # is an instance variable, while one prefixed with ## is a class variable. Check out the following example; its output is in the comments at the end of the puts lines:
class Test
##shared = 1
def value
##shared
end
def value=(value)
##shared = value
end
end
class AnotherTest < Test; end
t = Test.new
puts "t.value is #{t.value}" # 1
t.value = 2
puts "t.value is #{t.value}" # 2
x = Test.new
puts "x.value is #{x.value}" # 2
a = AnotherTest.new
puts "a.value is #{a.value}" # 2
a.value = 3
puts "a.value is #{a.value}" # 3
puts "t.value is #{t.value}" # 3
puts "x.value is #{x.value}" # 3
You can see that ##shared is shared between the classes; setting the value in an instance of one changes the value for all other instances of that class and even child classes, where a variable named #shared, with one #, would not be.
[Update]
As Phrogz mentions in the comments, it's a common idiom in Ruby to track class-level data with an instance variable on the class itself. This can be a tricky subject to wrap your mind around, and there is plenty of additional reading on the subject, but think about it as modifying the Class class, but only the instance of the Class class you're working with. An example:
class Polygon
class << self
attr_accessor :sides
end
end
class Triangle < Polygon
#sides = 3
end
class Rectangle < Polygon
#sides = 4
end
class Square < Rectangle
end
class Hexagon < Polygon
#sides = 6
end
puts "Triangle.sides: #{Triangle.sides.inspect}" # 3
puts "Rectangle.sides: #{Rectangle.sides.inspect}" # 4
puts "Square.sides: #{Square.sides.inspect}" # nil
puts "Hexagon.sides: #{Hexagon.sides.inspect}" # 6
I included the Square example (which outputs nil) to demonstrate that this may not behave 100% as you expect; the article I linked above has plenty of additional information on the subject.
Also keep in mind that, as with most data, you should be extremely careful with class variables in a multithreaded environment, as per dmarkow's comment.
# - Instance variable of a class
## - Class variable, also called as static variable in some cases
A class variable is a variable that is shared amongst all instances of a class. This means that only one variable value exists for all objects instantiated from this class. If one object instance changes the value of the variable, that new value will essentially change for all other object instances.
Another way of thinking of thinking of class variables is as global variables within the context of a single class.
Class variables are declared by prefixing the variable name with two # characters (##). Class variables must be initialized at creation time
## denotes a class variable, i.e. it can be inherited.
This means that if you create a subclass of that class, it will inherit the variable. So if you have a class Vehicle with the class variable ##number_of_wheels then if you create a class Car < Vehicle then it too will have the class variable ##number_of_wheels
The answers are partially correct because ## is actually a class variable which is per class hierarchy meaning it is shared by a class, its instances and its descendant classes and their instances.
class Person
##people = []
def initialize
##people << self
end
def self.people
##people
end
end
class Student < Person
end
class Graduate < Student
end
Person.new
Student.new
puts Graduate.people
This will output
#<Person:0x007fa70fa24870>
#<Student:0x007fa70fa24848>
So there is only one same ##variable for Person, Student and Graduate classes and all class and instance methods of these classes refer to the same variable.
There is another way of defining a class variable which is defined on a class object (Remember that each class is actually an instance of something which is actually the Class class but it is another story). You use # notation instead of ## but you can't access these variables from instance methods. You need to have class method wrappers.
class Person
def initialize
self.class.add_person self
end
def self.people
#people
end
def self.add_person instance
#people ||= []
#people << instance
end
end
class Student < Person
end
class Graduate < Student
end
Person.new
Person.new
Student.new
Student.new
Graduate.new
Graduate.new
puts Student.people.join(",")
puts Person.people.join(",")
puts Graduate.people.join(",")
Here, #people is single per class instead of class hierarchy because it is actually a variable stored on each class instance. This is the output:
#<Student:0x007f8e9d2267e8>,#<Student:0x007f8e9d21ff38>
#<Person:0x007f8e9d226158>,#<Person:0x007f8e9d226608>
#<Graduate:0x007f8e9d21fec0>,#<Graduate:0x007f8e9d21fdf8>
One important difference is that, you cannot access these class variables (or class instance variables you can say) directly from instance methods because #people in an instance method would refer to an instance variable of that specific instance of the Person or Student or Graduate classes.
So while other answers correctly state that #myvariable (with single # notation) is always an instance variable, it doesn't necessarily mean that it is not a single shared variable for all instances of that class.
# and ## in modules also work differently when a class extends or includes that module.
So given
module A
#a = 'module'
##a = 'module'
def get1
#a
end
def get2
##a
end
def set1(a)
#a = a
end
def set2(a)
##a = a
end
def self.set1(a)
#a = a
end
def self.set2(a)
##a = a
end
end
Then you get the outputs below shown as comments
class X
extend A
puts get1.inspect # nil
puts get2.inspect # "module"
#a = 'class'
##a = 'class'
puts get1.inspect # "class"
puts get2.inspect # "module"
set1('set')
set2('set')
puts get1.inspect # "set"
puts get2.inspect # "set"
A.set1('sset')
A.set2('sset')
puts get1.inspect # "set"
puts get2.inspect # "sset"
end
class Y
include A
def doit
puts get1.inspect # nil
puts get2.inspect # "module"
#a = 'class'
##a = 'class'
puts get1.inspect # "class"
puts get2.inspect # "class"
set1('set')
set2('set')
puts get1.inspect # "set"
puts get2.inspect # "set"
A.set1('sset')
A.set2('sset')
puts get1.inspect # "set"
puts get2.inspect # "sset"
end
end
Y.new.doit
So use ## in modules for variables you want common to all their uses, and use # in modules for variables you want separate for every use context.

Ruby - How to use the method parameter as the name of the variable?

How would I use the parameter value as the instance variable name of an object?
This is the object
Class MyClass
def initialize(ex,ey)
#myvar = ex
#myothervar = ey
end
end
I have the following method
def test(element)
instanceofMyClass.element #this obviously doesnt work
end
How can I have the test method return either myvar or myothervar value depending on the element parameter. I don't want to write an if condition though, I want to pass myvar or myother var via element to the object instance if possible.
def test(element)
instanceofMyClass.send(element.to_sym)
end
You'll get a missing method error if instanceofMyClass doesn't respond to element.
def test(element)
instanceofmyclass.instance_variable_get element
end
test :#myvar # => ex
test :#myothervar # => ey
I like the simplicity of send(), though one bad thing with it is that it can be used to access privates. The issue is still remains solution below, but at least then it's explicitly specified, and reader can see which methods are to be forwarded. The first one just uses delegation, while the second one uses more dynamic way to define methods on the fly.
require 'forwardable'
class A
extend Forwardable
def_delegators :#myinstance, :foo, :bar
class B
def foo
puts 'foo called'
end
def bar
puts 'bar called'
end
def quux
puts 'quux called'
end
def bif
puts 'bif called'
end
end
def initialize
#myinstance = B.new
end
%i(quux bif).each do |meth| # note that only A#quux and A#bif are defined dynamically
define_method meth do |*args_but_we_do_not_have_any|
#myinstance.send(meth)
end
end
end
a = A.new
a.foo
a.bar
a.quux
a.bif

Resources