Declaring an instance variable outside of `initialize` method - ruby

I have been taught to declare my instance variables with def initialize. I have been under the impression that I could declare instance variables only within my initialize methods.
Nevertheless, I declared an instance variable #foo outside my initialize method, and made it work as I intended:
class FooBar
def initialize(bar)
#bar = bar
end
def foo_as_instance_var
#foo = #bar.split(' ')
#foo
end
end
x = "something wicked this way comes"
y = FooBar.new(x)
puts y.foo_as_instance_var
Why am I able to declare an instance variable outside of initialize method? Since I can declare instance variables in any method, is there a best practices rule I should follow, regarding where to declare instance variables (i.e., declare them within initialize) or does it not matter?

I have been taught to declare my instance variables with def initialize
Since initialize is the first instance method call in an object's life cycle, you typically declare your instance variables right there in order to ensure properly initialized variables. It's also the first place I'd expect instance variables to be defined when reading code.
I have been under the impression that I could declare instance variables only within my initialize methods.
There's no such restriction. You can declare instance variable anywhere within your instance.
A common use is memoization:
class FooBar
def foo
#foo ||= expensive_operation
end
end
On the first call, this would evaluate expensive_operation and assign the result to #foo. On subsequent calls, #foo is returned.
Another popular example is Rails which uses instance variables to pass data from the controller to its view:
class FooController < ApplicationController
def index
#foos = Foo.all
end
end
is there a best practices rule I should follow, regarding where to declare instance variables
It depends on their purpose (see above examples). As a general rule, declare them in a way that avoids undefined variables (nil errors) and structure your code so it is easy to read / follow.

Just to add to Stefan's excellent answer
I have been taught to declare my instance variables with def initialize
A common mistake that ruby newbies make is something like this:
class Person
#name = "John"
def introduce
puts "Hi, my name is #{#name}"
end
end
And then they wonder why their names are not printed. To make this work, one can set the variable #name in the initializer, just as the instruction says.

Lets start with the biggest misnomer - in Ruby there is no separate step of declaring variables - Variables are declared as you set them.
What the difference? Look at Java for example:
public class Bicycle {
private int cadence;
private int gear;
private int speed;
public Bicycle(int startCadence, int startSpeed, int startGear) {
gear = startGear;
cadence = startCadence;
speed = startSpeed;
}
}
We have to declare all the instance variables before we set them in the initializer (Bicycle). The same code in Ruby reads:
class Bicycle
def initialize(cadence, speed, gear)
#cadence = cadence
#speed = speed
#gear = gear
end
end
There is no declaration - only assignment. Ruby will even let you access instance variables which have not been set without error.
irb(main):003:0> #not_set
=> nil
You can't do that (generally) in languages where variables must be defined*.
I have been taught to declare my instance variables with def
initialize. I have been under the impression that I could declare
instance variables only within my initialize methods.
Nonsense. You can assign instance variables anywhere. Its commonly done in everything from setters and mutators (methods that alter an object) to factory methods (class methods that return an instance) or anywhere that you are altering the state of an object.
class Book
def initialize(title, author)
#title = title
self.author = author # calls the setter.
end
# A factory method
def create_from_csv(filename)
# ...
end
# A very contrived setter
def author=(author)
#author = "#{author.forename.upcase}. #{author.surname}"
end
# a mutator
def out_of_print!
#out_of_print = true
#last_printed = Date.today
end
end
However the initialize method is where you should handle initializing your objects (duuh) and is thus the obvious place to set initial values.

Related

Local variables in `class` definition scope versus `def` method scope

