Why can't the Mail block see my variable?

I am new to Ruby and wondering why I get an error in this situation using the mail pearl in a simple Sinatra application:

post "/email/send" do @recipient = params[:email] Mail.deliver do to @recipient # throws error as this is undefined from ' server@domain.com ' subject 'testing sendmail' body 'testing sendmail' end erb :email_sent end 

This works fine:

 post "/email/send" do Mail.deliver do to ' me@domain.com ' from ' server@domain.com ' subject 'testing sendmail' body 'testing sendmail' end erb :email_sent end 

I suspect this is due to the block area and my misunderstanding.

+7
source share
3 answers

As Yulik says, Mail#delivery executes your block using #instance_exec , which simply changes self when the block starts (you cannot call the #to and #from inside the block otherwise).

What you can really do here is use the fact that blocks are closures . This means that it “remembers” all the local variables around it.

 recipient = params[:email] Mail.deliver do to recipient # 'recipient' is a local variable, not a method, not an instance variable ... end 

In short:

  • instance variables and method calls depend on self
  • #instance_exec modifies self ;
  • Local variables are independent of self and are stored in blocks because blocks are closed .
+14
source

If you read the documents for Mail , you will find a good alternative solution that will work. Instead of using:

 Mail.deliver do to @recipient # throws error as this is undefined from ' server@domain.com ' subject 'testing sendmail' body 'testing sendmail' end 

you can use the Mail new() method by passing parameters and ignoring the block:

 Mail.new( to: @recipient, from: ' server@domain.com ', subject: 'testing sendmail', body: 'testing sendmail' ).deliver! 

or alternative hash element definitions:

 Mail.new( :to => @recipient, :from => ' server@domain.com ', :subject => 'testing sendmail', :body => 'testing sendmail' ).deliver! 

In the folder or irb you will see:

 pry(main)> Mail.new( pry(main)* to: ' me@domain.com ', pry(main)* from: ' me@ ' << `hostname`.strip, pry(main)* subject: 'test mail gem', pry(main)* body: 'this is only a test' pry(main)* ).deliver! => #<Mail::Message:59273220, Multipart: false, Headers: <Date: Fri, 28 Oct 2011 09:01:14 -0700>, <From: me@myhost.domain.com >, <To: me@domain.com >, <Message-ID: < 4eaad1cab65ce_579b2e8e6c42976d@myhost.domain.com >>, <Subject: test mail gem>, <Mime-Version: 1.0>, <Content-Type: text/plain>, <Content-Transfer-Encoding: 7bit>> 

The new method has several options that you can use. This is also from the docs and may work better:

As an additional note, you can also create a new email by creating the Mail :: Message object directly, and then passing the values ​​through strings, characters, or direct method calls. See Mail :: Message for more information.

  mail = Mail.new mail.to = ' mikel@test.lindsaar.net ' mail[:from] = ' bob@test.lindsaar.net ' mail['subject'] = 'This is an email' mail.body = 'This is the body' 

then mail.deliver! .

Also note that in the previous example, there are several ways to access the various headers in the message envelope. This is a flexible stone that seems to be well thought out and beautifully follows the ruby ​​path.

+8
source

I think because the zip stone uses instance_exec under the hood. instance_exec uses instance_exec variables from the called object, not from the caller. What I would do is to find a method in the mail engine that does not use instance tricks, but passes an explicit configuration object to the block and comes from there. Makes some gray hair.

+3
source

All Articles