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:
- always use
self.method_name =
when you want to call the setter within a method - 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:
custom = val
is not a method call, it assigns to a local variable namedcustom
. 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?- 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
Prefer %W(...) to a Literal Array
Are There Primitive Types in Ruby
Splitting String into Pair of Characters in Ruby
How to Parse a Url and Extract the Required Substring
How to Send an Http Put Request in Ruby
Convert a String of 0-F into a Byte Array in Ruby
How to Stringize/Serialize Ruby Code
How to Memoize a Method That May Return True, False, or Nil in Ruby
Error Installing SQLite3 Gem via Bundler
Ruby on Linux Pty Goes Away Without Eof, Raises Errno::Eio
"Msvcrt-Ruby18.Dll Was Not Found" with Ruby
Capybara Synchronize with Has_No_Css
Errno::Eaccess: Permission Denied @ Dir_S_Mkdir
Install Nokogiri 1.6.1 Under Ruby 2.0.0P353 (Rvm Based Installation) Fails (Osx Mavericks)