So I am just starting to make my way through The Pragmatic Bookself's, "Programming Ruby 1.9 (3rd Edition)" and I've come across some code that I need a little clarification on.
If you own the book, it's in Chapter 3's, "Classes, Objects, and Variables," specifically in the section about virtual attributes.
Basically, a class is defined with an initializer that sets a couple of instance variables, one of which is #price. That variable has an accessor / mutator created with attr_accessor like so:
attr_accessor :price
That class also has a virtual attribute called, price_in_cents which simply returns the value from this line:
Integer(price*100 + 0.5)
Now my question is why is price in the virtual attribute not prefixed with an #? It is clearly dealing with an instance variable. Executing the code without the # works just the same as with; why is that?
P.S. Sorry for not just posting the code wholesale—given that this is a question about code in a book, I wasn't sure what legal right I'd have to post.
That's a receiverless message send.
In Ruby, the receiver self is implicit: you can leave it out if you want to. So, price is basically the same as self.price (ignoring access restrictions).
In other words, it's calling the method price you defined with attr_accessor.
You can easily confirm that that two references (price and #price) point to the same objects:
#!/usr/bin/env ruby
class MyPrice < Object
attr_accessor :price
def initialize(price = 0)
#price = price
end
def price_in_cents
Integer(price * 100 + 0.5)
end
def objects
puts "#price.object_id: #{#price.object_id}"
puts "price.object_id: #{price.object_id}"
end
end
p = MyPrice.new(150)
p.objects
puts "price in cents: #{p.price_in_cents}"
Which produces:
# ./price.rb
#price.object_id: 301
price.object_id: 301
price in cents: 15000
The fact that price and #price both have an object id of 301 shows you that they are both references to a single object.
This question was posted years back but just in case anybody is running into similar issues like the example on top.
I don't think self.price has anything to do with attr_accessor, self.price only applies to the class where attr_accessor becomes a shortcut for instance variable. You don't need to input the '#' in front of the attr_accessor because it acts as a shortcut for your setters and getters if you are familiar to Java. Part of Ruby's easy going way of getting things done faster more eloquently.
Related
So, I've been studying Ruby for over two weeks now and I'm having some issues understanding the whole OOP thing.
In this lesson, in the exercises in the end, we are asked to create a superclass of vehicle and add non-specific behavior to it, and then create a MyCar and a MyTruck subclasses that inherit from it and also have a constant that separates them. So I did like so:
class Vehicle
attr_accessor :color, :curr_speed
attr_reader :year, :model
def initialize(y, c, m)
#year = y
#color = c
#model = m
end
#SOME OTHER METHODS
def to_s
"My #{VEHICLE_TYPE} is a #{self.model} #{self.year} of color #{self.color}."
end
end
class MyCar < Vehicle
VEHICLE_TYPE = 'car'
end
class MyTruck < Vehicle
VEHICLE_TYPE = 'truck'
end
I also redefined the to_s method so that it would return a string that would say what kind of vehicle it is along with the other info. Now, the exercise does not ask us to do that -- in fact, they define a to_s method for MyCar and another to MyTruck that starts with "My car..." or "My truck..." but I feel like this goes against the DRY principle.
Thing is, it seems that Ruby does not accept a subclass variable passed in a superclass method. If I use #{VEHICLE_TYPE} it throws a uninitialized constant Vehicle::VEHICLE_TYPE (NameError), and if I use #{self.VEHICLE_TYPE} it throws a undefined method 'VEHICLE_TYPE' for Vehicle:Class (NoMethodError).
Am I correct in my assumption that Ruby does not accept a subclass variable in a superclass? And how could I go about to fix this issue in a similar fashion? Because I thought about simply adding another parameter to initizalize for type, store in a superclass instance variable and be done with it, but I don't think it would serve the purpose of the exercise.
And bear in mind that I'm a newbie!
Thanks in advance!
That's right. The superclass can't use the shortcut notation of VEHICLE_TYPE to access the constant you've defined.
When you want to access a constant outside of the class it is defined in, you can access it using a qualified name like:
Car::VEHICLE_TYPE
You were getting pretty close on your self.VEHICLE_TYPE attempt. But to provide a generic replacement for something like Car::, you'd need to use self.class::.
So your to_s method would look like:
def to_s
"My #{self.class::VEHICLE_TYPE} is a #{self.model} #{self.year} of color #{self.color}."
end
This will work as long as each instantiable class defines that constant.
I see that with this code:
# filename: play.rb
class A
attr_reader :a
def initialize(num)
#a=num # I have to use # here
end
def m1
p "---"
p #a
p a
end
end
obj = A.new(1)
obj.m1
I get the output
$ ruby play.rb
"---"
1
1
As you see I can refer to a as either #a or a in the m1 method and both work.
Which should I use when and why?
In this case you don't use a local variable a but a getter method that gives you #a because that's have attr_reader :a. It generates a method #a() used as an getter.
What you really do is:
def m1
p "---"
p #a
p a()
end
If I have the accessor, I use it, not the instance variable. It lets me change the getter later.
I find this an interesting question, one that I had not given much thought to before today. I consulted the Ruby Style Guide, expecting it would provide some good advice, but it is curiously mute on the subject.
I make four suggestions below. These concern setters as well as getters. Others may disagree and have good reasons for doing so. Let's discuss! I look forward to reading comments.
In reading my remarks it may be helpful to put yourself in the place of someone reading code they wrote some time ago or someone reading someone else's code for the first time.
1. Getters used exclusively within a class should be private
I expect this is my least debatable suggestion, as I can see only disadvantages to making any method public when there is no reason to do so.
2. Methods named after instance variables should not have side effects
By this I mean if there is an instance variable #quantity, a method named :quantity should do no more than return the value of #quantity (i.e., be a getter) and a method named :quantity= should do no more than assign a value to #quantity (i.e., be a setter). That is, such methods should not have side effects.
Suppose, for example, a ("pseudo-") setter were defined to assign a value to an instance variable after accounting for a 10% spoilage factor:
def quantity=(q)
0.9 * q
end
If the reader of the code were to miss this definition, but knew there was an instance variable #quantity, the natural assumption would be that :quantity= was a setter, which might mask errors or waste time in testing/debugging. Better, I think, would be to write:
class C
attr_writer :quantity
def initialize
end
#...
def adj_quantity_for_spoilage
self.quantity *= 0.9
end
#...
end
c = C.new
c.quantity = 100
c.adj_quantity_for_spoilage
3. Do not use setters within a class
For example, I suggest writing:
class C
attr_accessor :quantity
def change(x)
#quantity = x
end
end
rather than:
class C
attr_accessor :quantity
def change(x)
self.quantity = x
end
end
The main reason is that it is all-to-easy to inadvertently omit self. in self.quantity = x, in which case x would be incorrectly and silently assigned to a newly-created local variable quantity.
There is a secondary reason in the case where there is no need to access the setter from outside that class. As with getters, we would want setters to be private in this situation, but that is not possible since they must have the explicit receiver self. Recall that private methods, by definition, do not have explicit receivers.
Lastly, I see no argument for using self.quantity over #quantity, particularly in view of the fact that the former requires the keying of four additional characters.1
4. Do not use getters within a class
I expect this to be my most controversial suggestion and I will be the first to admit that if one rejects it the earth will no doubt continue to orbit the sun. Moreover, I concede that using getters in this way does save the typing of one character, the hard-to-reach-without-looking "#".
When I read:
x = m(#quantity)
I know immediately that #quantity is an instance variable, regardless of my familiarity with the code. If I've forgotten (or never knew) what the variable contains or how it is used, the path to my elucidation is clear. True, if I know there is an instance variable #quantity, the above has no advantage over:
x = m(quantity)
If, however, I don't know if there is an instance variable #quantity, I would be faced with three possibilities: quantity is a local variable, a getter or a method that is not a getter. The time required to complete my investigation should not take much longer than if I were tracking down #quantity, but those seconds do add up.
Let's consider another thing: what are the consequences of misspelling the name of an instance variable versus misspelling its getter (something I do frequently):
x = m(#qauntity)
versus:
x = m(qauntity)
#qauntity will return nil, which may lead to an exception being raised, but possibly not soon or not at all.
qauntity will almost certainly raise an "no method or variable" exception immediately, giving it the edge in this situation.
In sum, I am suggesting that it generally is best to use getters and setters outside of class definitions only, and that they have no side effects.
Your thoughts?
1. Over a lifetime of coding, typing those four extra characters could amount to hours of time wasted that could otherwise be used productively (e.g., playing pong).
I have recently learned how to create classes, although I am not ENTIRELY sure where and why I should use them.
I'd use them to create objects, which have similar methods/properties.
I tried making a gag code, but I stumbled upon a question I can't find an answer to.
class Person
def initialize(name,health)
#name = name
#hp = health
end
def kick
#hp -= 1
if (#hp <= 0)
puts "#{#name} got REKT!"
end
end
end
#Friends
michael = Person.new("Michael", 10)
10.times { michael.kick }
Even though this code works, I'm wondering if it is possible to use/call mihchael's hp outside the class? Perhaps sort of like a hash? michael[#hp]? But this doesn't work, even if i set hp to be a global variable.
Should all if/else statements who check object's properties be inside the class?
Thank you very much
The standard way to do this in Ruby is to create an accessor:
class Person
attr_reader :name
attr_reader :hp
end
Then outside the class you can call things like:
puts "#{michael.name} has only #{michael.hp} HP left"
The reason you create objects is to organize your code and data into logical contexts and containers.
As someone who previously had only a limited exposure to programming and this was mainly in Python and C++, I find Ruby to be really refreshing and enjoyable language, however I am having a little trouble understanding Ruby's use of class constructors and virtual accessors, specifically with what is and what is not considered to be a constructor and whether I understand virtual accessors correctly.
Here's an example (credit is due to the Pragmatic Bookshelf, Programming Ruby 1.9 & 2.0):
class BookInStock
attr_accessor :isbn, :price # is this part of the class constructor?
def initialize(isbn, price) # and is the initialize method part of a constructor or just a regular method?
#isbn = isbn
#price = price
end
def price_in_cents
Integer(price*100+0.5)
end
def price_in_cents=(cents) # this is a 'virtual accessor' method, trough which I am able to update the price down the road...?
#price = cents / 100.0
end
end
book = BookInStock.new('isbn1', 23.50)
puts "Price: #{book.price}"
puts "Price in cents: #{book.price_in_cents}"
book.price_in_cents = 1234 # here I am updating the value thanks to the 'virtual accessor' declared earlier, as I understand it
puts "New price: #{book.price}"
puts "New price in cents: #{book.price_in_cents}"
Thanks for all the help I could get understanding this piece of code.
1 attr_accessor :isbn, :price # is this part of the class constructor?
This line is to keep concept of access private member through method only philosophy. It's in effect equivalent to declare two private members and their getter and setter methods, has nothing to do with constructor.
2 def initialize(isbn, price) # and is the initialize method part of a constructor or just a regular method?
To put it in the easy way, this is THE constructor. The method get called upon 'new' keyword
3 def price_in_cents=(cents) # this is a 'virtual accessor' method, trough which I am able to update the price down the road...?
It's just an alternative of price= method, which takes argument in different format, the price= method is the setter method automatically generated by your question line 1.
4 book.price_in_cents = 1234 # here I am updating the value thanks to the 'virtual accessor' declared earlier, as I understand it
Keep in mind this feels like assigning a variable but really is accessing a setter method, in consistent with the concept reflected in your question line 1 and 3.
I need to make the following code functional by building a "Car" class. I feel I must be overlooking something simple. any help would be appreciated. The # indicates the expected output
# Make the following code functional by building a Car class
c = Car.new("blue")
puts c.color # blue
puts c.repaint_count # 0
c.paint("red")
c.paint("green")
puts c.repaint_count # 2
puts c.color # green
here is what I have done:
class Car
##repaint_count = 0
def initialize(color)
#color = color
end
def self.paint(color)
#color = color
##repaint_color += 1
end
def self.color
#color
end
end
I guess I am being thrown by the c.color / c.paint: should I be defining these methods and setting them equal to class or something else ? I think I am missing something about classes and inheritance.
I guess I am being thrown by the c.color / c.paint: should I be
defining these methods and setting them equal to class or something
else ? I think I am missing something about classes and inheritance.
I think in fact you are over-complicating it by worrying about these things at this stage. The question is not about inheritance. Although in some ways it is poorly specified in that it is possible to mis-interpret the question text and assign some properties to the class other than the instance.
So first things, you have got that the question expects you to implement a Car class, and that there is internal state to track for the current color and the number of times it has changed. You have partly mis-understood the repaint count and made it a class variable. It needs to be an instance variable - it is intended to be the number of times a specific car has been re-painted, not the number of times any car has been re-painted. Although the example numbers would be the same, the difference is that the question asks for c.repaint_count not Car.repaint_count, and c is an instance of Car, hence you want to store the count as an instance variable - set it to 0 in the constructor.
Similar confusion in your accessor code. Ruby's use of self is a little confusing - it changes meaning on context in the code. If you changed your def self.paint to just def paint and similarly for color then with the change from last paragraph, you are pretty much done.
One last thing, you need to implement repaint_count accessor similar to how you have done with color (and again, without the self. which would make it a class method)
You seem to be confusing classes and instances. c is an instance of Car, and is not the class Car itself. Unless you want to count the total repaint_count throughout the Car class, you should not use a class variable ##repaint_count, but should use an instance variable. Your paint method is a class method, and is not well defined. In addition the definition body looks like you randomly put something.
class Car
attr_reader :color, :repaint_count
def initialize color
#color = color
#repaint_count = 0
end
def paint color
#color = color
#repaint_count += 1
end
end
Well, this looks like a homework question, which I'm not going to write for you. However I'll give you some pointers.
You create/ open up a class like this.
class Foo
end
When you open up a class like this you can set it up to accept arguments immediately, like so:
class Foo
attr_accessor :bar, :bar_counter
def initialize(arg_1)
#bar = arg_1
#bar_counter = 0
end
# And add methods with any name like so.
def increase_bar
#bar_counter += 1
end
def change_bar(arg)
#bar = arg
end
end
This will explain the differences between attr_accessor, attr_reader, attr_writer https://stackoverflow.com/a/4371458/2167965
People have various opinions on Codecademy, but in my opinion it's perfect for teaching basic syntax like this. There's also Ruby Koans, and Ruby Test First.
My recommendations would be to start with codecademy to learn the syntax, and move to Test First to flesh out those concepts.