Question

Hi I have something like the folowing:

class TrialRequest

  attr_accessor :trial_email

  def initialize(email)
    @trial_email = email
    puts "Trial_email: #{trial_email}"
  end

  def create
    @email = ::Gmail.connect!(gmail_name, gmail_password) do |gmail|
      email = gmail.compose do
        to 'trial@domain.com'
        from trial_email
        subject trial_email
        text_part do
          content_type 'text/html; charset=UTF-8'
          body 'Sign me up.'
        end
      end
      #binding.pry
      gmail.deliver!(email)
    end
  end
end

The problem is that inside the compose block trial_email is not defined:

NameError: undefined local variable or method `trial_email' for #<Mail::Message:0x0000000431b830>

Is this a Ruby 1.9 issue or a gmail gem issue?

How should I go about making this method 'visible'/within the scope of the compose block?

Update: This is an issue/feature of the gmail gem - ruby 1.9 blocks have changed but not this much! In addition to the accepted answer, another workaround is to pass the data in as a method parameter:

def create(trial_email)
  ...
end
Was it helpful?

Solution

Looks like a GMail issue to me. Inside the blocks, self will be some object from the GMail gem so that you can have to, from, and similar DSL niceties available. You should be able to put self.trial_email into a local variable and then access that inside the blocks:

email_address = self.trial_email
@email = ::Gmail.connect!(gmail_name, gmail_password) do |gmail|
  email = gmail.compose do
    to 'trial@domain.com'
    from email_address
    subject email_address
    #...

OTHER TIPS

You're expecting (as you're entitled to) that the block should preserve the value of self, as it usually does. It looks like the gmail gem is using instance_exec here which allows it to change the value of self for the block to an instance of Mail::Message (which is why you can call to and from in that block even though you define no such methods)

While instance_exec is handy for producing nice DSLs, it is not without its downsides. Local variable scope isn't affected so you could store either trial_email or self in a local variable prior to the block and then use that local variable inside the block

The problem is that the block you pass to compose method is later passed to Mail.new and finally to Message.new (if I traced the chain correctly) and then this block is evaluated like that here:

instance_eval(&block)

As it's performed inside initialize method of a different object (instance of Message class) you do not have access to attributes of your TrialRequest object.

You can do the same thing without having any troubles like that:

email = gmail.compose
email.to = 'trial@domain.com'
email.from = trial_email
email.subject = trial_email

email.text_part do
  content_type 'text/html; charset=UTF-8'
  body 'Sign me up.'
end
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top