2014-11-06 5 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 действительно не имеет глобального масштаба. Ваше первое определение логина будет фактически привязано к объекту. Ознакомьтесь с принятым ответом на этот вопрос: http://stackoverflow.com/questions/1042384/how-do-you-use-global-variables-or-constant-values-in-ruby –

+0

@BrianDriscoll: Вы имеете в виду к неправильному вопросу. Этот вопрос дублирует [этот вопрос] (https://stackoverflow.com/questions/9593514/how-to-call-a-method-from-the-global-scope-with-same-name-as-an- instance-method). – Surya

+0

или этот вопрос: https://stackoverflow.com/questions/2681895/how-to-access-a-shadowed-global-function-in-ruby – Surya

ответ

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 мы имеем иерархию, где методы класса называются первым, чем глобальные методы Scope.

Эти методы с глобальным охватом относятся к классу 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

Я действительно новичок в рубине, так что это вопрос начального уровня.

Короткий ответ: метод экземпляра login() в классе Bob скрывает метод toplevel login(). Простое решение: изменить имя одного из методов.

Вот некоторые вещи, которые вы должны попробовать, чтобы узнать:

1) В рубина, каждый метод вызывается с объекта на левой стороне, например,

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 

...self равно объекту, называемому методом. В вашем случае, у вас есть этот код:

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) Все разработчики прикрепляются к классу . Текущий класс определяется значением переменной self: если self является классом, то текущий класс является просто значением self, но когда self не является классом, то текущий класс является классом self. Хорошо, давайте посмотрим, эти принципы в действии:

class Bob 
    puts self #=>Bob 

    def login(pass) 
    ... 
    end 
end 

Потому что само это класс, текущий класс равен себе, и Защита привязывается к классу Боба.

Что происходит на верхнем уровне?

puts self #=> main 

def login(user,pass) 
end 

Опытные Рубисты знакомы с main; это объект, который ruby ​​присваивает себе на верхнем уровне, то есть вне любых определений классов или методов - то, что вы вызываете global. Важным моментом является то, что main не является классом. В результате Войти верхний уровень() Защита присоединяется к классу главных, который является:

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 

... который выглядит вроде как определение метода - но без имени. Это означает, что вы используете метод входа() верхнего уровня. Рубин автоматически переходит в блок методы, указанной перед блоком:

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

И внутри методы Логина(), вы вызываете блок, используя слово yield --Оы, как будто yield были именем метода.

Обратите внимание, что это фактически sytnax ruby, то есть запись блока после вызова метода, который заставляет второй метод передаваться первому методу, а в первом методе вы можете вызвать переданный метод простым написанием 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. Здесь self - bob, поэтому 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() является методом экземпляра объекта, но затем вы вызываете login(), как метод класса Object. Вы действительно хотите пойти в круглость в верхней части структуры наследования? – 7stud

+0

7stud, хороший пункт. Благодаря! То, что у меня было, было не просто сбивающим с толку, это было неправильно. Я сделал исправление. Пожалуйста, дайте мне знать, останутся ли какие-либо ошибки или упущения. –

1

Для этого можно использовать super. Методы, определенные на верхнем уровне, магически становятся частными методами для всех объектов.

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

Смежные вопросы