如何从对象访问全局方法?

时间:2014-11-06 19:44:27

标签: ruby

我有这段代码:

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()函数?我该如何参考?

5 个答案:

答案 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。此处selfbob,因此方法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)

得到相同的结果。我们要不要?如果你有兴趣,我会告诉你。