Reputation: 249
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.)
Upvotes: 2
Views: 6002
Reputation: 369594
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
Upvotes: 1
Reputation: 102248
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.
Upvotes: 1
Reputation: 6649
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.
Upvotes: 3