ruby class collections - ruby

how does this work?
in irb:
>> class A
>> b = [1, 2,3]
>> end
=> [1, 2, 3]
Is b an instance variable? class variable? how would I access b from
outside the class? Is it used for meta-programming?

Is b an instance variable? class variable?
No, it's a local variable inside the class ... end scope.
how would I access b from outside the class?
You wouldn't. It goes out of scope (and is thus inaccessible) once it reaches the end.
Is it used for meta-programming?
It can be. Example:
class A
b = [1,2,3]
b.each do |i|
define_method("foo#{i}") do end
end
end
I've now defined the methods foo1, foo2 and foo3.
Of course this wouldn't behave any differently if I didn't create the variable b and just did [1,2,3].each directly. So creating a local variable by itself does nothing, it allows you to write cleaner code (the same as using local variables in a method).

b is a simple block variable, you cannot access it from outside of the block.
You can use class like this :
class Building
##count=0 #This is a class variable
MIN_HEIGHT=50 #This is a constant
attr_accessor :color, :size #grant access to instance variables
def initialize options
#color=options[:color] ##color is an instance variable
#size=options[:size] ##size too
##count=##count+1
end
def self.build options #This is a class method
# Adding a new building
building=Building.new options
end
end
#[...]
Building.build({:color=>'red', :size=>135})
blue_building=Building.new({:color=>'blue', :size=>55})
puts blue_building.color # How to use an instance variable
# => 'blue'
puts "You own #{Building.count.to_s} buildings !" # How to use a class variable
# => 'You own 2 buildings !'
puts Building::MIN_HEIGHT # How to use a constant
# => 50

Related

Ruby local variable scope

