ruby область видимости переменной в классах

Ру Нуби здесь. У меня есть класс Login, который входит в gmail, используя библиотеку net/IMAP. Происходит то, что я создаю новый экземпляр этого класса, например:

a = Login.new("username", "gmail.com", "passw")

Затем я работаю над другими классами, которые будут делать кое-что с почтовым ящиком. Проблема в том, что переменная @imap, которую я определил в Login, кажется, исчезла (я полагаю, из-за области видимости).

Вот как @imap объявляется в классе Login: @imap = Net::IMAP.new('imap.gmail.com',993,true,nil,false)

Итак, это:

  @today = Date.today
  @received_today = imap.search(["SINCE", @today.strftime("%d-%b-%Y")]).count.to_s

... возвращает ошибку. Это две ошибки, которые я получил, играя с этим. Первый — когда я использую imap, второй — когда я пытаюсь @imap:

NameError: undefined local variable or method `imap' for #<Object:0x10718d2a8>
NoMethodError: undefined method `search' for nil:NilClass

Каковы наилучшие методы борьбы с подобной ситуацией? Является ли единственным решением определить мои методы, которые делают «вещи» в том же классе, где я создаю новый экземпляр Net::IMAP? Является ли объявление @imap глобальной переменной $imap плохой практикой? Так запутался, держу пари, что ответ тоже очень прост и очевиден, но я просто его не вижу. Спасибо!


person krapdagn    schedule 02.03.2012    source источник


Ответы (2)


Этот:

@received_today = imap.search(["SINCE", @today.strftime("%d-%b-%Y")]).count.to_s

не будет работать, потому что в этот момент в области видимости нет imap, и поэтому вы получаете NameError. Когда вы попробуете это так:

@received_today = @imap.search(["SINCE", @today.strftime("%d-%b-%Y")]).count.to_s

Вы получаете NoMethodError, потому что переменные экземпляра, такие как @imap, автоматически создаются при первом использовании и инициализируются как nil. Ваш настоящий @imap находится в другом объекте, поэтому вы не можете ссылаться на него как на @imap где-либо еще.

Я думаю, вам нужна структура, похожая на эту:

class User
    def imap
        if(!@imap)
            @imap = Net::IMAP.new('imap.gmail.com', 993, true, nil, false)
            # and presumably an @imap.authenticate too...
        end
        @imap
    end
end

class OtherOne
    def some_method(user)
        @today = Date.today
        @received_today = user.imap.search(["SINCE", @today.strftime("%d-%b-%Y")]).count.to_s
    end
end

Держите свой Net::IMAP локализованным внутри вашего пользователя и позволяйте другим объектам использовать его, предоставляя простой метод доступа.

О, и эта глобальная идея $imap, я просто притворюсь, что не видел этого, поскольку глобальные почти всегда очень плохая идея.

person mu is too short    schedule 02.03.2012
comment
Спасибо! Это имеет смысл, так что в основном экземпляр класса User передается в качестве аргумента для some_method класса OtherOne, похоже. Меня беспокоит, что таким образом каждый раз, когда другой класс вызывает класс User, Net::IMAP входит в систему и аутентифицирует почтовый ящик. Похоже, это может стать медленным, если многие другие классы/методы продолжают пытаться что-то делать с почтовым ящиком и должны каждый раз входить в систему. Судя по вашему ответу, похоже, что невозможно просто войти в систему с помощью net/IMAP и оставить его открытым. - person krapdagn; 02.03.2012
comment
@krapdagn: Net::IMAP.new будет вызываться только один раз для каждого пользователя, после этого imap просто возвращает существующий @imap. Однако вам может понадобиться некоторая логика для повторного подключения, если @imap отключится. - person mu is too short; 02.03.2012

более короткий способ определить переменную imap в классе User, который почти такой же, как то, что опубликовал mu:

class User
   def imap
      @imap ||= Net::IMAP.new...
   end
end
person Chris Drappier    schedule 02.03.2012
comment
Спасибо. Мне нравится, что в Ruby есть несколько способов решения проблем. Приятно узнавать новые вещи, такие как ||= - person krapdagn; 02.03.2012