2014-11-06 79 views
0

我有這樣的代碼:如何從對象訪問全局方法?

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#行了錯誤的號碼參數錯誤。我猜這是因爲我沒有成功訪問全球login()函數呢?我如何參考它?

+0

Ruby實際上並沒有全局範圍。您的第一個登錄定義實際上將作用域爲Object。檢查出這個問題的接受答案:http://stackoverflow.com/questions/1042384/how-do-you-use-global-variables-or-constant-values-in-ruby – 2014-11-06 19:47:34

+0

@BrianDriscoll:你指的是到錯誤的問題。此問題是[此問題]的重複(https://stackoverflow.com/questions/9593514/how-to-call-a-method-from-the-global-scope-with-same-name-as-an-實例方法)。 – Surya 2014-11-06 19:59:25

+0

或這個問題:https://stackoverflow.com/questions/2681895/how-to-access-a-shadowed-global-function-in-ruby – Surya 2014-11-06 20:03:01

回答

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 
0

在Ruby中,我們必須在那裏類的方法被稱爲第一不是全球範圍的方法層次。

這些全局範圍的方法屬於Object類,但它們被聲明爲私有。

要訪問他們直接就可以使用發送方法,但它不建議

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') 
0

我真的很陌生,所以這是一個入門級的問題。

簡短的回答是:Bob類中的login()實例方法隱藏了頂級login()方法。簡單的解決方案是:更改其中一種方法的名稱。

這裏有一些事情你應該嘗試學習:

1)在Ruby中,每一個方法被調用,在左邊的對象,例如

some_obj.login 

在左手側上的對象被稱爲接收機

2)如果您沒有明確指定接收者,例如

login('bob',pass) #No receiver is specified on the left hand side 

...紅寶石使用稱爲在左手側的變量,例如:在內部的方法

self.login('bob', pass) 

3)一類中定義的,例如:

class Bob 
    def login(pass) 
    #IN HERE 
    end 
end 

...自我等於調用該方法的對象。你的情況,你有這樣的代碼:

bob = Bob.new 
bob.login('world') 

所以鮑勃是調用登錄()實例方法的對象,因此,你有這樣的:

class Bob 
    def login(pass) 
    #IN HERE, self is equal to bob 
    end 
end 

因此,紅寶石做到這一點:

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 

一個解決您的問題,像吉列爾梅·卡洛斯建議,是使用一個模塊 - 但你可以做,在一個簡單的方法:

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不是一個類時,那麼當前類是self的類。好吧,讓我們來看看這些原則在行動:

class Bob 
    puts self #=>Bob 

    def login(pass) 
    ... 
    end 
end 

因爲自我是一個類,當前類等於自我,和DEF附有自己的鮑勃類。

頂級會發生什麼?

puts self #=> main 

def login(user,pass) 
end 

有經驗的紅寶石主義者熟悉main;它是ruby在頂層(即任何類或方法定義之外)指定給自己的對象 - 您在調用的內容是global。重要的一點是main不是一個類。其結果是,頂級登錄()DEF本身重視主要的類,它是:

puts self #=>main 
puts self.class #=>Object 

def login(user,pass) 
end 

布賴恩·德里斯科爾提到,紅寶石不具有全局範圍 - 但這並不真的事,因爲反正def創建了一個新的作用域,它關閉了外部作用域,因此def之外的任何東西都不會在def內部可見(常量除外)。

你正在做的事通常是用紅寶石完成的,所謂的。塊允許您將第二種方法傳遞給第一種方法,然後在第一種方法中您可以調用第二種方法。這裏有一個例子:

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()方法的替身。紅寶石會自動將塊的塊之前規定的方法:

This method! 
    | 
    V 
bob.login('my password') 

和登錄()方法中,你使用這個詞yield --IT的調用該塊,就好像yield是該方法的名稱。

請注意,它實際上是ruby的sytnax,即在方法調用後編寫塊,導致第二個方法傳遞給第一個方法,並且在第一個方法中,您可以通過簡單地編寫yield(arg1, arg2, etc.)

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以外的類。

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) 

,並得到同樣的結果。我們要不要?我會讓你知道你是否感興趣。

+0

嗨卡里,**如果它不是一個私人的方法,我們可以用以下方法調用它:'Object.login('Bob',pass)**這有點令人困惑。你使用private_instance_methods()來顯示login()是Object的一個實例方法,但是你可以像Object的類方法那樣調用login()。你真的想進入繼承結構頂部的循環? – 7stud 2014-11-07 02:59:22

+0

7感謝,好點。謝謝!我所擁有的不僅僅是混淆,它是錯誤的。我已經做了一個修復。如果有任何錯誤或遺漏,請告訴我。 – 2014-11-07 07:04:59

1

您可以使用super。在頂層定義的方法神奇地變成所有對象的私有方法。

class Bob def login(pass) super('Bob', pass) end end