Here, I create a local variable in class scope:
class MyClass
x = 1
puts x
end
It prints 1 even if I don't create any instances of MyClass.
I want to use x in some method:
class MyClass
x = 1
def method
puts x
end
end
m = MyClass.new
m.method
And I can't. Why? I get that class definition creates a scope, but why is it not accessible in the method? Isn't scope of the method inside the scope of the class?
I can imagine that this is related to creation of a class. Since any class is an object of Class, maybe the scope of MyClass is the scope of some Class method, and the way of coupling methods of MyClass to that instance makes their scope completely different.
It also seems to me that I can't just create a scope with {} (like in C) or something like do..end. Am I correct?
Scope of a method is not inside the class. Each method has its own entirely new scope.
New scopes are created whenever you use the class, module, and def keywords. Using brackets, as in C, does not create a new scope, and in fact you cannot arbitrarily group lines of code using brackets. The brackets (or do...end) around a Ruby block create a block-level scope, where variables previously created in the surrounding scope are available, but variables created within the block scope do not escape into the surrounding scope afterward.
Instance methods share the scope of their instance variables with other instances methods. An instance variable defined in the scope of a class definition is available in class-level singleton methods, but not in instance methods of the class.
Illustration:
class Foo
x = 1 # available only here
#y = 2 # class-wide value
def self.class_x
#x # never set; nil value
end
def self.class_y
#y # class-wide value
end
def initialize(z)
x = 3 # available only here
#z = z # value for this instance only
end
def instance_x
#x # never set; nil
end
def instance_y
#y # never set; nil
end
def instance_z
#z # value for this instance only
end
end
Foo.class_x # => nil
Foo.class_y # => 2
Foo.new(0).instance_x # => nil
Foo.new(0).instance_y # => nil
foo3 = Foo.new(3)
foo4 = Foo.new(4)
foo3.instance_z # => 3
foo4.instance_z # => 4
You can access class-level instance variables from within instances using the class-level getter. Continuing the example above:
class Foo
def get_class_y
self.class.class_y
end
end
foo = Foo.new(0)
foo.get_class_y # => 2
There exists in Ruby the notion of a "class variable," which uses the ## sigil. In practice, there is almost never a reasonable use case for this language construct. Typically the goal can be better achieved using a class-level instance variable, as shown here.
Here, I create a local variable in class scope:
class MyClass
x = 1
puts x
end
It prints 1 even if I don't create any instances of MyClass.
Correct. The class definition body is executed when it is read. It's just code like any other code, there is nothing special about class definition bodies.
Ask yourself: how would methods like attr_reader/attr_writer/attr_accessor, alias_method, public/protected/private work otherwise? Heck, how would def work otherwise if it didn't get executed when the class is defined? (After all, def is just an expression like any other expression!)
That's why you can do stuff like this:
class FileReader
if operating_system == :windows
def blah; end
else
def blubb; end
end
end
I want to use x in some method:
class MyClass
x = 1
def method
puts x
end
end
m = MyClass.new
m.method
And I can't. Why? I get that class definition creates a scope, but why is it not accessible in the method? Isn't scope of the method inside the scope of the class?
No, it is not. There are 4 scopes in Ruby: script scope, module/class definition scope, method definition scope, and block/lambda scope. Only blocks/lambdas nest, all the others create new scopes.
I can imagine that this is related to creation of a class. Since any class is an object of Class, maybe the scope of MyClass is the scope of some Class method, and the way of coupling methods of MyClass to that instance makes their scope completely different.
Honestly, I don't fully understand what you are saying, but no, class definition scope is not method definition scope, class definition scope is class definition scope, and method definition scope is method definition scope.
It also seems to me that I can't just create a scope with {} (like in C) or something like do..end. Am I correct?
Like I said above: there are 4 scopes in Ruby. There is nothing like block scope in C. (The Ruby concept of "block" is something completely different than the C concept of "block.") The closest thing you can get is a JavaScript-inspired immediately-invoked lambda-literal, something like this:
foo = 1
-> {
bar = 2
foo + bar
}.()
# => 3
bar
# NameError
In general, that is not necessary in Ruby. In well-factored code, methods will be so small, that keeping track of local variables and their scopes and lifetimes is really not a big deal.
So just creating a class without any instances will lead to something
actually executing in runtime (even allocating may be)? That is very
not like C++. –
Check out this code:
Dog = Class.new do
attr_accessor :name
def initialize(name)
#name = name
end
end
If you execute that code, there won't be any output, but something still happened. For instance, a global variable named Dog was created, and it has a value. Here's the proof:
Dog = Class.new do
attr_accessor :name
def initialize(name)
#name = name
end
end
dog = Dog.new("Ralph")
puts dog.name
--output:--
Ralph
The assignment to the Dog constant above is equivalent to writing:
class Dog
...
...
end
And, in fact, ruby steps through each line inside the class definition and executes each line--unless the line of code is inside a def. The def is created but the code inside a def doesn't execute until the def is called.
A very common line you will see inside a class definition is:
attr_accessor :name
...which can be rewritten as:
attr_accessor(:name)
...which makes it obvious that it's a method call. Ruby executes that line--and calls the method--when you run a file containing the class definition. The attr_accessor method then dynamically creates and inserts a getter and a setter method into the class. At runtime. Yeah, this ain't C++ land anymore--welcome to NeverNever Land.
I get that class definition creates a scope, but why is it not
accessible in the method?
Because that is the way Matz decided things should be: a def creates a new scope, blocking visibility of variables outside the def. However, there are ways to open up the scope gates, so to speak: blocks can see the variables defined in the surrounding scope. Check out define_method():
class MyClass
x = 1
define_method(:do_stuff) do
puts x
end
end
m = MyClass.new
m.do_stuff
--output:--
1
The block is everything between do...end. In ruby, a block is a closure, which means that when a block is created, it captures the variables in the surrounding scope, and carries those variables with it until the the block is executed. A block is like an anonymous function, which gets passed to a method as an argument.
Note that if you use the Class.new trick, you can open two scope gates:
x = 1
MyClass = Class.new do
define_method(:do_stuff) do
puts x
end
end
m = MyClass.new
m.do_stuff
--output:--
1
Generally, ruby allows a programmer to do whatever they want, rules be damned.

