class Appthin
#fileDir = ["a","b"]
puts #fileDir.class
def findclass
puts #fileDir.class
checkFiles(#fileDir)
end
end
input
Array
NilClass
why they are different, it confused me.
This is because variables starting with an # symbol are instance variables, but when you're first calling it, there isn't an instance of Appthin initialized.
When you refer to an instance variable, it selects self and sets an instance variable for that.
If you're calling an instance variable from the body of your class, then self is the class itself, which is an instance of Class, but not an instance of Appthin.
The solution to your problem depends on what you're trying to do:
If you want to set a variable in the class body, which is accessible to all instances of the class, I'd use a constant.
class Appthin
FILE_DIR = ['a', 'b']
def find_class
puts FILE_DIR
end
end
If you're trying to set a variable belonging to a specific instance of Appthin, which can then be changed, set it in the initialize method:
class Appthin
def initialize
#file_dir = ['a', 'b']
end
def find_class
puts #file_dir
end
end
Related
class Artist
##song_count = []
attr_accessor :name, :songs
def initialize(name)
#name = name
#songs = []
end
def add_song(song)
#songs << song
end
def print_songs
songs.each {|song| puts song.name}
end
end
So in this example, it uses all two types, #songs and songs.
I'm having a hard time understanding why these are used, instead of using #songs for everything.
And then in this example,
def add_song(song)
self.songs << song
song.artist = self
##song_count +=1
end
Why is self.songs used instead of #songs?
Ok, so I forgot to say one more thing. In the first code snippet above,for method print_songs, why am I able to use songs.each instead of #songs.each? I was expected it to generate an error undefined songs.
Why is self.songs used instead of #songs
Using the method is more flexible. You're abstracting yourself from knowing how exactly it gets/stores data. The less you rely on implementation details, the easier it will be for you to change code later.
One small example, consider this implementation of songs
def songs
#songs ||= []
#songs
end
#songs may or may not have been assigned value prior to invocation of this method. But it doesn't care. It makes sure that #songs does have a sane default value. The concept is called "lazy initialization" and it's very tedious and error-prone to do if you use instance variables directly.
So, when in doubt, always use methods.
Difference between foo and #foo
Instance variables
Instance variables are defined within instance methods, and their names begin with #. Their value is only accessible within the specific object on which it was set. In other words, when we modify the value of an instance variable, the change only applies to that particular instance. Unlike local variables which are only available within the method where they were defined, instance variables are accessible by all methods within the object (instance methods of the class). Instance variables are the most commonly used type of variable in Ruby classes.
class Car
attr_reader :color
def set_color(color_receiverd_as_argument)
#color = color_receiverd_as_argument
end
end
car1 = Car.new
car1.color # Output: => nil
car1.set_color "black"
car1.color # Output: => "black"
car2 = Car.new
car2.set_color "silver"
car2.color # Output: => "silver"
In the example above, notice that:
Trying to access an instance variable before it's initialized will not raise an exception. Its default value is nil.
Changing the value of the color variable in one instance of the Car class does not affect the value of the same variable in the other instances.
Local variables
A local variable within a class is like any other local variable in Ruby. It is only accessible within the exact scope on which it's created. If defined within a method, it is only available inside that method.
class Car
def initialize
wheels = 4
end
def print_wheels
print wheels
end
end
c = Car.new
c.print_wheels # Output: NameError: undefined local variable or method `wheels'…
The self keyword
The self keyword is always available, and it points to the current object. In Ruby, all method calls consist of a message sent to a receiver. In other words, all methods are invoked on an object. The object on which the method is called is the receiver, and the method is the message. If we call "foo".upcase, the "foo" object is the receiver and upcase is the message. If we don't specify an object (a receiver) when calling a method, it is implicitly called on the self object.
Self keyword at class level
When used within a class but outside any instance methods, self refers to the class itself.
class Foo
##self_at_class_level = self
def initialize
puts "self at class level is #{##self_at_class_level}"
end
end
f = Foo.new # Output: self at class level is Foo
Self keyword at instance methods
When inside an instance method, the self keyword refers to that specific instance. In other words, it refers to the object where it was called.
class Meditation
def initialize
puts "self within an instance method is #{self}"
end
end
zazen = Meditation.new # Output: self within an instance method is #<Meditation:0x00000000ab2b38>
Notice that #<Meditation:0x00000000ab2b38> is a string representation of the zazen object, which is an instance of the Meditation class.
I want to create an empty array as a class instance variable in Ruby. However, my current method does not seem to work.
Here is my code:
class Something
#something = []
def dosomething
s = 5
#something << s
end
end
When I call the function, it gives me an undefined method traceback.
However, if I do something similar with class variables, i.e.:
class Something
##something = []
def dosomething
s = 5
##something << s
end
end
This works perfectly.
I know I can use the initialize method to actually create an empty list for #something, but is there another way of doing this without using the initialize method? And why does this work for class variables?
EDIT: Fixed typo
You need to use initialize as a constructor as below code and is there any reason why not to use initialize/constructor. And please fix a typo error in class definition Class Something to class Something no camel case or first letter capitalize while in class
class Something
def initialize
#something = Array.new
end
def dosomething
s = 5
#something << s
end
end
class variable ## are available to the whole class scope. so they are working in the code and if you want to use instance variable # you need to initialize it as above. The instance variable is share with instance/objects of a class
for more details visit the link Ruby initialize method
At first you have a typo. Change Classto class. Next I suggest to use the initialize method. While creating a new object this is the perfect place to initialize instance variables.
class Something
##my_class_variable = [1]
def initialize
#something = []
end
def dosomething
s = 5
#something << s
end
def self.get_my_class_variable
##my_class_variable
end
end
Your script will be read and executed from top to bottom and after this,
you can access the class Something. While the parser reads your script/class/module you can define class variables (##), execute mixins and extend the class with other modules. This is why you can define a class variable, but you can not define an instance variable. Because actually you have no instance object from your class. You only have a class object. In ruby everything is an object. And your class object has a defined class variable now:
Something.get_my_class_variable
# => [1]
Now you can create an instance from your class. With Something.new the initialize method will be invoked and your instance variable will be defined.
something = Something.new
something.dosomething
# => [5]
Later, if you are familar with this you can define getter and setter methods with attr_reader, attr_writer and attr_accessor for instance objects or cattr_reader, cattr_writer and cattr_accessor for class objects. For example:
class Something
attr_reader :my_something
def initialize
#my_something = []
end
def dosomething
s = 5
#my_something << s
end
end
something = Something.new
something.my_something
# => []
something.dosomething
# => [5]
something.my_something
# => [5]
Your problem in trying to access #something in your instance method is that, in the scope of instance methods, # variables refer to instance variables, and your #something is a class instance variable.
# variables are instance variables of the instance that is self when they are created. When #something was created, self was the class Something, not an instance of Something, which would be the case inside an instance method.
How then to access a class instance variable in an instance method? Like regular instance variables, this must be done via a method, as in attr_accessor. One way to do this is to use class << self to tell the Ruby interpreter that the enclosed code should be evaluated with the class (and not the instance) as self:
class C
#foo = 'hello'
class << self
attr_accessor :foo # this will be a class method
end
def test_foo # this is, of course, an instance method
puts self.class.foo # or puts C.foo
end
end
We can show that this works in irb:
2.3.0 :005 > C.foo
=> "hello"
2.3.0 :006 > C.new.test_foo
hello
You have correctly created a class instance variable, #something, and initialized it to an empty array. There are two ways for instances to obtain or change the value of that variable. One is to use the methods Object#instance_variable_get and Object#instance_variable_set (invoked on the class):
class Something
#something = []
def dosomething
s = 5
self.class.instance_variable_get(:#something) << s
end
end
sthg = Something.new
sthg.dosomething
Something.instance_variable_get(:#something)
#=> 5
The other way is to create an accessor for the variable. There are several ways to do that. My preference is the following:
Something.singleton_class.send(:attr_accessor, :something)
Something.something #=> [5]
In your dosomething method you would write:
self.class.something << s
Here is my code:
class Klass
["thing", nil].each do |i|
instance_variable_set("##{i}reqs", {})
end
def initialize(var)
#reqs[var] = self
end
end
Klass.new("hello")
Which gives me the error:
in initialize': undefined method[]=' for nil:NilClass (NoMethodError)
I shouldn't be getting this error because the loop at the top should have initialized #reqs in it's second iteration. What is going on?
Instance variables belong to particular instances. That's why they are called instance variables.
In line 3, you set the instance variable called #reqs of the object Klass. In line 6, you access the instance variable called #reqs of an instance of the class Klass. Those are two completely different, distinct objects each with its own set of instance variables. Heck, those two objects don't even have the same class! (Klass's class is Class, whereas Klass.new's class is Klass.)
In line 6, #reqs is uninitialized, and uninitialized instance variables evaluate to nil.
There are many different ways to fix this, depending on your exact circumstances and requirements, the easiest way would be to initialize the instance variables in the initialize method, after all, that's what that method is there for:
class Klass
def initialize(var)
['thing', nil].each do |i|
instance_variable_set(:"##{i}reqs", {})
end
#reqs[var] = self
end
end
Klass.new('hello')
Remember, the problem was that the instance variables were initialized in one object, and accessed in another. This solution moves the initialization to the same object that was doing the reading.
However, the dual is also possible: move the reading to where the initialized variables are:
class Klass
['thing', nil].each do |i|
instance_variable_set(:"##{i}reqs", {})
end
def initialize(var)
self.class.instance_variable_get(:#reqs)[var] = self
end
end
Klass.new('hello')
This is kind of ugly, so let's add an attr_reader:
class Klass
['thing', nil].each do |i|
instance_variable_set(:"##{i}reqs", {})
end
class << self; attr_reader :reqs end
def initialize(var)
self.class.reqs[var] = self
end
end
Klass.new('hello')
Obviously, these two do very different things. It is unclear from your question which of the two you actually want.
A third possibility would be using class variables:
class Klass
['thing', nil].each do |i|
class_variable_set(:"###{i}reqs", {})
end
def initialize(var)
##reqs[var] = self
end
end
Klass.new('hello')
Note that this does yet another different thing. Again, whether you want that or not is not clear from your question.
The loop at the top is defining an instance variable for the Class, not for any object of the class.
So for the object, it doesn't exist.
From the looks of it, you want a hash common to the whole class where you store each created object in the hash. Assuming you don't have issues with class inheritance, you'd be better of with a class variable.
so...
class Klass
["thing",nil].each do |i|
class_variable_set("###{i}reqs", {})
end
def initialize(var)
##reqs[var] = self
end
end
Klass.new("hello")
If you define class like this:
class Klass
instance_variable_set(:#name, 'dog')
def self.name
#name
end
def name
#name
end
end
then
Klass.name # => 'dog'
instance = Klass.new
instance.name # => nil
Now you can see the difference. Your variable is defined on class level, not instance level.
class Something
#b = [4432]
def screen
puts #b.class
end
end
s = Something.new
s.screen
outputs 'Nilclass'. Was wondering, why does an instance variable which is defined inside a class always part of NilClass?
Instance variables belong to an object (aka an instance), that's why they are called instance variables. Every instance has its own instance variables.
In your case, there are two objects: Something (which is an instance of Class) and s (which is an instance of Something). Each of those two objects has its own set of instance variables. Something has an instance variable called #b which points to [4432]. s has no instance variable named #b because you never assign to it, and uninitialized instance variables evaluate to nil.
You need to set it like this:
class Something
def initialize
#b = [4432]
end
def screen
puts #b.class
end
end
The way you did it, the variable belongs to Something class itself, not its instance. Observe:
class Something
#b = [4432]
end
s = Something.new
s.instance_variable_get(:#b) # => nil # !> instance variable #b not initialized
Something.instance_variable_get(:#b) # => [4432]
Generally the instance variable must be defined inside the constructor whereas in ruby the default constructor is initialize the syntax is
def initialize
end #these is the default constructor in ruby
so when we define the insatnce variable inside the constructor and when we create the instance of a class then that instance/object will contain the copy of instance variables
most important thing is that though the instance/object contains the instance variable the instance/object cannot access it why because by default the instance data is private so in order to access it we need to define the getters and setter for those instance variable
class Something
attr_accessor:b
def initialize
#b = [4432]
end
s=Something.new
puts"#{s.b}"
Because the variable #b does not exist!. For e.g. the following would produce the same results you see.
class Something
#b = [4432]
def screen
puts #a.class #=> note #a which is non-existent
end
end
s = Something.new
s.screen
Whereas
class Something
#b = [4432]
def screen
puts #a.class
end
def self.screen
puts #b.class
end
end
s = Something.new
s.screen #=> NilClass
Something.screen #=> Array
if you initialize #b outside the initializer, you want #b scope to be a class variable, so you have to call it ##b :
##b has the same value for all Instance of your Something Class
like :
class Somthing
##b = [4432]
def initialize
#[...]
end
def screen
puts ##b.class
end
end
#Jörg W Mittag answer is correct. I just wont to add, that defining an instance variable in class != defining instance variable in an instance of that class. To create an instance variable, in your case in s instance you need to add an initialize method witch gets triggered when new method is called on a class.
def initialize(b_value = default_value)
#b = b_value
end
class MainController < ApplicationController
#my_var = 123
def index
var1 = #my_var
end
def index2
var2 = #my_var
end
end
Why is neither var1 no var2 equal to 123?
Variables with # are instance variables in ruby. If you're looking for class variables, they're prefixed with ##, so you should be using ##my_var = 123 instead.
And the reason you can't use instance variables that way, is because if you define instance variables outside methods, they don't live in the same scope as your methods, but only live while your class is interpreted.
var1 in your example is a local variable, which will only be visible inside the index method.
Examples:
class Foo
##class_variable = "I'm a class variable"
def initialize
#instance_variable = "I'm an instance variable in a Foo class"
local_variable = "I won't be visible outside this method"
end
def instance_method_returning_an_instance_variable
#instance_variable
end
def instance_method_returning_a_class_variable
##class_variable
end
def self.class_method_returning_an_instance_variable
#instance_variable
end
def self.class_method_returning_a_class_variable
##class_variable
end
end
Foo.new
=> #<Foo:0x007fc365f1d8c8 #instance_variable="I'm an instance variable in a Foo class">
Foo.new.instance_method_returning_an_instance_variable
=> "I'm an instance variable in a Foo class"
Foo.new.instance_method_returning_a_class_variable
=> "I'm a class variable"
Foo.class_method_returning_an_instance_variable
=> nil
Foo.class_method_returning_a_class_variable
=> "I'm a class variable"
#my_var, in your sample code, is an instance variable on the class MainController. That is, it's a class-level instance variable, and not an instance-level instance variable. It exists in a totally different scope to the instance variable associated with an instance of the class.
Within the body of your instance methods, index and index2, you are attempting to dereference an instance variable on an object that is an instance of class MainController, but you have not defined that instance variable anywhere, so you get back nil.
If you want to use #my_var as a class-level instance variable, you can get its value from within an instance of the class like this:
var1 = self.class.instance_variable_get(:#my_var)
Class variables are indicated with a ## prefix, and their use is not entirely encouraged. A couple of minutes with Google will tell you why.
Because code executes in different context. You can see here:
class MainController
puts self
def print_self
puts self
end
end
#=> MainController
MainController.new.print_self #=> <MainController:0x00000001761140>
As you can see in first print the self is MainController, in second print the self is the object which derived from MainController class.
In the assignment to #my_vay this variable belongs to MainController, and in the second cases, the #my_var belongs to object (not a class) and these varaibles are different.