2011-10-28 3 views
7

Tôi mới đến Ruby và tự hỏi tại sao tôi nhận được một lỗi trong tình huống này bằng cách sử dụng 'mail' viên ngọc trong một ứng dụng Sinatra đơn giản:Tại sao khối thư không thể thấy biến của tôi?

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 

này tuy nhiên hoạt động tốt:

post "/email/send" do 

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

    erb :email_sent 

end 

Tôi nghi ngờ đây là một cái gì đó để làm với phạm vi khối và sự hiểu lầm của tôi về nó.

+1

Bạn có chắc chắn rằng vấn đề của bạn là với cá thể var, chứ không phải với 'params [: email]'? Bạn đã thử xuất nó chưa? Ngoài ra, một biến cục bộ phải đủ ở đây, vì khối đó là một đóng cửa. –

Trả lời

14

Như Julik nói, Mail#delivery thực hiện khối bằng cách sử dụng #instance_exec, mà chỉ đơn giản thay đổi self trong khi chạy một khối (bạn sẽ không thể gọi các phương thức #to#from bên trong các khối khác).

Điều bạn thực sự có thể làm ở đây là sử dụng thực tế rằng các khối là đóng cửa. Có nghĩa là nó "nhớ" tất cả các biến cục bộ xung quanh nó.

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

Một lần nữa, một thời gian ngắn:

  • dụ các biến và các cuộc gọi phương pháp phụ thuộc vào self
  • #instance_exec thay đổi self;
  • biến cục bộ không phụ thuộc vào self và được ghi nhớ bởi các khối vì các khối là đóng cửa.
+0

Và một chút bổ sung: hành vi này không phụ thuộc vào phiên bản Ruby. Vì vậy, tôi muốn xóa các từ "Ruby 1.9" khỏi tiêu đề câu hỏi, nó có thể gây nhầm lẫn. –

+0

Vì vậy, sai lầm của tôi đã được sử dụng một biến dụ? Tôi đã sử dụng một biến mẫu vì tôi muốn dữ liệu đó có sẵn trong mẫu ERB và - nếu tôi không nhầm - điều đó có nghĩa là nó cần phải là một cá thể var. Tôi có thể làm việc xung quanh đó, hoặc bằng cách tuyên bố một biến địa phương, hoặc sử dụng một trong các phương pháp tiếp cận Tin Man cho thấy. Cảm ơn, Ruby yêu thương cho đến nay, nhưng rất nhiều để tìm hiểu :) –

3

Tôi nghĩ rằng đó là vì đá quý Mail sử dụng instance_exec dưới mui xe. instance_exec sử dụng các biến mẫu từ đối tượng mà nó đang được gọi và không phải từ người gọi. Những gì tôi muốn làm là tìm một phương thức trong Mail gem mà không sử dụng các thủ thuật instance nhưng chuyển một đối tượng cấu hình rõ ràng tới khối đó và tiến hành từ đó. Spares một vài sợi lông màu xám.

8

Nếu bạn đọc qua tài liệu cho Mail hơn nữa, bạn sẽ tìm thấy giải pháp thay thế tốt đẹp sẽ hoạt động. Thay vì sử dụng:

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

bạn có thể sử dụng phương pháp new() Mail, đi qua trong các thông số, và bỏ qua những khối:

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

hoặc các định nghĩa yếu tố băm thay thế:

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

Trong tọc mạch hoặc irb bạn sẽ thấy:

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! 
=> #<Mail::Message:59273220, Multipart: false, Headers: <Date: Fri, 28 Oct 2011 09:01:14 -0700>, <From: [email protected]>, <To: [email protected]>, <Message-ID: <[email protected]>>, <Subject: test mail gem>, <Mime-Version: 1.0>, <Content-Type: text/plain>, <Content-Transfer-Encoding: 7bit>> 

Phương thức new có một số biến thể bạn có thể sử dụng. Đây cũng là tài liệu và có thể hoạt động tốt hơn:

Lưu ý rằng bạn cũng có thể tạo email mới thông qua tạo thư :: Đối tượng tin nhắn trực tiếp rồi chuyển giá trị qua chuỗi, ký hiệu hoặc trực tiếp các cuộc gọi phương thức. Xem thư :: Tin nhắn để biết thêm thông tin.

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

Tiếp theo mail.deliver!.

Cũng lưu ý, trong ví dụ trước, có nhiều cách để truy cập vào các tiêu đề khác nhau trong phong bì thư. Đó là một viên ngọc linh hoạt dường như được suy nghĩ tốt và độc đáo theo cách Ruby.

+0

Cảm ơn bạn đã làm nghiên cứu cho tôi :) Tôi đã nhìn quá chặt chẽ vào các tài liệu GitHub, thay vì đi xem xét những cái RubyGems. Bài học kinh nghiệm ở đó. Cảm ơn một lần nữa. –