Ruby - Calling Setters from Within an Object

Calling a setter method via a symbol

I assume you're trying to dynamically set the value of various attributes on an object.

try the following:

object.send("#{attr}=", value)

There was a similar question asked yesterday - it might help as it's a more specific example.

Calling accessor methods from within a Model object in Rails

When you define an attribute in ActiveRecord, the following methods are available

# gets the value for needs_review
def needs_review
end

# sets the value for needs_review
def needs_review=(value)
end

You can call the setter using

needs_review = "hello"

but this is the same way you set a variable. When you call the statement within a method, Ruby gives higher precedence to variables assignment, thus a variable with that name will be created.

def one
# variable needs_review created with value foo
needs_review = "foo"
needs_review
end

one
# => returns the value of the variable

def two
needs_review
end

two
# => returns the value of the method needs_review
# because no variable needs_review exists in the context
# of the method

As a rule of thumb:

  1. always use self.method_name = when you want to call the setter within a method
  2. optionally use self.method_name when a local variable with the same name exists in the context

Ruby/Rails: Understanding ruby getter-setter methods and instances

When you do

@dog = Dog.new

You do two spearate things

1) Create an instance variable @dog for whatever object your code is currently inside

2) Instantiate a new instance of Dog (with all its methods and attributes) and assign a reference to it to @dog

@dog is a variable, that just happens to point at the Dog instance ("instance of class" generally same meaning as "object") you created at that point. You can set other variables to point to the same instance, and in Ruby this is generally how you pass data around. Objects contain instance variables, and those instance variables point to yet more objects.

Using the assignment operator (i.e "=") you can point a variable at any other object.

To answer your questions in turn:

When I am in the owner class and I call @dog.popularity how does it
know the value of popularity for that instance?

You have to be careful in Ruby (and OO languages in general) to differentiate between class and object in your descriptions and questions. Ruby I'm assuming you are referring to a line of code in the Owner class, and that you intend it to work with an owner object. I'd also assume that @dog is an attribute you have added to Owner.

In which case, Ruby knows because @dog points to the Dog object that you added to owner. Each Dog object has its own copy of all of Dog's instance variables. You do need to take care in Ruby though, because variables point to objects, that you aren't simply passing in the same Dog object to all the owners (i.e. they all effectively share a single dog). So you need to understand when you are creating new instances (via new) and when you are simply handling existing references.

At runtime are all methods processed and then that instance just
always is tied to the value at the time?

No. At runtime, basic Ruby will only perform the assignments that you have coded. Instance variables may not even exist until the code that assigns them has been run. If you use attr_reader etc methods, then the variables will at least exist (but will be nil unless you assign something during initialize)

Why do Ruby setters need self. qualification within the class?

The important thing to remember here is that Ruby methods can be (un)defined at any point, so to intelligently resolve the ambiguity, every assignment would need to run code to check whether there is a method with the assigned-to name at the time of assignment.

define_method for setter wont work inside class call

module M
def create(name)
define_method("#{name}=") do |value|
"called #{name} with #{value}"
end
end
end

class C
extend M
create(:custom)
def initialize(val)
puts public_send(:custom=, val) # this is the only change needed
end
end

C.new('haha')
# called custom with haha

I only had to change one line in your code.

There were two problems with your code:

  1. custom = val is not a method call, it assigns to a local variable named custom. If you want to call a setter, you need to make it explicit that you are calling a method, by providing an explicit receiver: self.custom = val. See Why do Ruby setters need “self.” qualification within the class?
  2. Assignments evaluate to the right-hand side of the assignment. The return value of a setter method is ignored, unless you don't use assignment syntax, i.e. public_send. See Why does irb echo the right hand side of an assignment instead of the return value in the case of a setter method?

setter method for hash with send()

Yes. The method then is []=

Example:

hash = {:a => 1, :b => 2}
hash.send :[]=, :a, 3
hash # => {:a => 3, :b => 2}

calling setter function ruby (see comment after fname=(fname)

Because the interpreter will think you want to assign the value of the local variable fname to the local variable fname. To make it work, you have to be more explicit:

self.fname = fname

Trying to learn / understand Ruby setter and getter methods

You can interact with that instance variable from other methods belonging to that instance, even if there is no getter:

def noise=(noise)
@noise = noise
end

def last_noise
@noise
end

There doesn't need to be a getter defined with the same name as the method; the two are not linked at all. The getter is needed to "get" the value of the instance variable, but only in a short syntax.

What's happening in your example is that you're initializing a new object (Human.new), and then using a method (noise=, yes the method name contains the = symbol) that just-so-happens to define an instance variable (that is, a variable just for that instance), and then finally retrieving that instance variable with another method call.

You can actually use instance_variable_get to get the instance variable without defining any getter at all:

man = Human.new
man.noise = "Howdie"
man.instance_variable_get("@noise")

This will return "Howdie", even though there is no getter defined.

And no, I don't think he's using an older version of Ruby.



Related Topics



Leave a reply



Submit