メールブロックが私の変数を見ることができないのはなぜですか?

私はRubyの初心者で、単純なSinatraアプリで 'mail'という宝石を使ってこのような状況でエラーが発生する理由を疑問に思っています。

post "/email/send" do

  @recipient = params[:email]

  Mail.deliver do 
    to @recipient # throws error as this is undefined
    from '[email protected]'
    subject 'testing sendmail'
    body 'testing sendmail'
  end

  erb :email_sent

end

しかしこれはうまくいく:

post "/email/send" do

  Mail.deliver do 
    to '[email protected]'
    from '[email protected]'
    subject 'testing sendmail'
    body 'testing sendmail'
  end

  erb :email_sent

end

私はこれがブロックの範囲と私の誤解と関係があると考えています。

8
あなたの問題は、 params [:email] ではなく、インスタンスvarにあるのでしょうか?あなたはそれを出力しようとしましたか?とにかくブロックがクロージャなので、ローカル変数もここで十分でなければなりません。
追加された 著者 Michael Kohl,

3 答え

Julikが言うように、 #instance_exec を使用してブロックを実行すると、 Mail#delivery はブロックを実行している間に self それ以外の場合はブロック内のメソッド #to および #from を呼び出します)。

ここで実際にできることは、ブロックがクロージャであるという事実を使用することです。これは、その周りのすべてのローカル変数を「覚えている」ことを意味します。

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

繰り返しますが、

  • instance variables and method calls depend on self
  • #instance_exec changes the self;
  • local variables don't depend on self and are remembered by blocks because blocks are closures.
14
追加された
だから私の間違いはインスタンス変数を使用していた?私はインスタンス変数を使用しました。なぜなら、そのデータをERBテンプレートで利用できるようにしたいと思ったからです。誤解されていなければ、インスタンス変数にする必要があります。ローカル変数を宣言するか、Tin Manが示唆するアプローチのいずれかを使用して、この問題を回避できます。おかげで、これまでRubyを愛していましたが、学ぶべきことはたくさんあります:)
追加された 著者 Ciaran Archer,
そして少し追加されました。この動作はRubyのバージョンに依存しません。だから私は質問のタイトルから "Ruby 1.9"という言葉を削除すると、混乱するかもしれません。
追加された 著者 Daniel Vartanov,

Mail のドキュメントを読んでください。さらに優れた代替ソリューションが見つかるでしょう。使用するよりも:

Mail.deliver do 
  to @recipient # throws error as this is undefined
  from '[email protected]'
  subject 'testing sendmail'
  body 'testing sendmail'
end

Mailの new()メソッドを使用してパラメータを渡し、ブロックを無視することができます。

Mail.new(
  to:      @recipient,
  from:    '[email protected]',
  subject: 'testing sendmail',
  body:    'testing sendmail'
).deliver!

または代替のハッシュ要素の定義:

Mail.new(
  :to      => @recipient,
  :from    => '[email protected]',
  :subject => 'testing sendmail',
  :body    => 'testing sendmail'
).deliver!

pry、またはirbであなたは見るでしょう:

pry(main)> Mail.new(
pry(main)* to: '[email protected]',
pry(main)* from: '[email protected]' << `hostname`.strip,
pry(main)* subject: 'test mail gem',
pry(main)* body: 'this is only a test'
pry(main)* ).deliver!
=> #, , , >, , , , >

new メソッドにはいくつかのバリエーションがありますあなたは使うことができます。これはドキュメントのものでもあり、うまくいくかもしれません:

補足として、Mail :: Messageオブジェクトを直接作成し、文字列、シンボル、または直接メソッド呼び出しを介して値を渡すことで、新しい電子メールを作成することもできます。詳細については、Mail :: Messageを参照してください。

 mail = Mail.new
 mail.to = '[email protected]'
 mail[:from] = '[email protected]'
 mail['subject'] = 'This is an email'
 mail.body = 'This is the body'

mail.deliver!が続きます。

また、前の例では、メッセージエンベロープのさまざまなヘッダーにアクセスする複数の方法があることにも注意してください。それはよく考えられているように見え、Rubyのやり方にしっかりと従っている柔軟な宝石です。

8
追加された
私のためにその研究をしてくれてありがとう:) RubyGemsのものを見るのではなく、GitHubのドキュメントをあまりにも綿密に見ていただろう。レッスンはそこで学んだ。再度、感謝します。
追加された 著者 Ciaran Archer,

メール宝石がボンネットの下で instance_exec を使用しているからだと思います。 instance_exec は、呼び出し元からではなく呼び出されているオブジェクトからインスタンス変数を使用します。私がやることは、インスタンストリックを使用せずに明示的な設定オブジェクトをブロックに渡してそこから処理するMail Gemのメソッドを見つけることです。いくつかの灰色の毛を惜しまない。

3
追加された