Question

I'm missing some basic Ruby variables scope knowledge here. I'm trying to send an email using Mail (this is a simplified version of my production script but it has everything to show the problem):

#!/usr/bin/ruby

require 'rubygems'
require 'mail'

@final_file=File.new("/usr/shared/name/ruby/file2.html","w")
@final_file.write("foo-bar")
puts @final_file.path

Mail.defaults do
  delivery_method :smtp,{ :address => 'localhost', :port => 25,:openssl_verify_mode => OpenSSL::SSL::VERIFY_NONE}
end

mail = Mail.new do
      from 'me@somehost.net'
        to 'mymail@mydomain.com'
   subject 'Some subject'
      body "here the body"
  add_file :filename => 'filename.html', :content => File.read(@final_file.path)
end

mail.deliver!

The output I get is:

/usr/shared/name/ruby/file2.html
email2.rb:19: warning: instance variable @final_file not initialized
email2.rb:19:in `block in <main>': undefined method `path' for nil:NilClass (NoMethodError)
        from /usr/lib64/ruby/gems/1.9.1/gems/mail-2.5.4/lib/mail/message.rb:132:in `instance_eval'
        from /usr/lib64/ruby/gems/1.9.1/gems/mail-2.5.4/lib/mail/message.rb:132:in `initialize'
        from /usr/lib64/ruby/gems/1.9.1/gems/mail-2.5.4/lib/mail/mail.rb:50:in `new'
        from /usr/lib64/ruby/gems/1.9.1/gems/mail-2.5.4/lib/mail/mail.rb:50:in `new'
        from email2.rb:14:in `<main>'

So at first the file is created and filled with some text, then the puts command prints the file's path, as expected, but for some reason, after the mail variable declaration, the @final_file variable is set to nil, I can't figure out what is happening here, it seems like a garbage collector is getting rid of that variable.

Was it helpful?

Solution

What you see is because the block inside Mail.new is in a different scope than @final_file, as the code calls:

  if block_given?
    instance_eval(&block)
  end

This means that the block is run from within Mail's scope. This enables you to call methods like from and to.

As far as I understand from the code, you can pass arguments to the initializer like this:

mail = Mail.new(final_path: @final_file.path) do
      from 'me@somehost.net'
        to 'mymail@mydomain.com'
   subject 'Some subject'
      body "here the body"
  add_file :filename => 'filename.html', :content => File.read(self[:final_path])
end

(this is untested, so the syntax needed might be a little different).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top