Ruby - Calling setters from within an object [duplicate] - ruby

This question already has answers here:
Why do Ruby setters need "self." qualification within the class?
(3 answers)
Closed 7 years ago.
I've been working through the Pragmatic Programmers 'Programming Ruby' book and was wondering if it was possible to call a setter method within a class rather than just assigning to the instance variable directly.
class BookInStock
attr_reader :isbn, :price
def initialize (isbn, price)
#isbn = isbn
#price = Float(price)
end
def price_in_cents
Integer(price*100 + 0.5)
end
def price_in_cents=(cents)
#price = cents/100.0
end
def price=(dollars)
price = dollars if dollars > 0
end
end
In this case I am using a setter to ensure that the price can't be negative. What i want to know is if it is possible to call the price setter from within the price_in_cents setter so that I wont have to write extra code to ensure that the price will be positive.
Thanks in advance

Use self.setter, ie:
def price_in_cents=(cents)
self.price = cents/100.0
end

Related

ruby attr_accessor/instance method confused [duplicate]

This question already has answers here:
Why do Ruby setters need "self." qualification within the class?
(3 answers)
Closed 4 years ago.
I am trying to understand the attr_accessor, and while digging I get pretty confused with the following behaviour:
class Item
def change_price
price=(2)
end
def price=(value)
#price = value
end
def price
#price
end
end
my_item = Item.new
p my_item.price
my_item.change_price
p my_item.price
=> nil
nil
I would expect the price to be set to 2. Clearly I totally misunderstood something that I thought obvious.
Would anybody be kind enough to explain me where I am being thick?
Thank you
Attribute setter (any function trailing with an equal sign) must be called on the explicit receiver. Otherwise, the local variable price is being created and assigned to the value.
Fix:
def change_price
# price=(2)
self.price=(2)
end

Confused by Ruby's class constructors and virtual accessors

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.

Ruby Classes; Error: Uninitialized constant BookInStock::ArguementError (NameError)

