我有这段代码:
def login(user,pass)
end
class Bob
def login(pass)
login('bob',pass) #ERROR#
end
end
login('hello','world')
bob = Bob.new
bob.login('world')
当我尝试从命令行执行代码时,我在#ERROR#
注释的行上得到错误的Arguments错误数。我猜这是因为我没有成功访问全局login()
函数?我该如何参考?
答案 0 :(得分:1)
您可以使用super
。在顶层定义的方法神奇地成为所有对象的私有方法。
class Bob
def login(pass)
super('Bob', pass)
end
end
答案 1 :(得分:0)
如果你能解释一下你究竟想做什么,可能会有更好的方法。但是,如果你必须这样做,那么:
def login(user, pass)
puts 'global login'
puts "user: #{user}, pass: #{pass}"
end
class Bob
def login(pass)
self.class.send(:login, 'bob',pass) #ERROR#
end
end
login('hello','world')
bob = Bob.new
bob.login('world')
#=> global login
#=> user: hello, pass: world
#=> global login
#=> user: bob, pass: world
答案 2 :(得分:0)
在Ruby中,我们有一个层次结构,其中类方法首先被调用,而不是全局范围方法。
这些全局范围的方法属于Object
类,但它们被声明为私有。
要直接访问它们,您可以使用send方法,但不建议使用
Object.send(:login, param1, param2)
解决此问题的更好方法是使用模块。
创建模块:
<强> login.rb 强>
module Login
def login(user, pass)
end
end
并将其包含在您的课程中:
<强> bob.rb 强>
require 'login'
include Login
class Bob
login(pass)
Login::login('bob', pass)
end
end
bob = Bob.new
bob.login('test')
答案 3 :(得分:0)
我对ruby很新,所以这是一个入门级问题。
简短的回答是:类Bob中的login()实例方法隐藏了toplevel login()方法。简单的解决方案是:更改其中一种方法的名称。
以下是您应该尝试学习的一些内容:
1)在ruby中,每个方法都在左侧调用一个对象,例如
some_obj.login
左侧的对象称为接收器。
2)如果您没有明确指定接收器,例如
login('bob',pass) #No receiver is specified on the left hand side
... ruby在左侧使用一个名为 self 的变量,例如:
self.login('bob', pass)
3)在类中定义的方法内部,例如:
class Bob
def login(pass)
#IN HERE
end
end
... self等于调用方法的对象。在您的情况下,您有以下代码:
bob = Bob.new
bob.login('world')
所以bob是调用login()实例方法的对象,因此你有这个:
class Bob
def login(pass)
#IN HERE, self is equal to bob
end
end
因此,ruby这样做:
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
像Guilherme Carlos建议的那样,你的问题的一个解决方案是使用一个模块 - 但你可以用更简单的方式做到这一点:
module MyAuthenticationMethods
def login(user, pass)
puts "user: #{user}, pass: #{pass}"
end
end
class Bob
def login(pass)
MyAuthenticationMethods::login('bob',pass)
end
end
但是,通常您将模块放在自己的文件中,然后require
。模块解决问题的原因是模块名称以大写字母开头,这意味着它是常量 - 您可以从代码中的任何位置访问常量。
4)所有def都附加到当前类。当前类由自变量的值决定:如果self是一个类,那么当前类只是self的值,但是当self不是类时,则当前类是self的类。好的,让我们看看这些原则:
class Bob
puts self #=>Bob
def login(pass)
...
end
end
因为self是一个类,所以当前类等于self,并且def将自己附加到Bob类。
顶层会发生什么?
puts self #=> main
def login(user,pass)
end
有经验的rubyists熟悉main
;它是ruby在顶层分配给self的对象,即在任何类或方法定义之外 - 你所谓的 global 。重要的是main
不是一个类。因此,顶级login()def将自己附加到main的类,即:
puts self #=>main
puts self.class #=>Object
def login(user,pass)
end
Brian Driscoll提到红宝石没有全局范围 - 但这无论如何都不重要,因为def会创建一个新的范围来封闭外部范围,因此def内部不存在任何内容。 def(常数除外)。
您尝试做的事情通常是使用所谓的块在ruby中完成的。块允许您将第二个方法传递给第一个方法,然后在第一个方法内部可以调用第二个方法。这是一个例子:
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
该代码中的块是这一部分:
do |username, pword|
puts username, pword
end
...看起来有点像方法定义 - 但没有名字。这是您的顶级login()方法的替代品。 Ruby自动将块传递给块之前指定的方法:
This method!
|
V
bob.login('my password')
在login()方法中,您使用单词yield
调用块 - 就好像yield
是方法的名称一样。
请注意,它实际上是ruby的sytnax,即在方法调用之后编写一个块,导致第二个方法被传递给第一个方法,而在第一个方法中,您可以通过简单地编写{{{ 1}}。
答案 4 :(得分:0)
我们来看看:
def login(user, pass)
puts "#{user}'s #{pass}"
end
class Bob
def login(pass)
greeting
login('Bob',pass) #ERROR#
end
def greeting
puts "hi"
end
end
当我们跑步时:
bob = Bob.new
bob.login('world')
我们得到:
hi
ArgumentError: wrong number of arguments (2 for 1)
你知道为什么会引发异常。
我们通过将它们与任何参数一起发送到接收器来执行方法。最初,我们将带有参数login
的方法'world'
发送给接收方bob
。但等等,在login
中,没有指定接收器。接收者要么是明确的(例如,在课外,bob.greeting
),要么是未指定的,在这种情况下,它们被假定为self
。此处self
为bob
,因此方法greeting
中的login
相当于方法中的self.greeting
或类外的bob.greeting
。< / p>
greeting
执行login
后,我们希望执行类外的方法login
。因此,我们必须使用明确的接收器。但它的课程是什么? (我们知道它有一个!)加载此代码后,请在IRB中尝试:
method(:login).owner #=> Object
我们在“顶层”运行此处:
self #=> main
self.class #=> Object
因此可以在我们的程序中的任何地方调用它。唯一的复杂因素是当我们在一个具有相同名称的实例方法的类中时。
好的,login
在课程Bob
之外是类Object
的方法。它是类方法还是实例方法?
Object.methods.include?(:login) #=> false
Object.instance_methods.include?(:login) #=> false
既不!嗯。那么它必须是私有方法:
Object.private_methods.include?(:login) #=> true
Object.private_instance_methods.include?(:login) #=> true
是的,实际上,它既是私有类方法又是私有实例方法(类Object
)。这有点令人困惑,但是为什么它是两者以及为什么它是私有的答案在于Ruby的对象模型,并且不能用几句话来解释,所以必须等待另一天。
我们可以使用方法Object#send
调用私有方法,这就是我们要做的。我们使用私有类方法,因此接收方将是Object
:
def login(user,pass)
puts "#{user}'s #{pass}"
end
class Bob
def login(pass)
greeting
Object.send(:login, "Bob", pass)
end
def greeting
puts "hi"
end
end
bob = Bob.new
bob.login('world')
# hi
# Bob's world
乌拉!
额外信用:由于login
都是(私有)类方法和实例方法,我们应该能够在操作行中插入new
:
Object.new.send(:login, "Bob", pass)
得到相同的结果。我们要不要?如果你有兴趣,我会告诉你。