Sorry for the newbie question but how do I pass/access attar_accessor to and from multiple classes? in my example below the Foo class never is able to see the updated values from the Level class.
class Level
attr_accessor :level, :speed
end
class Foo
attr_reader :a, :b
def initialize
#x = Level.new()
#a = #x.level
#b = #x.speed
end
def reload
#a = #x.level
#b = #x.speed
end
end
#testa = Foo.new()
#testb = Level.new()
#testb.level = 5
#testb.speed = 55
puts "Value for attr_accessor is:"
puts "#{#testb.level}"
puts "#{#testb.speed}"
puts "#{#testa.a}"
puts "#{#testa.b}"
#testa.reload
puts "#{#testa.a}"
puts "#{#testa.b}"
Instance of Level class you declaring in Foo's constructor and #testb are two different objects.
You might want to edit your Foo class this way:
class Foo
def initialize(level)
#x = level # very strange name for such a thing.
#a = #x.level
#b = #x.speed
end
# rest of the class body is the same as yours
# so it is omitted
end
And then do your tests:
level = Level.new() # BTW: no need of instance variables here.
foo = Foo.new(level) # good job. Your foo has "captured" the level.
# et cetera
Related
I have searched on here for an answer to this question and couldn't find what I was after (in exact terms) so I'm gonna be slightly greedy and ask for some of the communities time. I hope to make my question as applicable as possible.
So, some context, I've been struggling with the idea of classes, class variables and methods for the last week but over the last 2 days have made serious headways in my understanding. However now I am faced with inheritance and can't work out why super would be used when I can just inherit without using it.
For example:
class Animal
def initialize (type, breed, age)
#type = type
#breed = breed
#age = age
end
end
class Dog < Animal
end
class Cat < Animal
end
class Fish
end
woof = Dog.new("dog", "shitzu", 12)
meow = Cat.new("cat", "tabby", 5)
fish = Fish.new("fish", "gold", 2)
Output:
=> #<Dog:0x00000001447680 #type="dog", #breed="shitzu", #age=12>
=> #<Cat:0x0000000140c918 #type="cat", #breed="tabby", #age=5>
ArgumentError: wrong number of arguments (given 3, expected 0)
As you can see, I have been able to inherit from Animal on both my Dog and Cat classes, which I marked for inheritance, but on my Fish I have not been able to, because I didn't inherit.
If someone could explain why we use super, and point out flaws in my understanding, I'd be very grateful, I understand that I may be completely just misunderstanding usage here, but I'd like to just get it clarified. Thanks for your time, appreciate the help.
Using super lets a class override a method that it inherits from its parent and customize it.
For instance, in your example, Dog and Cat inherit #initialize from Animal - but what if we wanted some special logic for Dog?
class Dog < Animal
def initialize(type, breed, age)
raise "Sorry, dogs don't live that long!" if age > 100
# Everything looks good - let Animal#initialize run now
super
end
end
This lets Dog customize what its initialize method does, but still call through to the original inherited method.
Excerpted from Inheritance - What is inherited?. The original author was nus. Attribution details can be found on the contributor page. The source is licenced under CC BY-SA 3.0 and may be found in the Documentation archive. Reference topic ID: 625 and example ID: 14883.
Methods are inherited
class A
def boo; p 'boo' end
end
class B < A; end
b = B.new
b.boo # => 'boo'
Class methods are inherited
class A
def self.boo; p 'boo' end
end
class B < A; end
p B.boo # => 'boo'
Constants are inherited
class A
WOO = 1
end
class B < A; end
p B::WOO # => 1
But beware, they can be overridden:
class B
WOO = WOO + 1
end
p B::WOO # => 2
Instance variables are inherited:
class A
attr_accessor :ho
def initialize
#ho = 'haha'
end
end
class B < A; end
b = B.new
p b.ho # => 'haha'
Beware, if you override the methods that initialize instance variables without calling super, they will be nil. Continuing from above:
class C < A
def initialize; end
end
c = C.new
p c.ho # => nil
Class instance variables are not inherited:
class A
#foo = 'foo'
class << self
attr_accessor :foo
end
end
class B < A; end
p B.foo # => nil
# The accessor is inherited, since it is a class method
#
B.foo = 'fob' # possible
Class variables aren't really inherited
They are shared between the base class and all subclasses as 1 variable:
class A
##foo = 0
def initialize
##foo += 1
p ##foo
end
end
class B < A;end
a = A.new # => 1
b = B.new # => 2
So continuing from above:
class C < A
def initialize
##foo = -10
p ##foo
end
end
a = C.new # => -10
b = B.new # => -9
I have a ruby class, and in one of the methods, it calls an external function, and pass in all instance variables, and continue with the return value. Here is the code:
class MyClass
attr_accessor :name1
attr_accessor :name2
...
attr_accessor :namen
def inner_func():
all_vars = ???? # how to collect all my instance variables into a dict/Hash?
res = out_func(all_vars)
do_more_stuff(res)
end
end
The problem is the instance variables might vary in subclasses. I can't refer them as their names. So, is there a way to do this? Or Am I thinking in a wrong way?
You can use instance_variables to collect them in an Array. You will get all initialized instance variables.
class MyClass
attr_accessor :name1
attr_accessor :name2
...
attr_accessor :namen
def inner_func():
all_vars = instance_variables
res = out_func(all_vars)
do_more_stuff(res)
end
end
You could keep track of all accessors as you create them:
class Receiver
def work(arguments)
puts "Working with #{arguments.inspect}"
end
end
class MyClass
def self.attr_accessor(*arguments)
super
#__attribute_names__ ||= []
#__attribute_names__ += arguments
end
def self.attribute_names
#__attribute_names__
end
def self.inherited(base)
parent = self
base.class_eval do
#__attribute_names__ = parent.attribute_names
end
end
def attributes
self.class.attribute_names.each_with_object({}) do |attribute_name, result|
result[attribute_name] = public_send(attribute_name)
end
end
def work
Receiver.new.work(attributes)
end
attr_accessor :foo
attr_accessor :bar
end
class MySubclass < MyClass
attr_accessor :baz
end
Usage
my_class = MyClass.new
my_class.foo = 123
my_class.bar = 234
my_class.work
# Working with {:foo=>123, :bar=>234}
my_subclass = MySubclass.new
my_subclass.foo = 123
my_subclass.bar = 234
my_subclass.baz = 345
my_subclass.work
# Working with {:foo=>123, :bar=>234, :baz=>345}
I am doing the ruby koans and I am on the DiceSet project. I've made the DiceSet class but my instance variables don't seem to persist with the instance like I thought they would. My code is
class DiceSet
attr_reader :values
#values = []
puts #values.class
def roll(number_of_rolls)
(1..number_of_rolls).each do |roll|
puts #values.class
#values << (1..6).to_a.sample
end
return #values
end
end
The koan then uses my DiceSet class with
dice = DiceSet.new
dice.roll(5)
puts dice.values.class
assert dice.values.is?(Array)
I put the puts commands in there to follow whats happening with the #values instance variable and only the first puts #values.class says its an Array class. All the others are returning NilClass. Am I using instance variables incorrectly or is there something else I am missing? Do instance variables get deallocated after a method call?
EDIT: My class works correctly now that I have put #values = [] in the roll method as suggested below. My only question now, is why the roll method thinks that #values is a NilClass instead of an array when I put #values = [] in an initialize method
In Ruby everything are objects. The Ruby interpreter assumes that all instance variables belong to the current object self. This is also true in a class definition. The role of self belongs to the class itself, so the instance variable #values belongs to the class. Don’t get confused! Instance variables of the class are different from instance
variables of that class’s objects. Also you don't need specify return keyword explicitly Try this:
class DiceSet
attr_accessor :values
def roll(number_of_rolls)
#values = []
(1..number_of_rolls).each do |roll|
#values << (1..6).to_a.sample
end
#values
end
end
dice = DiceSet.new
dice.roll(5)
puts dice.values.class
assert dice.values.is_a?(Array)
Each DiceSet instance has its own #values, and furthermore, the class DiceSet also has its own #values. They are all different from one another. If you want the instances and the class to share the same variable, you should use a class variable ##values.
Just put the declaration of #values = [] in the initialized method and your code should work as expected.
class DiceSet
attr_reader :values
def initialize()
#values = []
end
def roll(number_of_rolls)
(1..number_of_rolls).each do |roll|
puts #values.class
#values << (1..6).to_a.sample
end
return #values
end
end
Try this:
class Cat
attr_accessor :age
def initialize
#age = 12
end
#age = 6
def meow
puts "I'm #{#age}"
end
def self.meow
puts "I'm #{#age}, going on #{#age+1}"
end
end
Cat.age = 4 # => NoMethodError: undefined method `age=' for Cat:Class
p Cat.age # =? NoMethodError: undefined method `age' for Cat:Class
Cat.meow # => I'm 6, going on 7
cat = Cat.new
p cat.age # => 12
cat.meow # => I'm 12
cat.age = 20 # => 20
cat.meow # => I'm 20
Were I to add
class << self
attr_accessor :age
end
the first three lines of output would become:
Cat.age = 4 # => 4
p Cat.age # => 4
Cat.meow # => I'm 4, going on 5
In Ruby, when defining the contents of a class with class_exec, I am getting unexpected results. When I define a class variable in the block sent to class_exec, the class variable is being defined on Object instead of the class on which class_exec is being called:
class X; end
X.class_exec do
##inner_value = "123"
def inner_value
##inner_value
end
def inner_value=(arg)
##inner_value = arg
end
end
obj1 = X.new
puts obj1.inner_value
puts ##inner_value
puts Object.class_variables
Produces:
123
123
##inner_value
This does not happen when using class_eval:
X.class_eval(<<-RUBY)
##inner_value = "123"
def inner_value
##inner_value
end
def inner_value=(arg)
##inner_value = arg
end
RUBY
obj1 = X.new
puts obj1.inner_value
puts ##inner_value
puts Object.class_variables
Produces:
123
and an error:
uninitialized class variable ##inner_value in Object (NameError)
The results with class_eval are what I would expect to happen in both cases. I have tried this with both MRI 1.8.7 and MRI 1.9.3 and got the same results running on Windows XP.
Is this expected behavior? If so, why? If not, bug?
class variables are bound to the class in which they are declared at compile time. The block passed to class_exec is compiled before it is passed to class_exec, so the class variables are bound to Object.
I guess your class_exec is at the top level, which is in Object, so that's where they go. To demonstrate:
public
class Object
##x = "ribbit"
end
def foo
puts "test: #{##x}"
end
x = Object.new
x.foo
This is why when you use class vars in a module, all classes that include that module (through the included methods) will see the same class variables. The class variables are bound to the module. If you run this:
class WithClassVars
def self.classvars
#classvars ||= {}
end
def classvars
self.class.classvars
end
end
class A < WithClassVars;end
class B < WithClassVars;end
a = A.new
b = B.new
a.classvars[:a] = 1
b.classvars[:a] = 2
puts a.classvars
puts b.classvars
a and b will end up with the same data.
If you pass your code as a string to class_eval, the string is compiled in class_eval, so you can make sure they are in the right class then.
So if you want to store per-class data, you have to either go with class_eval, or use some mechanism to use a class's instance variables. Say:
class WithClassVars
def self.classvars
#classvars ||= {}
end
def classvars
self.class.classvars
end
end
class A < WithClassVars;end
class B < WithClassVars;end
a = A.new
b = B.new
a.classvars[:a] = 1
b.classvars[:a] = 2
puts a.classvars
puts b.classvars
If creating a class variable is often dangerous and unpredictable why do we need them?
If solution is just to use class instance variable with the class level accessors:
class Foo
#variable = :something
def self.getvariable
#variable
end
def self.setvariable(value)
#variable = value
end
end
Then why do we need class variables???
Class variables have their use on occasion, but I agree that using the eigenclass is frequently more useful:
class Foo
#bar = 'bar'
class << self
attr_accessor :bar
end
end
puts Foo.bar # bar
puts Foo.bar = 'baz' # baz
The above is safe with inheritance, because it sets a variable in the Foo constant, rather than a class variable.
Foo.new.instance_eval { puts ##bar } # error
This has several causes:
It's syntactic sugar. You can always get a class variable (whether you are in class or instance scope) using ##var. This won't work for instance variables of the class.
Class variables persist for the singleton classes of the instances of this class. Example:
class Test
#instance_var = 0
##class_var = 0
def self.instance_var
#instance_var
end
def self.class_var
##class_var
end
end
Test.instance_var #=> 0
Test.class_var #=> 0
Test.new.singleton_class.instance_var #=> nil
Test.new.singleton_class.class_var #=> 0
Here is an example (think ActiveRecord):
class Base
def connect(connection)
##connection = connection
end
def connection
##connection
end
end
class User < Base
end
class SuperUser < User
end
Base.new.connect("A connection")
puts User.new.connection #=> A connection
puts SuperUser.new.connection #=> A connection
The trick here is that class variable is accessible from an instance method and is inherited. Try this:
class Base
def self.connect(connection)
#connection = connection
end
def self.connection
#connection
end
def connection
self.class.connection
end
end
class User < Base
end
Base.connect("A connection")
puts User.new.connection #=> nil
You will get nil as self.connection tries to access it's own class instance variable (from User class), and it is not inherited.
Added: And yes, it can be dangerous if you misuse it:
##a = "A"
class A
def self.a
##a
end
def a
##a
end
end
puts A.a #=> A
puts A.new.a #=> A