Difference between #instance_variable and attr_accessor

I Just started learning ruby and I don't see the difference between an #instace_variable and an attribute declared using attr_accessor.
What is the difference between the following two classes:
class MyClass
#variable1
end
and
class MyClass
attr_accessor :variable1
end
I searched lot of tutorials online and everybody uses different notation, Does it have to do anything with the ruby version? I also searched few old threads in StackOverflow
What is attr_accessor in Ruby?
What's the Difference Between These Two Ruby Class Initialization Definitions?
But still I am not able to figure out what is the best way to use.
An instance variable is not visible outside the object it is in; but when you create an attr_accessor, it creates an instance variable and also makes it visible (and editable) outside the object.
Example with instance variable (not attr_accessor)
class MyClass
def initialize
#greeting = "hello"
end
end
m = MyClass.new
m.greeting #results in the following error:
#NoMethodError: undefined method `greeting' for #<MyClass:0x007f9e5109c058 #greeting="hello">
Example using attr_accessor:
class MyClass
attr_accessor :greeting
def initialize
#greeting = "hello"
end
end
m2 = MyClass.new
m2.greeting = "bonjour" # <-- set the #greeting variable from outside the object
m2.greeting #=> "bonjour" <-- didn't blow up as attr_accessor makes the variable accessible from outside the object
Hope that makes it clear.
Instance variables are not directly visible outside of the class.
class MyClass
def initialize
#message = "Hello"
end
end
msg = MyClass.new
#message
#==> nil # This #message belongs to the global object, not msg
msg.message
#==> NoMethodError: undefined method `message'
msg.#message
#==> SyntaxError: syntax error, unexpected tIVAR
Now, you can always do this:
msg.instance_eval { #message }
or ask for the variable directly like this:
msg.instance_variable_get :#message
But that's awkward and sort of cheating. Poking around someone else's class may be educational, but your client code shouldn't be required to do it to get reliable results. So if you want clients to be able to see those values, don't make them use the above techniques; instead, define a method to expose the value explicitly:
class MyClass
def message
return #message
end
end
msg.message
# ==> "Hello"
Because you so often want to do that, Ruby provides a shortcut to make it easier. The code below has exactly the same result as the code above:
class MyClass
attr_reader :message
end
That's not a new type of variable; it's just a shorthand way to define the method. You can look at msg.methods and see that it now has a message method.
Now, what if you want to allow outsiders to not only see the value of an instance variable, but change it, too? For that, you have to define a different method for assignment, with a = in the name:
class MyClass
def message=(new_value)
#message = new_value
end
end
msg.message = "Good-bye"
msg.message
# ==> "Good-bye"
Note that the assignment operators are semi-magical here; even though there's a space between msg.message and =, Ruby still knows to call the message= method. Combination operators like += and so on will trigger calls to the method as well.
Again, this is a common design, so Ruby provides a shortcut for it, too:
class MyClass
attr_writer :message
end
Now, if you use attr_writer by itself, you get an attribute that can be modified, but not seen. There are some odd use cases where that's what you want, but most of the time, if you are going to let outsiders modify the variable, you want them to be able to read it, too. Rather than having to declare both an attr_reader and an attr_writer, you can declare both at once like so:
class MyClass
attr_accessor :message
end
Again, this is just a shortcut for defining methods that let you get at the instance variable from outside of the class.
attr_accesor gives you methods to read and write the instance variables. Instance variables are deasigned to be hidden from outside world so to communicate with them we should have attr_ibute accesor methods.
In OOPS we have a concept called encapsulation which means, the internal representation of an object is generally hidden from view outside of the object's definition. Only the Object 'itself' can mess around with its own internal state. The outside world cannot.
Every object is usually defined by its state and behavior, in ruby the instance variables is called internal state or state of the object and according to OOPS the state should not be accessed by any other object and doing so we adhere to Encapsulation.
ex: class Foo
def initialize(bar)
#bar = bar
end
end
Above, we have defined a class Foo and in the initialize method we have initialized a instance variable (attribute) or (property). when we create a new ruby object using the new method, which in turn calls the initialize method internally, when the method is run, #bar instance variable is declared and initialized and it will be saved as state of the object.
Every instance variable has its own internal state and unique to the object itself, every method we define in the class will alter the internal state of the object according to the method definition and purpose. here initialize method does the same, such as creating a new instance variable.
var object = Foo.new(1)
#<Foo:0x00000001910cc0 #bar=1>
In the background, ruby has created an instance variable (#bar =1) and stored the value as state of the object inside the object 'object'. we can be able to check it with 'instance_variables' method and that methods returns an array containing all the instance variables of the object according to present state of the object.
object.instance_variables
#[
[0]: #bar
]
we can see '#bar' instance variable above. which is created when we called the initialize method on the object. this '#bar' variable should not be visible (hidden) by default and so it cannot be seen by others from outside of the object except the object, from inside. But, an object can mess around with its own internal state and this means it can show or change the values if we give it a way to do so, these two can be done by creating a new instance methods in the class.
when we want to see the #bar variable by calling it we get an error, as by default we cannot see the state of an object.
show = object.bar
#NoMethodError: undefined method `bar' for #<Foo:0x00000001910cc0 #bar=1>
#from (irb):24
#from /home/.rvm/rubies/ruby-2.0.0-p648/bin/irb:12:in `<main>'
But we can access the variables by two methods, these two are called setter and getter methods, which allow the object to show or change its internal state (instance variables/attributes/properties) respectively.
class Foo
def bar
#bar
end
def bar=(new_bar)
#bar = new_bar
end
end
We have defined a getter(bar) and setter(bar=) methods, we can name them any way but the instance variable inside must the same as instance variable to which we want to show or change the value. setters and getters are a violation to OOPS concepts in a way but they are also very powerful methods.
when we define the two methods by re-opening the class and defining them, when we call the object with the methods, we can be able to view the instance variables(here #foo) and change its value as well.
object.bar
1
object.bar=2
2
object.bar
2
Here we have called the bar method (getter) which returns the value of #bar and then we have called bar= method (setter) which we supplied a new_value as argument and it changes the value of instance variable (#bar) and we can look it again by calling bar method.
In ruby we have a method called attr_accessor , which combines the both setter and getter methods, we define it above the method definitions inside the class. attr_* methods are shortcut to create methods (setter and getter)
class Foo
attr_accessor :bar
end
we have to supply a symbol (:bar) as argument to the attr_accessor method which creates both setter and getter methods internally with the method names as supplied symbol name.
If we need only a getter method, we can call attr_reader :bar
If we need only a setter method, we can call attr_writer :bar
attr_accessor creates both attr_writer and attr_reader methods
we can supply as many instance variables as we want to the attr_* methods seperated by commas
class Foo
attr_writer :bar
attr_reader :bar
attr_accessor :bar, :baz
end
Because attr_accessor defines methods, you can call them from outside the class. A #variable is only accessible from inside the class.
And another answer more compact (for Java developers)
attr_accessor :x creates the getters and setters to #x
class MyClassA
attr_accessor :x
end
is the same as
class MyClassB
def x=(value) #java's typical setX(..)
#x=value
end
def x
#x
end
end

How to set private instance variable used within a method test?

Given a class with a couple of instance variables and some methods. Some instance variables are set accessible via attr_reader and attr_accessor. Thus the others are private.
Some of the private instance variables get set within one of the instance methods and read/utilized within another method.
For testing I'm using RSpec. As I'm still new to Ruby and want to get all things right, I defined my tests being rather fine-grained. Thus I've got one describe block for each instance method, which themselves are partitioned into a subset of contexts and its. General environmental preconditions are defined with before.
However, when testing one of the methods, which is utilizing but not setting one of the private variables, I need to call the other method, which is setting this variable. This seems rather overweight and not modular for me.
Is there a way of forcing a private instance variable to a certain value. Similar to "ripping out" the value of a private instance variable with Object::instance_eval(:var).
As you answered in your question the easiest way to set instance variable is with instance_eval method:
obj.instance_eval('#a = 1')
Another way is to use instance_variable_set:
obj.instance_variable_set(:#a, 1)
But I would not recommend to do this in your specs. Specs are all about testing behavior of an object and testing behaviour by breaking class encapsulation with instance_eval will make your tests more fragile and implementation dependent.
Alternative approach to object state isolation is to stub accessor methods:
class UnderTest
attr_accessor :a
def test_this
do_something if a == 1
end
end
#in your test
under_test = UnderTest.new
under_test.stub(:a).and_return(1)
Use instance_variable_set:
class SomeClass
attr_reader :hello
def initialize
#hello = 5
end
# ...
end
a = SomeClass.new
a.hello # => 5
a.instance_variable_set("#hello", 7)
a.hello # => 7
I just solved it by creating a child and adding an accessor:
class HasSecrets
#muahaha
end
class TestMe < HasSecrets
attr_accessor(:muahaha)
end
def test_stuff
pwned = TestMe.new()
pwned.muahaha = 'calc.exe'
end

I guess some Ruby internals

class MyClass
def instance_variable=(var)
puts "inside getter"
instance_variable = var
end
def function_1
self.instance_variable = "whatever"
end
def function_2
#instance_variable = "whatever"
end
end
myclass = MyClass.new
myclass.function1
results wiht "inside getter" on the console
myclass.function2
does not.
Im new to Ruby, do not know the difference, couldnt find it on the web...
Thanks in advance!
EDIT:
I assumed that by appending the "=", I overwrite a getter method for an implicitly defined instance variable "instance_variable."
That's also the reason why I called it that way.
Im not used to be allowed to use "=" in function names.
Thats why I assumed it would had some special meaning.
Thanks for your help.
EDIT2:
I just thought I really overwrite the assignment and not only the getter. I got it all mixed up.
Sorry and Thanks.
You have (misleading) named your setter instance_variable. It is not an instance variable, it is a method that sets an instance variable.
When you call self.instance_variable= you are calling that method. When you set #instance_variable directly you are setting the variable itself, and that is why the setter method is not called.
A more idiomatic naming convention would be something like:
def name=(value)
#name = value
end
Of course, for simply, pass-through type getters and setters you can use
attr_reader :name #generates getter only
attr_writer :name #generates setter only, not very common
attr_accessor :name #generates getter and setter
The above methods are syntactic sugar which generate the get and/or set methods for you. They can be overriden later to provide additional functionality if needed.
EDIT: I see that you have made an update and just wanted to point out that this method doesn't set an instance variable at all:
def instance_variable=(var)
puts "inside getter"
instance_variable = var
end
In this case instance_variable is simply a local variable and will be discarded as soon as the method exits. Local variables take precedence over instance methods, and instance variables always begin with a # symbol.

Ruby instance variables and methods

Sometimes I see an instance variable defined as #my_variable. However, sometimes I see self.my_variable. When is each used?
Instance variables (#variable) correspond to private variables in other languages. self.myvariable is actually not a variable, but a call to a method. Similarly, if you write self.myvariable = something, it is actually a call to self.myvariable=(something). This corresponds to properties with getters and setters in other languages.
class Foo
def initialize
#bar = 42
end
def xyzzy
123
end
def xyzzy=(value)
puts "xyzzy set to #{value}!"
end
end
obj = Foo.new
puts obj.xyzzy # prints: 123
obj.xyzzy = 2 # prints: xyzzy set to 2
puts obj.bar # error: undefined method 'bar'
You can use attr_reader and attr_accessor to automatically define getters and setters for an instance variable. attr_reader will only generate a getter, while attr_accessor generates both.
class Parrot
attr_accessor :volts
def voom
puts "vooming at #{#volts} volts!"
end
end
polly = Parrot.new
polly.volts = 4000
polly.voom
Instance variables are more primary things than methods calling them. In self.myVariable, myVariable is a method referring to the instance variable #myVariable, and that method is defined usually by attr_reader or attr_accessor.
One purpose of object orientated programming is to encapsule things particular to an instance inside that instance and make it inaccessible from outside of it. This way, you can avoid unwanted conflicts of name. This is true for instance variables. They are usually parameters to be handeled within the instance, and not to be used outside of it.
Within an instance, its instance variables can be directly referred to, and hence there is no need to refer to them via method calls. You should directly call the variable #myVariable.
From outside of an instance, you cannot directly refer to the instance variables because of the reason mentioned above. But sometimes, you do need to refer to them. The purpose of using the method myVariable is to refer to the instance variable from outside of an instance.
#my_variable refers directly to the instance variable, and is (for the most part) inaccessible from outside that instance.
self.my_variable is using an accessor method (as defined with attr_reader, attr_writer or attr_accessor internally. This is in cases where there may not be an instance variable named #my_variable (as is the case with ActiveRecord model attributes) or where the internal state differs from what is exposed publicly.

Resources