ruby attr_accessor/instance method confused [duplicate] - ruby

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

Related

Why can I change the method name when I call a setter method from a class? [duplicate]

This question already has answers here:
confused with Ruby accessor methods
(2 answers)
Closed 3 years ago.
I'm looking at this example:
class Person
attr_reader :name, :age #creates getter method's age & name
def initialize(name)
#name = name
end
def age=(a) # creates setter method for age
#age = a
end
end
mike = Person.new('Mike')
mike.age = 20 # calling setter method
mike.age # calling getter method, returns 20
And I'm trying to understand why "mike.age = 20" is the equivalent to saying "mike.age=(20)".
I understand that we don't have to use parentheses in Ruby for the arguments that we're passing into the method. So I know why "mike.age = 20" is the same as "mike.age= 20". However, I'm having trouble understanding why we can put a space after "age". Isn't the equals sign a part of the method name? For example, I know I can't say:
def is_even?(n)
n.even?
end
p is_even ? 3
Because the question mark is a part of my method name, I know that I can't separate it with a space. So why am I able to separate "age" and the equals sign with a space in the setter method in the first example?
Here's the StackOverflow link where 'mu is too short' answers this:
Ruby setter method syntax method=(value) - Comparison to Java
When you add two numbers, for example
4 + 4
This is what's going on
4.send(:+, 4)
The send method just invokes a method. The first argument is the method you want to call passed in as a symbol :+ and the second argument is the argument you want to pass into the method 4
In your case
mike.age = 20
is actually this
mike.send(:age=, 20)
Here, we are calling the age= method and passing in 20 as the argument to age=
Heres the link to the Ruby docs about the send method https://ruby-doc.org/core-2.6.4/Object.html#method-i-send

Implementation of attr_accessor [duplicate]

This question already has answers here:
Why do Ruby setters need "self." qualification within the class?
(3 answers)
Closed 5 years ago.
Excuse me for the noob question.Please explain me outputs of the below ruby programme for implementing attr_accessor.
class SimpleService
attr_accessor :name
def initialize(name)
#name = name
end
def process
if false # some condition met
name = 'Akshay'
end
name
end
end
When I execute this class
SimpleService.new('John Doe').process
=> nil
Why is the result nil?
when I use self explicitly to name
def process
if false # some condition met
self.name = 'Akshay'
end
name
end
Now the output is
SimpleService.new('John Doe').process
=> "John Doe"
why is the result now "John Doe"?
I am a beginner in ruby.
Thanks in advance!
The thing is when you call name = you implicitly declare new local variable. Try this:
def process
name = 'Akshay'
puts local_variables.inspect
end
Why is it that way is a complicated question, discussed many times there and here. The setter always requires in explicit receiver. Period.
Once you have the line name = 'Akshay' inside a method, you introduce a new local variable and this method’s scope gets extended with new local variable name, despite how is was declared. It’s basically done by ruby parser.
And local variables take precedence over instance methods. That is why what is returned in the last line is a local variable. That was apparently not set, due to falsey condition above. Hence nil.

Ruby scopes: Diference between MyClass.new and ::MyClass.new [duplicate]

This question already has answers here:
Ruby: what does :: prefix do?
(3 answers)
Closed 7 years ago.
I know that the double colon (::) is basically a namespace resolution operator. But in this particular case, I'm not sure in which scope I'm working. Does it mean that I want MyClass class from the ruby core? Sort of like ~ means home directory in bash..
Imagine the following code:
class A
def a
puts 'TOPMOST'
end
end
module B
class A
def a
puts 'NESTED'
end
end
def self.topmost
::A.new.a
end
def self.nested
A.new.a
end
end
B.topmost will print "TOPMOST", and B.nested will print "NESTED".
So, ::A means not “from ruby core”, but rather “from no module.”

How to access member variable using accessor which name is being passed in variable in Ruby? [duplicate]

This question already has answers here:
How to call methods dynamically based on their name? [duplicate]
(5 answers)
Closed 7 years ago.
I have a list of instances of a class and I have a hash with changes I’d like to apply to these instances. I can’t figure how to access member variable which name I have in list of changes.
E.g.
class Foo
attr_accessor: foo
def initialize value
#foo = value
end
end
f = Foo.new("bar")
I can obviously access #foo with f.foo, but say I have list of changes in form like changes = {"foo" => "baz"}.
Now I wonder wheter there is a way to do something like this:
changes.each do |k,v|
f.k = v
end
to have f.foo changed to "baz".
send method can help you assign attributes dynamically.
changes.each do |k,v|
f.send("#{k}=", v)
end

Ruby - Calling setters from within an object [duplicate]

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

Resources