I'm new to ruby but trying my best to grasp it. The ques is...
Define a class BookInStock which represents a book with an isbn number, isbn, and price of the book as a floating-point number, price, as attributes. The constructor should accept the ISBN number (a string) as the first argument and price as second argument, and should raise ArgumentError (one of Ruby's built-in exception types) if the ISBN number is the empty string or if the price is less than or equal to zero.
Include the proper getters and setters for these attributes. Include a method price_as_string that returns the price of the book with a leading dollar sign and trailing zeros, that is, a price of 20 should display as "$20.00" and a price of 33.8 should display as "$33.80".
My code:-
class BookInStock
#getters and setters
attr_reader :isbn, :price
def isbn= (isbn)
if isbn == ''
raise ArguementError.new("Must have an ISBN number")
end
#isbn = isbn
end
def price= (price)
price = price.to_f
if price <= 0
raise ArguementError.new("Must have price")
end
#price = price
end
def initialize(isbn, price)
self.isbn = #isbn
self.price = #price
end
def self.price_as_string
return "$" + sprinff("%.2f", #price)
end
end
However, I'm getting an error when I try
BookInStock.new('',9.00)
I get a Uninitialized constant BookInStock::ArgumentError (NameError)
Upon doing some research on the error it says that I may be referring to a class or module that doesn't exist?
PLease help, thanks
You have made a small typo.
Its ArgumentError not ArguementError
Please refer
http://apidock.com/ruby/ArgumentError
Alright. You have few more problems too. Lets take them one by one.
Your Constructor:
Inside your constructor you have the code,
self.isbn = #isbn
self.price = #price
When you write a variable with the # character, it referes to the instance variable. So when you are writing self.isbn = #isbn, you are actually doing #isbn=#isbn and you are not assigning the argument passed to the constructor to #isbn. So you should do:
self.isbn = isbn
self.price = price
Next is your price_as_string method. When you declare a method as self.method_name it becomes a class method. Such methods are called using the Class like so - BookInStock.price_as_string. But the attribute used inside this method(#price) is not a class variable but an instance variable. And hence your price_as_string should be either a instance method (written without self.) or this self.price_as_string must take price as an argument. like so - self.price_as_string(price). But a good Object Oriented design would be to have this as an instance method.
Thanks #arup

Reader and Writer Methods [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
Who can explain me the code snippets of "def name" and "def name=()"?
I don't understand why it is defined 2 times.
class Bird < Animal
def initialize(name, length)
#name
#length
end
def name
#name
end
def name=(new_name)
#name = new_name
end
end
These are getter and setter methods
bird = Bird.new("pigeon", length)
=> #<Bird:0x007f93e9b41278>
bird.name
=> "pigeon"
bird.name = 'seagull'
=> "seagull"
bird.name
=> "seagull"
Both of the methods you defined in the class are what's being called here - the call to
bird.name = 'seagull'
is actually syntactic sugar for the method call
bird.name=('seagull')
Of course, the most "rubyish" way to write these methods would be one call to attr_accessor:
class Bird < Animal
attr_accessor :name
def initialize(name, length)
#name = name
#length = length
end
end
This will give you both the getter and setter methods for the name attribute
One returns a value, one sets a value. Look at the method bodies.
def name
#name # return #name
end
def name=(new_name)
#name = new_name # set #name to your argument
end
Also, your initialize method does nothing. I assume you want to set those variables to the arguments you are taking.
def initialize(name, length)
#name, #length = name, length
end
These methods are known as "getters" and "setters", because (unsurprisingly), they 'get' or 'set' a variable.
You can also think of one as a 'command,' which will make some change to the data, and one as a 'query,' which will simply report a value (this is a helpful distinction to keep in mind when you are designing your own methods as well).
Ruby includes three class methods that define these getters and setters for you: attr_reader defines the getter, attr_writer defines the setter, and attr_accessor defines both.
You could just interact directly with the instance variable, but in most situations, code is easier to change and reason about when you use methods to access data rather than messing with the data itself.
I don't understand why it is defined 2 times.
def name to read value from #name and def name= to write value to #name.
The below is a reader(attr_reader) method
def name; #name; end # shortcut is attr_reader :name
The below is a writer(attr_writer) method
def name=(new_name) ; #name = new_name ; end # shortcut is attr_writer :name
A very good documentation What is an accessor?

Ruby undefined method 'each' for class

I am getting an error when I create a class of Stats and another a container class. The error is
test.rb:43:in `<main>' undefined method `each' for #<Boyfriends:0x2803db8 #boyfriends=[, , , ]> (NoMethodError)
which makes absolute sense because that class indeed does not contain that method, but should ruby search the parents and grandparents classes for the method? The script displays the desired output; it just inlays the error with the output like so
test.rb:43:in `<main>'I love Rikuo because he is 8 years old and has a 13 inch nose
I love dolar because he is 12 years old and has a 18 inch nose
I love ghot because he is 53 years old and has a 0 inch nose
I love GRATS because he is unknown years old and has a 9999 inch nose
: undefined method `each' for #<Boyfriends:0x2803db8 #boyfriends=[, , , ]> (NoMethodError)
Here is the code
class Boyfriends
def initialize
#boyfriends = Array.new
end
def append(aBoyfriend)
#boyfriends.push(aBoyfriend)
self
end
def deleteFirst
#boyfriends.shift
end
def deleteLast
#boyfriends.pop
end
def [](key)
return #boyfriends[key] if key.kind_of?(Integer)
return #boyfriends.find { |aBoyfriend| aBoyfriend.name }
end
end
class BoyfriendStats
def initialize(name, age, nose_size)
#name = name
#age = age
#nose_size = nose_size
end
def to_s
puts "I love #{#name} because he is #{#age} years old and has a #{#nose_size} inch nose"
end
attr_reader :name, :age, :nose_size
attr_writer :name, :age, :nose_size
end
list = Boyfriends.new
list.append(BoyfriendStats.new("Rikuo", 8, 13)).append(BoyfriendStats.new("dolar", 12, 18)).append(BoyfriendStats.new("ghot", 53, 0)).append(BoyfriendStats.new("GRATS", "unknown", 9999))
list.each { |boyfriend| boyfriend.to_s }
Which makes absolute sense because that class indeed does not contain that method, but as I've been reading should ruby search the classes parents and grandparents for the method?
That's correct, but you didn't declare any superclasses so the superclass will be Object. Which also doesn't have an each method.
If you want an enumerable method, you'll have to define it yourself - you'll probably want to iterate over the array.
In that case, you could just define an own each method that just passes the passed block down to the arrays each method:
class Boyfriends
def each(&block)
#boyfriends.each(&block)
end
end
The &block here let's you capture a passed block by name. If you're new to ruby, this probably doesn't mean much to you, and explaining how it works is somewhat beyond the scope of this question. The accepted answer in this Question does a pretty good job of explaining how blocks and yield work.
once you got an each method, you can also pull in Enumerable for a number of convenience methods:
class Boyfriends
include Enumerable
end
Also, to_s is a method that should return a string, so you should remove the puts in BoyfriendStats#to_s.

Resources