I'm really new to ruby, so this is an entry level question.
The short answer is: the login() instance method in class Bob hides the toplevel login() method. The easy solution is: change the name of one of the methods.
Here are some things you should try to learn:
1) In ruby, every method is called with an object on the left hand side, e.g.
some_obj.login
The object on the left hand side is called the receiver.
2) If you don't explicitly specify a receiver, e.g.
login('bob',pass) #No receiver is specified on the left hand side
...ruby uses a variable called self on the left hand side, e.g.:
self.login('bob', pass)
3) Inside a method defined inside a class, e.g.:
class Bob
def login(pass)
#IN HERE
end
end
...self is equal to the object that called the method. In your case, you have this code:
bob = Bob.new
bob.login('world')
So bob is the object that is calling the login() instance method, and therefore you have this:
class Bob
def login(pass)
#IN HERE, self is equal to bob
end
end
Therefore, ruby does this:
class Bob
def login(pass)
#login('bob', pass) =>This line gets converted to this:
self.login('bob',pass) #ERROR#
#IN HERE, self is equal to bob
#So ruby executes this:
#bob.login('bob', pass) #ERROR: too many arguments#
end
end
One solution to your problem, like Guilherme Carlos suggested, is to use a module--but you can do that in a simpler way:
module MyAuthenticationMethods
def login(user, pass)
puts "user: #{user}, pass: #{pass}"
end
end
class Bob
def login(pass)
MyAuthenticationMethods::login('bob',pass)
end
end
However, generally you put a module in its own file and then require
it. The reason a module solves your problem is because a module name starts with a capital letter, which means it's a constant--and you can access a constant from anywhere in your code.
4) All def's attach themselves to the current class. The current class is determined by the value of the self variable: if self is a class, then the current class is just the value of self, but when self isn't a class, then the current class is self's class. Okay, let's see those principles in action:
class Bob
puts self #=>Bob
def login(pass)
...
end
end
Because self is a class, the current class is equal to self, and the def attaches itself to the Bob class.
What happens at the top level?
puts self #=> main
def login(user,pass)
end
Experienced rubyists are familiar with main
; it is the object that ruby assigns to self at the top level, i.e. outside any class or method definitions--what you are calling global. The important point is that main
is not a class. As a result, the top level login() def attaches itself to main's class, which is:
puts self #=>main
puts self.class #=>Object
def login(user,pass)
end
Brian Driscoll mentioned that ruby doesn't have a global scope--but that doesn't realy matter anyway because a def creates a new scope which closes off the outer scope, so nothing that exists outside the def is visible inside the def (except constants).
What you are trying to do is often done in ruby with what are called blocks. Blocks allow you to pass a second method to the first method, and then inside the first method you can call the second method. Here is an example:
class Bob
def login(pass)
yield('bob', pass) #yield calls the block with the specified arguments
end
end
bob = Bob.new
bob.login('my password') do |username, pword|
puts username, pword
end
The block in that code is this part:
do |username, pword|
puts username, pword
end
...which looks sort of like a method definition--but without a name. That is the standin for your top level login() method. Ruby automatically passes the block to the method specified before the block:
This method!
|
V
bob.login('my password')
And inside the login() method, you call the block using the word yield
--it's as if yield
were the name of the method.
Note that it is actually ruby's sytnax, i.e. writing a block after a method call, that causes a second method to be passed to the first method, and in the first method you can call the passed in method by simply writing yield(arg1, arg2, etc.)
.