I'm struggling with variable scope in ruby. I was under the impression that local variables were accessible by methods below them. Looking at the following code however I'm getting an undefined variable error.
a = Array.new
def testing()
a.push("test")
end
testing
Using global variables it works just fine, how can I avoid using global variables?
There isn't much to say here except that local variables in Ruby are only accessible in the scope in which they are defined and any blocks (closures) defined in that scope that capture them. Since in Ruby, unlike some other dynamic languages, method are not closures, they do not capture local variables.
If you tried, say, this:
irb(main):001:0> a = 3
=> 3
irb(main):002:0> define_method(:testing) do
irb(main):003:1* puts a
irb(main):004:1> end
=> :testing
irb(main):005:0> testing
3
It works, since the code is in a block instead of a method.
Defining a method in the top-level can be quite confusing. Let's wrap your code in a class instead:
class Foo
a = []
def testing
a << 'test'
end
end
(I've shortened Array.new to [] and a.push(...) to a << ...)
Foo#testing can be called via:
foo = Foo.new
foo.testing
#=> undefined local variable or method `a'
Apparently, this doesn't work. The first a is a local variable in the scope of the class body, whereas the second a is a local variable within an instance method.
Moving the variable initialization out of the class body into the initialize method doesn't work either, because local variables are not shared across methods:
class Foo
def initialize
a = [] # <- one 'a'
end
def testing
a << 'test' # <- another 'a'
end
end
To get this working, you have to use an instance variable:
class Foo
def initialize
#a = []
end
def testing
#a << 'test'
end
end
foo = Foo.new
foo.testing
#=> ["test"]
foo.testing
#=> ["test", "test"]
You could use instance variables. Any variable whose name begins with # is an instance variable and is available anywhere in the class or method in which it is defined. For example, the variable #A defined within class B will be available to any methods in B.
2.3.3 :007 > def testing()
2.3.3 :008?> [].push("test")
2.3.3 :009?> end
=> :testing
2.3.3 :010 > testing
=> ["test"]
You can't let local variables accessible by methods below them , you can use block like the answer by #Linuxios, or use the way that it easy work.

Ruby class variable initial value [duplicate]

This question already has answers here:
Total newbie: Instance variables in ruby?
(4 answers)
Closed 5 years ago.
In PHP I write:
Class Test{
public $a=100;
}
$a=new Test();
echo $a->a; //prints 100
It prints 100, but In ruby when I write:
class Test
#a=100
attr_accessor :a
end
a=Test.new
puts a.a #=> prints nil
a is nil and it prints nil. Why?
You're mixing things a bit.
What you've defined with #a = 100 is a class instance variable.
What you're going to have access to with attr_accessor :a is an instance variable #a.
Example of instance variable usage:
class A
def initialize a
#a = a
end
attr_accessor :a
end
instance = A.new(2)
#=> 2
instance.instance_variables
#=> [:#a]
instance.a
#=> 2
Example of class instance variable usage:
class A
#a = 1
class << self
attr_accessor :a
end
end
A.a
#=> nil
A.a = 2
#=> 2
A.a
#=> 2
instance = A.new
instance.class.a # access instance's class instance variable
#=> 2
Instances variables belong to objects (aka instances), that's why they are called "instance variables" after all.
There are two objects here: Test, which is an instance of Class, and a, which is an instance of Test. Both are objects just like any other object. Both can have instance variables just like any other object.
Both have an instance variable called #a. Foo's #a has been initialized to 100. a's #a hasn't been initialized at all, and unitialized instance variables evaluate to nil.
So, your problem is that you are confusing which instance you are looking at. Instance variables are always looked up in self, and inside a class definition body, self is the class being defined.
This may sound pedantic, but I find that understanding that Ruby is actually much simpler than people sometimes want you to believe, will ultimately help you.
Try this instead:
class Test
attr_accessor :a
def initialize
#a = 100
end
end
a = Test.new
puts a.a
The reason this is happening is that this line:
#a = 100
is assigning to an instance variable but not the one you think. It is being assigned to Test.a, (Test being an instance of Class) rather than to an instance variable a belonging to each instance of Test
In turn, this line:
attr_accessor :a
is declaring an accessor to an instance variable called a (accessible through any instance of Test)
Thus you should either have
class Test
#a = 100
class << self
attr_accessor :a
end
end
puts Test.a #=> will print 100
or
class Test
def initialize
#a = 100
end
attr_accessor :a
end
a = Test.new
puts a.a #=> will print 100
It depends if you just want a class constant or if you want an attribute with a default value on each instance. The latter is a direct translation of your PHP code.

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 and class variables in inherit class

class A
def set(v)
##v = v
end
def put
puts ##v
end
end
class B < A
end
class C < A
end
B.new.set 'b'
B.new.put # => b
C.new.set 'c'
C.new.put # => c
B.new.put # => c
Why? And how should I write this to have 'b' in last B.new.put?
Here is a nice article on the subject - Class and Instance Variables In Ruby.
Basically, what you can do is:
class A
class << self
attr_accessor :class_var
end
def set_class_var(value)
self.class.class_var = value
end
def get_class_var
self.class.class_var
end
end
class B < A; end
A.class_var = 'a'
B.class_var = 'b'
puts A.class_var # => a
puts B.class_var # => b
A.new.set_class_var 'aa'
B.new.set_class_var 'bb'
puts A.new.get_class_var # => aa
puts B.new.get_class_var # => bb
To understand it you should think about A as an instance of Class class (and that's how it is in Ruby). But every object in Ruby has its own singleton class that stores object-specific stuff like methods defined on object itself:
a = A.new
def a.foo
puts 'foo'
end
In that case foo is method defined only for a object and not for every instance of A class. And another way to define method in object's singleton class is like that:
class << a # open a's singleton class
def bar # define method that will be available only on 'a' object
puts 'bar'
end
end
In the first code snippet we use that approach to define class_var attribute accessor in the context of singleton class of our A class (it's a bit tricky, so you need to think about it). As the result class itself has class_var variable as well as its descendant class B. The difference is that every one of them has its own class_var variable that do not interfere.
Another option is to pull out class_inheritable_accessor code from Rails and include its behavior in your classes. See here for a good discussion and the guts of the code.
Perhaps you don't really want a class variable, though.
Assigning a value to a class variable (an ## variable) sets it for EVERY instance of the class. It even "sets" it for instances that "aren't created yet." So, consider this...
B.new.set 'b' # OK, that set ##v for that particular instance of B
B.new.put # Hey, you just created *another* new instance of B!
How can ##v have a value in that one? The second object's value of ##v would be unset, except for the fact that ##v is a class variable, so it has the same value for every instance of the class.

ruby class problem

I have the following ruby code:
class Mp
def initialize
Test.new.mytest
Work.new.mywork
ha
address
end
def ha
puts "message from ha"
end
def address
a='see'
end
end
class Test
def mytest
m=Mp.new
puts "Message from test do you #{m.address}"
end
end
class Work
def mywork
puts "message from work"
end
end
Mp.new
This works fine except the part in def mytest where I'm trying to put out the m.address. Thanks for your help in advance.
Actually the reason it doesn't work has nothing to do with printing the address. It's one line before that:
m = Mp.new this creates a new Mp object. However inside Mp's initialize method a new Test object is created and its mytest method is called. The mytest method then again creates a new Mp object and so on. In other words: Test#mytest and Mp#initialize are mutually and infinitely recursive.
Edit in response to your comment:
I'm not quite sure I understood the question. If you mean "How do I access the variable a which was set in the address method, after address has been called": you don't. a is a local variable that goes out of scope once the method has returned. If you want to set an instance variable use #a = 'see'. # denotes instance variables in ruby. If you want to be able to access that variable from outside the object, use attr_accessor :a to define accessor methods for #a.
An example:
class Mp
attr_accessor :c
def initialize
initialize_variables
puts #c
puts #b # I can access #c and #b here because it's an instance variable
# and I'm within the same object
# puts a # This does not work because a is a local variable from the
# initialize_variables method and no longer in scope
end
def initialize_variables
a = "a"
#b = "b"
#c = "c"
puts a # I can access a here because I'm still inside the method
# where a was defined
end
end
m = Mp.new
# puts m.a
# puts m.b # These don't work because there are no methods a or b
puts m.c # This works because attr_accessor defined a method c which
# returns the content of m's #c variable
You've got an infinite loop. You create a new object of class Mp, which in turn creates a new object of class Test and then calls its mytest method, which in turn creates another object of class Mp, which in turn...

Resources