【问题标题】:Why can't the Mail block see my variable?为什么邮件块看不到我的变量?
【发布时间】:2016-01-24 19:59:17
【问题描述】:

我是 Ruby 新手,想知道为什么在简单的 Sinatra 应用程序中使用“邮件”gem 在这种情况下会出错:

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

但是这很好用:

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

我怀疑这与块范围和我对它的误解有关。

【问题讨论】:

  • 你确定你的问题是实例变量,而不是params[:email]?你试过输出吗?一个局部变量在这里也应该足够了,因为这个块无论如何都是一个闭包。

标签: ruby sinatra


【解决方案1】:

正如 Julik 所说,Mail#delivery 使用 #instance_exec 执行您的块,它只是在运行块时更改 self(否则您将无法在块内调用方法 #to#from) .

你真正能做的是使用块是闭包的事实。这意味着它“记住”了它周围的所有局部变量。

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

再次,简短地说:

  • 实例变量和方法调用依赖于self
  • #instance_exec更改self
  • 局部变量不依赖于self,并且被块记住,因为块是闭包

【讨论】:

  • 还有一点补充:这种行为不依赖于 Ruby 版本。所以我会从问题标题中删除单词“Ruby 1.9”,这可能会造成混淆。
  • 所以我的错误是使用实例变量?我使用了一个实例变量,因为我希望该数据在 ERB 模板中可用,并且 - 如果我没记错的话 - 这意味着它需要是一个实例变量。我可以通过声明一个局部变量或使用 The Tin Man 建议的方法之一来解决这个问题。谢谢,到目前为止很喜欢 Ruby,但还有很多东西要学:)
【解决方案2】:

如果您进一步阅读Mail 的文档,您会发现一个不错的替代解决方案。而不是使用:

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

可以使用Mail的new()方法,传入参数,忽略块:

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

或备用哈希元素定义:

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

在 pry 或 irb 你会看到:

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>>

new 方法有多种可供您使用。这也来自文档,可能效果更好:

附带说明,您还可以通过直接创建 Mail::Message 对象然后通过字符串、符号或直接方法调用传入值来创建新电子邮件。有关详细信息,请参阅邮件::消息。

 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'

后跟mail.deliver!

还要注意,在前面的示例中,有多种方法可以访问邮件信封中的各种标头。这是一个灵活的 gem,似乎经过深思熟虑,并且很好地遵循了 Ruby 的方式。

【讨论】:

  • 感谢您为我做这项研究 :) 我一直在仔细查看 GitHub 文档,而不是去查看 RubyGems 文档。那里吸取的教训。再次感谢。
【解决方案3】:

我认为这是因为 Mail gem 在后台使用了instance_execinstance_exec 使用来自被调用对象的实例变量,而不是来自调用者的实例变量。我要做的是在 Mail gem 中找到一个不使用实例技巧但将显式配置对象传递给块的方法,然后从那里继续。留了几根白发。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-28
    • 2015-03-27
    • 2019-02-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多