Search code examples
rubyscopeimapaccessor

Implementing accessor methods in Ruby


This is a follow up question to: ruby variable scoping across classes. The solution makes sense to me conceptually, but I can't get it to work. Thought maybe with more code someone could help me.

I have a class Login that declares a new IMAP class, authenticates, and picks a mailbox.

I then am trying to create a separate class that will "do stuff" in the mailbox. For example, calculate the number of emails received. The problem is that the @imap instance of Net::IMAP doesn't pass from the Login class to the Stat class -- I'm getting no method errors for imap.search in the new class. I don't want to re-log in and re-authenticate each time I need to "do some stuff" with the mailbox. I'm trying to implement the solution in the other thread, but can't seem to get it to work.

Here's the Login class:

class Login
  def initialize(user, domain, pass)
    @username = user
    @domain = domain
    @pass = pass

    #check if gmail or other domain
    gmail_test =  @domain.include? "gmail.com"
    if gmail_test == true 
      @imap = Net::IMAP.new('imap.gmail.com',993,true,nil,false)
      @imap.login(@username + "@" + @domain, @pass)
    else
      @imap = Net::IMAP.new("mail." + @domain)
      @imap.authenticate('LOGIN', @username + "@" + @domain, @pass)
    end
    return self
  end

  #enable mailbox select    
  def mailbox(box)
    @mailbox = box 
    @mailbox_array = @imap.list('','*').collect{ |mailbox| mailbox.name } #create array of mailboxes
    @matching_mailbox = @mailbox_array.grep(/#{@mailbox}/i) #search for mailbox along list
    if @matching_mailbox.empty? == true #if no results open INBOX
       @mailbox = "INBOX"
       @imap.examine(@mailbox)
    else  
       @imap.examine(@matching_mailbox.first.to_s) #if multiple results, select first and examine
       @mailbox = @matching_mailbox.first.to_s
    end 
    return self   
  end    
end

I want to be able to say:

Login.new("user", "domain", "pass").mailbox("in") 

and then something like:

class Stat
  def received_today()
    #emails received today
    @today = Date.today
    @received_today = @imap.search(["SINCE", @today.strftime("%d-%b-%Y")]).count.to_s
    puts @domain + " " + @mailbox.to_s + ": " + @received_today + " -- Today\n\n" #(" + @today.strftime("%d-%b-%Y") + ")
  end
end

And be able to call

Stat.new.received_today and have it not throw a "no method search" error. Again, the other question contains pseudo_code and a high level explanation of how to use an accessor method to do this, but I can't implement it regardless of how many hours I've tried (been up all night)...

All I can think is that I am doing this wrong at a high level, and the stat calculation needs to be a method for the Login class, not a separate class. I really wanted to make it a separate class, however, so I could more easily compartmentalize... Thanks!


Solution

  • OK --- After much head banging on the wall, I got this to work.

    Added these three methods to class Login:

      def get_imap
        @imap
      end
      def get_domain
        @domain
      end
      def get_mailbox
        @mailbox
      end
    

    Changed class Stat to:

    class Stat
           def received_today(login)
               #emails received today
             @today = Date.today
             @received_today = login.get_imap.search(["SINCE", @today.strftime("%d-%b-%Y")]).count.to_s
           #  puts @received_today
             puts login.get_domain + " " + login.get_mailbox.to_s + ": " + @received_today + " -- Today\n\n" 
           end
          end
    

    Now this actually works, and doesn't say undefined method search or imap:

    b = Login.new("user", "domain", "pass").mailbox("box") 
    c = Stat.new
    c.received_today(b)
    

    I'm pretty sure there is a way to use attr_accessor to do this as well, but couldn't figure out the syntax. Anyway, this works and enables me to use the @imap var from class Login in class Stat so I can write methods to "do_stuff" with it. Thanks for the help and please don't hesitate to tell me this is horrible Ruby or not best practices. I'd love to hear the Ruby way to accomplish this.

    Edit for attr_accessor or attr_reader use:

    Just add it to class Login and then can say login.imap.search#stuff in class Stat with no problem.