Can I initialize a class variable using a global variable? (ruby) - ruby

Do I have create extra method for this kind of assignment? ##variable = #global_variable Why? I want to have some variables that hold values and definitions to be accessible all through my script and have only one place of definition.
#global_variable = 'test'
class Test
##variable = #global_variable
def self.display
puts ##variable
end
end
Test.display #gives nil

In Ruby, global variables are prefixed with a $, not a #.
$global = 123
class Foo
##var = $global
def self.display
puts ##var
end
end
Foo.display
correctly outputs 123.
What you've done is assign an instance variable to the Module or Object class (not sure which); that instance variable is not in scope of the class you've defined.

Related

How to read a class's instance variable scope and visibility

Why is it not possible to read a class's instance variable value if it was set within the class using the attr_accessor?
Is it because the the instance variable is "private?"
Setting the attribute this way works:
class Test
attr_accessor :magic
end
bob = Test.new
bob.magic = "cat" #set
print bob.magic #read
Setting the attribute this way fails:
class Test
attr_accessor :magic
#magic = "spell"
end
bob = Test.new
print bob.magic #this resolves to 'nil'
There is a significant difference between instance variables and class instance variables.
class Test
#magic = "spell" # class instance var, since the scope is class
def initialize
#magic = 42 # instance var, scope is instance
end
end
Those two live together, since they are defined on different objects:
Test.instance_variable_get(:#magic) #⇒ "spell"
Test.new.instance_variable_get(:#magic) #⇒ 42
That said, attr_accessor reads the variable from the scope, it was defined for. Yours was defined in class scope, therefore it reads the instance scoped variable.
To read the class instance variable, define attr_accessor on class’ singleton class level:
class Test
singleton_class.send :attr_accessor, :magic # reads class instance var ⇓
#magic = "spell" # class instance var, since the scope is class
attr_accessor :magic # reads instance var ⇓
def initialize
#magic = 42 # instance var, scope is instance
end
end
Test.magic
#⇒ "spell"
Test.new.magic
#⇒ 42
You can access Test's #magic by defining attr_accessor within the singleton class of Test:
class Test
#magic = "spell"
class << self
attr_accessor :magic
end
end
Test.magic #=> "spell"
You should move #magic declaration/assignment to the constructor, like this:
class Test
attr_accessor :magic
def initialize
#magic = "spell"
end
end
bob = Test.new
print bob.magic #spell
When you declare instance variable (#) outside any method (self), it becomes unreachable by the class instance.
Instance variables belong to objects (aka "instances"), that's why they are called instance variables.
In your first example, you are setting the instance variable #magic on bob to 'cat' and then you are reading the instance variable #magic on bob.
In your second example, you are setting the instance variable #magic on Test (which is a completely different object than bob) and then you are reading the instance variable #magic from bob, which hasn't been set yet, and uninitialized instance variables evaluate to nil.
Remember: classes are objects just like any other object. They can have instance variables just like any other object.

How to Initialize Class Arrays in Ruby

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

Ruby: Why is an instance variable defined inside a class nil?

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

Ruby variable scopes

# encoding: utf-8
class Person
attr_reader :short_name
def initialize(short_name)
#short_name = short_name
end
def greeting_line
short_name = short_name.downcase
"Hello #{short_name}"
end
end
person = Person.new("MS. LEE")
puts person.short_name => "MS. LEE"
puts person.greeting_line => NoMethodError: undefined method `downcase' for nil:NilClass
The exception occurs at "short_name = short_name.downcase" since (short_name = short_name) makes short_name become nil.
Why is "short_name" on the right side is not getting the value from the instance method "short_name"?
When you say
var = value
it always refers to the local variable var, even if you have the methods var and var= defined. (In your case, you have the method short_name defined by attr_reader, but you don't have short_name= defined.)
You have a couple ways around this. You can use the instance variable directly:
#var = value
Or you can use the var= method with an explicit self receiver:
self.var = value
This second form only works if you have a var= method defined, either explicitly, or with attr_accessor or attr_writer.
Now, when you do something like
foo = foo
This always introduces a local variable, foo on the left hand side. When ruby sees foo on the right hand side of the =, it resolves to the local variable foo, since foo is now in scope. So this always just sets foo back to its default value, nil.
In your case in greeting_line scope short_name is pointing to local variable and not to instance method short_name, for this example to work you should use self.short_name (this will tell ruby to use instance method)
def greeting_line
short_name = self.short_name.downcase
"Hello #{short_name}"
end
You simply forgot the # when referencing short_name
#short_name
is an instance variable
short_name
is a local variable bound to the scope of the greeting_line function
Try this:
def greeting_line
short_name = #short_name.downcase
"Hello #{short_name}"
end
You should use:
attr_accessor :short_name
And not attr_reader.
attr_reader produces the following method in your class:
def short_name
#short_name
end
attr_accessor will also produce the method:
def short_name=(val)
#short_name = val
end
Which will allow you to freely set the short_name as it was a local variable.
This is a nice blog post that demonstrates attr_reader, attr_writer and attr_accessor statements in ruby.

Rails and class variables

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.

Resources