Does Ruby's define_method have special access to instance variables? [duplicate] - ruby

This question already has answers here:
Calling Instance Variables without #
(4 answers)
Closed 2 years ago.
I am learning Ruby and have stumbled upon some code similar to the one below, which shows the difference between instance variables and class instance variables. I've tested it in my console and it works like described (outputs "John"). What I don't understand is how define_method accesses the #name instance variable without preceding name with a #? Does it have a special capability that allows it to do so?
class User
attr_reader :name
def self.name
"User"
end
def initialize(name)
#name = name
end
define_method(:output_name) do
puts name
end
end
user1 = User.new("John")
user1.output_name #=> “John”

It's about scope
define_method(:output_name) do
puts name
end
The puts name part of this has instance scope.
Therefore it has access to instance methods such as the one generated by
attr_reader :name

Related

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.”

Inject outer scope when monkey-patching instance method in Ruby [duplicate]

This question already has answers here:
Is it possible to define a Ruby singleton method using a block?
(2 answers)
Closed 7 years ago.
I have a container class Foo with a method frob, and I want to add a similarly named method, which will delegate to the container, to each of the contained elements.
First I tried
self.children.each do |c|
def c.frob
self.frob
end
end
but this of course leads to SystemStackError: stack level too deep, as self is c at that point. I then tried
parent = self
self.children.each do |c|
def c.frob
parent.frob
end
end
but local variables aren't part of the closure of the newly defined method, so I get undefined local variable or method 'parent'.
I came up with the following hack, which works:
self.children.each do |c|
c.instance_variable_set('#parent', self)
def c.frob
#parent.frob
end
end
However, it pollutes the variable space of the child with something that's only needed by this one method. How can I get parent/self in there while keeping the newly defined method self-contained?
This should work:
children.each do |c|
parent = self
c.send(:define_method, :frob) do
parent.frob
end
end

What's the purpose of the 'attr_accessor' in creating objects in Ruby? [duplicate]

This question already has answers here:
What is attr_accessor in Ruby?
(20 answers)
Closed 8 years ago.
Before I expand my question, let me state that I have read the answers to the questions here, here and here. So requesting you to not mark my question as duplicate as those answers didn't make me understand the purpose of attr_accessor. My question is more to do with the logic and not the syntax.
I've created two sets of code below. The sets are identical to each other except that one set simply doesn't have the attr_accessor line. When I ran both sets, they both gave me the same output. So, logically speaking, what difference does the attr_accessor line make, when both sets of code gave me the same intended output?
Code Set 1:
class Animal
def initialize(name)
#name = name
end
end
class Cat < Animal
def talk
"Meaow!"
end
end
class Dog < Animal
def talk
"Woof!"
end
end
animals = [Cat.new("Flossie"), Dog.new("Clive"), Cat.new("Max")]
animals.each do |animal|
puts animal.talk
end
#Output:
#Meaow!
#Woof!
#Meaow!
Code Set 2:
class Animal
attr_accessor :name #this line is the only difference between the two code sets.
def initialize(name)
#name = name
end
end
class Cat < Animal
def talk
"Meaow!"
end
end
class Dog < Animal
def talk
"Woof!"
end
end
animals = [Cat.new("Flossie"), Dog.new("Clive"), Cat.new("Max")]
animals.each do |animal|
puts animal.talk
end
#Output:
#Meaow!
#Woof!
#Meaow!
Both sets of code call the Animal class to create new instances of animal objects WITH names. I stress on "...WITH names." because the attr_accessor (in the 2nd set) is defining the :name attribute. But in the 1st code set, I have deleted the attr_accessor but still managed to create object instances with the name attribute.
attr_accessor :attribute_name is shorthand for:
def attribute_name
#attribute_name
end
def attribute_name=(value)
#attribute_name = value
end
and it's for setting instance variable. In your code snipped, you set instance variable directly in initialize method, so you don't need attr_accessor.
Instance variables can always be read/written inside instance methods, which your code demonstrates. attr_accessor makes instance variables readable/writable outside the class (by defining accessor methods). By adding it to your second example, you allow the following:
cat = Cat.new("Garfield")
puts cat.name
cat.name = "Maru"
which would raise NoMethodError in your first example.

Instance variables in a Ruby Class object [duplicate]

This question already has answers here:
Difference between class variables and class instance variables?
(3 answers)
Closed 7 years ago.
Given the Ruby code below, can someone help me understand the different use cases between #v and ##w? I understand that the class C is an object of the class Class, and because of this, #v is an instance variable of the class C object.
class C
#v = "I'm an instance variable of the class C object."
puts #v
##w = "I'm a class variable of the class C."
puts ##w
end
Instance Variable's scope is just limited to the object of the Class. For eg. If you intantiate the Class C by creating an object then you have the access to #v.
Where as The Class Variables span through out the class i.e. they are also visible to the instances of the Classes(ie Objects) and other class methods.
Related Reading:
Difference between class variables and class instance variables?
http://www.railstips.org/blog/archives/2006/11/18/class-and-instance-variables-in-ruby/
Instance variables are used each time an object is created and if they are unintialized, they have a nil value, and class variables need to be initialized, and if they aren't, they produce and error.
One of the biggest reasons is sub-classing. If you plan on sub-classing you would want to use class variables. Here is a link that talks about the two and when to use what:
http://www.railstips.org/blog/archives/2006/11/18/class-and-instance-variables-in-ruby/
Here is a link that should help describe the difference between the two:
http://www.tutorialspoint.com/ruby/ruby_variables.htm
Here is some code from the site I just mentioned that shows both being used:
#!/usr/bin/ruby
class Customer
##no_of_customers=0
def initialize(id, name, addr)
#cust_id=id
#cust_name=name
#cust_addr=addr
end
def display_details()
puts "Customer id ##cust_id"
puts "Customer name ##cust_name"
puts "Customer address ##cust_addr"
end
def total_no_of_customers()
##no_of_customers += 1
puts "Total number of customers: ###no_of_customers"
end
end
# Create Objects
cust1=Customer.new("1", "John", "Wisdom Apartments, Ludhiya")
cust2=Customer.new("2", "Poul", "New Empire road, Khandala")
# Call Methods
cust1.total_no_of_customers()
cust2.total_no_of_customers()

Resources