['board'].each{|script| require_relative script}
class GameRunner
#board = Board.new
def initialize
end
def getBoard
#board
end
end
This piece of code generates an error when getBoard is called. But when I move the instantiation of #board to the initialize block there is no errors. Why?
Edit: An answer with a more clear explanation on what is going on: Ruby class instance variable vs. class variable
Here #board is an instance variable. Instance variables belong to an object (instance), hence why they are called instance variables.
You have two references to an instance variable named #board in your code. Now, ask yourself: which object do they belong to? In other words: what is self at the point where you reference the instance variable?
class GameRunner
# Here, `self` is `GameRunner`
#board = Board.new
def getBoard
# Here, `self` is an *instance* of `GameRunner`
#board
end
end
At the first reference to #board, self is the GameRunner class itself. Remember, class are objects just like any other object; they are instances of the Class class just like strings are instances of the String class, integers are instances of the Integer class, and game runners are instances of the GameRunner class.
You can easily see that the instance variable has been defined and initialized:
GameRunner.instance_variables
#=> [:#board]
GameRunner.instance_variable_get(:#board)
#=> #<Board:0x0000deadbeef1230>
At your second mention, however, self is an instance of GameRunner, and not the GameRunner class itself.
Or, to put it differently: you have two completely independent instance variables of two completely independent objects. The instance variables just happen to have the same name.
It's exactly the same as if you did:
game_runner1 = GameRunner.new
game_runner2 = GameRunner.new
The instance variables of game_runner1 and game_runner2 are private to each of those two objects. game_runner1 does not know anything about the instance variables of game_runner2 and vice versa. The same thing is true about game_runner1 and GameRunner.
Again, it is important to remember that classes are just objects like any other object.
It looks like what you actually want is to have both references refer to the same instance variable, namely an instance variable of an instance of GameRunner. You can achieve that by moving the assignment into an instance method, something like this:
class GameRunner
def initializeBoard
# Here, `self` is an *instance* of `GameRunner`
#board = Board.new
end
def getBoard
# Here, `self` is an *instance* of `GameRunner`
#board
end
end
However, this is somewhat annoying because you always have to remember to call initializeBoard before you can use the object, and you have to make sure that once you have called initializeBoard, you never call it again.
To make initialization tasks like this easier, Ruby has a convention: the default implementation of Class#new will call a method named initialize on the newly allocated object:
class Class
def new(...)
obj = allocate
obj.initialize(...)
obj
end
end
[This is not quite accurate because initialize is private by default, so it would be more like obj.__send__(:initialize, ...), but you get the idea.]
So, if we simply rename the initializeBoard method to initialize, that will ensure that our instance variable is always initialized by GameRunner::new:
class GameRunner
def initialize
# Here, `self` is an *instance* of `GameRunner`
#board = Board.new
end
def getBoard
# Here, `self` is an *instance* of `GameRunner`
#board
end
end
Note that your code violates multiple Ruby community coding standards:
Ruby uses 2 spaces for indentation, not 4.
There should be no empty line after class or before end
Method names use snake_case, not camelCase. IOW, your getter method should be called get_board.
… Except it shouldn't, because getters should simply be called noun, not get_noun, i.e. your getter method should be called simply board.
Lastly, trivial getters should not be defined by hand, but using the core Module#attr_reader method.
If we combine all of this, your class should look like this:
class GameRunner
attr_reader :board
def initialize
#board = Board.new
end
end
I, personally, prefer to avoid referring to instance variables directly as much as possible, and only use getters and setters. However, that is not a majority coding style, that is just my personal preference:
class GameRunner
attr_reader :board
private
def initialize
self.board = Board.new
end
attr_writer :board
end
Here #board is a class variable, not an instance variable.
What you probably mean is:
class GameRunner
# Anything declared here is assumed to be class-level
def initialize
# Anything inside an instance method is an instance variable
#board = Board.new
end
def getBoard
#board
end
end
Since classes are objects, the class can also have its own instance variables. Confusingly they also use the same # prefix.
It's worth noting that accessors like this are usually declared in Ruby as:
attr_reader :board
Which makes the method for you. The get prefix is almost always omitted because mutator methods (e.g. set) are the same but with the = suffix.
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.
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.
I want to initialize my_attr_reader. Changing attr_reader to cattr_reader doesn't help because there is such a method cattr_reader for some reason.
How can I do that?
module Mod1
def method1
puts "method1 from Mod1"
end
end
MyClass = Object.new
class << MyClass
include Mod1
attr_reader :my_attr_reader
my_attr_reader = "111" # doesn't get initialized
def initialize
self.my_attr_reader = "123" # doesn't get initialized
end
def my_class1_method1
puts "MyClass method1"
end
end
MyClass.my_class1_method1
MyClass.method1
p MyClass.my_attr_reader # nil
P.S. Why does include work here, whereas extend doesn't, even so it should be exactly the opposite?
You have a few problems here, so I'll split this up into sections.
A note about cattr_reader
cattr_reader would probably be useful for you, but it is a part of Rails, not Ruby. You will not be able to use this in Ruby code without first including the right parts of Rails.
How to use attr_reader
Your main problem here is that attr_reader and cattr_reader create instance and class variables respectively, but you are using local variables instead. Instance variables start with #, and class variables start with ##. Class variables have odd and confusing behaviors, and cattr_reader isn't built into Ruby as I mentioned above, so I would recommend using attr_reader on the class level.
Why does include work here, whereas extend doesn't, even so it should be exactly the opposite?
extend adds in class-level methods, while include includes instance methods. You are defining method as an instance method of Mod1, so you should be using include.
I Just started learning ruby and I don't see the difference between an #instace_variable and an attribute declared using attr_accessor.
What is the difference between the following two classes:
class MyClass
#variable1
end
and
class MyClass
attr_accessor :variable1
end
I searched lot of tutorials online and everybody uses different notation, Does it have to do anything with the ruby version? I also searched few old threads in StackOverflow
What is attr_accessor in Ruby?
What's the Difference Between These Two Ruby Class Initialization Definitions?
But still I am not able to figure out what is the best way to use.
An instance variable is not visible outside the object it is in; but when you create an attr_accessor, it creates an instance variable and also makes it visible (and editable) outside the object.
Example with instance variable (not attr_accessor)
class MyClass
def initialize
#greeting = "hello"
end
end
m = MyClass.new
m.greeting #results in the following error:
#NoMethodError: undefined method `greeting' for #<MyClass:0x007f9e5109c058 #greeting="hello">
Example using attr_accessor:
class MyClass
attr_accessor :greeting
def initialize
#greeting = "hello"
end
end
m2 = MyClass.new
m2.greeting = "bonjour" # <-- set the #greeting variable from outside the object
m2.greeting #=> "bonjour" <-- didn't blow up as attr_accessor makes the variable accessible from outside the object
Hope that makes it clear.
Instance variables are not directly visible outside of the class.
class MyClass
def initialize
#message = "Hello"
end
end
msg = MyClass.new
#message
#==> nil # This #message belongs to the global object, not msg
msg.message
#==> NoMethodError: undefined method `message'
msg.#message
#==> SyntaxError: syntax error, unexpected tIVAR
Now, you can always do this:
msg.instance_eval { #message }
or ask for the variable directly like this:
msg.instance_variable_get :#message
But that's awkward and sort of cheating. Poking around someone else's class may be educational, but your client code shouldn't be required to do it to get reliable results. So if you want clients to be able to see those values, don't make them use the above techniques; instead, define a method to expose the value explicitly:
class MyClass
def message
return #message
end
end
msg.message
# ==> "Hello"
Because you so often want to do that, Ruby provides a shortcut to make it easier. The code below has exactly the same result as the code above:
class MyClass
attr_reader :message
end
That's not a new type of variable; it's just a shorthand way to define the method. You can look at msg.methods and see that it now has a message method.
Now, what if you want to allow outsiders to not only see the value of an instance variable, but change it, too? For that, you have to define a different method for assignment, with a = in the name:
class MyClass
def message=(new_value)
#message = new_value
end
end
msg.message = "Good-bye"
msg.message
# ==> "Good-bye"
Note that the assignment operators are semi-magical here; even though there's a space between msg.message and =, Ruby still knows to call the message= method. Combination operators like += and so on will trigger calls to the method as well.
Again, this is a common design, so Ruby provides a shortcut for it, too:
class MyClass
attr_writer :message
end
Now, if you use attr_writer by itself, you get an attribute that can be modified, but not seen. There are some odd use cases where that's what you want, but most of the time, if you are going to let outsiders modify the variable, you want them to be able to read it, too. Rather than having to declare both an attr_reader and an attr_writer, you can declare both at once like so:
class MyClass
attr_accessor :message
end
Again, this is just a shortcut for defining methods that let you get at the instance variable from outside of the class.
attr_accesor gives you methods to read and write the instance variables. Instance variables are deasigned to be hidden from outside world so to communicate with them we should have attr_ibute accesor methods.
In OOPS we have a concept called encapsulation which means, the internal representation of an object is generally hidden from view outside of the object's definition. Only the Object 'itself' can mess around with its own internal state. The outside world cannot.
Every object is usually defined by its state and behavior, in ruby the instance variables is called internal state or state of the object and according to OOPS the state should not be accessed by any other object and doing so we adhere to Encapsulation.
ex: class Foo
def initialize(bar)
#bar = bar
end
end
Above, we have defined a class Foo and in the initialize method we have initialized a instance variable (attribute) or (property). when we create a new ruby object using the new method, which in turn calls the initialize method internally, when the method is run, #bar instance variable is declared and initialized and it will be saved as state of the object.
Every instance variable has its own internal state and unique to the object itself, every method we define in the class will alter the internal state of the object according to the method definition and purpose. here initialize method does the same, such as creating a new instance variable.
var object = Foo.new(1)
#<Foo:0x00000001910cc0 #bar=1>
In the background, ruby has created an instance variable (#bar =1) and stored the value as state of the object inside the object 'object'. we can be able to check it with 'instance_variables' method and that methods returns an array containing all the instance variables of the object according to present state of the object.
object.instance_variables
#[
[0]: #bar
]
we can see '#bar' instance variable above. which is created when we called the initialize method on the object. this '#bar' variable should not be visible (hidden) by default and so it cannot be seen by others from outside of the object except the object, from inside. But, an object can mess around with its own internal state and this means it can show or change the values if we give it a way to do so, these two can be done by creating a new instance methods in the class.
when we want to see the #bar variable by calling it we get an error, as by default we cannot see the state of an object.
show = object.bar
#NoMethodError: undefined method `bar' for #<Foo:0x00000001910cc0 #bar=1>
#from (irb):24
#from /home/.rvm/rubies/ruby-2.0.0-p648/bin/irb:12:in `<main>'
But we can access the variables by two methods, these two are called setter and getter methods, which allow the object to show or change its internal state (instance variables/attributes/properties) respectively.
class Foo
def bar
#bar
end
def bar=(new_bar)
#bar = new_bar
end
end
We have defined a getter(bar) and setter(bar=) methods, we can name them any way but the instance variable inside must the same as instance variable to which we want to show or change the value. setters and getters are a violation to OOPS concepts in a way but they are also very powerful methods.
when we define the two methods by re-opening the class and defining them, when we call the object with the methods, we can be able to view the instance variables(here #foo) and change its value as well.
object.bar
1
object.bar=2
2
object.bar
2
Here we have called the bar method (getter) which returns the value of #bar and then we have called bar= method (setter) which we supplied a new_value as argument and it changes the value of instance variable (#bar) and we can look it again by calling bar method.
In ruby we have a method called attr_accessor , which combines the both setter and getter methods, we define it above the method definitions inside the class. attr_* methods are shortcut to create methods (setter and getter)
class Foo
attr_accessor :bar
end
we have to supply a symbol (:bar) as argument to the attr_accessor method which creates both setter and getter methods internally with the method names as supplied symbol name.
If we need only a getter method, we can call attr_reader :bar
If we need only a setter method, we can call attr_writer :bar
attr_accessor creates both attr_writer and attr_reader methods
we can supply as many instance variables as we want to the attr_* methods seperated by commas
class Foo
attr_writer :bar
attr_reader :bar
attr_accessor :bar, :baz
end
Because attr_accessor defines methods, you can call them from outside the class. A #variable is only accessible from inside the class.
And another answer more compact (for Java developers)
attr_accessor :x creates the getters and setters to #x
class MyClassA
attr_accessor :x
end
is the same as
class MyClassB
def x=(value) #java's typical setX(..)
#x=value
end
def x
#x
end
end
I am writing an internal DSL in Ruby. For this, I need to programmatically create named classes and nested classes. What is the best way to do so? I recon that there are two ways to do so:
Use Class.new to create an anonymous class, then use define_method to add methods to it, and finally call const_set to add them as named constants to some namespace.
Use some sort of eval
I've tested the first way and it worked, but being new to Ruby, I am not sure that putting classes as constants is the right way.
Are there other, better ways? If not, which of the above is preferable?
If you want to create a class with a dynamic name, you'll have to do almost exactly what you said. However, you do not need to use define_method. You can just pass a block to Class.new in which you initialize the class. This is semantically identical to the contents of class/end.
Remember with const_set, to be conscientious of the receiver (self) in that scope. If you want the class defined globally you will need to call const_set on the TopLevel module (which varies in name and detail by Ruby).
a_new_class = Class.new(Object) do
attr_accessor :x
def initialize(x)
print #{self.class} initialized with #{x}"
#x = x
end
end
SomeModule.const_set("ClassName", a_new_class)
c = ClassName.new(10)
...
You don't really need to use const_set. The return value of Class.new can be assigned to
a constant and the block of Class.new is class_eval.
class Ancestor; end
SomeClass = Class.new(Ancestor) do
def initialize(var)
print "#{self.class} initialized with #{var}"
end
end
=> SomeClass
SomeClass.new("foo")
# SomeClass initialized with foo=> #<SomeClass:0x668b68>
Should be like this
a_new_class = Class.new(Object) do
attr_accessor :x
def initialize(x)
#x = x
end
end
SomeModule = Module.new
SomeModule.const_set("ClassName", a_new_class)
c = SomeModule::ClassName.new(10)