I see that with this code:
# filename: play.rb
class A
attr_reader :a
def initialize(num)
#a=num # I have to use # here
end
def m1
p "---"
p #a
p a
end
end
obj = A.new(1)
obj.m1
I get the output
$ ruby play.rb
"---"
1
1
As you see I can refer to a as either #a or a in the m1 method and both work.
Which should I use when and why?
In this case you don't use a local variable a but a getter method that gives you #a because that's have attr_reader :a. It generates a method #a() used as an getter.
What you really do is:
def m1
p "---"
p #a
p a()
end
If I have the accessor, I use it, not the instance variable. It lets me change the getter later.
I find this an interesting question, one that I had not given much thought to before today. I consulted the Ruby Style Guide, expecting it would provide some good advice, but it is curiously mute on the subject.
I make four suggestions below. These concern setters as well as getters. Others may disagree and have good reasons for doing so. Let's discuss! I look forward to reading comments.
In reading my remarks it may be helpful to put yourself in the place of someone reading code they wrote some time ago or someone reading someone else's code for the first time.
1. Getters used exclusively within a class should be private
I expect this is my least debatable suggestion, as I can see only disadvantages to making any method public when there is no reason to do so.
2. Methods named after instance variables should not have side effects
By this I mean if there is an instance variable #quantity, a method named :quantity should do no more than return the value of #quantity (i.e., be a getter) and a method named :quantity= should do no more than assign a value to #quantity (i.e., be a setter). That is, such methods should not have side effects.
Suppose, for example, a ("pseudo-") setter were defined to assign a value to an instance variable after accounting for a 10% spoilage factor:
def quantity=(q)
0.9 * q
end
If the reader of the code were to miss this definition, but knew there was an instance variable #quantity, the natural assumption would be that :quantity= was a setter, which might mask errors or waste time in testing/debugging. Better, I think, would be to write:
class C
attr_writer :quantity
def initialize
end
#...
def adj_quantity_for_spoilage
self.quantity *= 0.9
end
#...
end
c = C.new
c.quantity = 100
c.adj_quantity_for_spoilage
3. Do not use setters within a class
For example, I suggest writing:
class C
attr_accessor :quantity
def change(x)
#quantity = x
end
end
rather than:
class C
attr_accessor :quantity
def change(x)
self.quantity = x
end
end
The main reason is that it is all-to-easy to inadvertently omit self. in self.quantity = x, in which case x would be incorrectly and silently assigned to a newly-created local variable quantity.
There is a secondary reason in the case where there is no need to access the setter from outside that class. As with getters, we would want setters to be private in this situation, but that is not possible since they must have the explicit receiver self. Recall that private methods, by definition, do not have explicit receivers.
Lastly, I see no argument for using self.quantity over #quantity, particularly in view of the fact that the former requires the keying of four additional characters.1
4. Do not use getters within a class
I expect this to be my most controversial suggestion and I will be the first to admit that if one rejects it the earth will no doubt continue to orbit the sun. Moreover, I concede that using getters in this way does save the typing of one character, the hard-to-reach-without-looking "#".
When I read:
x = m(#quantity)
I know immediately that #quantity is an instance variable, regardless of my familiarity with the code. If I've forgotten (or never knew) what the variable contains or how it is used, the path to my elucidation is clear. True, if I know there is an instance variable #quantity, the above has no advantage over:
x = m(quantity)
If, however, I don't know if there is an instance variable #quantity, I would be faced with three possibilities: quantity is a local variable, a getter or a method that is not a getter. The time required to complete my investigation should not take much longer than if I were tracking down #quantity, but those seconds do add up.
Let's consider another thing: what are the consequences of misspelling the name of an instance variable versus misspelling its getter (something I do frequently):
x = m(#qauntity)
versus:
x = m(qauntity)
#qauntity will return nil, which may lead to an exception being raised, but possibly not soon or not at all.
qauntity will almost certainly raise an "no method or variable" exception immediately, giving it the edge in this situation.
In sum, I am suggesting that it generally is best to use getters and setters outside of class definitions only, and that they have no side effects.
Your thoughts?
1. Over a lifetime of coding, typing those four extra characters could amount to hours of time wasted that could otherwise be used productively (e.g., playing pong).
Related
I'm kind of stumped at the workings of a Ruby class. I understand everything that is going on inside of the class here, but it's the variable assignment and calling of the class that confuses me.
class BankAccount
def initialize(name)
#transactions = []
#balance = 0 #
end
def deposit
print "How much would you like to deposit?"
amount = gets.chomp
#balance += amount.to_f
puts "$#{amount} deposited."
end
def show_balance
puts "Your balance is #{#balance}."
end
end
bank_account = BankAccount.new("James Dean")
bank_account.class
bank_account.deposit
bank_account.show_balance
When we did...
bank_account = BankAccount.new("James Dean")
... it seems as though whenever I call bank_account, it will use the .new method (which I am lead to believe activates the initialize method) to create a new account with the given name.
Then, from what I understand when we call .class on bank_account, it just outputs the class type.
But, next we call bank_account.deposit. Although, I'm under the impression that whenever we call bank_account now, it will create a new account using the BankAccount class... So by doing bank_account.deposit, what I envision it is actually doing is:
BankAccount.new("James Dean").deposit
So if I make two deposits (or even use bank_account.show_balance), then from my understanding, it will be creating a new account with the name James Dean over and over and over, every time I use the bank_account variable. (Because bank_account = BankAccount.new)
Can anyone help me clear this up and understand it a bit better?
Also, just on a side note, could we ever use:
BankAccount.new(gets.chomp)
so that we could create new account with names according to user input?
(Thanks everyone. This is my third question and my former two have apparently been posted in the wrong places. -_- Hoping I got this one right.)
Although, I'm under the impression that whenever we call bank_account now, it will create a new account using the BankAccount class.
That's not right. Your code only calls BankAccount.new once. The result is assigned to bank_account. Each time you access bank_account, you're accessing the constructed object created on the first line.
When you write x = Y.new, you're not assigning the expression Y.new to x, you're evaluating the expression Y.new and assigning the result to x.
Similarly, if you write x = my_function(), the function is evaluated and the result is assigned to x; any references to x from this point are completely decoupled from the function.
Are there any plans to implement ruby behavior similar to the CoffeeScript feature of specifying an instance variable name in a method argument list?
Like
class User
def initialize(#name, age)
# #name is set implicitly, but #age isn't.
# the local variable "age" will be set, just like it currently works.
end
end
I'm aware of this question: in Ruby can I automatically populate instance variables somehow in the initialize method? , but all the solutions (including my own) don't seem to fit the ruby simplicity philosophy.
And, would there be any downsides for having this behavior?
UPDATE
One of the reasons for this is the DRY (don't repeat yourself) philosophy of the ruby community. I often find myself needing to repeat the name of an argument variable because I want it to be assigned to the instance variable of the same name.
def initialize(name)
# not DRY
#name = name
end
One downside I can think of is that it may look as though a method is doing nothing if it has no body. If you're scanning quickly, this may look like a no-op. But I think given time, we can adapt.
Another downside: if you're setting other instance variables in the body, and you try to be readable by putting all the assignments at the beginning, it can take more cognitive "power" to see that there assignments also happening in the argument list. But I don't think this is any harder than, say, seeing a constant or method call and having to jump to its definition.
# notice: instance var assignments are happening in 2 places!
def initialize(#name)
#errors = []
end
After some pondering, I wondered if it's possible to actually get the argument names from a ruby method. If so, I could use a special argument prefix like "iv_" to indicate which args should be set as instance variables.
And it is possible: How to get argument names using reflection.
Yes! So I can maybe write a module to handle this for me. Then I got stuck because if I call the module's helper method, it doesn't know the values of the arguments because they're local to the caller. Ah, but ruby has Binding objects.
Here's the module (ruby 1.9 only):
module InstanceVarsFromArgsSlurper
# arg_prefix must be a valid local variable name, and I strongly suggest
# ending it with an underscore for readability of the slurped args.
def self.enable_for(mod, arg_prefix)
raise ArgumentError, "invalid prefix name" if arg_prefix =~ /[^a-z0-9_]/i
mod.send(:include, self)
mod.instance_variable_set(:#instance_vars_from_args_slurper_prefix, arg_prefix.to_s)
end
def slurp_args(binding)
defined_prefix = self.class.instance_variable_get(:#instance_vars_from_args_slurper_prefix)
method_name = caller[0][/`.*?'/][1..-2]
param_names = method(method_name).parameters.map{|p| p.last.to_s }
param_names.each do |pname|
# starts with and longer than prefix
if pname.start_with?(defined_prefix) and (pname <=> defined_prefix) == 1
ivar_name = pname[defined_prefix.size .. -1]
eval "##{ivar_name} = #{pname}", binding
end
end
nil
end
end
And here's the usage:
class User
InstanceVarsFromArgsSlurper.enable_for(self, 'iv_')
def initialize(iv_name, age)
slurp_args(binding) # this line does all the heavy lifting
p [:iv_name, iv_name]
p [:age, age]
p [:#name, #name]
p [:#age, #age]
end
end
user = User.new("Methuselah", 969)
p user
Output:
[:iv_name, "Methuselah"]
[:age, 969]
[:#name, "Methuselah"]
[:#age, nil]
#<User:0x00000101089448 #name="Methuselah">
It doesn't let you have an empty method body, but it is DRY. I'm sure it can be enhanced further by merely specifying which methods should have this behavior (implemented via alias_method), rather than calling slurp_args in each method - the specification would have to be after all the methods are defined though.
Note that the module and helper method name could probably be improved. I just used the first thing that came to mind.
Well, actually...
class User
define_method(:initialize) { |#name| }
end
User.new(:name).instance_variable_get :#name
# => :name
Works in 1.8.7, but not in 1.9.3. Now, just where did I learn about this...
I think you answered your own question, it does not fit the ruby simplicity philosophy. It would add additional complexity for how parameters are handled in methods and moves the logic for managing variables up into the method parameters. I can see the argument that it makes the code less readable a toss up, but it does strike me as not very verbose.
Some scenarios the # param would have to contend with:
def initialize( first, last, #scope, #opts = {} )
def search( #query, condition )
def ratchet( #*arg )
Should all of these scenarios be valid? Just the initialize? The #*arg seems particularly dicey in my mind. All these rules and exclusions make the Ruby language more complicated. For the benefit of auto instance variables, I do not think it would be worth it.
I'm just learning to program and have decided to try Ruby. I'm sure this is a stupid question, but the instructor is talking about setter and getter methods, and I'm confused. Here is the example:
class Human
def noise=(noise)
#noise = noise
end
def noise
#noise
end
end
From this, the class is instantiated, and I can puts this out:
man = Human.new
man.noise=("Howdie!")
puts man.noise
This results in Howdie!
Now what confuses me is that the instructor is saying without the getter method (the 2nd of the two methods), there is no way to interact with the instance variable #noise.
But when I remove the getter method, I'm able to still access #noise, see example:
class Human
def noise=(noise)
#noise = noise
end
end
man = Human.new
puts man.noise=("Howdie!")
This works the same as when the getter method it used.
So now I'm confused. Why is the getter needed? What does the instructor mean by not being able to access the instance variable without it? Is it possible he's using an older version of Ruby?
Thanks in advance for your help.
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.
The line of code
puts man.noise=("Howdie!")
does NOT use the getter method, so the getter method does not need to be defined for it to work. That line just uses the setter method. The return value of the setter method is automatically equal to whatever is on the right-hand side of the equal sign, so "Howdie!" gets passed to puts.
The line of code
puts man.noise
DOES use the getter method, and it would not work if you remove the getter method.
Surely they both return a value, but their behavior are different.
Let's say there is already a member #a .
with getter, one obtains the current value of #a, without modifying it.
with setter, one modifies #a, and get its new value as return value.
when thinking about behavior of setter, note:
the old value of #a can not be obtained with setter, and got overwritten.
what returned by setter, is actually already known before invoking setter.
May be the attention for getters and setters is because some other languages allow you to access class variables directly. Python:
class k:
i = 100
print k.i # 100
k.i = 200
print k.i # 200
In contrast, Ruby keeps all of its variables completely private to the class and only exposes them through accessor methods.
In your example, if you remove the getter, you can indeed interact with the variable (that is: change it) trough the setter, but you can't get it back (in a regular way) when you need it.
What's the Ruby convention for referring to instance variables inside an instance method?
Consider the following variations:
def MyClass
attr_accessor :q
def initialize(z)
#q = z
end
def method_one
puts q
end
def method_two
puts #q
end
def method_three
puts self.q
end
end
Which convention q, #q or self.q is preferred?
The whole reason that Ruby uses instance variables and accessor methods (with syntax sugar to look like you're getting direct access to properties) is so that you can refactor your classes and external consumers of your interface
puts musicObj.minutes
don't have to know if you internally have:
attr_accessor :minutes
or
def minutes
#seconds / 60.0
end
This same rational applies equally well to your own code. If you have a class with 100 methods that all reference your #minutes, and then you decide that you really should be storing #seconds you will need to change 100 methods. On the other hand, if you had used minutes (your "method_one") instead, none of your methods would need to change.
There is a small performance hit for using the method-invocation notation instead of directly accessing the instance variable. If speed is critical you may want to consider direct access; otherwise, I encourage you to eat your own dog food and use any publicly-accessible interfaces you expose, when they exist.
And, as #Alex answered, you must use the self.foo = 42 notation when invoking a 'setter' method, as foo = 42 will always set a local variable instead.
self.q is used when setting the variable
self.q = 'Some value.'
whereas q is used when getting the variable
puts q
do_something_with_variable(q)
Of course, these are purely conventions. You can substitute self.q for q when getting the variable whenever you like (although consistency is recommended).
I find this discussion quite enlightening with regards to using #q.
I'm thinking in:
class X
def new()
#a = 1
end
def m( other )
#a == other.#a
end
end
x = X.new()
y = X.new()
x.m( y )
But it doesn't works.
The error message is:
syntax error, unexpected tIVAR
How can I compare two private attributes from the same class then?
There have already been several good answers to your immediate problem, but I have noticed some other pieces of your code that warrant a comment. (Most of them trivial, though.)
Here's four trivial ones, all of them related to coding style:
Indentation: you are mixing 4 spaces for indentation and 5 spaces. It is generally better to stick to just one style of indentation, and in Ruby that is generally 2 spaces.
If a method doesn't take any parameters, it is customary to leave off the parantheses in the method definition.
Likewise, if you send a message without arguments, the parantheses are left off.
No whitespace after an opening paranthesis and before a closing one, except in blocks.
Anyway, that's just the small stuff. The big stuff is this:
def new
#a = 1
end
This does not do what you think it does! This defines an instance method called X#new and not a class method called X.new!
What you are calling here:
x = X.new
is a class method called new, which you have inherited from the Class class. So, you never call your new method, which means #a = 1 never gets executed, which means #a is always undefined, which means it will always evaluate to nil which means the #a of self and the #a of other will always be the same which means m will always be true!
What you probably want to do is provide a constructor, except Ruby doesn't have constructors. Ruby only uses factory methods.
The method you really wanted to override is the instance method initialize. Now you are probably asking yourself: "why do I have to override an instance method called initialize when I'm actually calling a class method called new?"
Well, object construction in Ruby works like this: object construction is split into two phases, allocation and initialization. Allocation is done by a public class method called allocate, which is defined as an instance method of class Class and is generally never overriden. It just allocates the memory space for the object and sets up a few pointers, however, the object is not really usable at this point.
That's where the initializer comes in: it is an instance method called initialize, which sets up the object's internal state and brings it into a consistent, fully defined state which can be used by other objects.
So, in order to fully create a new object, what you need to do is this:
x = X.allocate
x.initialize
[Note: Objective-C programmers may recognize this.]
However, because it is too easy to forget to call initialize and as a general rule an object should be fully valid after construction, there is a convenience factory method called Class#new, which does all that work for you and looks something like this:
class Class
def new(*args, &block)
obj = alloc
obj.initialize(*args, &block)
return obj
end
end
[Note: actually, initialize is private, so reflection has to be used to circumvent the access restrictions like this: obj.send(:initialize, *args, &block)]
Lastly, let me explain what's going wrong in your m method. (The others have already explained how to solve it.)
In Ruby, there is no way (note: in Ruby, "there is no way" actually translates to "there is always a way involving reflection") to access an instance variable from outside the instance. That's why it's called an instance variable after all, because it belongs to the instance. This is a legacy from Smalltalk: in Smalltalk there are no visibility restrictions, all methods are public. Thus, instance variables are the only way to do encapsulation in Smalltalk, and, after all, encapsulation is one of the pillars of OO. In Ruby, there are visibility restrictions (as we have seen above, for example), so it is not strictly necessary to hide instance variables for that reason. There is another reason, however: the Uniform Access Principle.
The UAP states that how to use a feature should be independent from how the feature is implemented. So, accessing a feature should always be the same, i.e. uniform. The reason for this is that the author of the feature is free to change how the feature works internally, without breaking the users of the feature. In other words, it's basic modularity.
This means for example that getting the size of a collection should always be the same, regardless of whether the size is stored in a variable, computed dynamically every time, lazily computed the first time and then stored in a variable, memoized or whatever. Sounds obvious, but e.g. Java gets this wrong:
obj.size # stored in a field
vs.
obj.getSize() # computed
Ruby takes the easy way out. In Ruby, there is only one way to use a feature: sending a message. Since there is only one way, access is trivially uniform.
So, to make a long story short: you simply can't access another instance's instance variable. you can only interact with that instance via message sending. Which means that the other object has to either provide you with a method (in this case at least of protected visibility) to access its instance variable, or you have to violate that object's encapsulation (and thus lose Uniform Access, increase coupling and risk future breakage) by using reflection (in this case instance_variable_get).
Here it is, in all its glory:
#!/usr/bin/env ruby
class X
def initialize(a=1)
#a = a
end
def m(other)
#a == other.a
end
protected
attr_reader :a
end
require 'test/unit'
class TestX < Test::Unit::TestCase
def test_that_m_evaluates_to_true_when_passed_two_empty_xs
x, y = X.new, X.new
assert x.m(y)
end
def test_that_m_evaluates_to_true_when_passed_two_xs_with_equal_attributes
assert X.new('foo').m(X.new('foo'))
end
end
Or alternatively:
class X
def m(other)
#a == other.instance_variable_get(:#a)
end
end
Which one of those two you chose is a matter of personly taste, I would say. The Set class in the standard library uses the reflection version, although it uses instance_eval instead:
class X
def m(other)
#a == other.instance_eval { #a }
end
end
(I have no idea why. Maybe instance_variable_get simply didn't exist when Set was written. Ruby is going to be 17 years old in February, some of the stuff in the stdlib is from the very early days.)
There are several methods
Getter:
class X
attr_reader :a
def m( other )
a == other.a
end
end
instance_eval:
class X
def m( other )
#a == other.instance_eval { #a }
end
end
instance_variable_get:
class X
def m( other )
#a == other.instance_variable_get :#a
end
end
I don't think ruby has a concept of "friend" or "protected" access, and even "private" is easily hacked around. Using a getter creates a read-only property, and instance_eval means you have to know the name of the instance variable, so the connotation is similar.
If you don't use the instance_eval option (as #jleedev posted), and choose to use a getter method, you can still keep it protected
If you want a protected method in Ruby, just do the following to create a getter that can only be read from objects of the same class:
class X
def new()
#a = 1
end
def m( other )
#a == other.a
end
protected
def a
#a
end
end
x = X.new()
y = X.new()
x.m( y ) # Returns true
x.a # Throws error
Not sure, but this might help:
Outside of the class, it's a little bit harder:
# Doesn't work:
irb -> a.#foo
SyntaxError: compile error
(irb):9: syntax error, unexpected tIVAR
from (irb):9
# But you can access it this way:
irb -> a.instance_variable_get(:#foo)
=> []
http://whynotwiki.com/Ruby_/_Variables_and_constants#Variable_scope.2Faccessibility